]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #8096 from mind04/pdns-notify-db-queries
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Fri, 14 Feb 2020 18:22:32 +0000 (19:22 +0100)
committerGitHub <noreply@github.com>
Fri, 14 Feb 2020 18:22:32 +0000 (19:22 +0100)
auth: do not update identical notified serials

23 files changed:
.circleci/config.yml
build-scripts/travis.sh
docs/manpages/pdns_control.1.rst
docs/manpages/pdns_server.1.rst
ext/yahttp/yahttp/reqresp.hpp
ext/yahttp/yahttp/url.hpp
pdns/Makefile.am
pdns/arguments.cc
pdns/arguments.hh
pdns/common_startup.cc
pdns/dnsbackend.cc
pdns/dynhandler.cc
pdns/filterpo.cc
pdns/filterpo.hh
pdns/nsecrecords.cc
pdns/pdns_recursor.cc
pdns/receiver.cc
pdns/recursordist/test-filterpo_cc.cc
pdns/syncres.cc
pdns/tcpreceiver.cc
pdns/test-dnsrecords_cc.cc
regression-tests.recursor-dnssec/test_Protobuf.py
regression-tests.recursor-dnssec/test_RPZ.py

index 945022d48efe6de98d6754a32a5208a872acfb45..a5e6b6fc45ccf68c3689b36a9c9c9f5df543be4b 100644 (file)
@@ -614,7 +614,7 @@ jobs:
             TDS_Version=7.1
             __EOF__
       - run:
-          command: cat /usr/share/tdsodbc/odbcinst.ini >> /etc/odbcinst.ini
+          command: cat /usr/share/tdsodbc/odbcinst.ini <(echo Threading=1) >> /etc/odbcinst.ini
       - run:
           name: create database
           command: echo 'create database pdns' | isql -v pdns-mssql-docker-nodb sa SAsa12%%
index 35fbfebaa27a5352860c6fd828bcde87c3836026..7b0f5314cc1809f8c67d181081f0e96d30900574 100755 (executable)
@@ -511,6 +511,8 @@ test_auth() {
   # run "./timestamp ./start-test-stop 5300 gmysql-nsec3-optout-both"
   run "./timestamp ./start-test-stop 5300 gmysql-nsec3-narrow"
 
+  run "sudo perl -i -pe 's/\]/]\nThreading=1/g' /etc/odbcinst.ini"
+  run "cat /etc/odbcinst.ini"
   run "export GODBC_SQLITE3_DSN=pdns-sqlite3-1"
   run "./timestamp ./start-test-stop 5300 godbc_sqlite3-nsec3"
 
index 04fc888261f8ad9491471e744e2246e130a3a98a..8c1b8d1ff07272eefa34aeb42e8c2573c772128e 100644 (file)
@@ -63,13 +63,10 @@ ccounts
 
 Show the content of the cache.
 
-current-config
-^^^^^^^^^^^^^^
+current-config [diff]
+^^^^^^^^^^^^^^^^^^^^^
 
-Show the currently running configuration. The output has the same
-format as ``pdns_server --config``. You'll notice that all the
-configuration values are uncommented. This is because PowerDNS
-simply has values, and the default isn't known at runtime.
+Show the currently running configuration. The output has the same format as ``pdns_server --config``. With the diff option only modified options are included in the output.
 
 cycle
 ^^^^^
index 4f0ea24c19eb3cfa6233210c2ca40c97c61d4333..ceb86f12a6874f48052e43ebdfe56b7d1aa7948f 100644 (file)
@@ -27,6 +27,10 @@ See the online documentation for all options
 --control-console       Run the server in a special monitor mode. This enables detailed
                         logging and exposes the raw control socket.
 --loglevel=<LEVEL>      Set the logging level.
+--config                Show the currently configuration. There are three optional values:
+                        --config=default show the default configuration.
+                        --config=diff    show modified options in the curent configuration.
+                        --config=check   parse the current configuration, with error checking.
 --help                  To view more options that are available use this program.
 
 See also
index d9e7a197282c5a257264f0f51e0c9c965e5ca07f..1d310923a9c3a8f6c8e84d243e3ddc841545f90f 100644 (file)
@@ -249,7 +249,7 @@ public:
     }
     void setup(const std::string& method_, const std::string& url_) {
       this->url.parse(url_);
-      this->headers["host"] = this->url.host;
+      this->headers["host"] = this->url.host.find(":") == std::string::npos ? this->url.host : "[" + this->url.host + "]";
       this->method = method_;
       std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
       this->headers["user-agent"] = "YaHTTP v1.0";
index 0002c29339bc0b3702366d8be2f38b6643f4559a..fbdd48de86509e5595c51a094189e422c27d5879 100644 (file)
@@ -38,7 +38,18 @@ namespace YaHTTP {
              host = url.substr(pos, pos1-pos);
              pos = pos1;
           }
-          if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
+          if (host.at(0) == '[') { // IPv6
+            if ((pos1 = host.find_first_of("]")) == std::string::npos) {
+              // incomplete address
+              return false;
+            }
+            size_t pos2;
+            if ((pos2 = host.find_first_of(":", pos1)) != std::string::npos) {
+              std::istringstream tmp(host.substr(pos2 + 1));
+              tmp >> port;
+            }
+            host = host.substr(1, pos1 - 1);
+          } else if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
              std::istringstream tmp(host.substr(pos1+1));
              tmp >> port;
              host = host.substr(0, pos1);
index 9de4b641d4c475fa9b292e0c3d7374bef2520c04..35256e084efbcfba2e28bc1c718cb093f34ee0a0 100644 (file)
@@ -1250,7 +1250,7 @@ endif
 endif
 
 pdns.conf-dist: pdns_server
-       $(AM_V_GEN)./pdns_server --no-config --config 2>/dev/null > $@
+       $(AM_V_GEN)./pdns_server --no-config --config=default 2>/dev/null > $@
 
 testrunner_SOURCES = \
        arguments.cc \
index b41bdbcddfbb6067673f52b472ced93a6b601807..2c1ad265748998ddcefaeac6a9943b8687968e42 100644 (file)
@@ -50,6 +50,19 @@ string & ArgvMap::set(const string &var)
   return params[var];
 }
 
+void ArgvMap::setDefault(const string &var, const string &value)
+{
+  if(! defaultmap.count(var))
+    defaultmap.insert(pair<string, string>(var, value));
+}
+
+void ArgvMap::setDefaults()
+{
+  for(map<string,string>::const_iterator i=params.begin();i!=params.end();++i)
+    if(! defaultmap.count(i->first))
+      defaultmap.insert(*i);
+}
+
 bool ArgvMap::mustDo(const string &var)
 {
   return ((*this)[var]!="no") && ((*this)[var]!="off");
@@ -141,29 +154,47 @@ string ArgvMap::helpstring(string prefix)
   return help;
 }
 
