]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #8949 from rgacogne/ddist-doh-rotation-delay
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 19 Mar 2020 08:56:32 +0000 (09:56 +0100)
committerGitHub <noreply@github.com>
Thu, 19 Mar 2020 08:56:32 +0000 (09:56 +0100)
dnsdist: Set the DoH ticket rotation delay before loading tickets

18 files changed:
build-scripts/test-recursor-bulk
docs/changelog/4.3.rst
docs/secpoll.zone
pdns/filterpo.cc
pdns/filterpo.hh
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
pdns/pdns_recursor.cc
pdns/rec-lua-conf.cc
pdns/rec-protobuf.cc
pdns/rec-protobuf.hh
pdns/recursordist/docs/lua-config/rpz.rst
pdns/recursordist/test-filterpo_cc.cc
pdns/rpzloader.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/ws-recursor.cc
regression-tests.recursor-dnssec/test_Protobuf.py

index 5448eb819da4a7a0b7104cad44025ae28a4a341f..9921b9e89645c27f71356e0ac809a956fd372729 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 export PDNSRECURSOR=${PDNSRECURSOR:-/usr/sbin/pdns_recursor}
+export PDNSRECCONTROL=${PDNSRECCONTROL:-/usr/bin/rec_control}
 export DNSBULKTEST=${DNSBULKTEST:-/usr/bin/dnsbulktest}
 
 if [ "$0" != "./build-scripts/test-recursor-bulk" ]; then
@@ -38,7 +39,7 @@ for IPv6 in 0 1; do
       export context="${version}_v6:${IPv6}_csv:${CSV%%.*}"
       export IPv6
       export CSV
-      RECURSOR=$PDNSRECURSOR THRESHOLD=0 TRACE=no time ./recursor-test 5401 $domains || EXIT=1
+      RECURSOR=$PDNSRECURSOR RECCONTROL=$PDNSRECCONTROL THRESHOLD=0 TRACE=no time ./recursor-test 5401 $domains || EXIT=1
       mv -f recursor.log recursor-${context}.log
       sleep 10
     done
index f1e598c91e2b74d2e9deea95d116be45c8375ca1..b666d6d713fbaed98d14a31ce073ce99a9e0561b 100644 (file)
@@ -1,6 +1,25 @@
 Changelogs for 4.3.x
 ====================
 
+.. changelog::
+  :version: 4.3.0-rc2
+  :released: 18th of March 2020
+
+  This is the first Release Candidate for version 4.3.0 of the Authoritative Server.
+  The version called 4.3.0-rc1 was never released because of the cache cleanup change mentioned below.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 8924
+
+    Make sure we look at 10% of all cached items during cleanup (Kees Monshouwer)
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 8936
+
+    emit correct NSEC/NSEC3 bitmaps in hidden key situations (Robin Geuze)
+
 .. changelog::
   :version: 4.3.0-beta2
   :released: 21st of February 2020
index a709c7782085ca103df02f333af4d8b231e132a0..dc16c95bf001058e1e7133b5a82fff0d9d46e185 100644 (file)
@@ -1,4 +1,4 @@
-@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020030304 10800 3600 604800 10800
+@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020031801 10800 3600 604800 10800
 @       3600    IN  NS  pdns-public-ns1.powerdns.com.
 @       3600    IN  NS  pdns-public-ns2.powerdns.com.
 
@@ -62,7 +62,9 @@ auth-4.2.0.security-status                              60 IN TXT "1 OK"
 auth-4.2.1.security-status                              60 IN TXT "1 OK"
 auth-4.3.0-alpha1.security-status                       60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
 auth-4.3.0-beta1.security-status                        60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-auth-4.3.0-beta2.security-status                        60 IN TXT "1 OK"
+auth-4.3.0-beta2.security-status                        60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0-rc1.security-status                          60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0-rc2.security-status                          60 IN TXT "1 OK"
 
 ; Auth Debian
 auth-3.4.1-2.debian.security-status                     60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
