]> granicus.if.org Git - pdns/commitdiff
RPZ: Implement NSDNAME and NSIP RPZ capabilities
authorPieter Lexis <pieter.lexis@powerdns.com>
Wed, 27 Jul 2016 20:34:08 +0000 (22:34 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 25 Aug 2016 08:44:34 +0000 (10:44 +0200)
Closes #2897

This also adds an extra bool 'wantsRPZ' to the Lua engine so RPZ
processing can be disabled for queries (Closes #4226).

Furthermore, IPv6 for RPZ is implemented.

docs/markdown/recursor/scripting.md
pdns/filterpo.cc
pdns/filterpo.hh
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
pdns/pdns_recursor.cc
pdns/rpzloader.cc
pdns/syncres.cc
pdns/syncres.hh

index 62c1aa9c94761a4fcc1a6eec0f763cd5606c38dd..b40d623af4e191b3d01de3ee8d70cc01915aeb5e 100644 (file)
@@ -97,6 +97,7 @@ The DNSQuestion object contains at least the following fields:
   * policyAction: The action taken by the engine
   * policyCustom: The CNAME content for the `pdns.policyactions.Custom` response, a string
   * policyTTL: The TTL in seconds for the `pdns.policyactions.Custom` response
+* wantsRPZ - A boolean that indicates the use of the Policy Engine, can be set to `false` in `preresolve` to disable RPZ for this query
 
 It also supports the following methods:
 
index 55037f85b2fba84a02ffad300ff682eeae6d22fd..d24aa83681af03a1c573cb9316d82144d1e66d42 100644 (file)
@@ -71,8 +71,19 @@ DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qnam
     }
   }
   return pol;
-}  
+}
 
+DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress& address) const
+{
+  //  cout<<"Got question for nameserver IP "<<address.toString()<<endl;
+  for(const auto& z : d_zones) {
+    if(auto fnd=z.propolNSAddr.lookup(address)) {
+      //      cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
+      return fnd->second;;
+    }
+  }
+  return Policy{PolicyKind::NoAction, nullptr, "", 0};
+}
 
 DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca) const
 {
@@ -162,6 +173,12 @@ void DNSFilterEngine::addNSTrigger(const DNSName& n, Policy pol, int zone)
   d_zones[zone].propolName[n]=pol;
 }
 
+void DNSFilterEngine::addNSIPTrigger(const Netmask& nm, Policy pol, int zone)
+{
+  assureZones(zone);
+  d_zones[zone].propolNSAddr.insert(nm).second = pol;
+}
+
 bool DNSFilterEngine::rmClientTrigger(const Netmask& nm, Policy pol, int zone)
 {
   assureZones(zone);
@@ -192,3 +209,11 @@ bool DNSFilterEngine::rmNSTrigger(const DNSName& n, Policy pol, int zone)
   d_zones[zone].propolName.erase(n); // XXX verify policy matched? =pol;
   return true;
 }
+
+bool DNSFilterEngine::rmNSIPTrigger(const Netmask& nm, Policy pol, int zone)
+{
+  assureZones(zone);
+  auto& pols = d_zones[zone].propolNSAddr;
+  pols.erase(nm);
+  return true;
+}
index b153bbc94b2b5f75c68db22f48db18eb8ccd1de2..4cabe524cc8b352e8cef52a52dae6b89c7f431c4 100644 (file)
@@ -82,16 +82,19 @@ public:
   void addClientTrigger(const Netmask& nm, Policy pol, int zone=0);
   void addQNameTrigger(const DNSName& nm, Policy pol, int zone=0);
   void addNSTrigger(const DNSName& dn, Policy pol, int zone=0);
+  void addNSIPTrigger(const Netmask& nm, Policy pol, int zone=0);
   void addResponseTrigger(const Netmask& nm, Policy pol, int zone=0);
 
   bool rmClientTrigger(const Netmask& nm, Policy pol, int zone=0);
   bool rmQNameTrigger(const DNSName& nm, Policy pol, int zone=0);
   bool rmNSTrigger(const DNSName& dn, Policy pol, int zone=0);