-string ArgvMap::configstring(bool current)
+string ArgvMap::configstring(bool running, bool full)
 {
   string help;
 
-  if (current)
-    help="# Autogenerated configuration file based on running instance\n";
+  if (running)
+    help="# Autogenerated configuration file based on running instance ("+nowTime()+")\n\n";
   else
-    help="# Autogenerated configuration file template\n";
-  
+    help="# Autogenerated configuration file template\n\n";
+
   for(const auto& i: helpmap) {
     if(d_typeMap[i.first]=="Command")
       continue;
 
-    help+="#################################\n";
-    help+="# ";
-    help+=i.first;
-    help+="\t";
-    help+=i.second;
-    help+="\n#\n";
-    if (current) {
-      help+=i.first+"="+params[i.first]+"\n\n";
+    if (! defaultmap.count(i.first)) {
+      throw ArgException(string("Default for parameter '")+i.first+"' not set");
+    }
+
+    if (!running || full) {
+      help+="#################################\n";
+      help+="# ";
+      help+=i.first;
+      help+="\t";
+      help+=i.second;
+      help+="\n#\n";
+    } else {
+      if (defaultmap[i.first] == params[i.first]) {
+        continue;
+      }
+    }
+
+    if (! running || defaultmap[i.first] == params[i.first]) {
+      help+="# ";
+    }
+
+    if (running) {
+      help+=i.first+"="+params[i.first]+"\n";
+      if (full) {
+        help+="\n";
+      }
     } else {
-      help+="# "+i.first+"="+params[i.first]+"\n\n";
+      help+=i.first+"="+defaultmap[i.first]+"\n\n";
     }
   }
   return help;
index d91a95a533e07b853da9c006782f9987ae777502..6e41dae475a441a55670b211c76fd49e8558b709 100644 (file)
@@ -106,9 +106,11 @@ public:
   void setCmd(const string &, const string &); //!< Add a command flag
   string &setSwitch(const string &, const string &); //!< Add a switch flag
   string helpstring(string prefix=""); //!< generates the --help
-  string configstring(bool current=false); //!< generates the --mkconfig
+  string configstring(bool current, bool full); //!< generates the --config
   bool contains(const string &var, const string &val);
   bool isEmpty(const string &var); //!< checks if variable has value
+  void setDefault(const string &var, const string &value);
+  void setDefaults();
 
   vector<string>list();
   string getHelp(const string &item);
@@ -123,6 +125,7 @@ private:
   typedef map<string,string> params_t;
   params_t params;
   map<string,string> helpmap;
+  map<string,string> defaultmap;
   map<string,string> d_typeMap;
   vector<string> d_cmds;
   std::set<string> d_cleared;
index 3b0e93b0bd83c75d19db36e60f09ca3b50fa53a6..da3c8e337153e386f30219efe87c24e6bdd674d0 100644 (file)
@@ -236,6 +236,7 @@ void declareArguments()
   ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
 
   ::arg().set("rng", "Specify the random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto";
+  ::arg().setDefaults();
 }
 
 static time_t s_start=time(0);
index cf30da3c3a60e016392c8ad9f91ad285f0b077c4..7a142f40e841d443871de76398a8b6a1098ffc0c 100644 (file)
@@ -66,6 +66,7 @@ void BackendFactory::declare(const string &suffix, const string &param, const st
 {
   string fullname=d_name+suffix+"-"+param;
   arg().set(fullname,help)=value;
+  arg().setDefault(fullname,value);
 }
 
 const string &BackendFactory::getName() const
index 9e8f36c24017c350a0e575b6a6510f8f359db9bb..db1615909d6df4a2a784905090acefbfdbaf9b56 100644 (file)
@@ -68,7 +68,13 @@ static void dokill(int)
 
 string DLCurrentConfigHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
-  return ::arg().configstring(true);
+  if(parts.size() > 1) {
+    if(parts.size() == 2 && parts[1] == "diff") {
+      return ::arg().configstring(true, false);
+    }
+    return "Syntax: current-config [diff]";
+  }
+  return ::arg().configstring(true, true);
 }
 
 string DLRQuitHandler(const vector<string>&parts, Utility::pid_t ppid)
index a5e4705e17e2590c070be727284c2aaacbb72ad0..95f7b70d64b73a40f0b111e694056be80faadbc3 100644 (file)
@@ -115,7 +115,7 @@ bool DNSFilterEngine::Zone::findExactNamedPolicy(const std::unordered_map<DNSNam
   return false;
 }
 
-DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const
+bool DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
 {
   // cout<<"Got question for nameserver name "<<qname<<endl;
   std::vector<bool> zoneEnabled(d_zones.size());
@@ -124,7 +124,10 @@ DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qnam
   for (const auto& z : d_zones) {
     bool enabled = true;
     const auto zoneName = z->getName();
-    if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+    if (z->getPriority() >= pol.d_priority) {
+      enabled = false;
+    }
+    else if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
       enabled = false;
     }
     else {
@@ -140,9 +143,8 @@ DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qnam
     ++count;
   }
 
-  Policy pol;
   if (allEmpty) {
-    return pol;
+    return false;
   }
 
   /* prepare the wildcard-based names */
@@ -159,29 +161,30 @@ DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qnam
       ++count;
       continue;
     }
-
     if (z->findExactNSPolicy(qname, pol)) {
       // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
-      return pol;
+      return true;
     }
 
     for (const auto& wc : wcNames) {
       if (z->findExactNSPolicy(wc, pol)) {
         // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
-        return pol;
+        return true;
       }
     }
     ++count;
   }
 
-  return pol;
+  return false;
 }
 
-DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const
+bool DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
 {
-  Policy pol;
   //  cout<<"Got question for nameserver IP "<<address.toString()<<endl;
   for(const auto& z : d_zones) {
+    if (z->getPriority() >= pol.d_priority) {
+      break;
+    }
     const auto zoneName = z->getName();
     if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
       continue;
@@ -189,13 +192,13 @@ DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress&
 
     if(z->findNSIPPolicy(address, pol)) {
       //      cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
-      return pol;
+      return true;
     }
   }
-  return pol;
+  return false;
 }
 
-DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies) const
+bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
 {
   // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
   std::vector<bool> zoneEnabled(d_zones.size());
@@ -203,16 +206,20 @@ DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, co
   bool allEmpty = true;
   for (const auto& z : d_zones) {
     bool enabled = true;
-    const auto zoneName = z->getName();
-    if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+    if (z->getPriority() >= pol.d_priority) {
       enabled = false;
-    }
-    else {
-      if (z->hasQNamePolicies() || z->hasClientPolicies()) {
-        allEmpty = false;
+    } else {
+      const auto zoneName = z->getName();
+      if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+        enabled = false;
       }
       else {
-        enabled = false;
+        if (z->hasQNamePolicies() || z->hasClientPolicies()) {
+          allEmpty = false;
+        }
+        else {
+          enabled = false;
+        }
       }
     }
 
@@ -220,9 +227,8 @@ DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, co
     ++count;
   }
 
-  Policy pol;
   if (allEmpty) {
-    return pol;
+    return false;
   }
 
   /* prepare the wildcard-based names */
@@ -240,32 +246,31 @@ DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, co
       continue;
     }
 
