]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add suffixmatch option to expungeByName 5159/head
authorRobin Geuze <robing@transip.nl>
Fri, 17 Mar 2017 11:58:46 +0000 (12:58 +0100)
committerRobin Geuze <robing@transip.nl>
Fri, 17 Mar 2017 11:58:46 +0000 (12:58 +0100)
pdns/README-dnsdist.md
pdns/dnsdist-cache.cc
pdns/dnsdist-cache.hh
pdns/dnsdist-lua2.cc
pdns/test-dnsdistpacketcache_cc.cc
regression-tests.dnsdist/test_Caching.py

index c3d0450740d7f9dcd77bbf1cf92e592fb7f34af0..b3acec5df67740352ca27b087b7bd5686fc88b07 100644 (file)
@@ -920,11 +920,11 @@ entries remain in the cache. For example, to remove all expired entries:
 getPool("poolname"):getCache():purgeExpired(0)
 ```
 
-Specific entries can also be removed using the `expungeByName(DNSName [, qtype=ANY])`
-method.
+Specific entries can also be removed using the `expungeByName(DNSName [, qtype=ANY, suffixMatch=false])`
+method. If suffixMatch is set to true it will remove any entries below DNSName.
 
 ```
-getPool("poolname"):getCache():expungeByName(newDNSName("powerdns.com"), dnsdist.A)
+getPool("poolname"):getCache():expungeByName(newDNSName("powerdns.com"), dnsdist.A, true)
 ```
 
 Finally, the `expunge(n)` method will remove all entries until at most `n`
@@ -1535,7 +1535,7 @@ instantiate a server with additional parameters
     * `unsetCache()`: remove the packet cache from this pool
  * PacketCache related:
     * `expunge(n)`: remove entries from the cache, leaving at most `n` entries
-    * `expungeByName(DNSName [, qtype=ANY])`: remove entries matching the supplied DNSName and type from the cache
+    * `expungeByName(DNSName [, qtype=ANY, suffixMatch=false])`: remove entries matching the supplied DNSName and type from the cache. If suffixMatch is specified also removes names below DNSName
     * `isFull()`: return true if the cache has reached the maximum number of entries
     * `newPacketCache(maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60])`: return a new PacketCache
     * `printStats()`: print the cache stats (hits, misses, deferred lookups and deferred inserts)
index 9b5b48ce20274f739dffe3acc25c2c8b9bc4d846..7b41b63d484309552e6b19de23bd2723350cb448 100644 (file)
@@ -258,18 +258,15 @@ void DNSDistPacketCache::expunge(size_t upTo)
   d_map.erase(beginIt, endIt);
 }
 
-void DNSDistPacketCache::expungeByName(const DNSName& name, uint16_t qtype)
+void DNSDistPacketCache::expungeByName(const DNSName& name, uint16_t qtype, bool suffixMatch)
 {
   WriteLock w(&d_lock);
 
   for(auto it = d_map.begin(); it != d_map.end(); ) {
     const CacheValue& value = it->second;
-    uint16_t cqtype = 0;
-    uint16_t cqclass = 0;
-    DNSName cqname(value.value.c_str(), value.len, sizeof(dnsheader), false, &cqtype, &cqclass, nullptr);
 
-    if (cqname == name && (qtype == QType::ANY || qtype == cqtype)) {
-        it = d_map.erase(it);
+    if ((value.qname == name || (suffixMatch && value.qname.isPartOf(name))) && (qtype == QType::ANY || qtype == value.qtype)) {
+      it = d_map.erase(it);
     } else {
       ++it;
     }
index 10164c7c640eda555cf04dba6cef2172c4e4bd64..b4c180c7856fd74b74489661570abeb735b38ff9 100644 (file)
@@ -37,7 +37,7 @@ public:
   bool get(const DNSQuestion& dq, uint16_t consumed, uint16_t queryId, char* response, uint16_t* responseLen, uint32_t* keyOut, uint32_t allowExpired=0, bool skipAging=false);
   void purgeExpired(size_t upTo=0);
   void expunge(size_t upTo=0);
-  void expungeByName(const DNSName& name, uint16_t qtype=QType::ANY);
+  void expungeByName(const DNSName& name, uint16_t qtype=QType::ANY, bool suffixMatch=false);
   bool isFull();
   string toString();
   uint64_t getSize() const { return d_map.size(); }
index 0c3ebc919f1f16a5173f59cef51cb2ad8e3d2ad7..e7016f05b8f07309080aba4ce24322133faf4487 100644 (file)
@@ -694,9 +694,13 @@ void moreLua(bool client)
     g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
     g_lua.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired);
     g_lua.registerFunction("expunge", &DNSDistPacketCache::expunge);
-    g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype)>("expungeByName", [](std::shared_ptr<DNSDistPacketCache> cache, const DNSName& dname, boost::optional<uint16_t> qtype) {
+    g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
+                std::shared_ptr<DNSDistPacketCache> cache,
+                const DNSName& dname,
+                boost::optional<uint16_t> qtype,
+                boost::optional<bool> suffixMatch) {
         if (cache) {
-          cache->expungeByName(dname, qtype ? *qtype : QType::ANY);
+          cache->expungeByName(dname, qtype ? *qtype : QType::ANY, suffixMatch ? *suffixMatch : false);
         }
       });
     g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
index 4a944536e0d7217987cdb0bc36e7667044a5b19d..a8b022361592bcaf8cce3b77b4bf8359767b152e 100644 (file)
@@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
   ComboAddress remote;
   try {
     for(counter = 0; counter < 100000; ++counter) {
-      DNSName a=DNSName("hello ")+DNSName(std::to_string(counter));
+      DNSName a=DNSName(std::to_string(counter))+DNSName(" hello");
       BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
 
       vector<uint8_t> query;
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
     size_t deleted=0;
     size_t delcounter=0;
     for(delcounter=0; delcounter < counter/1000; ++delcounter) {
-      DNSName a=DNSName("hello ")+DNSName(std::to_string(delcounter));
+      DNSName a=DNSName(std::to_string(delcounter))+DNSName(" hello");
       vector<uint8_t> query;
       DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
       pwQ.getHeader()->rd = 1;
@@ -80,11 +80,12 @@ BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
     }
     BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped - deleted);
 
+
     size_t matches=0;
     vector<DNSResourceRecord> entry;
     size_t expected=counter-skipped-deleted;
     for(; delcounter < counter; ++delcounter) {
-      DNSName a(DNSName("hello ")+DNSName(std::to_string(delcounter)));
+      DNSName a(DNSName(std::to_string(delcounter))+DNSName(" hello"));
       vector<uint8_t> query;
       DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
       pwQ.getHeader()->rd = 1;
@@ -94,10 +95,13 @@ BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
       uint16_t responseSize = sizeof(response);
       DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, (struct dnsheader*) query.data(), len, query.size(), false);
       if(PC.get(dq, a.wirelength(), pwQ.getHeader()->id, response, &responseSize, &key)) {
-       matches++;
+        matches++;
       }
     }
     BOOST_CHECK_EQUAL(matches, expected);
+
+    PC.expungeByName(DNSName(" hello"), QType::ANY, true);
+    BOOST_CHECK_EQUAL(PC.getSize(), 0);
   }
   catch(PDNSException& e) {
     cerr<<"Had error: "<<e.reason<<endl;
index 832ca80af2fe103c548abcf40cd8c2335fdc0d9b..ad6c9992f50be35ecec7b63f8ed28e18fc51d5a5 100644 (file)
@@ -819,6 +819,168 @@ class TestCacheManagement(DNSDistTest):
             total += self._responsesCounter[key]
         self.assertEquals(total, misses)
 
+    def testCacheExpungeByNameAndSuffix(self):
+        """
+        Cache: Expunge by name
+
+        """
+        misses = 0
+        ttl = 600
+        name = 'expungebyname.suffix.cache.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    ttl,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        name2 = 'expungebyname.suffixother.cache.tests.powerdns.com.'
+        query2 = dns.message.make_query(name2, 'A', 'IN')
+        response2 = dns.message.make_response(query2)
+        rrset2 = dns.rrset.from_text(name2,
+                                     ttl,
+                                     dns.rdataclass.IN,
+                                     dns.rdatatype.A,
+                                     '127.0.0.1')
+        response2.answer.append(rrset2)
+
+        # Miss
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+        misses += 1
+
+        # next queries should hit the cache
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response)
+
+        # cache another entry
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query2.id
+        self.assertEquals(query2, receivedQuery)
+        self.assertEquals(response2, receivedResponse)
+        misses += 1
+
+        # queries for name and name 2 should hit the cache
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response)
+
+        (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response2)
+
+        # remove cached entries from name
+        self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffix.cache.tests.powerdns.com.\"), dnsdist.ANY, true)")
+
+        # Miss for name
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+        misses += 1
+
+        # next queries for name should hit the cache again
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response)
+
+        # queries for name2 should still hit the cache
+        (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response2)
+
+        total = 0
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
+
+        self.assertEquals(total, misses)
+
+    def testCacheExpungeByNameAndTypeAndSuffix(self):
+        """
+        Cache: Expunge by name and type
+
+        """
+        misses = 0
+        ttl = 600
+        name = 'expungebynameandtype.suffixtype.cache.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    ttl,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        query2 = dns.message.make_query(name, 'AAAA', 'IN')
+        response2 = dns.message.make_response(query2)
+        rrset2 = dns.rrset.from_text(name,
+                                     ttl,
+                                     dns.rdataclass.IN,
+                                     dns.rdatatype.AAAA,
+                                     '::1')
+        response2.answer.append(rrset2)
+
+        # Miss
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+        misses += 1
+
+        # next queries should hit the cache
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response)
+
+        # cache another entry
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query2.id
+        self.assertEquals(query2, receivedQuery)
+        self.assertEquals(response2, receivedResponse)
+        misses += 1
+
+        # queries for name A and AAAA should hit the cache
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response)
+
+        (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response2)
+
+        # remove cached entries from name A
+        self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffixtype.cache.tests.powerdns.com.\"), dnsdist.A, true)")
+
+        # Miss for name A
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+        misses += 1
+
+        # next queries for name A should hit the cache again
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response)
+
+        # queries for name AAAA should still hit the cache
+        (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, response2)
+
+        total = 0
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
+        self.assertEquals(total, misses)
+
 class TestCachingTTL(DNSDistTest):
 
     _maxCacheTTL = 86400