+  bool rmNSIPTrigger(const Netmask& nm, Policy pol, int zone=0);
   bool rmResponseTrigger(const Netmask& nm, Policy pol, int zone=0);
 
 
   Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm) const;
   Policy getProcessingPolicy(const DNSName& qname) const;
+  Policy getProcessingPolicy(const ComboAddress& address) const;
   Policy getPostPolicy(const vector<DNSRecord>& records) const;
 
   size_t size() {
@@ -100,10 +103,11 @@ public:
 private:
   void assureZones(int zone);
   struct Zone {
-    std::map<DNSName, Policy> qpolName;
-    NetmaskTree<Policy> qpolAddr;
-    std::map<DNSName, Policy> propolName;
-    NetmaskTree<Policy> postpolAddr;
+    std::map<DNSName, Policy> qpolName;   // QNAME trigger (RPZ)
+    NetmaskTree<Policy> qpolAddr;         // Source address
+    std::map<DNSName, Policy> propolName; // NSDNAME (RPZ)
+    NetmaskTree<Policy> propolNSAddr;     // NSIP (RPZ)
+    NetmaskTree<Policy> postpolAddr;      // IP trigger (RPZ)
   };
   vector<Zone> d_zones;
 
index 4018bd2b4ddbce5772ad8fb6b6a01e027dae8e25..c3e416f74620e46a260d70b7cc3bff81d7b4a2e5 100644 (file)
@@ -51,7 +51,7 @@ bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& lo
   return false;
 }
 
-bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& res, bool* variable)
+bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& res, bool* variable, bool* wantsRPZ)
 {
   return false;
 }
@@ -359,6 +359,7 @@ RecursorLua4::RecursorLua4(const std::string& fname)
   d_lw->registerMember("udpAnswer", &DNSQuestion::udpAnswer);
   d_lw->registerMember("udpQueryDest", &DNSQuestion::udpQueryDest);
   d_lw->registerMember("udpCallback", &DNSQuestion::udpCallback);
+  d_lw->registerMember("wantsRPZ", &DNSQuestion::wantsRPZ);
   d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy);
   d_lw->registerMember("policyName", &DNSFilterEngine::Policy::d_name);
   d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind);
@@ -521,29 +522,29 @@ RecursorLua4::RecursorLua4(const std::string& fname)
   d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0);
 }
 
-bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
+bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable, bool* wantsRPZ)
 {
-  return genhook(d_preresolve, remote, local, query, qtype, isTcp, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable);
+  return genhook(d_preresolve, remote, local, query, qtype, isTcp, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable, wantsRPZ);
 }
 
 bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable)
 {
-  return genhook(d_nxdomain, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable);
+  return genhook(d_nxdomain, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable, 0);
 }
 
 bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable)
 {
-  return genhook(d_nodata, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable);
+  return genhook(d_nodata, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable, 0);
 }
 
 bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
 {
-  return genhook(d_postresolve, remote, local, query, qtype, isTcp, res, 0, 0, appliedPolicy, policyTags, ret, variable);
+  return genhook(d_postresolve, remote, local, query, qtype, isTcp, res, 0, 0, appliedPolicy, policyTags, ret, variable, 0);
 }
 
 bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret)
 {
-  return genhook(d_preoutquery, ns, requestor, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, 0);
+  return genhook(d_preoutquery, ns, requestor, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, 0, 0);
 }
 
 bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader& dh)
@@ -571,7 +572,7 @@ int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet,
   return 0;
 }
 
-bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
+bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable, bool* wantsRPZ)
 {
   if(!func)
     return false;
@@ -588,8 +589,10 @@ bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const Com
   dq->rcode = ret;
   dq->policyTags = policyTags;
   dq->appliedPolicy = appliedPolicy;
+  dq->wantsRPZ = wantsRPZ;
   bool handled=func(dq);
   if(variable) *variable |= dq->variable; // could still be set to indicate this *name* is variable, even if not 'handled'
+  *wantsRPZ = dq->wantsRPZ; // Even if we did not handle the query, RPZ could be disabled
 
   if(handled) {
 loop:;
index 3d08a38666ab52d514eb440767fe3edf80835222..56e51fff9b5ba8f7ec123b0429c0db3cb647a514 100644 (file)
@@ -44,7 +44,7 @@ private:
 public:
   explicit RecursorLua4(const std::string& fname);
   ~RecursorLua4(); // this is so unique_ptr works with an incomplete type
-  bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
+  bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable, bool* wantsRPZ);
   bool nxdomain(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable);
   bool nodata(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable);
   bool postresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
@@ -91,11 +91,12 @@ private:
     DNSFilterEngine::Policy* appliedPolicy;
     std::vector<std::string>* policyTags;
     bool isTcp;
+    bool wantsRPZ;
   };
 
   typedef std::function<bool(std::shared_ptr<DNSQuestion>)> luacall_t;
   luacall_t d_preresolve, d_nxdomain, d_nodata, d_postresolve, d_preoutquery, d_postoutquery;
-  bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res,  const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
+  bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res,  const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable, bool* wantsRPZ);
   typedef std::function<bool(ComboAddress,ComboAddress, struct dnsheader)> ipfilter_t;
   ipfilter_t d_ipfilter;
 };
index 48a1bd9aef0d086022ffff9e2a67b6484ae29625..82faf81453db22d48e67e0388ea12ba35436cdb2 100644 (file)
@@ -660,6 +660,8 @@ void startDoResolve(void *p)
 
     auto luaconfsLocal = g_luaconfs.getLocal();
     DNSFilterEngine::Policy appliedPolicy;
