"Fossies" - the Fresh Open Source Software Archive

Member "pdns-auth-4.2.0/pdns/filterpo.cc" (27 Aug 2019, 15499 Bytes) of package /linux/misc/dns/pdns-auth-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "filterpo.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.13_vs_4.2.0.

    1 /*
    2  * This file is part of PowerDNS or dnsdist.
    3  * Copyright -- PowerDNS.COM B.V. and its contributors
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of version 2 of the GNU General Public License as
    7  * published by the Free Software Foundation.
    8  *
    9  * In addition, for the avoidance of any doubt, permission is granted to
   10  * link this program with OpenSSL and to (re)distribute the binaries
   11  * produced as the result of such linking.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   21  */
   22 
   23 #include <cinttypes>
   24 #include <iostream>
   25 
   26 #include "filterpo.hh"
   27 #include "namespaces.hh"
   28 #include "dnsrecords.hh"
   29 
   30 DNSFilterEngine::DNSFilterEngine()
   31 {
   32 }
   33 
   34 bool DNSFilterEngine::Zone::findQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
   35 {
   36   return findNamedPolicy(d_qpolName, qname, pol);
   37 }
   38 
   39 bool DNSFilterEngine::Zone::findNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
   40 {
   41   return findNamedPolicy(d_propolName, qname, pol);
   42 }
   43 
   44 bool DNSFilterEngine::Zone::findNSIPPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const
   45 {
   46   if (const auto fnd = d_propolNSAddr.lookup(addr)) {
   47     pol = fnd->second;
   48     return true;
   49   }
   50   return false;
   51 }
   52 
   53 bool DNSFilterEngine::Zone::findResponsePolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const
   54 {
   55   if (const auto fnd = d_postpolAddr.lookup(addr)) {
   56     pol = fnd->second;
   57     return true;
   58   }
   59   return false;
   60 }
   61 
   62 bool DNSFilterEngine::Zone::findClientPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const
   63 {
   64   if (const auto fnd = d_qpolAddr.lookup(addr)) {
   65     pol = fnd->second;
   66     return true;
   67   }
   68   return false;
   69 }
   70 
   71 bool DNSFilterEngine::Zone::findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol) const
   72 {
   73   /* for www.powerdns.com, we need to check:
   74      www.powerdns.com.
   75        *.powerdns.com.
   76                 *.com.
   77                     *.
   78    */
   79 
   80   std::unordered_map<DNSName, DNSFilterEngine::Policy>::const_iterator iter;
   81   iter = polmap.find(qname);
   82 
   83   if(iter != polmap.end()) {
   84     pol=iter->second;
   85     return true;
   86   }
   87 
   88   DNSName s(qname);
   89   while(s.chopOff()){
   90     iter = polmap.find(g_wildcarddnsname+s);
   91     if(iter != polmap.end()) {
   92       pol=iter->second;
   93       return true;
   94     }
   95   }
   96   return false;
   97 }
   98 
   99 DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const
  100 {
  101   //  cout<<"Got question for nameserver name "<<qname<<endl;
  102   Policy pol;
  103   for(const auto& z : d_zones) {
  104     const auto zoneName = z->getName();
  105     if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
  106       continue;
  107     }
  108 
  109     if(z->findNSPolicy(qname, pol)) {
  110       //      cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
  111       return pol;
  112     }
  113   }
  114   return pol;
  115 }
  116 
  117 DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const
  118 {
  119   Policy pol;
  120   //  cout<<"Got question for nameserver IP "<<address.toString()<<endl;
  121   for(const auto& z : d_zones) {
  122     const auto zoneName = z->getName();
  123     if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
  124       continue;
  125     }
  126 
  127     if(z->findNSIPPolicy(address, pol)) {
  128       //      cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
  129       return pol;
  130     }
  131   }
  132   return pol;
  133 }
  134 
  135 DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies) const
  136 {
  137   //  cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
  138   Policy pol;
  139   for(const auto& z : d_zones) {
  140     const auto zoneName = z->getName();
  141     if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
  142       continue;
  143     }
  144 
  145     if(z->findQNamePolicy(qname, pol)) {
  146       //      cerr<<"Had a hit on the name of the query"<<endl;
  147       return pol;
  148     }
  149 
  150     if(z->findClientPolicy(ca, pol)) {
  151       //    cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
  152       return pol;
  153     }
  154   }
  155 
  156   return pol;
  157 }
  158 
  159 DNSFilterEngine::Policy DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const
  160 {
  161   Policy pol;
  162   ComboAddress ca;
  163   for(const auto& r : records) {
  164     if(r.d_place != DNSResourceRecord::ANSWER)
  165       continue;
  166     if(r.d_type == QType::A) {
  167       if (auto rec = getRR<ARecordContent>(r)) {
  168         ca = rec->getCA();
  169       }
  170     }
  171     else if(r.d_type == QType::AAAA) {
  172       if (auto rec = getRR<AAAARecordContent>(r)) {
  173         ca = rec->getCA();
  174       }
  175     }
  176     else
  177       continue;
  178 
  179     for(const auto& z : d_zones) {
  180       const auto zoneName = z->getName();
  181       if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
  182         continue;
  183       }
  184 
  185       if(z->findResponsePolicy(ca, pol)) {
  186     return pol;
  187       }
  188     }
  189   }
  190   return pol;
  191 }
  192 
  193 void DNSFilterEngine::assureZones(size_t zone)
  194 {
  195   if(d_zones.size() <= zone)
  196     d_zones.resize(zone+1);
  197 }
  198 
  199 void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol)
  200 {
  201   pol.d_name = d_name;
  202   pol.d_type = PolicyType::ClientIP;
  203   d_qpolAddr.insert(nm).second=std::move(pol);
  204 }
  205 
  206 void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol)
  207 {
  208   pol.d_name = d_name;
  209   pol.d_type = PolicyType::ResponseIP;
  210   d_postpolAddr.insert(nm).second=std::move(pol);
  211 }
  212 
  213 void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
  214 {
  215   auto it = d_qpolName.find(n);
  216 
  217   if (it != d_qpolName.end()) {
  218     auto& existingPol = it->second;
  219 
  220     if (pol.d_kind != PolicyKind::Custom && !ignoreDuplicate) {
  221       throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(pol.d_kind) + " but a policy of kind " + getKindToString(existingPol.d_kind) + " already exists for the following QName: " + n.toLogString());
  222     }
  223 
  224     if (existingPol.d_kind != PolicyKind::Custom && ignoreDuplicate) {
  225       throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but there was already an existing policy for the following QName: " + n.toLogString());
  226     }
  227 
  228     existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size());
  229 
  230     std::move(pol.d_custom.begin(), pol.d_custom.end(), std::back_inserter(existingPol.d_custom));
  231   }
  232   else {
  233     auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second;
  234     qpol.d_name = d_name;
  235     qpol.d_type = PolicyType::QName;
  236   }
  237 }
  238 
  239 void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol)
  240 {
  241   pol.d_name = d_name;
  242   pol.d_type = PolicyType::NSDName;
  243   d_propolName.insert({n, std::move(pol)});
  244 }
  245 
  246 void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol)
  247 {
  248   pol.d_name = d_name;
  249   pol.d_type = PolicyType::NSIP;
  250   d_propolNSAddr.insert(nm).second = std::move(pol);
  251 }
  252 
  253 bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol)
  254 {
  255   d_qpolAddr.erase(nm);
  256   return true;
  257 }
  258 
  259 bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol)
  260 {
  261   d_postpolAddr.erase(nm);
  262   return true;
  263 }
  264 
  265 bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol)
  266 {
  267   auto it = d_qpolName.find(n);
  268   if (it == d_qpolName.end()) {
  269     return false;
  270   }
  271 
  272   auto& existing = it->second;
  273   if (existing.d_kind != DNSFilterEngine::PolicyKind::Custom) {
  274     d_qpolName.erase(it);
  275     return true;
  276   }
  277 
  278   /* for custom types, we might have more than one type,
  279      and then we need to remove only the right ones. */
  280   if (existing.d_custom.size() <= 1) {
  281     d_qpolName.erase(it);
  282     return true;
  283   }
  284 
  285   bool result = false;
  286   for (auto& toRemove : pol.d_custom) {
  287     for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) {
  288       if (**it == *toRemove) {
  289         existing.d_custom.erase(it);
  290         result = true;
  291         break;
  292       }
  293     }
  294   }
  295 
  296   return result;
  297 }
  298 
  299 bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, const Policy& pol)
  300 {
  301   d_propolName.erase(n); // XXX verify policy matched? =pol;
  302   return true;
  303 }
  304 
  305 bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, const Policy& pol)
  306 {
  307   d_propolNSAddr.erase(nm);
  308   return true;
  309 }
  310 
  311 DNSRecord DNSFilterEngine::Policy::getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const
  312 {
  313   DNSRecord dr;
  314   dr.d_name = qname;
  315   dr.d_type = custom->getType();
  316   dr.d_ttl = d_ttl;
  317   dr.d_class = QClass::IN;
  318   dr.d_place = DNSResourceRecord::ANSWER;
  319   dr.d_content = custom;
  320 
  321   if (dr.d_type == QType::CNAME) {
  322     const auto content = std::dynamic_pointer_cast<CNAMERecordContent>(custom);
  323     if (content) {
  324       DNSName target = content->getTarget();
  325       if (target.isWildcard()) {
  326         target.chopOff();
  327         dr.d_content = std::make_shared<CNAMERecordContent>(qname + target);
  328       }
  329     }
  330   }
  331 
  332   return dr;
  333 }
  334 
  335 std::vector<DNSRecord> DNSFilterEngine::Policy::getCustomRecords(const DNSName& qname, uint16_t qtype) const
  336 {
  337   if (d_kind != PolicyKind::Custom) {
  338     throw std::runtime_error("Asking for a custom record from a filtering policy of a non-custom type");
  339   }
  340 
  341   std::vector<DNSRecord> result;
  342 
  343   for (const auto& custom : d_custom) {
  344     if (qtype != QType::ANY && qtype != custom->getType() && custom->getType() != QType::CNAME) {
  345       continue;
  346     }
  347 
  348     DNSRecord dr;
  349     dr.d_name = qname;
  350     dr.d_type = custom->getType();
  351     dr.d_ttl = d_ttl;
  352     dr.d_class = QClass::IN;
  353     dr.d_place = DNSResourceRecord::ANSWER;
  354     dr.d_content = custom;
  355 
  356     if (dr.d_type == QType::CNAME) {
  357       const auto content = std::dynamic_pointer_cast<CNAMERecordContent>(custom);
  358       if (content) {
  359         DNSName target = content->getTarget();
  360         if (target.isWildcard()) {
  361           target.chopOff();
  362           dr.d_content = std::make_shared<CNAMERecordContent>(qname + target);
  363         }
  364       }
  365     }
  366 
  367     result.emplace_back(getRecordFromCustom(qname, custom));
  368   }
  369 
  370   return result;
  371 }
  372 
  373 std::string DNSFilterEngine::getKindToString(DNSFilterEngine::PolicyKind kind)
  374 {
  375   static const DNSName drop("rpz-drop."), truncate("rpz-tcp-only."), noaction("rpz-passthru.");
  376   static const DNSName rpzClientIP("rpz-client-ip"), rpzIP("rpz-ip"),
  377     rpzNSDname("rpz-nsdname"), rpzNSIP("rpz-nsip.");
  378   static const std::string rpzPrefix("rpz-");
  379 
  380   switch(kind) {
  381   case DNSFilterEngine::PolicyKind::NoAction:
  382     return noaction.toString();
  383   case DNSFilterEngine::PolicyKind::Drop:
  384     return drop.toString();
  385   case DNSFilterEngine::PolicyKind::NXDOMAIN:
  386     return g_rootdnsname.toString();
  387   case PolicyKind::NODATA:
  388     return g_wildcarddnsname.toString();
  389   case DNSFilterEngine::PolicyKind::Truncate:
  390     return truncate.toString();
  391   default:
  392     throw std::runtime_error("Unexpected DNSFilterEngine::Policy kind");
  393   }
  394 }
  395 
  396 std::string DNSFilterEngine::getTypeToString(DNSFilterEngine::PolicyType type)
  397 {
  398   switch(type) {
  399   case DNSFilterEngine::PolicyType::None:
  400     return "none";
  401   case DNSFilterEngine::PolicyType::QName:
  402     return "QName";
  403   case DNSFilterEngine::PolicyType::ClientIP:
  404     return "Client IP";
  405   case DNSFilterEngine::PolicyType::ResponseIP:
  406     return "Response IP";
  407   case DNSFilterEngine::PolicyType::NSDName:
  408     return "Name Server Name";
  409   case DNSFilterEngine::PolicyType::NSIP:
  410     return "Name Server IP";
  411   default:
  412     throw std::runtime_error("Unexpected DNSFilterEngine::Policy type");
  413   }
  414 }
  415 
  416 std::vector<DNSRecord> DNSFilterEngine::Policy::getRecords(const DNSName& qname) const
  417 {
  418   std::vector<DNSRecord> result;
  419 
  420   if (d_kind == PolicyKind::Custom) {
  421     result = getCustomRecords(qname, QType::ANY);
  422   }
  423   else {
  424     DNSRecord dr;
  425     dr.d_name = qname;
  426     dr.d_ttl = static_cast<uint32_t>(d_ttl);
  427     dr.d_type = QType::CNAME;
  428     dr.d_class = QClass::IN;
  429     dr.d_content = DNSRecordContent::mastermake(QType::CNAME, QClass::IN, getKindToString(d_kind));
  430     result.push_back(std::move(dr));
  431   }
  432 
  433   return result;
  434 }
  435 
  436 void DNSFilterEngine::Zone::dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol) const
  437 {
  438   auto records = pol.getRecords(name);
  439   for (const auto& dr : records) {
  440     fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str());
  441   }
  442 }
  443 
  444 DNSName DNSFilterEngine::Zone::maskToRPZ(const Netmask& nm)
  445 {
  446   int bits = nm.getBits();
  447   DNSName res(std::to_string(bits));
  448   const auto& addr = nm.getNetwork();
  449 
  450   if (addr.isIPv4()) {
  451     const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&addr.sin4.sin_addr.s_addr);
  452     res += DNSName(std::to_string(bytes[3]) + "." + std::to_string(bytes[2]) + "." + std::to_string(bytes[1]) + "." + std::to_string(bytes[0]));
  453   }
  454   else {
  455     DNSName temp;
  456     const auto str = addr.toString();
  457     const auto len = str.size();
  458     std::string::size_type begin = 0;
  459 
  460     while (begin < len) {
  461       std::string::size_type end = str.find(":", begin);
  462       std::string sub;
  463       if (end != string::npos) {
  464         sub = str.substr(begin, end - begin);
  465       }
  466       else {
  467         sub = str.substr(begin);
  468       }
  469 
  470       if (sub.empty()) {
  471         temp = DNSName("zz") + temp;
  472       }
  473       else {
  474         temp = DNSName(sub) + temp;
  475       }
  476 
  477       if (end == string::npos) {
  478         break;
  479       }
  480       begin = end + 1;
  481     }
  482     res += temp;
  483   }
  484 
  485   return res;
  486 }
  487 
  488 
  489 void DNSFilterEngine::Zone::dumpAddrPolicy(FILE* fp, const Netmask& nm, const DNSName& name, const Policy& pol) const
  490 {
  491   DNSName full = maskToRPZ(nm);
  492   full += name;
  493 
  494   auto records = pol.getRecords(full);
  495   for (const auto& dr : records) {
  496     fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str());
  497   }
  498 }
  499 
  500 void DNSFilterEngine::Zone::dump(FILE* fp) const
  501 {
  502   /* fake the SOA record */
  503   auto soa = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "fake.RPZ. hostmaster.fake.RPZ. " + std::to_string(d_serial) + " " + std::to_string(d_refresh) + " 600 3600000 604800");
  504   fprintf(fp, "%s IN SOA %s\n", d_domain.toString().c_str(), soa->getZoneRepresentation().c_str());
  505 
  506   for (const auto& pair : d_qpolName) {
  507     dumpNamedPolicy(fp, pair.first + d_domain, pair.second);
  508   }
  509 
  510   for (const auto& pair : d_propolName) {
  511     dumpNamedPolicy(fp, pair.first + DNSName("rpz-nsdname.") + d_domain, pair.second);
  512   }
  513 
  514   for (const auto pair : d_qpolAddr) {
  515     dumpAddrPolicy(fp, pair->first, DNSName("rpz-client-ip.") + d_domain, pair->second);
  516   }
  517 
  518   for (const auto pair : d_propolNSAddr) {
  519     dumpAddrPolicy(fp, pair->first, DNSName("rpz-nsip.") + d_domain, pair->second);
  520   }
  521 
  522   for (const auto pair : d_postpolAddr) {
  523     dumpAddrPolicy(fp, pair->first, DNSName("rpz-ip.") + d_domain, pair->second);
  524   }
  525 }