index 95f7b70d64b73a40f0b111e694056be80faadbc3..087be580a5d35091d1822a56fd35ce84b76b8fb3 100644 (file)
@@ -123,11 +123,11 @@ bool DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unord
   bool allEmpty = true;
   for (const auto& z : d_zones) {
     bool enabled = true;
-    const auto zoneName = z->getName();
-    if (z->getPriority() >= pol.d_priority) {
+    const auto& zoneName = z->getName();
+    if (z->getPriority() >= pol.getPriority()) {
       enabled = false;
     }
-    else if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+    else if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
       enabled = false;
     }
     else {
@@ -182,11 +182,11 @@ bool DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std
 {
   //  cout<<"Got question for nameserver IP "<<address.toString()<<endl;
   for(const auto& z : d_zones) {
-    if (z->getPriority() >= pol.d_priority) {
+    if (z->getPriority() >= pol.getPriority()) {
       break;
     }
-    const auto zoneName = z->getName();
-    if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+    const auto& zoneName = z->getName();
+    if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
       continue;
     }
 
@@ -206,11 +206,11 @@ bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& c
   bool allEmpty = true;
   for (const auto& z : d_zones) {
     bool enabled = true;
-    if (z->getPriority() >= pol.d_priority) {
+    if (z->getPriority() >= pol.getPriority()) {
       enabled = false;
     } else {
-      const auto zoneName = z->getName();
-      if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+      const auto& zoneName = z->getName();
+      if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
         enabled = false;
       }
       else {
@@ -289,11 +289,11 @@ bool DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std:
       continue;
 
     for (const auto& z : d_zones) {
-      if (z->getPriority() >= pol.d_priority) {
+      if (z->getPriority() >= pol.getPriority()) {
         break;
       }
-      const auto zoneName = z->getName();
-      if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+      const auto& zoneName = z->getName();
+      if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
         continue;
       }
 
@@ -313,14 +313,14 @@ void DNSFilterEngine::assureZones(size_t zone)
 
 void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol)
 {
-  pol.d_name = d_name;
+  pol.d_zoneData = d_zoneData;
   pol.d_type = PolicyType::ClientIP;
   d_qpolAddr.insert(nm).second=std::move(pol);
 }
 
 void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol)
 {
-  pol.d_name = d_name;
+  pol.d_zoneData = d_zoneData;
   pol.d_type = PolicyType::ResponseIP;
   d_postpolAddr.insert(nm).second=std::move(pol);
 }
@@ -346,21 +346,21 @@ void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool
   }
   else {
     auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second;
-    qpol.d_name = d_name;
+    qpol.d_zoneData = d_zoneData;
     qpol.d_type = PolicyType::QName;
   }
 }
 
 void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol)
 {
-  pol.d_name = d_name;
+  pol.d_zoneData = d_zoneData;
   pol.d_type = PolicyType::NSDName;
   d_propolName.insert({n, std::move(pol)});
 }
 
 void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol)
 {
-  pol.d_name = d_name;
+  pol.d_zoneData = d_zoneData;
   pol.d_type = PolicyType::NSIP;
   d_propolNSAddr.insert(nm).second = std::move(pol);
 }
@@ -638,3 +638,10 @@ void DNSFilterEngine::Zone::dump(FILE* fp) const
     dumpAddrPolicy(fp, pair.first, DNSName("rpz-ip.") + d_domain, pair.second);
   }
 }
+
+void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags)
+{
+  for (const auto& tag : newTags) {
+    tags.insert(tag);
+  }
+}
index 43c7404ff16e27ce2a0873560592783fed4fc830..b42e47ea9699f0dcb6fbef48a043239ba765831d 100644 (file)
@@ -74,13 +74,21 @@ public:
   static std::string getKindToString(PolicyKind kind);
   static std::string getTypeToString(PolicyType type);
 