+    // Used to tell syncres later on if we should apply NSDNAME and NSIP RPZ triggers for this query
+    bool wantsRPZ(true);
     RecProtoBufMessage pbMessage(RecProtoBufMessage::Response);
 #ifdef HAVE_PROTOBUF
     if (luaconfsLocal->protobufServer) {
@@ -744,47 +746,50 @@ void startDoResolve(void *p)
     appliedPolicy = dfepol;
 
     // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
-    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer)) {
-
-      switch(appliedPolicy.d_kind) {
-        case DNSFilterEngine::PolicyKind::NoAction:
-          break;
-        case DNSFilterEngine::PolicyKind::Drop:
-          g_stats.policyDrops++;
-          g_stats.policyResults[appliedPolicy.d_kind]++;
-          delete dc;
-          dc=0;
-          return; 
-        case DNSFilterEngine::PolicyKind::NXDOMAIN:
-          g_stats.policyResults[appliedPolicy.d_kind]++;
-          res=RCode::NXDomain;
-          goto haveAnswer;
-        case DNSFilterEngine::PolicyKind::NODATA:
-          g_stats.policyResults[appliedPolicy.d_kind]++;
-          res=RCode::NoError;
-          goto haveAnswer;
-        case DNSFilterEngine::PolicyKind::Custom:
-          g_stats.policyResults[appliedPolicy.d_kind]++;
-          res=RCode::NoError;
-          spoofed.d_name=dc->d_mdp.d_qname;
-          spoofed.d_type=appliedPolicy.d_custom->getType();
-          spoofed.d_ttl = appliedPolicy.d_ttl;
-          spoofed.d_class = 1;
-          spoofed.d_content = appliedPolicy.d_custom;
-          spoofed.d_place = DNSResourceRecord::ANSWER;
-          ret.push_back(spoofed);
-          goto haveAnswer;
-        case DNSFilterEngine::PolicyKind::Truncate:
-          if(!dc->d_tcp) {
+    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer, &wantsRPZ)) {
+
+      sr.d_wantsRPZ = wantsRPZ;
+      if(wantsRPZ) {
+        switch(appliedPolicy.d_kind) {
+          case DNSFilterEngine::PolicyKind::NoAction:
+            break;
+          case DNSFilterEngine::PolicyKind::Drop:
+            g_stats.policyDrops++;
             g_stats.policyResults[appliedPolicy.d_kind]++;
-            res=RCode::NoError;        
-            pw.getHeader()->tc=1;
+            delete dc;
+            dc=0;
+            return; 
+          case DNSFilterEngine::PolicyKind::NXDOMAIN:
+            g_stats.policyResults[appliedPolicy.d_kind]++;
+            res=RCode::NXDomain;
             goto haveAnswer;
-          }
-          break;
+          case DNSFilterEngine::PolicyKind::NODATA:
+            g_stats.policyResults[appliedPolicy.d_kind]++;
+            res=RCode::NoError;
+            goto haveAnswer;
+          case DNSFilterEngine::PolicyKind::Custom:
+            g_stats.policyResults[appliedPolicy.d_kind]++;
+            res=RCode::NoError;
+            spoofed.d_name=dc->d_mdp.d_qname;
+            spoofed.d_type=appliedPolicy.d_custom->getType();
+            spoofed.d_ttl = appliedPolicy.d_ttl;
+            spoofed.d_class = 1;
+            spoofed.d_content = appliedPolicy.d_custom;
+            spoofed.d_place = DNSResourceRecord::ANSWER;
+            ret.push_back(spoofed);
+            goto haveAnswer;
+          case DNSFilterEngine::PolicyKind::Truncate:
+            if(!dc->d_tcp) {
+              g_stats.policyResults[appliedPolicy.d_kind]++;
+              res=RCode::NoError;      
+              pw.getHeader()->tc=1;
+              goto haveAnswer;
+            }
+            break;
+        }
       }
 
-      // Query got not handled for Policy reasons, now actually go out to find an answer
+      // Query got not handled for QNAME Policy reasons, now actually go out to find an answer
       try {
         res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
         shouldNotValidate = sr.wasOutOfBand();
@@ -795,8 +800,55 @@ void startDoResolve(void *p)
         res = RCode::ServFail;
       }
 
-      dfepol = luaconfsLocal->dfe.getPostPolicy(ret);
-      appliedPolicy = dfepol;
+      // 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.
+        appliedPolicy = sr.d_appliedPolicy;
+        g_stats.policyResults[appliedPolicy.d_kind]++;
+        switch(appliedPolicy.d_kind) {
+          case DNSFilterEngine::PolicyKind::NoAction: // This can never happen
+            throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
+          case DNSFilterEngine::PolicyKind::Drop:
+            g_stats.policyDrops++;
+            delete dc;
+            dc=0;
+            return;
+          case DNSFilterEngine::PolicyKind::NXDOMAIN:
+            ret.clear();
+            res=RCode::NXDomain;
+            goto haveAnswer;
+
+          case DNSFilterEngine::PolicyKind::NODATA:
+            ret.clear();
+            res=RCode::NoError;
+            goto haveAnswer;
+
+          case DNSFilterEngine::PolicyKind::Truncate:
+            if(!dc->d_tcp) {
+              ret.clear();
+              res=RCode::NoError;
+              pw.getHeader()->tc=1;
+              goto haveAnswer;
+            }
+            break;
+
+          case DNSFilterEngine::PolicyKind::Custom:
+            ret.clear();
+            res=RCode::NoError;
+            spoofed.d_name=dc->d_mdp.d_qname;
+            spoofed.d_type=appliedPolicy.d_custom->getType();
+            spoofed.d_ttl = appliedPolicy.d_ttl;
+            spoofed.d_class = 1;
+            spoofed.d_content = appliedPolicy.d_custom;
+            spoofed.d_place = DNSResourceRecord::ANSWER;
+            ret.push_back(spoofed);
+            goto haveAnswer;
+        }
+      }
+
+      if (wantsRPZ) {
+        dfepol = luaconfsLocal->dfe.getPostPolicy(ret);
+        appliedPolicy = dfepol;
+      }
 
       if(t_pdl->get()) {
         if(res == RCode::NoError) {
@@ -814,45 +866,47 @@ void startDoResolve(void *p)
        (*t_pdl)->postresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer);
       }
 