+    if (z->findClientPolicy(ca, pol)) {
+      // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
+      return true;
+    }
+
     if (z->findExactQNamePolicy(qname, pol)) {
       // cerr<<"Had a hit on the name of the query"<<endl;
-      return pol;
+      return true;
     }
 
     for (const auto& wc : wcNames) {
       if (z->findExactQNamePolicy(wc, pol)) {
         // cerr<<"Had a hit on the name of the query"<<endl;
-        return pol;
+        return true;
       }
     }
 
-    if (z->findClientPolicy(ca, pol)) {
-      // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
-      return pol;
-    }
-
     ++count;
   }
 
-  return pol;
+  return false;
 }
 
-DNSFilterEngine::Policy DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const
+bool DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
 {
-  Policy pol;
   ComboAddress ca;
   for (const auto& r : records) {
     if (r.d_place != DNSResourceRecord::ANSWER)
@@ -284,17 +289,20 @@ DNSFilterEngine::Policy DNSFilterEngine::getPostPolicy(const vector<DNSRecord>&
       continue;
 
     for (const auto& z : d_zones) {
+      if (z->getPriority() >= pol.d_priority) {
+        break;
+      }
       const auto zoneName = z->getName();
       if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
         continue;
       }
 
       if (z->findResponsePolicy(ca, pol)) {
-       return pol;
+       return true;
       }
     }
   }
-  return pol;
+  return false;
 }
 
 void DNSFilterEngine::assureZones(size_t zone)
index fd2a3da267bd2ddcd63bedf1a36d9dd01c01d9c1..612aeb26ee347fa514feaa9f2158fc8ebe486ac1 100644 (file)
@@ -26,6 +26,7 @@
 #include "dnsparser.hh"
 #include <map>
 #include <unordered_map>
+#include <limits>
 
 /* This class implements a filtering policy that is able to fully implement RPZ, but is not bound to it.
    In other words, it is generic enough to support RPZ, but could get its data from other places.
 class DNSFilterEngine
 {
 public:
-  enum class PolicyKind { NoAction, Drop, NXDOMAIN, NODATA, Truncate, Custom};
-  enum class PolicyType { None, QName, ClientIP, ResponseIP, NSDName, NSIP };
-
+  enum class PolicyKind : uint8_t { NoAction, Drop, NXDOMAIN, NODATA, Truncate, Custom};
+  enum class PolicyType : uint8_t { None, QName, ClientIP, ResponseIP, NSDName, NSIP };
+  typedef uint16_t Priority;
+  static const Priority maximumPriority = std::numeric_limits<Priority>::max();
+  
   static std::string getKindToString(PolicyKind kind);
   static std::string getTypeToString(PolicyType type);
 
   struct Policy
   {
-    Policy(): d_name(nullptr), d_kind(PolicyKind::NoAction), d_type(PolicyType::None), d_ttl(0)
+    Policy(): d_name(nullptr), d_ttl(0), d_priority(maximumPriority), d_kind(PolicyKind::NoAction), d_type(PolicyType::None)
     {
     }
 
-    Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr<std::string> name=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_name(name), d_kind(kind), d_type(type), d_ttl(ttl)
+    Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr<std::string> name=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_name(name), d_ttl(ttl), d_priority(maximumPriority), d_kind(kind), d_type(type)
     {
     }
 
@@ -90,14 +93,15 @@ public:
 
     std::vector<std::shared_ptr<DNSRecordContent>> d_custom;
     std::shared_ptr<std::string> d_name; // the name of the policy
-    PolicyKind d_kind;
-    PolicyType d_type;
     /* Yup, we are currently using the same TTL for every record for a given name */
     int32_t d_ttl;
+    Priority d_priority;
+    PolicyKind d_kind;
+    PolicyType d_type;
 
   private:
     DNSRecord getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const;
-};
+  };
 
   class Zone {
   public:
@@ -194,7 +198,27 @@ public:
     {
       return !d_postpolAddr.empty();
     }
-
+    Priority getPriority() const {
+      return d_priority;
+    }
+    void setPriority(Priority p) {
+      d_priority = p;
+      for (auto& pair : d_qpolName) {
+        pair.second.d_priority = p;
+      }
+      for (auto& pair : d_propolName) {
+        pair.second.d_priority = p;
+      }
+      for (auto& pair : d_qpolAddr) {
+        pair.second.d_priority = p;
+      }
+      for (auto& pair : d_propolNSAddr) {
+        pair.second.d_priority = p;
+      }
+      for (auto& pair : d_postpolAddr) {
+        pair.second.d_priority = p;
+      }
+    }
   private:
     static DNSName maskToRPZ(const Netmask& nm);
     static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
@@ -211,6 +235,7 @@ public:
     std::shared_ptr<std::string> d_name;
     uint32_t d_serial{0};
     uint32_t d_refresh{0};
+    Priority d_priority;
   };
 
   DNSFilterEngine();
@@ -244,6 +269,7 @@ public:
   }
   size_t addZone(std::shared_ptr<Zone> newZone)
   {
+    newZone->setPriority(d_zones.size());
     d_zones.push_back(newZone);
     return (d_zones.size() - 1);
   }
@@ -251,14 +277,44 @@ public:
   {
     if (newZone) {
       assureZones(zoneIdx);
+      newZone->setPriority(zoneIdx);
       d_zones[zoneIdx] = newZone;
     }
   }
 
-  Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies) const;
-  Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const;
-  Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const;
-  Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const;
+  bool getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+  bool getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+  bool getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+  bool getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+
+  // A few convenience methods for the unit test code
+  Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
+    Policy policy;
+    policy.d_priority = p;
+    getQueryPolicy(qname, nm, discardedPolicies, policy);
+    return policy;
+  }
+
+  Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
+    Policy policy;
+    policy.d_priority = p;
+    getProcessingPolicy(qname, discardedPolicies, policy);
+    return policy;
+  }
+
+  Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
+    Policy policy;
+    policy.d_priority = p;
+    getProcessingPolicy(address, discardedPolicies, policy);
+    return policy;
+  }
+
+  Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
+    Policy policy;
+    policy.d_priority = p;
+    getPostPolicy(records, discardedPolicies, policy);
+    return policy;
+  }
 
   size_t size() const {
     return d_zones.size();
index dc97e3bf48723d04c7fcbd0c1b54a1d2e33aff9d..3b16bad8738f7bfd30d1b34252d7bc27f97a0227 100644 (file)
@@ -110,15 +110,19 @@ void NSECBitmap::fromPacket(PacketReader& pr)
   }
   
   for(unsigned int n = 0; n+1 < bitmap.size();) {
-    unsigned int window=static_cast<unsigned char>(bitmap[n++]);
-    unsigned int blen=static_cast<unsigned char>(bitmap[n++]);
+    uint8_t window=static_cast<uint8_t>(bitmap[n++]);
+    uint8_t blen=static_cast<uint8_t>(bitmap[n++]);
 
     // end if zero padding and ensure packet length
-    if(window == 0 && blen == 0) {
+    if (window == 0 && blen == 0) {
       break;
     }
 
-    if(n + blen > bitmap.size()) {
+    if (blen > 32) {
+      throw MOADNSException("NSEC record with invalid bitmap length");
+    }
+
+    if (n + blen > bitmap.size()) {
       throw MOADNSException("NSEC record with bitmap length > packet length");
     }
 
index ebfb3440a55872e28618b31e40ea3ab430e05ab9..d3fc95fdf484985bea590f623772ec9e7ccd246f 100644 (file)
@@ -1326,8 +1326,8 @@ static void startDoResolve(void *p)
     }
 
     // Check if the query has a policy attached to it
-    if (wantsRPZ && appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
-      appliedPolicy = luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies);
+    if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+      luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies, appliedPolicy);
     }
 
     // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