+  struct PolicyZoneData
+  {
+    /* shared by all the policies from a single zone */
+    std::unordered_set<std::string> d_tags;
+    std::string d_name;
+    Priority d_priority{maximumPriority};
+  };
+
   struct Policy
   {
-    Policy(): d_name(nullptr), d_ttl(0), d_priority(maximumPriority), d_kind(PolicyKind::NoAction), d_type(PolicyType::None)
+    Policy(): d_ttl(0), 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_ttl(ttl), d_priority(maximumPriority), d_kind(kind), d_type(type)
+    Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr<PolicyZoneData> data=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_zoneData(data), d_ttl(ttl), d_kind(kind), d_type(type)
     {
     }
 
@@ -88,14 +96,56 @@ public:
     {
       return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom;
     }
+
+    const std::string& getName() const
+    {
+      static const std::string notSet;
+      if (d_zoneData) {
+        return d_zoneData->d_name;
+      }
+      return notSet;
+    }
+
+    void setName(const std::string& name)
+    {
+      /* until now the PolicyZoneData was shared,
+         we now need to copy it, then write to it */
+      std::shared_ptr<PolicyZoneData> newZoneData;
+      if (d_zoneData) {
+        newZoneData = std::make_shared<PolicyZoneData>(*d_zoneData);
+      }
+      else {
+        newZoneData = std::make_shared<PolicyZoneData>();
+      }
+      newZoneData->d_name = name;
+      d_zoneData = newZoneData;
+    }
+
+    const std::unordered_set<std::string>& getTags() const
+    {
+      static const std::unordered_set<std::string> notSet;
+      if (d_zoneData) {
+        return d_zoneData->d_tags;
+      }
+      return notSet;
+    }
+
+    Priority getPriority() const
+    {
+      static Priority notSet = maximumPriority;
+      if (d_zoneData) {
+        return d_zoneData->d_priority;
+      }
+      return notSet;
+    }
+
     std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const;
     std::vector<DNSRecord> getRecords(const DNSName& qname) const;
 
     std::vector<std::shared_ptr<DNSRecordContent>> d_custom;
-    std::shared_ptr<std::string> d_name; // the name of the policy
+    std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
     /* 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;
 
@@ -105,6 +155,10 @@ public:
 
   class Zone {
   public:
+    Zone(): d_zoneData(std::make_shared<PolicyZoneData>())
+    {
+    }
+
     void clear()
     {
       d_qpolAddr.clear();
@@ -119,7 +173,7 @@ public:
     }
     void setName(const std::string& name)
     {
-      d_name = std::make_shared<std::string>(name);
+      d_zoneData->d_name = name;
     }
     void setDomain(const DNSName& domain)
     {
@@ -133,9 +187,13 @@ public:
     {
       d_refresh = refresh;
     }
-    const std::shared_ptr<std::string> getName() const
+    void setTags(std::unordered_set<std::string>&& tags)
     {
-      return d_name;
+      d_zoneData->d_tags = std::move(tags);
+    }
+    const std::string& getName() const
+    {
+      return d_zoneData->d_name;
     }
 
     DNSName getDomain() const
@@ -199,25 +257,10 @@ public:
       return !d_postpolAddr.empty();
     }
     Priority getPriority() const {
-      return d_priority;
+      return d_zoneData->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;
-      }
+      d_zoneData->d_priority = p;
     }
   private:
     static DNSName maskToRPZ(const Netmask& nm);
@@ -232,10 +275,9 @@ public:
     NetmaskTree<Policy> d_propolNSAddr;     // NSIP (RPZ)
     NetmaskTree<Policy> d_postpolAddr;      // IP trigger (RPZ)
     DNSName d_domain;
-    std::shared_ptr<std::string> d_name;
+    std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
     uint32_t d_serial{0};
     uint32_t d_refresh{0};
-    Priority d_priority{0};
   };
 
   DNSFilterEngine();
@@ -261,7 +303,7 @@ public:
   {
     for (const auto zone : d_zones) {
       const auto& zName = zone->getName();
-      if (zName && *zName == name) {
+      if (zName == name) {
         return zone;
       }
     }
@@ -290,28 +332,32 @@ public:
   // 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;
+    policy.d_zoneData = std::make_shared<PolicyZoneData>();
+    policy.d_zoneData->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;
+    policy.d_zoneData = std::make_shared<PolicyZoneData>();
+    policy.d_zoneData->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;
+    policy.d_zoneData = std::make_shared<PolicyZoneData>();
+    policy.d_zoneData->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;
+    policy.d_zoneData = std::make_shared<PolicyZoneData>();
+    policy.d_zoneData->d_priority = p;
     getPostPolicy(records, discardedPolicies, policy);
     return policy;
   }
@@ -323,3 +369,5 @@ private:
   void assureZones(size_t zone);
   vector<std::shared_ptr<Zone>> d_zones;
 };
+
+void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags);
index 404a773d8dd91b530283b96b713e9df41c15052b..3b13bd464db95c5b0d12197a78ee53173154a90c 100644 (file)
@@ -269,12 +269,10 @@ void RecursorLua4::postPrepareContext()
   d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy);
   d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyName",
     [](const DNSFilterEngine::Policy& pol) -> std::string {
-      if(pol.d_name)
-        return *pol.d_name;
-      return std::string();
+      return pol.getName();
     },
     [](DNSFilterEngine::Policy& pol, const std::string& name) {
-      pol.d_name = std::make_shared<std::string>(name);
+      pol.setName(name);
     });
   d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind);
   d_lw->registerMember("policyType", &DNSFilterEngine::Policy::d_type);
@@ -360,12 +358,13 @@ void RecursorLua4::postPrepareContext()
   d_lw->registerFunction("getRecords", &DNSQuestion::getRecords);
   d_lw->registerFunction("setRecords", &DNSQuestion::setRecords);
 
-  d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } });
+  d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->insert(tag); } });
   d_lw->registerFunction<void(DNSQuestion::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](DNSQuestion& dq, const std::vector<std::pair<int, std::string> >& tags) {
       if (dq.policyTags) {
         dq.policyTags->clear();
+        dq.policyTags->reserve(tags.size());
         for (const auto& tag : tags) {
-          dq.policyTags->push_back(tag.second);
+          dq.policyTags->insert(tag.second);
         }
       }
     });
@@ -373,6 +372,7 @@ void RecursorLua4::postPrepareContext()
       std::vector<std::pair<int, std::string> > ret;
       if (dq.policyTags) {
         int count = 1;
+        ret.reserve(dq.policyTags->size());
         for (const auto& tag : *dq.policyTags) {
           ret.push_back({count++, tag});
         }
@@ -537,7 +537,7 @@ bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& loca
   return false; // don't block
 }
 
-unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const
+unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const
 {
   if(d_gettag) {
     std::vector<std::pair<int, const ProxyProtocolValue*>> proxyProtocolValuesMap;
@@ -552,8 +552,9 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn
     if (policyTags) {
       const auto& tags = std::get<1>(ret);
       if (tags) {
+        policyTags->reserve(policyTags->size() + tags->size());
         for (const auto& tag : *tags) {
-          policyTags->push_back(tag.second);
+          policyTags->insert(tag.second);
         }
       }
     }
@@ -582,7 +583,7 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn
 struct pdns_ffi_param
 {
 public:
-  pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional<int>& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_)
+  pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::unordered_set<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional<int>& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_)
   {
   }
 
@@ -597,7 +598,7 @@ public:
   const ComboAddress& local;
   const ComboAddress& remote;
   const Netmask& ednssubnet;
-  std::vector<std::string>& policyTags;
+  std::unordered_set<std::string>& policyTags;
   std::vector<DNSRecord>& records;
   const EDNSOptionViewMap& ednsOptions;
   const std::vector<ProxyProtocolValue>& proxyProtocolValues;
@@ -616,7 +617,7 @@ public:
   bool tcp;
 };
 
-unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const
+unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const
 {
   if (d_gettag_ffi) {
     pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, rcode, ttlCap, variable, tcp, logQuery, logResponse, followCNAMERecords);
@@ -880,7 +881,7 @@ void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag)
 
 void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name)
 {
-  ref->policyTags.push_back(std::string(name));
+  ref->policyTags.insert(std::string(name));
 }
 
 void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name)
index 88bf6fea8ccd908f0fa54b04d4b46a1698ea0770..5b3dcb50026c51f1d703aeaf01dc602e1a849fde 100644 (file)
@@ -75,7 +75,7 @@ public:
     const uint16_t* ednsFlags{nullptr};
     vector<DNSRecord>* currentRecords{nullptr};
     DNSFilterEngine::Policy* appliedPolicy{nullptr};
-    std::vector<std::string>* policyTags{nullptr};
+    std::unordered_set<std::string>* policyTags{nullptr};
     const std::vector<ProxyProtocolValue>* proxyProtocolValues{nullptr};
     std::unordered_map<std::string,bool>* discardedPolicies{nullptr};
     std::string requestorId;
@@ -115,8 +115,8 @@ public:
     DNSName followupName;
   };
 
-  unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
-  unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
+  unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
+  unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
 
   void maintenance() const;
   bool prerpz(DNSQuestion& dq, int& ret) const;
index 088cad58e3bdd023a8a7d265e7a34985b53bedb7..bfaf00ba43a7476b8caabd5c843811e22fed27d7 100644 (file)
@@ -271,7 +271,7 @@ struct DNSComboWriter {
   {
   }
 
-  DNSComboWriter(const std::string& query, const struct timeval& now, std::vector<std::string>&& policyTags, LuaContext::LuaObject&& data, std::vector<DNSRecord>&& records): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_records(std::move(records)), d_data(std::move(data))
+  DNSComboWriter(const std::string& query, const struct timeval& now, std::unordered_set<std::string>&& policyTags, LuaContext::LuaObject&& data, std::vector<DNSRecord>&& records): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_records(std::move(records)), d_data(std::move(data))
   {
   }
 
@@ -331,7 +331,7 @@ struct DNSComboWriter {
   struct timeval d_kernelTimestamp{0,0};
 #endif
   std::string d_query;
-  std::vector<std::string> d_policyTags;
+  std::unordered_set<std::string> d_policyTags;
   std::vector<DNSRecord> d_records;
   LuaContext::LuaObject d_data;
   EDNSSubnetOpts d_ednssubnet;
@@ -813,7 +813,7 @@ catch(...)
 }
 
 #ifdef HAVE_PROTOBUF
-static void protobufLogQuery(uint8_t maskV4, uint8_t maskV6, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::vector<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName)
+static void protobufLogQuery(uint8_t maskV4, uint8_t maskV6, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName)
 {
   if (!t_protobufServers) {
     return;
@@ -1132,6 +1132,59 @@ int followCNAMERecords(vector<DNSRecord>& ret, const QType& qtype)
   return rcode;
 }
 
+enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop };
+
+static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& ret, DNSPacketWriter& pw)
+{
+  /* don't account truncate actions for TCP queries, since they are not applied */
+  if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !dc->d_tcp) {
+    ++g_stats.policyResults[appliedPolicy.d_kind];
+  }
+
+  switch (appliedPolicy.d_kind) {
+
+  case DNSFilterEngine::PolicyKind::NoAction:
+      return PolicyResult::NoAction;
+
+  case DNSFilterEngine::PolicyKind::Drop:
+    ++g_stats.policyDrops;
+    return PolicyResult::Drop;
+
+  case DNSFilterEngine::PolicyKind::NXDOMAIN:
+    ret.clear();
+    res = RCode::NXDomain;
+    return PolicyResult::HaveAnswer;
+
+  case DNSFilterEngine::PolicyKind::NODATA:
+    ret.clear();
+    res = RCode::NoError;
+    return PolicyResult::HaveAnswer;
+
+  case DNSFilterEngine::PolicyKind::Truncate:
+    if (!dc->d_tcp) {
+      ret.clear();
+      res = RCode::NoError;
+      pw.getHeader()->tc = 1;
+      return PolicyResult::HaveAnswer;
+    }
+    return PolicyResult::NoAction;
+
+  case DNSFilterEngine::PolicyKind::Custom:
+    ret.clear();
+    res = RCode::NoError;
+    {
+      auto spoofed = appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
+      for (auto& dr : spoofed) {
+        ret.push_back(dr);
+        handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
+      }
+    }
+    return PolicyResult::HaveAnswer;
+  }
+
+  return PolicyResult::NoAction;
+}
+
 static void startDoResolve(void *p)
 {
   auto dc=std::unique_ptr<DNSComboWriter>(reinterpret_cast<DNSComboWriter*>(p));
@@ -1271,7 +1324,6 @@ static void startDoResolve(void *p)
     int res = RCode::NoError;
 
     DNSFilterEngine::Policy appliedPolicy;
-    std::vector<DNSRecord> spoofed;
     RecursorLua4::DNSQuestion dq(dc->d_source, dc->d_destination, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_tcp, variableAnswer, wantsRPZ, dc->d_logResponse);
     dq.ednsFlags = &edo.d_extFlags;
     dq.ednsOptions = &ednsOpts;
@@ -1334,52 +1386,29 @@ static void startDoResolve(void *p)
 
     // Check if the query has a policy attached to it
     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 (luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies, appliedPolicy)) {
+        mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags());
+      }
     }
 
     // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
     if(!t_pdl || !t_pdl->preresolve(dq, res)) {
 
       sr.setWantsRPZ(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]++;
-            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=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
-            for (const auto& dr : spoofed) {
-              ret.push_back(dr);
-              handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
-            }
-            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;
+      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+        auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+        if (policyResult == PolicyResult::HaveAnswer) {
+          goto haveAnswer;
+        }
+        else if (policyResult == PolicyResult::Drop) {
+          return;
         }
       }
 
       // Query got not handled for QNAME Policy reasons, now actually go out to find an answer
       try {
         sr.d_appliedPolicy = appliedPolicy;
+        sr.d_policyTags = std::move(dc->d_policyTags);
         res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
         shouldNotValidate = sr.wasOutOfBand();
       }