-      g_stats.policyResults[appliedPolicy.d_kind]++;
-      switch(appliedPolicy.d_kind) {
-      case DNSFilterEngine::PolicyKind::NoAction:
-       break;
-      case DNSFilterEngine::PolicyKind::Drop:
-       g_stats.policyDrops++;
-       delete dc;
-       dc=0;
-       return; 
-      case DNSFilterEngine::PolicyKind::NXDOMAIN:
-       ret.clear();
-       res=RCode::NXDomain;
-       goto haveAnswer;
-       
-      case DNSFilterEngine::PolicyKind::NODATA:
-       ret.clear();
-       res=RCode::NoError;
-       goto haveAnswer;
-       
-      case DNSFilterEngine::PolicyKind::Truncate:
-       if(!dc->d_tcp) {
-         ret.clear();
-         res=RCode::NoError;   
-         pw.getHeader()->tc=1;
-         goto haveAnswer;
-       }
-       break;
-
-      case DNSFilterEngine::PolicyKind::Custom:
-       ret.clear();
-       res=RCode::NoError;
-       spoofed.d_name=dc->d_mdp.d_qname;
-       spoofed.d_type=appliedPolicy.d_custom->getType();
-       spoofed.d_ttl = appliedPolicy.d_ttl;
-       spoofed.d_class = 1;
-       spoofed.d_content = appliedPolicy.d_custom;
-       spoofed.d_place = DNSResourceRecord::ANSWER;
-       ret.push_back(spoofed);
-       goto haveAnswer;
+      if (wantsRPZ) { //XXX This block is repeated, see above
+        g_stats.policyResults[appliedPolicy.d_kind]++;
+        switch(appliedPolicy.d_kind) {
+          case DNSFilterEngine::PolicyKind::NoAction:
+            break;
+          case DNSFilterEngine::PolicyKind::Drop:
+            g_stats.policyDrops++;
+            delete dc;
+            dc=0;
+            return; 
+          case DNSFilterEngine::PolicyKind::NXDOMAIN:
+            ret.clear();
+            res=RCode::NXDomain;
+            goto haveAnswer;
+
+          case DNSFilterEngine::PolicyKind::NODATA:
+            ret.clear();
+            res=RCode::NoError;
+            goto haveAnswer;
+
+          case DNSFilterEngine::PolicyKind::Truncate:
+            if(!dc->d_tcp) {
+              ret.clear();
+              res=RCode::NoError;
+              pw.getHeader()->tc=1;
+              goto haveAnswer;
+            }
+            break;
+
+          case DNSFilterEngine::PolicyKind::Custom:
+            ret.clear();
+            res=RCode::NoError;
+            spoofed.d_name=dc->d_mdp.d_qname;
+            spoofed.d_type=appliedPolicy.d_custom->getType();
+            spoofed.d_ttl = appliedPolicy.d_ttl;
+            spoofed.d_class = 1;
+            spoofed.d_content = appliedPolicy.d_custom;
+            spoofed.d_place = DNSResourceRecord::ANSWER;
+            ret.push_back(spoofed);
+            goto haveAnswer;
+        }
       }
     }
   haveAnswer:;