@@ -1386,6 +1386,7 @@ static void startDoResolve(void *p)
         res = -2;
       }
       dq.validationState = sr.getValidationState();
+      appliedPolicy = sr.d_appliedPolicy;
 
       // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
       if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve.
@@ -1428,8 +1429,8 @@ static void startDoResolve(void *p)
         }
       }
 
-      if (wantsRPZ && appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
-        appliedPolicy = luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies);
+      if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+        luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy);
       }
 
       if(t_pdl) {
@@ -4799,6 +4800,7 @@ int main(int argc, char **argv)
     ::arg().setCmd("help","Provide a helpful message");
     ::arg().setCmd("version","Print version string");
     ::arg().setCmd("config","Output blank configuration");
+    ::arg().setDefaults();
     g_log.toConsole(Logger::Info);
     ::arg().laxParse(argc,argv); // do a lax parse
 
@@ -4828,7 +4830,7 @@ int main(int argc, char **argv)
     }
 
     if(::arg().mustDo("config")) {
-      cout<<::arg().configstring()<<endl;
+      cout<<::arg().configstring(false, true);
       exit(0);
     }
 
index 3caf56e49322c5d1fab6fe2bca93530f5f739af4..599e47fdc98ec33f22ab0ec40ea2ee23a18f3ebd 100644 (file)
@@ -415,7 +415,7 @@ int main(int argc, char **argv)
     string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
     cleanSlashes(configname);
 
-    if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
+    if(::arg()["config"] != "default" && !::arg().mustDo("no-config")) // "config" == print a configuration file
       ::arg().laxFile(configname.c_str());
     
     ::arg().laxParse(argc,argv); // reparse so the commandline still wins
@@ -512,7 +512,25 @@ int main(int argc, char **argv)
     }
     
     if(::arg().mustDo("config")) {
-      cout<<::arg().configstring()<<endl;
+      string config = ::arg()["config"];
+      if (config == "default") {
+        cout<<::arg().configstring(false, true);
+      } else if (config == "diff") {
+          cout<<::arg().configstring(true, false);
+      } else if (config == "check") {
+        try {
+          if(!::arg().mustDo("no-config"))
+            ::arg().file(configname.c_str());
+          ::arg().parse(argc,argv);
+          exit(0);
+        }
+        catch(const ArgException &A) {
+          cerr<<"Fatal error: "<<A.reason<<endl;
+          exit(1);
+        }
+      } else {
+        cout<<::arg().configstring(true, true);
+      }
       exit(0);
     }
 
@@ -569,7 +587,7 @@ int main(int argc, char **argv)
     DynListener::registerFunc("REMOTES", &DLRemotesHandler, "get top remotes");
     DynListener::registerFunc("SET",&DLSettingsHandler, "set config variables", "<var> <value>");
     DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler, "retrieve slave domain", "<domain>");
-    DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler, "retrieve the current configuration");
+    DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler, "retrieve the current configuration", "[diff|default]");
     DynListener::registerFunc("LIST-ZONES",&DLListZones, "show list of zones", "[master|slave|native]");
     DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin, "Login to a PKCS#11 token", "<module> <slot> <pin>");
 