@@ -1394,50 +1423,26 @@ static void startDoResolve(void *p)
       }
       dq.validationState = sr.getValidationState();
       appliedPolicy = sr.d_appliedPolicy;
+      dc->d_policyTags = std::move(sr.d_policyTags);
 
       // 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++;
-            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=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
-            for (const auto& dr : spoofed) {
-              ret.push_back(dr);
-              handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
-            }
-            goto haveAnswer;
+        if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
+          throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
+        }
+        auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+        if (policyResult == PolicyResult::HaveAnswer) {
+          goto haveAnswer;
+        }
+        else if (policyResult == PolicyResult::Drop) {
+          return;
         }
       }
 
       if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
-        luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy);
+        if (luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy)) {
+          mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags());
+        }
       }
 
       if(t_pdl) {
@@ -1458,41 +1463,13 @@ static void startDoResolve(void *p)
       }
 
       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++;
-            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=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
-            for (const auto& dr : spoofed) {
-              ret.push_back(dr);
-              handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
-            }
-            goto haveAnswer;
+
+        auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+        if (policyResult == PolicyResult::HaveAnswer) {
+          goto haveAnswer;
+        }
+        else if (policyResult == PolicyResult::Drop) {
+          return;
         }
       }
     }
@@ -1666,11 +1643,11 @@ static void startDoResolve(void *p)
     }
 #endif /* NOD_ENABLED */
 #ifdef HAVE_PROTOBUF