index f326f1504ae03b13d85dcbfaaa3ef739a905cac0..c98b136f01f432e29afd384e4a6a3b68f6ba62be 100644 (file)
 static Netmask makeNetmaskFromRPZ(const DNSName& name)
 {
   auto parts = name.getRawLabels();
-  if(parts.size() < 5) 
+  /*
+   * why 2?, the minimally valid IPv6 address that can be encoded in an RPZ is
+   * $NETMASK.zz (::/$NETMASK)
+   * Terrible right?
+   */
+  if(parts.size() < 2 || parts.size() > 9)
     throw PDNSException("Invalid IP address in RPZ: "+name.toString());
-  return Netmask(parts[4]+"."+parts[3]+"."+parts[2]+"."+parts[1]+"/"+parts[0]);
+
+  bool isV6 = (stoi(parts[0]) > 32);
+  bool hadZZ = false;
+
+  for (auto &part : parts) {
+    // Check if we have an IPv4 octet
+    for (auto c : part)
+      if (!isdigit(c))
+        isV6 = true;
+
+    if (pdns_iequals(part,"zz")) {
+      if (hadZZ)
+        throw PDNSException("more than one 'zz' label found in RPZ name"+name.toString());
+      part = "";
+      isV6 = true;
+      hadZZ = true;
+    }
+  }
+
+  if (isV6 && parts.size() < 9 && !hadZZ)
+    throw PDNSException("No 'zz' label found in an IPv6 RPZ name shorter than 9 elements: "+name.toString());
+
+  if (parts.size() == 5 && !isV6)
+    return Netmask(parts[4]+"."+parts[3]+"."+parts[2]+"."+parts[1]+"/"+parts[0]);
+
+  string v6;
+
+  for (uint8_t i = parts.size()-1 ; i > 0; i--) {
+    v6 += parts[i];
+    if (parts[i] == "" && i == 1 && i == parts.size()-1)
+        v6+= "::";
+    if (parts[i] == "" && i != parts.size()-1)
+        v6+= ":";
+    if (parts[i] != "" && i != 1)
+      v6 += ":";
+  }
+  v6 += "/" + parts[0];
+
+  return Netmask(v6);
 }
 
 void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std::string& polName, bool addOrRemove, boost::optional<DNSFilterEngine::Policy> defpol, int place)
@@ -84,9 +127,8 @@ void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std::
     else
       target.rmNSTrigger(filt, pol);
   } else       if(dr.d_name.isPartOf(rpzClientIP)) {
-
-    auto nm=makeNetmaskFromRPZ(dr.d_name);
-
+    DNSName filt=dr.d_name.makeRelative(rpzClientIP);
+    auto nm=makeNetmaskFromRPZ(filt);
     if(addOrRemove)
       target.addClientTrigger(nm, pol);
     else
@@ -94,14 +136,19 @@ void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std::
     
   } else       if(dr.d_name.isPartOf(rpzIP)) {
     // cerr<<"Should apply answer content IP policy: "<<dr.d_name<<endl;
-    auto nm=makeNetmaskFromRPZ(dr.d_name);
+    DNSName filt=dr.d_name.makeRelative(rpzIP);
+    auto nm=makeNetmaskFromRPZ(filt);
     if(addOrRemove)
       target.addResponseTrigger(nm, pol);
     else
       target.rmResponseTrigger(nm, pol);
   } else if(dr.d_name.isPartOf(rpzNSIP)) {
-    cerr<<"Should apply to nameserver IP address policy HAVE NOTHING HERE"<<endl;
-
+    DNSName filt=dr.d_name.makeRelative(rpzNSIP);
+    auto nm=makeNetmaskFromRPZ(filt);
+    if(addOrRemove)
+      target.addNSIPTrigger(nm, pol);
+    else
+      target.rmNSIPTrigger(nm, pol);
   } else {
     if(addOrRemove)
       target.addQNameTrigger(dr.d_name, pol);
index e69fb194a4a809d2cb63ab62eb754feba60920dc..7bf5ad9159ffc0ec2fba7b1f8b8e2db6102e53d9 100644 (file)
@@ -409,7 +409,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
     prefix=d_prefix;
     prefix.append(depth, ' ');
   }
-  
+
   LOG(prefix<<qname<<": Wants "<< (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing in query for "<<qtype.getName()<<endl);
 
   int res=0;
@@ -472,6 +472,10 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
 
   LOG(prefix<<qname<<": failed (res="<<res<<")"<<endl);
   ;
+
+  if (res == -2)
+    return res;
+
   return res<0 ? RCode::ServFail : res;
 }
 
@@ -941,7 +945,11 @@ static void addNXNSECS(vector<DNSRecord>&ret, const vector<DNSRecord>& records)
   }
 }
 