index a583aa68e49a3d4befac4ab850277ddf9f348f60..f508925357061abc96320c7ffa32cb6a9553bfae 100644 (file)
@@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* blocked NS name */
-    auto matchingPolicy = dfe.getProcessingPolicy(nsName, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getProcessingPolicy(nsName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSDName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
 
@@ -71,19 +71,19 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
     BOOST_CHECK(zonePolicy == matchingPolicy);
 
     /* but a subdomain should not be blocked (not a wildcard, and this is not suffix domain matching */
-    matchingPolicy = dfe.getProcessingPolicy(DNSName("sub") + nsName, std::unordered_map<std::string, bool>());
+    matchingPolicy = dfe.getProcessingPolicy(DNSName("sub") + nsName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
     BOOST_CHECK(zone->findExactNSPolicy(DNSName("sub") + nsName, zonePolicy) == false);
   }
 
   {
     /* blocked NS name via wildcard */
-    const auto matchingPolicy = dfe.getProcessingPolicy(DNSName("sub.sub.wildcard.wolf."), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getProcessingPolicy(DNSName("sub.sub.wildcard.wolf."), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSDName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
 
     /* looking for wildcard.wolf. should not match *.wildcard-blocked. */
-    const auto notMatchingPolicy = dfe.getProcessingPolicy(DNSName("wildcard.wolf."), std::unordered_map<std::string, bool>());
+    const auto notMatchingPolicy = dfe.getProcessingPolicy(DNSName("wildcard.wolf."), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(notMatchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
 
     /* a direct lookup would not match */
@@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* allowed NS name */
-    const auto matchingPolicy = dfe.getProcessingPolicy(DNSName("ns.bad.rabbit."), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getProcessingPolicy(DNSName("ns.bad.rabbit."), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
     DNSFilterEngine::Policy zonePolicy;
     BOOST_CHECK(zone->findExactNSPolicy(DNSName("ns.bad.rabbit."), zonePolicy) == false);
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* blocked NS IP */
-    const auto matchingPolicy = dfe.getProcessingPolicy(nsIP, std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getProcessingPolicy(nsIP, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSIP);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
     DNSFilterEngine::Policy zonePolicy;
@@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* allowed NS IP */
-    const auto matchingPolicy = dfe.getProcessingPolicy(ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getProcessingPolicy(ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
     DNSFilterEngine::Policy zonePolicy;
     BOOST_CHECK(zone->findNSIPPolicy(ComboAddress("192.0.2.142"), zonePolicy) == false);
@@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* blocked qname */
-    auto matchingPolicy = dfe.getQueryPolicy(blockedName, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(blockedName, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
     DNSFilterEngine::Policy zonePolicy;
@@ -130,19 +130,19 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
     BOOST_CHECK(zonePolicy == matchingPolicy);
 
     /* but a subdomain should not be blocked (not a wildcard, and this is not suffix domain matching */
-    matchingPolicy = dfe.getQueryPolicy(DNSName("sub") + blockedName, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    matchingPolicy = dfe.getQueryPolicy(DNSName("sub") + blockedName, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
     BOOST_CHECK(zone->findExactQNamePolicy(DNSName("sub") + blockedName, zonePolicy) == false);
   }
 
   {
     /* blocked NS name via wildcard */
-    const auto matchingPolicy = dfe.getQueryPolicy(DNSName("sub.sub.wildcard-blocked."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(DNSName("sub.sub.wildcard-blocked."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
 
     /* looking for wildcard-blocked. should not match *.wildcard-blocked. */
-    const auto notMatchingPolicy = dfe.getQueryPolicy(DNSName("wildcard-blocked."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto notMatchingPolicy = dfe.getQueryPolicy(DNSName("wildcard-blocked."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(notMatchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
 
     /* a direct lookup would not match */
@@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* blocked client IP */
-    const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), clientIP, std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), clientIP, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
     DNSFilterEngine::Policy zonePolicy;
@@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
 
   {
     /* not blocked */
-    const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
     DNSFilterEngine::Policy zonePolicy;
     BOOST_CHECK(zone->findClientPolicy(ComboAddress("192.0.2.142"), zonePolicy) == false);
@@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
     DNSRecord dr;
     dr.d_type = QType::A;
     dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, responseIP.toString());
-    const auto matchingPolicy = dfe.getPostPolicy({dr}, std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getPostPolicy({dr}, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ResponseIP);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
     DNSFilterEngine::Policy zonePolicy;
@@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
     DNSRecord dr;
     dr.d_type = QType::A;
     dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.142");
-    const auto matchingPolicy = dfe.getPostPolicy({dr}, std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getPostPolicy({dr}, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
     DNSFilterEngine::Policy zonePolicy;
     BOOST_CHECK(zone->findResponsePolicy(ComboAddress("192.0.2.142"), zonePolicy) == false);
@@ -246,45 +246,45 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_wildcard_with_enc)
 
   {
     const DNSName tstName("bcbsks.com.102.112.2o7.net.");
-    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
   }
 
   {
     const DNSName tstName("2o7.net.");
-    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
   }
 
   // Once fixed the BOOST_WARN should becomes BOOST_CHECK
-  const string m("Please fix issue #8321");
+  const string m("Please fix issue #8231");
 
   {
     const DNSName tstName("112.2o7.net.");
-    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_WARN_MESSAGE(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None, m);
     BOOST_WARN_MESSAGE(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction, m);
   }
 
   {
     const DNSName tstName("102.112.2o7.net.");
-    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_WARN_MESSAGE(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None, m);
     BOOST_WARN_MESSAGE(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction, m);
   }
 
   {
     const DNSName tstName("com.112.2o7.net.");
-    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_WARN_MESSAGE(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None, m);
     BOOST_WARN_MESSAGE(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction, m);
   }
 
   {
     const DNSName tstName("wcmatch.2o7.net.");
-    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>());
+    auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
   }
@@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_local_data)
 
   {
     /* exact type does not exist, but we have a CNAME */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad1, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(bad1, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
     auto records = matchingPolicy.getCustomRecords(bad1, QType::A);
@@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_local_data)
 
   {
     /* exact type exists */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
 
@@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_local_data)
 
   {
     /* exact type exists */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
 
@@ -441,7 +441,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies)
 
   {
     /* zone 1 should match first */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
     auto records = matchingPolicy.getCustomRecords(bad, QType::A);
@@ -456,7 +456,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies)
 
   {
     /* zone 2 has an exact match for badUnderWildcard, but the wildcard from the first zone should match first */
-    const auto matchingPolicy = dfe.getQueryPolicy(badUnderWildcard, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>());
+    const auto matchingPolicy = dfe.getQueryPolicy(badUnderWildcard, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
     auto records = matchingPolicy.getCustomRecords(badUnderWildcard, QType::A);
@@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies)
 
   {
     /* zone 1 should still match if zone 2 has been disabled */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone2->getName()), true}});
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone2->getName()), true}}, DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
     auto records = matchingPolicy.getCustomRecords(bad, QType::A);
@@ -486,7 +486,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies)
 
   {
     /* if zone 1 is disabled, zone 2 should match */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}});
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
     BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
     auto records = matchingPolicy.getCustomRecords(bad, QType::A);
@@ -501,7 +501,194 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies)
 
   {
     /* if both zones are disabled, we should not match */
-    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}, {*(zone2->getName()), true}});
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}, {*(zone2->getName()), true}}, DNSFilterEngine::maximumPriority);
     BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
   }
 }
+
+BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
+{
+  DNSFilterEngine dfe;
+
+  auto zone1 = std::make_shared<DNSFilterEngine::Zone>();
+  zone1->setName("Unit test policy 0");
+
+  auto zone2 = std::make_shared<DNSFilterEngine::Zone>();
+  zone2->setName("Unit test policy 1");
+
+  const ComboAddress clientIP("192.0.2.128");
+  const DNSName bad("bad.example.com.");
+  const ComboAddress nsIP("192.0.2.1");
+  const DNSName nsName("ns.bad.wolf.");
+  const ComboAddress responseIP("192.0.2.254");
+
+  zone1->addClientTrigger(Netmask(clientIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "client1a.example.net.")}));
+  zone1->addQNameTrigger(bad, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "garden1a.example.net.")}));
+  zone1->addNSIPTrigger(Netmask(nsIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::NSIP, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "nsip1a.example.net.")}));
+  zone1->addNSTrigger(nsName, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::NSDName, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "nsname1a.example.net.")}));
+  zone1->addResponseTrigger(Netmask(responseIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ResponseIP, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "response1a.example.net.")}));
+
+  zone2->addClientTrigger(Netmask(clientIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "client2a.example.net.")}));
+  zone2->addQNameTrigger(bad, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::QName, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "garden2a.example.net.")}));
+  zone2->addNSIPTrigger(Netmask(nsIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::NSIP, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "nsip2a.example.net.")}));
+  zone2->addNSTrigger(nsName, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::NSDName, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "nsname2a.example.net.")}));
+  zone2->addResponseTrigger(Netmask(responseIP, 32), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ResponseIP, 0, nullptr, {DNSRecordContent::mastermake(QType::CNAME, QClass::IN, "response2a.example.net.")}));
+
+  dfe.addZone(zone1);
+  dfe.addZone(zone2);
+  BOOST_CHECK_EQUAL(zone1->getPriority(), 0);
+  BOOST_CHECK_EQUAL(zone2->getPriority(), 1);
+
+  {
+    /* client IP should match before qname */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.128"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "client1a.example.net.");
+  }
+
+  {
+    /* client IP and qname should match, but zone 1 is disabled and zone2's priority is too high */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.128"), {{*(zone1->getName()), true}}, 1);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+  }
+
+  {
+    /* zone 1 should match first */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden1a.example.net.");
+  }
+
+  {
+    /* zone 1 should still match if we require a priority < 1 */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), 1);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden1a.example.net.");
+  }
+
+  {
+    /* nothing should match if we require a priority < 0 */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), 0);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+  }
+
+  {
+    /* if we disable zone 1, zone 2 should match */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, DNSFilterEngine::maximumPriority);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "garden2a.example.net.");
+  }
+
+  {
+    /* if we disable zone 1, zone 2 should match, except if we require a priority < 1 */
+    const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, 1);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+  }
+
+  {
+    /* blocked NS name */
+    auto matchingPolicy = dfe.getProcessingPolicy(nsName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSDName);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "nsname1a.example.net.");
+  }
+
+  {
+    /* blocked NS name, except policy 1 is disabled and policy2's priority is too high */
+    auto matchingPolicy = dfe.getProcessingPolicy(nsName, {{*(zone1->getName()), true}}, 1);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+  }
+
+  {
+    /* blocked NS IP */
+    const auto matchingPolicy = dfe.getProcessingPolicy(nsIP, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::NSIP);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "nsip1a.example.net.");
+  }
+
+  {
+    /* blocked NS ip, except policy 1 is disabled and policy2's priority is too high */
+    auto matchingPolicy = dfe.getProcessingPolicy(nsIP, {{*(zone1->getName()), true}}, 1);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+  }
+
+  {
+    /* blocked A in the response */
+    DNSRecord dr;
+    dr.d_type = QType::A;
+    dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, responseIP.toString());
+    const auto matchingPolicy = dfe.getPostPolicy({dr}, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ResponseIP);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+    auto records = matchingPolicy.getCustomRecords(bad, QType::A);
+    BOOST_CHECK_EQUAL(records.size(), 1U);
+    const auto& record = records.at(0);
+    BOOST_CHECK(record.d_type == QType::CNAME);
+    BOOST_CHECK(record.d_class == QClass::IN);
+    auto content = std::dynamic_pointer_cast<CNAMERecordContent>(record.d_content);
+    BOOST_CHECK(content != nullptr);
+    BOOST_CHECK_EQUAL(content->getTarget().toString(), "response1a.example.net.");
+  }
+
+  {
+    /* blocked A in the response, except 1 is disabled and 2's priority is too high */
+    DNSRecord dr;
+    dr.d_type = QType::A;
+    dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, responseIP.toString());
+    const auto matchingPolicy = dfe.getPostPolicy({dr}, {{*(zone1->getName()), true}}, 1);
+    BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+    BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+  }
+}
index 291fa394be4d5a91c59b10bf4b0bfad067d76e86..095a2b1bbf35f83061c741b02c48ff0b08155c99 100644 (file)
@@ -1804,18 +1804,18 @@ bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& n
      the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
      process any further RPZ rules.
   */