-    if (t_protobufServers && !(luaconfsLocal->protobufExportConfig.taggedOnly && (!appliedPolicy.d_name || appliedPolicy.d_name->empty()) && dc->d_policyTags.empty())) {
+    if (t_protobufServers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && dc->d_policyTags.empty())) {
       pbMessage->setBytes(packet.size());
       pbMessage->setResponseCode(pw.getHeader()->rcode);
-      if (appliedPolicy.d_name) {
-        pbMessage->setAppliedPolicy(*appliedPolicy.d_name);
+      if (!appliedPolicy.getName().empty()) {
+        pbMessage->setAppliedPolicy(appliedPolicy.getName());
         pbMessage->setAppliedPolicyType(appliedPolicy.d_type);
       }
       pbMessage->setPolicyTags(dc->d_policyTags);
@@ -2392,7 +2369,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
   uint32_t qhash = 0;
   bool needECS = false;
   bool needXPF = g_XPFAcl.match(fromaddr);
-  std::vector<std::string> policyTags;
+  std::unordered_set<std::string> policyTags;
   LuaContext::LuaObject data;
   string requestorId;
   string deviceId;
index 046f1d485ba1700cdd03372cb1f45cdbb8ad50ac..73bffa823e5d3019963fcf006e8aef81f5ff8546 100644 (file)
@@ -51,9 +51,9 @@ typename C::value_type::second_type constGet(const C& c, const std::string& name
   return iter->second;
 }
 
-typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string > > rpzOptions_t;
+typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string, std::vector<std::pair<int, std::string>> > > rpzOptions_t;
 
-static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint)
+static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint, std::unordered_set<std::string>& tags)
 {
   if(have.count("policyName")) {
     polName = boost::get<std::string>(have["policyName"]);
@@ -61,7 +61,7 @@ static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::
   if(have.count("defpol")) {
     defpol=DNSFilterEngine::Policy();
     defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(have["defpol"]);
-    defpol->d_name = std::make_shared<std::string>(polName);
+    defpol->setName(polName);
     if(defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
       defpol->d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN,
                                                               boost::get<string>(have["defcontent"])));
@@ -82,6 +82,12 @@ static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::
   if(have.count("zoneSizeHint")) {
     zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have["zoneSizeHint"]));
   }