-/** returns -1 in case of no results, rcode otherwise */
+/** returns:
+ *  -1 in case of no results
+ *  -2 when a FilterEngine Policy was hit
+ *  rcode otherwise
+ */
 int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype,
                          vector<DNSRecord>&ret,
                          int depth, set<GetBestNSAnswer>&beenthere)
@@ -952,7 +960,28 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
     prefix.append(depth, ' ');
   }
 
-  LOG(prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact"<<endl);
+  LOG(prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact");
+
+  if(d_wantsRPZ) {
+    for (auto const &ns : nameservers) {
+      d_appliedPolicy = g_luaconfs.getLocal()->dfe.getProcessingPolicy(ns.first);
+      if (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<<"'"<<endl);
+        return -2;
+      }
+
+      // 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 = g_luaconfs.getLocal()->dfe.getProcessingPolicy(address);
+        if (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<<"'"<<endl);
+          return -2;
+        }
+      }
+    }
+  }
+
+  LOG(endl);
 
   for(;;) { // we may get more specific nameservers
     vector<DNSName > rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
@@ -993,12 +1022,6 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
         if(!tns->empty()) {
           LOG(prefix<<qname<<": Trying to resolve NS '"<<*tns<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
         }
-        //
-       // XXX NEED TO HANDLE OTHER POLICY KINDS HERE!
-       if(g_luaconfs.getLocal()->dfe.getProcessingPolicy(*tns).d_kind != DNSFilterEngine::PolicyKind::NoAction) {
-          g_stats.policyResults[g_luaconfs.getLocal()->dfe.getProcessingPolicy(*tns).d_kind]++;
-         throw ImmediateServFailException("Dropped because of policy");
-        }
 
         if(tns->empty()) {
           LOG(prefix<<qname<<": Domain has hardcoded nameserver");
@@ -1023,15 +1046,24 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
           continue;
         }
         else {
+          bool hitPolicy{false};
           LOG(prefix<<qname<<": Resolved '"<<auth<<"' NS "<<*tns<<" to: ");
           for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
             if(remoteIP != remoteIPs.begin()) {
               LOG(", ");
             }
             LOG(remoteIP->toString());
+            if (d_wantsRPZ) {
+              d_appliedPolicy = g_luaconfs.getLocal()->dfe.getProcessingPolicy(*remoteIP);
+              if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+                hitPolicy = true;
+                LOG(" (blocked by RPZ policy '"+d_appliedPolicy.d_name+"')");
+              }
+            }
           }
           LOG(endl);
-
+          if (hitPolicy) //implies d_wantsRPZ
+            return -2;
         }
 
         for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
@@ -1400,7 +1432,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
         return 0;
       }
       else if(realreferral) {
-        LOG(prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl);
+        LOG(prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, ");
        if(sawDS) {
          t_sstorage->dnssecmap[newauth]=true;
          /*      for(const auto& e : t_sstorage->dnssecmap)
@@ -1410,8 +1442,17 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
         auth=newauth;
 
         nameservers.clear();
-        for (auto const &nameserver : nsset)
+        for (auto const &nameserver : nsset) {
+          if (d_wantsRPZ) {
+            d_appliedPolicy = g_luaconfs.getLocal()->dfe.getProcessingPolicy(nameserver);
+            if (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<<"'"<<endl);
+              return -2;
+            }
+          }
           nameservers.insert({nameserver, {{}, false}});
+        }
+        LOG("looping to them"<<endl);
         break;
       }
       else if(!tns->empty()) { // means: not OOB, OOB == empty
index b86b55a4288e50c47ce3d2fac5c5a351225cd871..95eef6a5c22ba3624c02a908dc1b0042ad065c3d 100644 (file)
@@ -368,6 +368,8 @@ public:
   
   bool d_wasVariable{false};
   bool d_wasOutOfBand{false};
+  bool d_wantsRPZ{true};
+  DNSFilterEngine::Policy d_appliedPolicy{DNSFilterEngine::PolicyKind::NoAction, nullptr, "", 0};
   
   typedef multi_index_container <
     NegCacheEntry,