-  if (d_wantsRPZ && d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
+  if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
     for (auto const &ns : nameservers) {
-      d_appliedPolicy = dfe.getProcessingPolicy(ns.first, d_discardedPolicies);
-      if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+      bool match = dfe.getProcessingPolicy(ns.first, d_discardedPolicies, d_appliedPolicy);
+      if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
         LOG(", however nameserver "<<ns.first<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
         return true;
       }
 
       // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
       for (auto const &address : ns.second.first) {
-        d_appliedPolicy = dfe.getProcessingPolicy(address, d_discardedPolicies);
-        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+        match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
+        if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
           LOG(", however nameserver "<<ns.first<<" IP address "<<address.toString()<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
           return true;
         }
@@ -1833,9 +1833,9 @@ bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAd
      the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
      process any further RPZ rules.
   */
-  if (d_wantsRPZ && d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
-    d_appliedPolicy = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies);
-    if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+  if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+    bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
+    if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
       LOG(" (blocked by RPZ policy '"+(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")+"')");
       return true;
     }
@@ -3405,9 +3405,9 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
 
     nameservers.clear();
     for (auto const &nameserver : nsset) {
-      if (d_wantsRPZ && d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
-        d_appliedPolicy = dfe.getProcessingPolicy(nameserver, d_discardedPolicies);
-        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+      if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+        bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
+        if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
           LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
           throw PolicyHitException();
         }
index 94843022aeac434327a835adf9ea14bd6cbbb550..1f3e5f2bc568dc63933e52b129ea7ba010604686 100644 (file)
@@ -1168,7 +1168,7 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
     DLOG(g_log<<"Sending out SOA"<<endl);
     DNSZoneRecord soa = makeEditedDNSZRFromSOAData(dk, sd);
     outpacket->addRecord(soa);
-    if(securedZone) {
+    if(securedZone && outpacket->d_dnssecOk) {
       set<DNSName> authSet;
       authSet.insert(target);
       addRRSigs(dk, signatureDB, authSet, outpacket->getRRS());
index 1b15b8fc871b6f0bf840e85781bf85ff5be321e3..4688c90b4c12b9af3b4ae6df8792ee4c44743087 100644 (file)
@@ -488,6 +488,27 @@ BOOST_AUTO_TEST_CASE(test_nsec_records_types) {
   }
 }
 
+BOOST_AUTO_TEST_CASE(test_nsec_invalid_bitmap_len) {
+  auto validNSEC = DNSRecordContent::mastermake(QType::NSEC, QClass::IN, "host.example.com. A MX RRSIG NSEC AAAA NSEC3 TYPE1234 TYPE65535");
+  const DNSName powerdnsName("powerdns.com.");
+
+  vector<uint8_t> packet;
+  DNSPacketWriter writer(packet, powerdnsName, QType::NSEC, QClass::IN, 0);
+  writer.getHeader()->qr = 1;
+  writer.startRecord(powerdnsName, QType::NSEC, 100, QClass::IN, DNSResourceRecord::ANSWER, false);
+  validNSEC->toPacket(writer);
+  writer.commit();
+
+  size_t nsecDataPos = sizeof(dnsheader) + powerdnsName.wirelength() + sizeof(uint16_t) + sizeof (uint16_t) + powerdnsName.wirelength() + sizeof(uint16_t) + sizeof (uint16_t) + sizeof(uint32_t) + sizeof(uint16_t);
+  size_t typeBitMapsFieldPos = nsecDataPos + DNSName("host.example.com.").wirelength();
+  auto invalidPacket = packet;
+  /* set the bitmap length value to 33, while the maximum possible value is 32 */
+  invalidPacket.at(typeBitMapsFieldPos + 1) = 33;
+
+  MOADNSParser parser(false, reinterpret_cast<const char*>(packet.data()), packet.size());
+  BOOST_CHECK_THROW(MOADNSParser failParser(false, reinterpret_cast<const char*>(invalidPacket.data()), invalidPacket.size()), MOADNSException);
+}
+
 BOOST_AUTO_TEST_CASE(test_nsec3_records_types) {
 
   {
index 9a5d72fc4c5032b6ef5f9f376d93125f6f3fb206..cde1e19c5b9cb92f4abefcf9578f2280df68fdb1 100644 (file)
@@ -809,3 +809,57 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
       return 0
     end
     """ % (ProtobufTaggedExtraFieldsTest._requestorId, ProtobufTaggedExtraFieldsTest._deviceId, ProtobufTaggedExtraFieldsTest._deviceName)
+
+class ProtobufRPZTest(TestRecursorProtobuf):
+    """
+    This test makes sure that we correctly export the RPZ applied policy in our protobuf messages
+    """
+
+    _confdir = 'ProtobufRPZ'
+    _config_template = """
+auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
+    _lua_config_file = """
+    protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
+    rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir)
+
+    @classmethod
+    def generateRecursorConfig(cls, confdir):
+        authzonepath = os.path.join(confdir, 'example.rpz.zone')
+        with open(authzonepath, 'w') as authzone:
+            authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+sub.test 3600 IN A 192.0.2.42
+""".format(soa=cls._SOA))
+
+        rpzFilePath = os.path.join(confdir, 'zone.rpz')
+        with open(rpzFilePath, 'w') as rpzZone:
+            rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
+""".format(soa=cls._SOA))
+
+        super(ProtobufRPZTest, cls).generateRecursorConfig(confdir)
+
+    def testA(self):
+        name = 'sub.test.example.'
+        expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertRRsetInAnswer(res, expected)
+
+        # check the protobuf messages corresponding to the UDP query and answer
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+
+        # then the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+        self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.')
+        self.assertEquals(len(msg.response.rrs), 1)
+        rr = msg.response.rrs[0]
+        # we have max-cache-ttl set to 15
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+        self.checkNoRemainingMessage()
index e2760dc1dee19bd33985618775404f5de149ac8e..62ab149b9c488631bb2bc1d90bb6c64bbf3ff7c1 100644 (file)
@@ -225,7 +225,7 @@ log-rpz-changes=yes
     def tearDownClass(cls):
         cls.tearDownRecursor()
 
-    def checkBlocked(self, name, shouldBeBlocked=True, adQuery=False):
+    def checkBlocked(self, name, shouldBeBlocked=True, adQuery=False, singleCheck=False):
         query = dns.message.make_query(name, 'A', want_dnssec=True)
         query.flags |= dns.flags.CD
         if adQuery:
@@ -241,9 +241,11 @@ log-rpz-changes=yes
                 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
 
             self.assertRRsetInAnswer(res, expected)
+            if singleCheck:
+                break
 
-    def checkNotBlocked(self, name, adQuery=False):
-        self.checkBlocked(name, False, adQuery)
+    def checkNotBlocked(self, name, adQuery=False, singleCheck=False):
+        self.checkBlocked(name, False, adQuery, singleCheck)
 
     def checkCustom(self, qname, qtype, expected):
         query = dns.message.make_query(qname, qtype, want_dnssec=True)
@@ -461,21 +463,12 @@ class RPZFileRecursorTest(RPZRecursorTest):
     """
 
     _confdir = 'RPZFile'
-    _wsPort = 8042
-    _wsTimeout = 2
-    _wsPassword = 'secretpassword'
-    _apiKey = 'secretapikey'
     _lua_config_file = """
     rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz." })
     """ % (_confdir)
     _config_template = """
 auth-zones=example=configs/%s/example.zone
-webserver=yes
-webserver-port=%d
-webserver-address=127.0.0.1
-webserver-password=%s
-api-key=%s
-""" % (_confdir, _wsPort, _wsPassword, _apiKey)
+""" % (_confdir)
 
     @classmethod
     def generateRecursorConfig(cls, confdir):
@@ -524,21 +517,12 @@ class RPZFileDefaultPolRecursorTest(RPZRecursorTest):
     """
 
     _confdir = 'RPZFileDefaultPolicy'
-    _wsPort = 8042
-    _wsTimeout = 2
-    _wsPassword = 'secretpassword'
-    _apiKey = 'secretapikey'
     _lua_config_file = """
     rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", defpol=Policy.NoAction })
     """ % (_confdir)
     _config_template = """
 auth-zones=example=configs/%s/example.zone
-webserver=yes
-webserver-port=%d
-webserver-address=127.0.0.1
-webserver-password=%s
-api-key=%s
-""" % (_confdir, _wsPort, _wsPassword, _apiKey)
+""" % (_confdir)
 
     @classmethod
     def generateRecursorConfig(cls, confdir):
@@ -586,21 +570,12 @@ class RPZFileDefaultPolNotOverrideLocalRecursorTest(RPZRecursorTest):
     """
 
     _confdir = 'RPZFileDefaultPolicyNotOverrideLocal'
-    _wsPort = 8042
-    _wsTimeout = 2
-    _wsPassword = 'secretpassword'
-    _apiKey = 'secretapikey'
     _lua_config_file = """
     rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", defpol=Policy.NoAction, defpolOverrideLocalData=false })
     """ % (_confdir)
     _config_template = """
 auth-zones=example=configs/%s/example.zone
-webserver=yes
-webserver-port=%d
-webserver-address=127.0.0.1
-webserver-password=%s
-api-key=%s
-""" % (_confdir, _wsPort, _wsPassword, _apiKey)
+""" % (_confdir)
 
     @classmethod
     def generateRecursorConfig(cls, confdir):
@@ -644,28 +619,69 @@ tc.example.zone.rpz. 60 IN CNAME rpz-tcp-only.
         self.checkNXD('tc.example.', 'A')
         self.checkNotBlocked('drop.example.')
 
-class RPZOrderingPrecedenceRecursorTesT(RPZRecursorTest):
+class RPZSimpleAuthServer(object):
+
+    def __init__(self, port):
+        self._serverPort = port
+        listener = threading.Thread(name='RPZ Simple Auth Listener', target=self._listener, args=[])
+        listener.setDaemon(True)
+        listener.start()
+
+    def _getAnswer(self, message):
+
+        response = dns.message.make_response(message)
+        response.flags |= dns.flags.AA
+        records = [
+            dns.rrset.from_text('nsip.delegated.example.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.42')
+        ]
+
+        response.answer = records
+        return response
+
+    def _listener(self):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        try:
+            sock.bind(("127.0.0.1", self._serverPort))
+        except socket.error as e:
+            print("Error binding in the RPZ simple auth listener: %s" % str(e))
+            sys.exit(1)
+
+        while True:
+            try:
+                data, addr = sock.recvfrom(4096)
+                message = dns.message.from_wire(data)
+                if len(message.question) != 1:
+                    print('Invalid query, qdcount is %d' % (len(message.question)))
+                    break
+
+                answer = self._getAnswer(message)
+                if not answer:
+                    print('Unable to get a response for %s %d' % (message.question[0].name, message.question[0].rdtype))
+                    break
+
+                wire = answer.to_wire()
+                sock.sendto(wire, addr)
+
+            except socket.error as e:
+                print('Error in RPZ simple auth socket: %s' % str(e))
+
+rpzAuthServerPort = 4260
+rpzAuthServer = RPZSimpleAuthServer(rpzAuthServerPort)
+
+class RPZOrderingPrecedenceRecursorTest(RPZRecursorTest):
     """
     This test makes sure that the recursor respects the RPZ ordering precedence rules
     """
 
     _confdir = 'RPZOrderingPrecedence'
-    _wsPort = 8042
-    _wsTimeout = 2
-    _wsPassword = 'secretpassword'
-    _apiKey = 'secretapikey'
     _lua_config_file = """
     rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
     rpzFile('configs/%s/zone2.rpz', { policyName="zone2.rpz."})
     """ % (_confdir, _confdir)
     _config_template = """
 auth-zones=example=configs/%s/example.zone
-webserver=yes
-webserver-port=%d
-webserver-address=127.0.0.1
-webserver-password=%s
-api-key=%s
-""" % (_confdir, _wsPort, _wsPassword, _apiKey)
+forward-zones=delegated.example=127.0.0.1:%d
+""" % (_confdir, rpzAuthServerPort)
 
     @classmethod
     def generateRecursorConfig(cls, confdir):
@@ -674,6 +690,9 @@ api-key=%s
             authzone.write("""$ORIGIN example.
 @ 3600 IN SOA {soa}
 sub.test 3600 IN A 192.0.2.42
+passthru-then-blocked-by-higher 3600 IN A 192.0.2.66
+passthru-then-blocked-by-same 3600 IN A 192.0.2.66
+blocked-then-passhtru-by-higher 3600 IN A 192.0.2.100
 """.format(soa=cls._SOA))
 
         rpzFilePath = os.path.join(confdir, 'zone.rpz')
@@ -681,6 +700,10 @@ sub.test 3600 IN A 192.0.2.42
             rpzZone.write("""$ORIGIN zone.rpz.
 @ 3600 IN SOA {soa}
 *.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
+32.66.2.0.192.rpz-ip.zone.rpz. 60 IN A 192.0.2.1
+32.100.2.0.192.rpz-ip.zone.rpz. 60 IN CNAME rpz-passthru.
+passthru-then-blocked-by-same.example.zone.rpz. 60 IN CNAME rpz-passthru.
+32.1.0.0.127.rpz-nsip.zone.rpz. 60 IN CNAME rpz-passthru.
 """.format(soa=cls._SOA))
 
         rpzFilePath = os.path.join(confdir, 'zone2.rpz')
@@ -688,14 +711,92 @@ sub.test 3600 IN A 192.0.2.42
             rpzZone.write("""$ORIGIN zone2.rpz.
 @ 3600 IN SOA {soa}
 sub.test.example.com.zone2.rpz. 60 IN CNAME .
+passthru-then-blocked-by-higher.example.zone2.rpz. 60 IN CNAME rpz-passthru.
+blocked-then-passhtru-by-higher.example.zone2.rpz. 60 IN A 192.0.2.1
 32.42.2.0.192.rpz-ip 60 IN CNAME .
 """.format(soa=cls._SOA))
 
-        super(RPZOrderingPrecedenceRecursorTesT, cls).generateRecursorConfig(confdir)
+        super(RPZOrderingPrecedenceRecursorTest, cls).generateRecursorConfig(confdir)
 
-    def testRPZ(self):
+    def testRPZOrderingForQNameAndWhitelisting(self):
         # we should first match on the qname (the wildcard, not on the exact name since
         # we respect the order of the RPZ zones), see the pass-thru rule
-        # and stop RPZ processing. The subsequent rule on the content of the A
-        # should therefore not trigger a NXDOMAIN.
+        # and only process RPZ rules of higher precedence.
+        # The subsequent rule on the content of the A should therefore not trigger a NXDOMAIN.
         self.checkNotBlocked('sub.test.example.')
+
+    def testRPZOrderingWhitelistedThenBlockedByHigher(self):
+        # we should first match on the qname from the second RPZ zone,
+        # continue the resolution process, and get blocked by the content of the A record
+        # based on the first RPZ zone, whose priority is higher than the second one.
+        self.checkBlocked('passthru-then-blocked-by-higher.example.')
+
+    def testRPZOrderingWhitelistedThenBlockedBySame(self):
+        # we should first match on the qname from the first RPZ zone,
+        # continue the resolution process, and NOT get blocked by the content of the A record
+        # based on the same RPZ zone, since it's not higher.
+        self.checkCustom('passthru-then-blocked-by-same.example.', 'A', dns.rrset.from_text('passthru-then-blocked-by-same.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.66'))
+
+    def testRPZOrderBlockedThenWhitelisted(self):
+        # The qname is first blocked by the second RPZ zone
+        # Then, should the resolution process go on, the A record would be whitelisted
+        # by the first zone.
+        # This is what the RPZ specification requires, but we currently decided that we
+        # don't want to leak queries to malicious DNS servers and waste time if the qname is blacklisted.
+        # We might change our opinion at some point, though.
+        self.checkBlocked('blocked-then-passhtru-by-higher.example.')
+
+    def testRPZOrderDelegate(self):
+        # The IP of the NS we are going to contact is whitelisted (passthru) in zone 1,
+        # so even though the record (192.0.2.42) returned by the server is blacklisted
+        # by zone 2, it should not be blocked.
+        # We only test once because after that the answer is cached, so the NS is not contacted
+        # and the whitelist is not applied (yes, NSIP and NSDNAME are brittle).
+        self.checkNotBlocked('nsip.delegated.example.', singleCheck=True)
+
+class RPZNSIPCustomTest(RPZRecursorTest):
+    """
+    This test makes sure that the recursor handles custom RPZ rules in a NSIP
+    """
+
+    _confdir = 'RPZNSIPCustom'
+    _lua_config_file = """
+    rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
+    rpzFile('configs/%s/zone2.rpz', { policyName="zone2.rpz."})
+    """ % (_confdir, _confdir)
+    _config_template = """
+auth-zones=example=configs/%s/example.zone
+forward-zones=delegated.example=127.0.0.1:%d
+""" % (_confdir, rpzAuthServerPort)
+
+    @classmethod
+    def generateRecursorConfig(cls, confdir):
+        authzonepath = os.path.join(confdir, 'example.zone')
+        with open(authzonepath, 'w') as authzone:
+            authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+""".format(soa=cls._SOA))
+
+        rpzFilePath = os.path.join(confdir, 'zone.rpz')
+        with open(rpzFilePath, 'w') as rpzZone:
+            rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+32.1.0.0.127.rpz-nsip.zone.rpz. 60 IN A 192.0.2.1
+""".format(soa=cls._SOA))
+
+        rpzFilePath = os.path.join(confdir, 'zone2.rpz')
+        with open(rpzFilePath, 'w') as rpzZone:
+            rpzZone.write("""$ORIGIN zone2.rpz.
+@ 3600 IN SOA {soa}
+32.1.2.0.192.rpz-ip 60 IN CNAME .
+""".format(soa=cls._SOA))
+
+        super(RPZNSIPCustomTest, cls).generateRecursorConfig(confdir)
+
+    def testRPZDelegate(self):
+        # The IP of the NS we are going to contact should result in a custom record (192.0.2.1) from zone 1,
+        # so even though the record (192.0.2.1) returned by the server is blacklisted
+        # by zone 2, it should not be blocked.
+        # We only test once because after that the answer is cached, so the NS is not contacted
+        # and the whitelist is not applied (yes, NSIP and NSDNAME are brittle).
+        self.checkCustom('nsip.delegated.example.', 'A', dns.rrset.from_text('nsip.delegated.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1'))