+  if (have.count("tags")) {
+    const auto tagsTable = boost::get<std::vector<std::pair<int, std::string>>>(have["tags"]);
+    for (const auto& tag : tagsTable) {
+      tags.insert(tag.second);
+    }
+  }
 }
 
 #if HAVE_PROTOBUF
@@ -238,10 +244,12 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
         if(options) {
           auto& have = *options;
           size_t zoneSizeHint = 0;
-          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint);
+          std::unordered_set<std::string> tags;
+          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags);
           if (zoneSizeHint > 0) {
             zone->reserve(zoneSizeHint);
           }
+          zone->setTags(std::move(tags));
         }
         g_log<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
         zone->setName(polName);
@@ -286,10 +294,12 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
         if (options) {
           auto& have = *options;
           size_t zoneSizeHint = 0;
-          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint);
+          std::unordered_set<std::string> tags;
+          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags);
           if (zoneSizeHint > 0) {
             zone->reserve(zoneSizeHint);
           }
+          zone->setTags(std::move(tags));
 
           if(have.count("tsigname")) {
             tt.name=DNSName(toLower(boost::get<string>(have["tsigname"])));
index c7eec6943dc2138328c1090e4a6e0ce5136ecb3d..bd82ef9523108127ea0d257188f34c70f83e8dd1 100644 (file)
@@ -167,7 +167,7 @@ void RecProtoBufMessage::setAppliedPolicyType(const DNSFilterEngine::PolicyType&
 #endif /* HAVE_PROTOBUF */
 }
 
-void RecProtoBufMessage::setPolicyTags(const std::vector<std::string>& policyTags)
+void RecProtoBufMessage::setPolicyTags(const std::unordered_set<std::string>& policyTags)
 {
 #ifdef HAVE_PROTOBUF
   PBDNSMessage_DNSResponse* response = d_message.mutable_response();
index e4f76b843cb7d3e0750fe44fcceb347341557c60..a64faea61b67f75c98bbdf1acc959e7e229ba19d 100644 (file)
@@ -52,7 +52,7 @@ public:
 #endif /* NOD_ENABLED */
   void setAppliedPolicy(const std::string& policy);
   void setAppliedPolicyType(const DNSFilterEngine::PolicyType& policyType);
-  void setPolicyTags(const std::vector<std::string>& policyTags);
+  void setPolicyTags(const std::unordered_set<std::string>& policyTags);
   void addPolicyTag(const std::string& policyTag);
   void removePolicyTag(const std::string& policyTag);
   std::string getAppliedPolicy() const;
index 3b833eb2ec69309703d1c4b05aaa2335f2137824..8cd75debf51a75a82bb49554b807c4398dcf9089 100644 (file)
@@ -106,6 +106,12 @@ policyName
 ^^^^^^^^^^
 The name logged as 'appliedPolicy' in :doc:`protobuf <protobuf>` messages when this policy is applied.
 
+tags
+^^^^
+.. versionadded:: 4.4.0
+
+List of tags as string, that will be added to the policy tags exported over protobuf when a policy of this zone matches.
+
 zoneSizeHint
 ^^^^^^^^^^^^
 An indication of the number of expected entries in the zone, speeding up the loading of huge zones by reserving space in advance.
index f508925357061abc96320c7ffa32cb6a9553bfae..c95738cafb0b806f8b438d5e6ae10f3d3d1c07a4 100644 (file)
@@ -17,7 +17,7 @@ BOOST_AUTO_TEST_CASE(test_filter_policies_basic)
   std::string zoneName("Unit test policy 0");
   auto zone = std::make_shared<DNSFilterEngine::Zone>();
   zone->setName(zoneName);
-  BOOST_CHECK_EQUAL(*(zone->getName()), zoneName);
+  BOOST_CHECK_EQUAL(zone->getName(), zoneName);
   zone->setDomain(DNSName("powerdns.com."));
   BOOST_CHECK_EQUAL(zone->getDomain(), DNSName("powerdns.com."));
   zone->setSerial(42);
@@ -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}}, DNSFilterEngine::maximumPriority);
+    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}}, DNSFilterEngine::maximumPriority);
+    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,7 @@ 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}}, DNSFilterEngine::maximumPriority);
+    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);
   }
 }
@@ -556,7 +556,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
 
   {
     /* 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);
+    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);
   }
@@ -600,7 +600,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
 
   {
     /* 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);
+    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);
@@ -615,7 +615,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
 
   {
     /* 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);
+    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);
   }
@@ -637,7 +637,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
 
   {
     /* blocked NS name, except policy 1 is disabled and policy2's priority is too high */
-    auto matchingPolicy = dfe.getProcessingPolicy(nsName, {{*(zone1->getName()), true}}, 1);
+    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);
   }
@@ -659,7 +659,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
 
   {
     /* blocked NS ip, except policy 1 is disabled and policy2's priority is too high */
-    auto matchingPolicy = dfe.getProcessingPolicy(nsIP, {{*(zone1->getName()), true}}, 1);
+    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);
   }
@@ -687,7 +687,7 @@ BOOST_AUTO_TEST_CASE(test_multiple_filter_policies_order)
     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);
+    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 53cad7286056dbec5129967a2bbea051e72bb3c1..908c3a4384b9f493f3a236bf155fa2017a1f9372 100644 (file)
@@ -364,7 +364,7 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
 
   time_t refresh;
   DNSName zoneName = oldZone->getDomain();
-  std::string polName = oldZone->getName() ? *(oldZone->getName()) : zoneName.toString();
+  std::string polName = oldZone->getName().empty() ? oldZone->getName() : zoneName.toString();
 
   while (!sr) {
     /* if we received an empty sr, the zone was not really preloaded */
index b24a52d60f93e98db1ef6b6be1fa13d516002cc4..024cf09efa4fb878b3f32d3018d490f05dff965f 100644 (file)
@@ -1807,17 +1807,23 @@ bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& n
   if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
     for (auto const &ns : nameservers) {
       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;
+      if (match) {
+        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+        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.getName()<<"'"<<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) {
         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;
+        if (match) {
+          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+          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.getName()<<"'"<<endl);
+            return true;
+          }
         }
       }
     }
@@ -1835,9 +1841,12 @@ bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAd
   */
   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;
+    if (match) {
+      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+      if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+        LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
+        return true;
+      }
     }
   }
   return false;
@@ -3407,9 +3416,12 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
     for (auto const &nameserver : nsset) {
       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();
+        if (match) {
+          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+            LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+            throw PolicyHitException();
+          }
         }
       }
       nameservers.insert({nameserver, {{}, false}});
index 473f5b5392f990e42cf7253d2438ba1f3cc51c43..eaff86341e09dbcc6c89aa55c7c1338dc6d78855 100644 (file)
@@ -781,6 +781,7 @@ public:
 
   std::unordered_map<std::string,bool> d_discardedPolicies;
   DNSFilterEngine::Policy d_appliedPolicy;
+  std::unordered_set<std::string> d_policyTags;
   unsigned int d_authzonequeries;
   unsigned int d_outqueries;
   unsigned int d_tcpoutqueries;
index 8a0977495bda22314d3df0cb96b784a49539d9bf..cd8997309fc9b85691e25009f7cd1ce5afe38981 100644 (file)
@@ -404,8 +404,8 @@ static void apiServerRPZStats(HttpRequest* req, HttpResponse* resp) {
     auto zone = luaconf->dfe.getZone(i);
     if (zone == nullptr)
       continue;
-    auto name = zone->getName();
-    auto stats = getRPZZoneStats(*name);
+    const auto& name = zone->getName();
+    auto stats = getRPZZoneStats(name);
     if (stats == nullptr)
       continue;
     Json::object zoneInfo = {
@@ -416,7 +416,7 @@ static void apiServerRPZStats(HttpRequest* req, HttpResponse* resp) {
       {"last_update", (double)stats->d_lastUpdate},
       {"serial", (double)stats->d_serial},
     };
-    ret[*name] = zoneInfo;
+    ret[name] = zoneInfo;
   }
   resp->setBody(ret);
 }
index cde1e19c5b9cb92f4abefcf9578f2280df68fdb1..689f5478db1966aa9c7af68acfc55c2620d0c234 100644 (file)
@@ -202,6 +202,9 @@ class TestRecursorProtobuf(RecursorTest):
         self.assertEquals(msg.response.appliedPolicyType, policyType)
 
     def checkProtobufTags(self, msg, tags):
+        print(tags)
+        print('---')
+        print(msg.response.tags)
         self.assertEquals(len(msg.response.tags), len(tags))
         for tag in msg.response.tags:
             self.assertTrue(tag in tags)
@@ -550,7 +553,7 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
         # 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)
-        self.checkProtobufTags(msg, [self._tag_from_gettag])
+        self.checkProtobufTags(msg, [ self._tag_from_gettag ])
         # then the response
         msg = self.getFirstProtobufMessage()
         self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
@@ -559,7 +562,7 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
         # 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.84')
-        tags = [self._tag_from_gettag] + self._tags
+        tags = [ self._tag_from_gettag ] + self._tags
         self.checkProtobufTags(msg, tags)
         self.checkNoRemainingMessage()
 
@@ -863,3 +866,71 @@ sub.test 3600 IN A 192.0.2.42
         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()
+
+class ProtobufRPZTagsTest(TestRecursorProtobuf):
+    """
+    This test makes sure that we correctly export the RPZ tags in our protobuf messages
+    """
+
+    _confdir = 'ProtobufRPZTags'
+    _config_template = """
+auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
+    _tags = ['tag1', 'tag2']
+    _tags_from_gettag = ['tag1-from-gettag', 'tag2-from-gettag']
+    _tags_from_rpz = ['tag1-from-rpz', 'tag2-from-rpz' ]
+    _lua_config_file = """
+    protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } )
+    rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} })
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir, _tags_from_rpz[0], _tags_from_rpz[1])
+    _lua_dns_script_file = """
+    function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
+      return 0, { '%s', '%s' }
+    end
+    function preresolve(dq)
+      dq:addPolicyTag('%s')
+      dq:addPolicyTag('%s')
+      return false
+    end
+    """ % (_tags_from_gettag[0], _tags_from_gettag[1], _tags[0], _tags[1])
+
+    @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(ProtobufRPZTagsTest, 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.checkProtobufTags(msg, self._tags + self._tags_from_gettag + self._tags_from_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()