]> granicus.if.org Git - pdns/commitdiff
auth: Hash the entire query in the packet cache, split caches
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 10 Mar 2017 08:44:00 +0000 (09:44 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 10 Mar 2017 08:44:00 +0000 (09:44 +0100)
* The packet cache now behaves a lot like the ones in dnsdist and
the recursor, hashing the entire query (except the query id)
to prevent matching queries with, for example, different EDNS version.
* Since the packet and query caches have now much less in common, split
them into two different classes since it doesn't make sense to share
the same storage and maximum number of entries and doing so clarifies
the code a lot. This should also reduce contention.
* Add a new `max-packet-cache-entries` setting to control the number of
entries in the packet cache.
* Add a new metric, `query-cache-size`, the number of entries
in the query cache since `packetcache-size` is only about the packet
cache.

Note that contrary to the recursor one's, the new packet cache doesn't
skip the content of an EDNS Client Subnet option if present, in case it's
used by a backend to produce the response. We could easily change that, or
even make it configurable if we care.

42 files changed:
docs/markdown/authoritative/performance.md
docs/markdown/authoritative/settings.md
modules/remotebackend/Makefile.am
modules/remotebackend/test-remotebackend-http.cc
modules/remotebackend/test-remotebackend-json.cc
modules/remotebackend/test-remotebackend-pipe.cc
modules/remotebackend/test-remotebackend-post.cc
modules/remotebackend/test-remotebackend-unix.cc
modules/remotebackend/test-remotebackend-zeromq.cc
modules/remotebackend/test-remotebackend.cc
pdns/Makefile.am
pdns/auth-caches.cc [new file with mode: 0644]
pdns/auth-caches.hh [new file with mode: 0644]
pdns/auth-packetcache.cc [new file with mode: 0644]
pdns/auth-packetcache.hh [new file with mode: 0644]
pdns/auth-querycache.cc [new file with mode: 0644]
pdns/auth-querycache.hh [new file with mode: 0644]
pdns/cachecleaner.hh
pdns/common_startup.cc
pdns/common_startup.hh
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/dnsproxy.cc
pdns/dnswriter.cc
pdns/dnswriter.hh
pdns/dynhandler.cc
pdns/mastercommunicator.cc
pdns/packetcache.cc [deleted file]
pdns/packetcache.hh
pdns/packethandler.cc
pdns/pdnsutil.cc
pdns/recpacketcache.cc
pdns/recpacketcache.hh
pdns/recursordist/Makefile.am
pdns/recursordist/packetcache.hh [new symlink]
pdns/rfc2136handler.cc
pdns/tcpreceiver.cc
pdns/test-packetcache_cc.cc
pdns/testrunner.cc
pdns/ueberbackend.cc
pdns/ws-auth.cc
regression-tests.nobackend/counters/expected_result

index 3284024b3472280e2dedd46b3838043e47d54ec4..8906bb377ff512b951485f2841e44df02b8cbecc 100644 (file)
@@ -12,7 +12,7 @@ Different backends will have different characteristics - some will want to have
 
 This is done with the [`distributor-threads`](settings.md#distributor-threads) setting which says how many distributors will be opened for each receiver thread. Of special importance is the choice between 1 or more backends. In case of only 1 thread, PowerDNS reverts to unthreaded operation which may be a lot faster, depending on your operating system and architecture.
 
-Another very important setting is [`cache-ttl`](settings.md#cache-ttl). PowerDNS caches entire packets it sends out so as to save the time to query backends to assemble all data. The default setting of 20 seconds may be low for high traffic sites, a value of 60 seconds rarely leads to problems. Please be aware that if any TTL in the answer is shorter than this setting, the packet cache will respect the answer's shortest TTL.
+Other very important settings are [`cache-ttl`](settings.md#cache-ttl). PowerDNS caches entire packets it sends out so as to save the time to query backends to assemble all data. The default setting of 20 seconds may be low for high traffic sites, a value of 60 seconds rarely leads to problems. Please be aware that if any TTL in the answer is shorter than this setting, the packet cache will respect the answer's shortest TTL.
 
 Some PowerDNS operators set cache-ttl to many hours or even days, and use [`pdns_control`](running.md#pdns_control)` purge` to selectively or globally notify PowerDNS of changes made in the backend. Also look at the [Query Cache](#query-cache) described in this chapter. It may materially improve your performance.
 
@@ -21,16 +21,16 @@ To determine if PowerDNS is unable to keep up with packets, determine the value
 Logging truly kills performance as answering a question from the cache is an order of magnitude less work than logging a line about it. Busy sites will prefer to turn [`log-dns-details`](settings.md#log-dns-details) off.
 
 # Packet Cache
-PowerDNS by default uses the 'Packet Cache' to recognise identical questions and supply them with identical answers, without any further processing. The default time to live is 10 seconds. It has been observed that the utility of the packet cache increases with the load on your nameserver.
+PowerDNS by default uses the 'Packet Cache' to recognise identical questions and supply them with identical answers, without any further processing. The default time to live is 20 seconds and can be changed by setting `cache-ttl`. It has been observed that the utility of the packet cache increases with the load on your nameserver.
 
-Not all backends may benefit from the packetcache. If your backend is memory based and does not lead to context switches, the packetcache may actually hurt performance.
+Not all backends may benefit from the packet cache. If your backend is memory based and does not lead to context switches, the packet cache may actually hurt performance.
 
-The size of the packetcache can be observed with `/etc/init.d/pdns show packetcache-size`
+The maximum size of the packet cache is controlled by the `max-packet-cache-entries` entries since 4.1. Before that both the query cache and the packet cache used the `max-cache-entries` setting.
 
 # Query Cache
 Besides entire packets, PowerDNS can also cache individual backend queries. Each DNS query leads to a number of backend queries, the most obvious additional backend query is the check for a possible CNAME. So, when a query comes in for the 'A' record for 'www.powerdns.com', PowerDNS must first check for a CNAME for 'www.powerdns.com'.
 
-The Query Cache caches these backend queries, many of which are quite repetitive. PowerDNS only caches queries with no answer, or with exactly one. In the future this may be expanded but this lightweight solution is very simple and therefore fast.
+The Query Cache caches these backend queries, many of which are quite repetitive. The maximum number of entries in the cache is controlled by the `max-cache-entries` setting. Before 4.1 this setting also controls the maximum number of entries in the packet cache.
 
 Most gain is made from caching negative entries, ie, queries that have no answer. As these take little memory to store and are typically not a real problem in terms of speed-of-propagation, the default TTL for negative queries is a rather high 60 seconds.
 
@@ -49,6 +49,8 @@ daemon.
 * `corrupt-packets`: Number of corrupt packets received
 * `deferred-cache-inserts`: Number of cache inserts that were deferred because of maintenance
 * `deferred-cache-lookup`: Number of cache lookups that were deferred because of maintenance
+* `deferred-packetcache-inserts`: Number of packet cache inserts that were deferred because of maintenance
+* `deferred-packetcache-lookup`: Number of packet cache lookups that were deferred because of maintenance
 * `dnsupdate-answers`: Number of DNS update packets successfully answered
 * `dnsupdate-changes`: Total number of changes to records from DNS update
 * `dnsupdate-queries`: Number of DNS update packets received
@@ -64,6 +66,7 @@ daemon.
 * `qsize-q`: Number of packets waiting for database attention
 * `query-cache-hit`: Number of hits on the [query cache](performance.md#query-cache)
 * `query-cache-miss`: Number of misses on the [query cache](performance.md#query-cache)
+* `query-cache-size`: Number of entries in the query cache
 * `rd-queries`: Number of packets sent by clients requesting recursion (regardless of if we'll be providing them with recursion). Since 3.4.0.
 * `recursing-answers`: Number of packets we supplied an answer to after recursive processing
 * `recursing-questions`: Number of packets we performed recursive processing for
index 642782b6987e80d6f48e6d85a3efbdceb0ff7b7e..e1d55c610aeced6e64f4fed2d64c120c68736ce2 100644 (file)
@@ -472,8 +472,9 @@ Turn on master support. See ["Modes of operation"](modes-of-operation.md#master-
 * Integer
 * Default: 1000000
 
-Maximum number of cache entries. 1 million (the default) will generally suffice
-for most installations.
+Maximum number of entries in the query cache. 1 million (the default) will generally suffice
+for most installations. Starting with 4.1, the packet and query caches are distinct so you might
+also want to see `max-packet-cache-entries`.
 
 ## `max-ent-entries`
 * Integer
@@ -488,6 +489,14 @@ measure to avoid database explosion due to long names.
 
 Limit the number of NSEC3 hash iterations
 
+## `max-packet-cache-entries`
+* Integer
+* Default: 1000000
+
+Maximum number of entries in the packet cache. 1 million (the default) will generally suffice
+for most installations. This setting has been introduced in 4.1, previous used the `max-cache-entries`
+setting for both the packet and query caches.
+
 ## `max-queue-length`
 * Integer
 * Default: 5000
index e965c51b42abaffb6e500baa5e8f063cf32881d8..19eef3825e346030b641779f8c392c64ef453fb9 100644 (file)
@@ -95,6 +95,8 @@ BUILT_SOURCES = ../../pdns/dnslabeltext.cc
 
 libtestremotebackend_la_SOURCES = \
        ../../pdns/arguments.hh ../../pdns/arguments.cc \
+       ../../pdns/auth-packetcache.cc ../../pdns/auth-packetcache.hh \
+       ../../pdns/auth-querycache.cc ../../pdns/auth-querycache.hh \
        ../../pdns/base32.cc \
        ../../pdns/base64.cc \
        ../../pdns/dnsbackend.hh ../../pdns/dnsbackend.cc \
@@ -105,11 +107,12 @@ libtestremotebackend_la_SOURCES = \
        ../../pdns/dnsrecords.cc \
        ../../pdns/dnssecinfra.cc \
        ../../pdns/ednssubnet.cc \
+       ../../pdns/ednsoptions.cc ../../pdns/ednsoptions.hh \
        ../../pdns/iputils.cc \
        ../../pdns/logger.cc \
        ../../pdns/misc.cc \
        ../../pdns/nsecrecords.cc \
-       ../../pdns/packetcache.hh ../../pdns/packetcache.cc \
+       ../../pdns/packetcache.hh \
        ../../pdns/qtype.cc \
        ../../pdns/sillyrecords.cc \
        ../../pdns/statbag.cc \
index 97fa3c141ab294997a614de55c6d02eca3498372..e92d4bbe4c999b8db7ddb7525feb05b5df7b235c 100644 (file)
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
+#include "pdns/auth-packetcache.hh"
+#include "pdns/auth-querycache.hh"
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 ArgvMap &arg()
 {
   static ArgvMap arg;
index 1e341fb0e080903b6a8e7235ec37f1a95508ec10..fb56ee10a13441758588b8e847b019b6c2884806 100644 (file)
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
+#include "pdns/auth-packetcache.hh"
+#include "pdns/auth-querycache.hh"
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 ArgvMap &arg()
 {
   static ArgvMap arg;
index 80876d237619e95a1fbb24e3a0a37e912027d5aa..8d4ca4ea4356bd4d2f91f389a115851acf1c6872 100644 (file)
 #include "pdns/dnsrecords.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
+#include "pdns/auth-packetcache.hh"
+#include "pdns/auth-querycache.hh"
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 ArgvMap &arg()
 {
   static ArgvMap arg;
index a3858317ed56a9e1afdfdfcd6c1d3c5fdfefaa97..785fb6461b0c8bf5f52cee52328995866b2b5f76 100644 (file)
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
+#include "pdns/auth-packetcache.hh"
+#include "pdns/auth-querycache.hh"
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 ArgvMap &arg()
 {
   static ArgvMap arg;
index b9fd94abe05862539b7dc3517b222c9046d5c13d..bf3f9da20dd31079d08fc3dca82015e6ccc74a0c 100644 (file)
 #include "pdns/dnsrecords.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
+#include "pdns/auth-packetcache.hh"
+#include "pdns/auth-querycache.hh"
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 ArgvMap &arg()
 {
   static ArgvMap arg;
index 7015c04c31552249969763c6849324c7c5bfdbe9..9a08fc8fd5e780c797c7e1e1c58c515115b9e900 100644 (file)
 #include "pdns/dnsrecords.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
+#include "pdns/auth-packetcache.hh"
+#include "pdns/auth-querycache.hh"
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 ArgvMap &arg()
 {
   static ArgvMap arg;
index 96596cc5b3de1c9888ec2b075fbc93403502a09a..42e50ff30327a8e305f01ac4aa761469e54db912 100644 (file)
@@ -38,7 +38,6 @@
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
-#include "pdns/packetcache.hh"
 
 #include "test-remotebackend-keys.hh"
 
index 33b829ca5e25e6761320dadfeba949450f17d251..18cedbb86a546c2fe4d745b24751748058fcf530 100644 (file)
@@ -134,6 +134,9 @@ EXTRA_PROGRAMS = \
 pdns_server_SOURCES = \
        arguments.cc arguments.hh \
        auth-carbon.cc \
+       auth-caches.cc auth-caches.hh \
+       auth-packetcache.cc auth-packetcache.hh \
+       auth-querycache.cc auth-querycache.hh \
        backends/gsql/gsqlbackend.cc backends/gsql/gsqlbackend.hh \
        backends/gsql/ssql.hh \
        base32.cc base32.hh \
@@ -163,6 +166,7 @@ pdns_server_SOURCES = \
        dynhandler.cc dynhandler.hh \
        dynlistener.cc dynlistener.hh \
        dynmessenger.hh \
+       ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        gss_context.cc gss_context.hh \
        iputils.cc iputils.hh \
@@ -180,7 +184,7 @@ pdns_server_SOURCES = \
        namespaces.hh \
        nsecrecords.cc \
        opensslsigners.cc opensslsigners.hh \
-       packetcache.cc packetcache.hh \
+       packetcache.hh \
        packethandler.cc packethandler.hh \
        pdnsexception.hh \
        qtype.cc qtype.hh \
@@ -259,6 +263,9 @@ endif
 
 pdnsutil_SOURCES = \
        arguments.cc \
+       auth-caches.cc auth-caches.hh \
+       auth-packetcache.cc auth-packetcache.hh \
+       auth-querycache.cc auth-querycache.hh \
        backends/gsql/gsqlbackend.cc backends/gsql/gsqlbackend.hh \
        backends/gsql/ssql.hh \
        base32.cc \
@@ -279,6 +286,7 @@ pdnsutil_SOURCES = \
        dnssecsigner.cc \
        dnswriter.cc dnswriter.hh \
        dynlistener.cc \
+       ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc \
        gss_context.cc gss_context.hh \
        iputils.cc iputils.hh \
@@ -287,7 +295,6 @@ pdnsutil_SOURCES = \
        misc.cc misc.hh \
        nsecrecords.cc \
        opensslsigners.cc opensslsigners.hh \
-       packetcache.cc \
        pdnsutil.cc \
        qtype.cc \
        randomhelper.cc \
@@ -885,8 +892,8 @@ dnsreplay_SOURCES = \
        dnsrecords.cc \
        dnsreplay.cc \
        dnswriter.cc dnswriter.hh \
-       ednssubnet.cc ednssubnet.hh \
        ednsoptions.cc ednsoptions.hh \
+       ednssubnet.cc ednssubnet.hh \
        iputils.cc \
        logger.cc \
        misc.cc \
@@ -1104,6 +1111,9 @@ pdns.conf-dist: pdns_server
 
 testrunner_SOURCES = \
        arguments.cc \
+       auth-caches.cc auth-caches.hh \
+       auth-packetcache.cc auth-packetcache.hh \
+       auth-querycache.cc auth-querycache.hh \
        base32.cc \
        base64.cc \
        bindlexer.l \
@@ -1128,7 +1138,6 @@ testrunner_SOURCES = \
        misc.cc \
        nameserver.cc \
        nsecrecords.cc \
-       packetcache.cc \
        qtype.cc \
        rcpgenerator.cc \
        recpacketcache.cc recpacketcache.hh \
diff --git a/pdns/auth-caches.cc b/pdns/auth-caches.cc
new file mode 100644 (file)
index 0000000..c33575a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "auth-querycache.hh"
+#include "auth-packetcache.hh"
+
+extern AuthPacketCache PC;
+extern AuthQueryCache QC;
+
+/* empty all caches */
+uint64_t purgeAuthCaches()
+{
+  uint64_t ret = 0;
+  ret += PC.purge();
+  ret += QC.purge();
+  return ret;
+}
+
+ /* remove specific entries from all caches, can be $ terminated */
+uint64_t purgeAuthCaches(const std::string& match)
+{
+  uint64_t ret = 0;
+  ret += PC.purge(match);
+  ret += QC.purge(match);
+  return ret;
+}
+
+/* remove specific entries from all caches, no wildcard matching */
+uint64_t purgeAuthCachesExact(const DNSName& qname)
+{
+  uint64_t ret = 0;
+  ret += PC.purgeExact(qname);
+  ret += QC.purgeExact(qname);
+  return ret;
+}
+
+
+
diff --git a/pdns/auth-caches.hh b/pdns/auth-caches.hh
new file mode 100644 (file)
index 0000000..76c248f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef AUTH_CACHES_HH
+#define AUTH_CACHES_HH
+
+#include <string>
+#include <cstdint>
+
+#include "dnsname.hh"
+
+uint64_t purgeAuthCaches(); /* empty all caches */
+uint64_t purgeAuthCaches(const std::string& match); /* remove specific entries from all caches, can be $ terminated */
+uint64_t purgeAuthCachesExact(const DNSName& qname); /* remove specific entries from all caches, no wildcard matching */
+
+#endif /* AUTH_CACHES_HH */
diff --git a/pdns/auth-packetcache.cc b/pdns/auth-packetcache.cc
new file mode 100644 (file)
index 0000000..f28a6fd
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "auth-packetcache.hh"
+#include "logger.hh"
+#include "statbag.hh"
+#include "cachecleaner.hh"
+extern StatBag S;
+
+const unsigned int AuthPacketCache::s_mincleaninterval, AuthPacketCache::s_maxcleaninterval;
+
+AuthPacketCache::AuthPacketCache(size_t mapsCount): d_lastclean(time(nullptr))
+{
+  d_maps.resize(mapsCount);
+  for(auto& mc : d_maps) {
+    pthread_rwlock_init(&mc.d_mut, 0);
+  }
+
+  S.declare("packetcache-hit", "Number of hits on the packet cache");
+  S.declare("packetcache-miss", "Number of misses on the packet cache");
+  S.declare("packetcache-size", "Number of entries in the packet cache");
+  S.declare("deferred-packetcache-inserts","Amount of packet cache inserts that were deferred because of maintenance");
+  S.declare("deferred-packetcache-lookup","Amount of packet cache lookups that were deferred because of maintenance");
+
+  d_statnumhit=S.getPointer("packetcache-hit");
+  d_statnummiss=S.getPointer("packetcache-miss");
+  d_statnumentries=S.getPointer("packetcache-size");
+}
+
+AuthPacketCache::~AuthPacketCache()
+{
+  try {
+    vector<WriteLock*> locks;
+    for(auto& mc : d_maps) {
+      locks.push_back(new WriteLock(&mc.d_mut));
+    }
+    for(auto wl : locks) {
+      delete wl;
+    }
+  }
+  catch(...) {
+  }
+}
+
+bool AuthPacketCache::get(DNSPacket *p, DNSPacket *cached)
+{
+  cleanupIfNeeded();
+
+  if(!d_ttl) {
+    (*d_statnummiss)++;
+    return false;
+  }
+
+  uint32_t hash = canHashPacket(p->getString(), false);
+  p->setHash(hash);
+
+  string value;
+  bool haveSomething;
+  time_t now = time(nullptr);
+  auto& mc = getMap(p->qdomain);
+  {
+    TryReadLock rl(&mc.d_mut);
+    if(!rl.gotIt()) {
+      S.inc("deferred-packetcache-lookup");
+      return false;
+    }
+
+    haveSomething = getEntryLocked(mc.d_map, hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
+  }
+
+  if (!haveSomething) {
+    (*d_statnummiss)++;
+    return false;
+  }
+
+  if(cached->noparse(value.c_str(), value.size()) < 0) {
+    return false;
+  }
+
+  (*d_statnumhit)++;
+  cached->spoofQuestion(p); // for correct case
+  cached->qdomain = p->qdomain;
+  cached->qtype = p->qtype;
+
+  return true;
+}
+
+void AuthPacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxTTL)
+{
+  cleanupIfNeeded();
+
+  if (ntohs(q->d.qdcount) != 1) {
+    return; // do not try to cache packets with multiple questions
+  }
+
+  if (q->qclass != QClass::IN) // we only cache the INternet
+    return;
+
+  uint32_t ourttl = std::min(d_ttl, maxTTL);
+  if (ourttl == 0) {
+    return;
+  }  
+
+  uint32_t hash = q->getHash();
+  time_t now = time(nullptr);
+  CacheEntry entry;
+  entry.hash = hash;
+  entry.created = now;
+  entry.ttd = now + ourttl;
+  entry.qname = q->qdomain;
+  entry.qtype = q->qtype.getCode();
+  entry.value = r->getString();
+  entry.tcp = r->d_tcp;
+  
+  auto& mc = getMap(entry.qname);
+  {
+    TryWriteLock l(&mc.d_mut);
+    if (!l.gotIt()) {
+      S.inc("deferred-packetcache-inserts");
+      return;
+    }
+
+    auto& idx = mc.d_map.get<HashTag>();
+    auto range = idx.equal_range(hash);
+    auto iter = range.first;
+
+    for( ; iter != range.second ; ++iter)  {
+      if (iter->tcp != entry.tcp || iter->qtype != entry.qtype || iter->qname != entry.qname)
+        continue;
+
+      iter->value = entry.value;
+      iter->ttd = now + ourttl;
+      iter->created = now;
+      return;
+    }
+
+    /* no existing entry found to refresh */
+    mc.d_map.insert(entry);
+    (*d_statnumentries)++;
+  }
+}
+
+bool AuthPacketCache::getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
+{
+  auto& idx = map.get<HashTag>();
+  auto range = idx.equal_range(hash);
+
+  for(auto iter = range.first; iter != range.second ; ++iter)  {
+    if (iter->ttd < now)
+      continue;
+
+    if (iter->tcp != tcp || iter->qtype != qtype || iter->qname != qname)
+      continue;
+
+    value = iter->value;
+    return true;
+  }
+
+  return false;
+}
+
+/* clears the entire cache. */
+uint64_t AuthPacketCache::purge()
+{
+  d_statnumentries->store(0);
+
+  return purgeLockedCollectionsVector(d_maps);
+}
+
+uint64_t AuthPacketCache::purgeExact(const DNSName& qname)
+{
+  auto& mc = getMap(qname);
+  uint64_t delcount = purgeExactLockedCollection(mc, qname);
+
+  *d_statnumentries -= delcount;
+
+  return delcount;
+}
+
+/* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
+uint64_t AuthPacketCache::purge(const string &match)
+{
+  uint64_t delcount = 0;
+
+  if(ends_with(match, "$")) {
+    delcount = purgeLockedCollectionsVector(d_maps, match);
+    *d_statnumentries -= delcount;
+  }
+  else {
+    delcount = purgeExact(DNSName(match));
+  }
+
+  return delcount;
+}
+                          
+void AuthPacketCache::cleanup()
+{
+  uint64_t maxCached = d_maxEntries;
+  uint64_t cacheSize = *d_statnumentries;
+  uint64_t totErased = 0;
+
+  totErased = pruneLockedCollectionsVector(d_maps, maxCached, cacheSize);
+  *d_statnumentries -= totErased;
+
+  DLOG(L<<"Done with cache clean, cacheSize: "<<(*d_statnumentries)<<", totErased"<<totErased<<endl);
+}
+
+/* the logic:
+   after d_nextclean operations, we clean. We also adjust the cleaninterval
+   a bit so we slowly move it to a value where we clean roughly every 30 seconds.
+
+   If d_nextclean has reached its maximum value, we also test if we were called
+   within 30 seconds, and if so, we skip cleaning. This means that under high load,
+   we will not clean more often than every 30 seconds anyhow.
+*/
+
+void AuthPacketCache::cleanupIfNeeded()
+{
+  if (d_ops++ == d_nextclean) {
+    time_t now = time(nullptr);
+    int timediff = max((int)(now - d_lastclean), 1);
+
+    DLOG(L<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
+
+    if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
+      d_cleanskipped = true;
+      d_nextclean += d_cleaninterval;
+
+      DLOG(L<<"cleaning skipped, timediff: "<<timediff<<endl);
+
+      return;
+    }
+
+    if(!d_cleanskipped) {
+      d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
+      d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
+      d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
+
+      DLOG(L<<"new cleaninterval: "<<d_cleaninterval<<endl);
+    } else {
+      d_cleanskipped = false;
+    }
+
+    d_nextclean += d_cleaninterval;
+    d_lastclean=now;
+    cleanup();
+  }
+}
diff --git a/pdns/auth-packetcache.hh b/pdns/auth-packetcache.hh
new file mode 100644 (file)
index 0000000..d817b5d
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef AUTH_PACKETCACHE_HH
+#define AUTH_PACKETCACHE_HH
+
+#include <string>
+#include <map>
+#include "dns.hh"
+#include <boost/version.hpp>
+#include "namespaces.hh"
+using namespace ::boost::multi_index;
+
+#include <boost/multi_index/hashed_index.hpp> 
+
+#include "dnspacket.hh"
+#include "lock.hh"
+#include "packetcache.hh"
+
+/** This class performs 'whole packet caching'. Feed it a question packet and it will
+    try to find an answer. If you have an answer, insert it to have it cached for later use. 
+    Take care not to replace existing cache entries. While this works, it is wasteful. Only
+    insert packets that where not found by get()
+
+    Locking! 
+
+    The cache itself is protected by a read/write lock. Because deleting is a two step process, which 
+    first marks and then sweeps, a second lock is present to prevent simultaneous inserts and deletes.
+*/
+
+class AuthPacketCache : public PacketCache
+{
+public:
+  AuthPacketCache(size_t mapsCount=1024);
+  ~AuthPacketCache();
+
+  void insert(DNSPacket *q, DNSPacket *r, uint32_t maxTTL);  //!< We copy the contents of *p into our cache. Do not needlessly call this to insert questions already in the cache as it wastes resources
+
+  bool get(DNSPacket *p, DNSPacket *q); //!< We return a dynamically allocated copy out of our cache. You need to delete it. You also need to spoof in the right ID with the DNSPacket.spoofID() method.
+
+  void cleanup(); //!< force the cache to preen itself from expired packets
+  uint64_t purge();
+  uint64_t purge(const std::string& match); // could be $ terminated. Is not a dnsname!
+  uint64_t purgeExact(const DNSName& qname); // no wildcard matching here
+
+  uint64_t size() const { return *d_statnumentries; };
+
+  void setMaxEntries(uint64_t maxEntries) 
+  {
+    d_maxEntries = maxEntries;
+  }
+  void setTTL(uint32_t ttl)
+  {
+    d_ttl = ttl;
+  }  
+private:
+
+  struct CacheEntry
+  {
+    mutable string value;
+    DNSName qname;
+
+    mutable time_t created{0};
+    mutable time_t ttd{0};
+    uint32_t hash{0};
+    uint16_t qtype{0};
+    bool tcp{false};
+  };
+
+  struct HashTag{};
+  struct NameTag{};
+  struct SequenceTag{};
+  typedef multi_index_container<
+    CacheEntry,
+    indexed_by <
+      hashed_non_unique<tag<HashTag>, member<CacheEntry,uint32_t,&CacheEntry::hash> >,
+      ordered_non_unique<tag<NameTag>, member<CacheEntry,DNSName,&CacheEntry::qname>, CanonDNSNameCompare >,
+      sequenced<tag<SequenceTag>>
+      >
+    > cmap_t;
+
+  struct MapCombo
+  {
+    pthread_rwlock_t d_mut;    
+    cmap_t d_map;
+  };
+
+  vector<MapCombo> d_maps;
+  MapCombo& getMap(const DNSName& name)
+  {
+    return d_maps[name.hash() % d_maps.size()];
+  }
+
+  bool getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
+  void cleanupIfNeeded();
+
+  AtomicCounter d_ops{0};
+  AtomicCounter *d_statnumhit;
+  AtomicCounter *d_statnummiss;
+  AtomicCounter *d_statnumentries;
+
+  uint64_t d_maxEntries{0};
+  time_t d_lastclean; // doesn't need to be atomic
+  unsigned long d_nextclean{4096};
+  unsigned int d_cleaninterval{4096};
+  uint32_t d_ttl{0};
+  bool d_cleanskipped{false};
+
+  static const unsigned int s_mincleaninterval=1000, s_maxcleaninterval=300000;
+};
+
+#endif /* AUTH_PACKETCACHE_HH */
diff --git a/pdns/auth-querycache.cc b/pdns/auth-querycache.cc
new file mode 100644 (file)
index 0000000..e91349c
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "auth-querycache.hh"
+#include "logger.hh"
+#include "statbag.hh"
+#include "cachecleaner.hh"
+extern StatBag S;
+
+const unsigned int AuthQueryCache::s_mincleaninterval, AuthQueryCache::s_maxcleaninterval;
+
+extern StatBag S;
+
+AuthQueryCache::AuthQueryCache(size_t mapsCount): d_lastclean(time(nullptr))
+{
+  d_maps.resize(mapsCount);
+  for(auto& mc : d_maps) {
+    pthread_rwlock_init(&mc.d_mut, 0);
+  }
+
+  S.declare("query-cache-hit","Number of hits on the query cache");
+  S.declare("query-cache-miss","Number of misses on the query cache");
+  S.declare("query-cache-size", "Number of entries in the query cache");
+  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
+  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
+
+  d_statnumhit=S.getPointer("query-cache-hit");
+  d_statnummiss=S.getPointer("query-cache-miss");
+  d_statnumentries=S.getPointer("query-cache-size");
+}
+
+AuthQueryCache::~AuthQueryCache()
+{
+  try {
+    vector<WriteLock*> locks;
+    for(auto& mc : d_maps) {
+      locks.push_back(new WriteLock(&mc.d_mut));
+    }
+    for(auto wl : locks) {
+      delete wl;
+    }
+  }
+  catch(...) {
+  }
+}
+
+// called from ueberbackend
+bool AuthQueryCache::getEntry(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>& value, int zoneID)
+{
+  cleanupIfNeeded();
+
+  time_t now = time(nullptr);
+  uint16_t qt = qtype.getCode();
+  auto& mc = getMap(qname);
+  {
+    TryReadLock rl(&mc.d_mut);
+    if(!rl.gotIt()) {
+      S.inc("deferred-cache-lookup");
+      return false;
+    }
+
+    return getEntryLocked(mc.d_map, qname, qt, value, zoneID, now);
+  }
+}
+
+void AuthQueryCache::insert(const DNSName &qname, const QType& qtype, const vector<DNSZoneRecord>& value, uint32_t ttl, int zoneID)
+{
+  cleanupIfNeeded();
+
+  if(!ttl)
+    return;
+  
+  time_t now = time(nullptr);
+  CacheEntry val;
+  val.created = now;
+  val.ttd = now + ttl;
+  val.qname = qname;
+  val.qtype = qtype.getCode();
+  val.drs = value;
+  val.zoneID = zoneID;
+
+  auto& mc = getMap(val.qname);
+
+  {
+    TryWriteLock l(&mc.d_mut);
+    if(!l.gotIt()) {
+      S.inc("deferred-cache-inserts"); 
+      return;
+    }
+
+    bool inserted;
+    cmap_t::iterator place;
+    tie(place, inserted) = mc.d_map.insert(val);
+
+    if (!inserted) {
+      mc.d_map.replace(place, val);
+    }
+    else {
+      (*d_statnumentries)++;
+    }
+  }
+}
+
+bool AuthQueryCache::getEntryLocked(cmap_t& map, const DNSName &qname, uint16_t qtype, vector<DNSZoneRecord>& value, int zoneID, time_t now)
+{
+  auto& idx = boost::multi_index::get<HashTag>(map);
+  auto iter = idx.find(tie(qname, qtype, zoneID));
+
+  if (iter == idx.end())
+    return false;
+
+  if (iter->ttd < now)
+    return false;
+
+  value = iter->drs;
+  return true;
+}
+
+map<char,uint64_t> AuthQueryCache::getCounts()
+{
+  uint64_t queryCacheEntries=0, negQueryCacheEntries=0;
+
+  for(auto& mc : d_maps) {
+    ReadLock l(&mc.d_mut);
+    
+    for(cmap_t::const_iterator iter = mc.d_map.begin() ; iter != mc.d_map.end(); ++iter) {
+      if(iter->drs.empty())
+        negQueryCacheEntries++;
+      else
+        queryCacheEntries++;
+    }
+  }
+  map<char,uint64_t> ret;
+
+  ret['!']=negQueryCacheEntries;
+  ret['Q']=queryCacheEntries;
+  return ret;
+}
+
+/* clears the entire cache. */
+uint64_t AuthQueryCache::purge()
+{
+  d_statnumentries->store(0);
+
+  return purgeLockedCollectionsVector(d_maps);
+}
+
+uint64_t AuthQueryCache::purgeExact(const DNSName& qname)
+{
+  auto& mc = getMap(qname);
+  uint64_t delcount = purgeExactLockedCollection(mc, qname);
+
+  *d_statnumentries -= delcount;
+
+  return delcount;
+}
+
+/* purges entries from the querycache. If match ends on a $, it is treated as a suffix */
+uint64_t AuthQueryCache::purge(const string &match)
+{
+  uint64_t delcount = 0;
+
+  if(ends_with(match, "$")) {
+    delcount = purgeLockedCollectionsVector(d_maps, match);
+    *d_statnumentries -= delcount;
+  }
+  else {
+    delcount = purgeExact(DNSName(match));
+  }
+
+  return delcount;
+}
+
+void AuthQueryCache::cleanup()
+{
+  uint64_t maxCached = d_maxEntries;
+  uint64_t cacheSize = *d_statnumentries;
+  uint64_t totErased = 0;
+
+  totErased = pruneLockedCollectionsVector(d_maps, maxCached, cacheSize);
+
+  *d_statnumentries -= totErased;
+  DLOG(L<<"Done with cache clean, cacheSize: "<<*d_statnumentries<<", totErased"<<totErased<<endl);
+}
+
+/* the logic:
+   after d_nextclean operations, we clean. We also adjust the cleaninterval
+   a bit so we slowly move it to a value where we clean roughly every 30 seconds.
+
+   If d_nextclean has reached its maximum value, we also test if we were called
+   within 30 seconds, and if so, we skip cleaning. This means that under high load,
+   we will not clean more often than every 30 seconds anyhow.
+*/
+
+void AuthQueryCache::cleanupIfNeeded()
+{
+  if (d_ops++ == d_nextclean) {
+    time_t now = time(nullptr);
+    int timediff = max((int)(now - d_lastclean), 1);
+
+    DLOG(L<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
+
+    if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
+      d_cleanskipped = true;
+      d_nextclean += d_cleaninterval;
+
+      DLOG(L<<"cleaning skipped, timediff: "<<timediff<<endl);
+
+      return;
+    }
+
+    if(!d_cleanskipped) {
+      d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
+      d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
+      d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
+
+      DLOG(L<<"new cleaninterval: "<<d_cleaninterval<<endl);
+    } else {
+      d_cleanskipped = false;
+    }
+
+    d_nextclean += d_cleaninterval;
+    d_lastclean=now;
+    cleanup();
+  }
+}
diff --git a/pdns/auth-querycache.hh b/pdns/auth-querycache.hh
new file mode 100644 (file)
index 0000000..5e6d436
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef AUTH_QUERYCACHE_HH
+#define AUTH_QUERYCACHE_HH
+
+#include <string>
+#include <map>
+#include "dns.hh"
+#include <boost/version.hpp>
+#include "namespaces.hh"
+using namespace ::boost::multi_index;
+
+#include <boost/multi_index/hashed_index.hpp> 
+
+#include "dns.hh"
+#include "dnspacket.hh"
+#include "lock.hh"
+
+class AuthQueryCache : public boost::noncopyable
+{
+public:
+  AuthQueryCache(size_t mapsCount=1024);
+  ~AuthQueryCache();
+
+  void insert(const DNSName &qname, const QType& qtype, const vector<DNSZoneRecord>& content, uint32_t ttl, int zoneID);
+
+  bool getEntry(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>& entry, int zoneID);
+
+  size_t size() { return *d_statnumentries; } //!< number of entries in the cache
+  void cleanup(); //!< force the cache to preen itself from expired querys
+  uint64_t purge();
+  uint64_t purge(const std::string& match); // could be $ terminated. Is not a dnsname!
+  uint64_t purgeExact(const DNSName& qname); // no wildcard matching here
+
+  map<char,uint64_t> getCounts();
+
+  void setMaxEntries(uint64_t maxEntries)
+  {
+    d_maxEntries = maxEntries;
+  }
+private:
+
+  struct CacheEntry
+  {
+    DNSName qname;
+    mutable vector<DNSZoneRecord> drs;
+    mutable time_t created{0};
+    mutable time_t ttd{0};
+    uint16_t qtype{0};
+    int zoneID{-1};
+  };
+
+  struct HashTag{};
+  struct NameTag{};
+  struct SequenceTag{};
+  typedef multi_index_container<
+    CacheEntry,
+    indexed_by <
+      hashed_unique<tag<HashTag>, composite_key<CacheEntry,
+                                                         member<CacheEntry,DNSName,&CacheEntry::qname>,
+                                                         member<CacheEntry,uint16_t,&CacheEntry::qtype>,
+                                                         member<CacheEntry,int, &CacheEntry::zoneID> > > ,
+      ordered_non_unique<tag<NameTag>, member<CacheEntry,DNSName,&CacheEntry::qname>, CanonDNSNameCompare >,
+      sequenced<tag<SequenceTag>>
+                           >
+  > cmap_t;
+
+
+  struct MapCombo
+  {
+    pthread_rwlock_t d_mut;    
+    cmap_t d_map;
+  };
+
+  vector<MapCombo> d_maps;
+  MapCombo& getMap(const DNSName& qname)
+  {
+    return d_maps[qname.hash() % d_maps.size()];
+  }
+
+  bool getEntryLocked(cmap_t& map, const DNSName &content, uint16_t qtype, vector<DNSZoneRecord>& entry, int zoneID, time_t now);
+  void cleanupIfNeeded();
+
+  AtomicCounter d_ops{0};
+  AtomicCounter *d_statnumhit;
+  AtomicCounter *d_statnummiss;
+  AtomicCounter *d_statnumentries;
+
+  uint64_t d_maxEntries{0};
+  time_t d_lastclean; // doesn't need to be atomic
+  unsigned long d_nextclean{4906};
+  unsigned int d_cleaninterval{4096};
+  bool d_cleanskipped{false};
+
+  static const unsigned int s_mincleaninterval=1000, s_maxcleaninterval=300000;
+};
+
+#endif /* AUTH_QUERYCACHE_HH */
index 0adbe370df6da4ceeeb3a9ad81caa3032d1e54ca..f6916832109c8601b0c58ef578f57bfc631afa23 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef PDNS_CACHECLEANER_HH
 #define PDNS_CACHECLEANER_HH
 
+#include "lock.hh"
+
 // this function can clean any cache that has a getTTD() method on its entries, and a 'sequence' index as its second index
 // the ritual is that the oldest entries are in *front* of the sequence collection, so on a hit, move an item to the end
 // on a miss, move it to the beginning
@@ -101,4 +103,95 @@ template <typename T> void moveCacheItemToBack(T& collection, typename T::iterat
   moveCacheItemToFrontOrBack(collection, iter, false);
 }
 
+template <typename T> uint64_t pruneLockedCollectionsVector(vector<T>& maps, uint64_t maxCached, uint64_t cacheSize)
+{
+  time_t now = time(nullptr);
+  uint64_t totErased = 0;
+  uint64_t toTrim = 0;
+  uint64_t lookAt = 0;
+
+  // two modes - if toTrim is 0, just look through 10%  of the cache and nuke everything that is expired
+  // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
+  if (maxCached && cacheSize > maxCached) {
+    toTrim = cacheSize - maxCached;
+    lookAt = 5 * toTrim;
+  } else {
+    lookAt = cacheSize / 10;
+  }
+
+  for(auto& mc : maps) {
+    WriteLock wl(&mc.d_mut);
+    auto& sidx = boost::multi_index::get<2>(mc.d_map);
+    uint64_t erased = 0, lookedAt = 0;
+    for(auto i = sidx.begin(); i != sidx.end(); lookedAt++) {
+      if(i->ttd < now) {
+        i = sidx.erase(i);
+        erased++;
+      } else {
+        ++i;
+      }
+
+      if(toTrim && erased > toTrim / maps.size())
+        break;
+
+      if(lookedAt > lookAt / maps.size())
+        break;
+    }
+    totErased += erased;
+  }
+
+  return totErased;
+}
+
+template <typename T> uint64_t purgeLockedCollectionsVector(vector<T>& maps)
+{
+  uint64_t delcount=0;
+
+  for(auto& mc : maps) {
+    WriteLock wl(&mc.d_mut);
+    delcount += mc.d_map.size();
+    mc.d_map.clear();
+  }
+
+  return delcount;
+}
+
+template <typename T> uint64_t purgeLockedCollectionsVector(vector<T>& maps, const std::string& match)
+{
+  uint64_t delcount=0;
+  string prefix(match);
+  prefix.resize(prefix.size()-1);
+  DNSName dprefix(prefix);
+  for(auto& mc : maps) {
+    WriteLock wl(&mc.d_mut);
+    auto& idx = boost::multi_index::get<1>(mc.d_map);
+    auto iter = idx.lower_bound(dprefix);
+    auto start = iter;
+
+    for(; iter != idx.end(); ++iter) {
+      if(!iter->qname.isPartOf(dprefix)) {
+        break;
+      }
+      delcount++;
+    }
+    idx.erase(start, iter);
+  }
+
+  return delcount;
+}
+
+template <typename T> uint64_t purgeExactLockedCollection(T& mc, const DNSName& qname)
+{
+  uint64_t delcount=0;
+  WriteLock wl(&mc.d_mut);
+  auto& idx = boost::multi_index::get<1>(mc.d_map);
+  auto range = idx.equal_range(qname);
+  if(range.first != range.second) {
+    delcount += distance(range.first, range.second);
+    idx.erase(range.first, range.second);
+  }
+
+  return delcount;
+}
+
 #endif
index 1fa7275715993a8bc72a5b67868531c91ae981b9..3bbe126db41786566236a7344fafca341db4a187 100644 (file)
@@ -39,7 +39,8 @@ typedef Distributor<DNSPacket,DNSPacket,PacketHandler> DNSDistributor;
 
 ArgvMap theArg;
 StatBag S;  //!< Statistics are gathered across PDNS via the StatBag class S
-PacketCache PC; //!< This is the main PacketCache, shared across all threads
+AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads
+AuthQueryCache QC;
 DNSProxy *DP;
 DynListener *dl;
 CommunicatorClass Communicator;
@@ -170,7 +171,8 @@ void declareArguments()
   ::arg().set("setuid","If set, change user id to this uid for more security")="";
   ::arg().set("setgid","If set, change group id to this gid for more security")="";
 
-  ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
+  ::arg().set("max-cache-entries", "Maximum number of entries in the query cache")="1000000";
+  ::arg().set("max-packet-cache-entries", "Maximum number of entries in the packet cache")="1000000";
   ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
   ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
   ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
@@ -278,12 +280,6 @@ void declareStats(void)
 
   S.declare("qsize-q","Number of questions waiting for database attention", getQCount);
 
-  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
-  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
-
-  S.declare("query-cache-hit","Number of hits on the query cache");
-  S.declare("query-cache-miss","Number of misses on the query cache");
-
   S.declare("dnsupdate-queries", "DNS update packets received.");
   S.declare("dnsupdate-answers", "DNS update packets successfully answered.");
   S.declare("dnsupdate-refused", "DNS update packets that are refused.");
@@ -436,7 +432,7 @@ void *qthread(void *number)
         continue;
       }
     }
-    
+
     if(distributor->isOverloaded()) {
       if(logDNSQueries) 
         L<<"Dropped query, backends are overloaded"<<endl;
@@ -488,6 +484,10 @@ void mainthread()
    DNSPacket::s_udpTruncationThreshold = std::max(512, ::arg().asNum("udp-truncation-threshold"));
    DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing");
 
+   PC.setTTL(::arg().asNum("cache-ttl"));
+   PC.setMaxEntries(::arg().asNum("max-packet-cache-entries"));
+   QC.setMaxEntries(::arg().asNum("max-cache-entries"));
+
    stubParseResolveConf();
 
    if(!::arg()["chroot"].empty()) {
index fed4fcc2ad49121efd82655e9c22d0259018d5cc..d3874cd6b57a0025e768b71ccb82083ec6af9847 100644 (file)
@@ -22,7 +22,8 @@
 #ifndef COMMON_STARTUP_HH
 #define COMMON_STARTUP_HH
 
-#include "packetcache.hh"
+#include "auth-packetcache.hh"
+#include "auth-querycache.hh"
 #include "utility.hh"
 #include "arguments.hh"
 #include "communicator.hh"
@@ -36,7 +37,8 @@
 
 extern ArgvMap theArg;
 extern StatBag S;  //!< Statistics are gathered across PDNS via the StatBag class S
-extern PacketCache PC; //!< This is the main PacketCache, shared across all threads
+extern AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads
+extern AuthQueryCache QC;
 extern DNSProxy *DP;
 extern DynListener *dl;
 extern CommunicatorClass Communicator;
index 1b28744a460e8190edc0316aa0480e6e38132ed7..6af20a75e61637e98825cc875dd6df44c9f94efb 100644 (file)
@@ -130,6 +130,7 @@ DNSPacket::DNSPacket(const DNSPacket &orig)
   d=orig.d;
 
   d_isQuery = orig.d_isQuery;
+  d_hash = orig.d_hash;
 }
 
 void DNSPacket::setRcode(int v)
index 4b740344a9b29a2a444ce88bb08275435f0bbba2..7fb90f3cf07292cd406ae6b332a12409300c3625 100644 (file)
@@ -137,6 +137,9 @@ public:
     d_ednsrcode=extRCode;
   };
   uint8_t getEDNSRCode() const { return d_ednsrcode; };
+  uint32_t getHash() const { return d_hash; };
+  void setHash(uint32_t hash) { d_hash = hash; };
+
   //////// DATA !
 
   DNSName qdomain;  //!< qname of the question 4 - unsure how this is used
@@ -188,6 +191,8 @@ private:
   // WARNING! This is really 12 bits
   uint16_t d_ednsrcode;
 
+  uint32_t d_hash{0};
+  
   bool d_compress; // 1
   bool d_tsigtimersonly;
   bool d_wantsnsid;
index 91c60a416b6be28da38afe3f4e8f1aa972ea1d32..9dbbd060c73c7e9af6fef05c40801785c23fae00 100644 (file)
@@ -34,7 +34,6 @@
 #include "dns_random.hh"
 
 extern StatBag S;
-extern PacketCache PC;
 
 DNSProxy::DNSProxy(const string &remote)
 {
@@ -269,7 +268,6 @@ void DNSProxy::mainloop(void)
         if(sendmsg(i->second.outsock, &msgh, 0) < 0)
           L<<Logger::Warning<<"dnsproxy.cc: Error sending reply with sendmsg (socket="<<i->second.outsock<<"): "<<strerror(errno)<<endl;
         
-        PC.insert(&q, &p, true);
         i->second.created=0;
       }
     }
index 9d8a4e0b7243816bc686c4d5bc073f0765ba0a5d..916a23b9958ed866630e8cfe8bb7e2a51e5db80b 100644 (file)
@@ -94,14 +94,14 @@ void DNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t
   d_sor=d_content.size(); // this will remind us where to stuff the record size
 }
 
-void DNSPacketWriter::addOpt(uint16_t udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options)
+void DNSPacketWriter::addOpt(uint16_t udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options, uint8_t version)
 {
   uint32_t ttl=0;
 
   EDNS0Record stuff;
 
   stuff.extRCode=extRCode;
-  stuff.version=0;
+  stuff.version=version;
   stuff.Z=htons(Z);
 
   static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)");
index 3c5f33e266d123fe094bdf45c8a88204da2dedb1..49cf6955cac9ee49dac7bff778d7a02eac396deb 100644 (file)
@@ -70,7 +70,7 @@ public:
 
   /** Shorthand way to add an Opt-record, for example for EDNS0 purposes */
   typedef vector<pair<uint16_t,std::string> > optvect_t;
-  void addOpt(uint16_t udpsize, int extRCode, int Z, const optvect_t& options=optvect_t());
+  void addOpt(uint16_t udpsize, int extRCode, int Z, const optvect_t& options=optvect_t(), uint8_t version=0);
 
   /** needs to be called after the last record is added, but can be called again and again later on. Is called internally by startRecord too.
       The content of the vector<> passed to the constructor is inconsistent until commit is called.
index c787a80ef6d001605dec9c6ef62c8e4988122bf7..1f465054e2287283897de440294b58eebad59898 100644 (file)
@@ -22,7 +22,9 @@
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include "packetcache.hh"
+#include "auth-caches.hh"
+#include "auth-querycache.hh"
+#include "auth-packetcache.hh"
 #include "utility.hh"
 #include "dynhandler.hh"
 #include "statbag.hh"
@@ -121,14 +123,13 @@ string DLUptimeHandler(const vector<string>&parts, Utility::pid_t ppid)
 
 string DLPurgeHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
-  extern PacketCache PC;  
   DNSSECKeeper dk;
   ostringstream os;
   int ret=0;
 
   if(parts.size()>1) {
     for (vector<string>::const_iterator i=++parts.begin();i<parts.end();++i) {
-      ret+=PC.purge(*i);
+      ret+=purgeAuthCaches(*i);
       if(!boost::ends_with(*i, "$"))
        dk.clearCaches(DNSName(*i));
       else
@@ -136,7 +137,7 @@ string DLPurgeHandler(const vector<string>&parts, Utility::pid_t ppid)
     }
   }
   else {
-    ret=PC.purge();
+    ret = purgeAuthCaches();
     dk.clearAllCaches();
   }
 
@@ -146,11 +147,13 @@ string DLPurgeHandler(const vector<string>&parts, Utility::pid_t ppid)
 
 string DLCCHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
-  extern PacketCache PC;  
-  map<char,int> counts=PC.getCounts();
+  extern AuthPacketCache PC;
+  extern AuthQueryCache QC;
+  map<char,uint64_t> counts=QC.getCounts();
+  uint64_t packetEntries = PC.size();
   ostringstream os;
   bool first=true;
-  for(map<char,int>::const_iterator i=counts.begin();i!=counts.end();++i) {
+  for(map<char,uint64_t>::const_iterator i=counts.begin();i!=counts.end();++i) {
     if(!first) 
       os<<", ";
     first=false;
@@ -159,13 +162,12 @@ string DLCCHandler(const vector<string>&parts, Utility::pid_t ppid)
       os<<"negative queries: ";
     else if(i->first=='Q')
       os<<"queries: ";
-    else if(i->first=='p')
-      os<<"packets: ";
     else 
       os<<"unknown: ";
 
     os<<i->second;
   }
+  os<<"packets: "<<packetEntries;
 
   return os.str();
 }
index 310b11bec661af684cc2f719d4a0670ca1ce3464..667cb76486be5f628674ddfb4d8a10545c24e83c 100644 (file)
@@ -22,7 +22,7 @@
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include "packetcache.hh"
+#include "auth-caches.hh"
 #include "utility.hh"
 #include <errno.h>
 #include "communicator.hh"
@@ -149,8 +149,7 @@ void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
   // do this via the FindNS class, d_fns
   
   for(auto& di : cmdomains) {
-    extern PacketCache PC;
-    PC.purgeExact(di.zone);
+    purgeAuthCachesExact(di.zone);
     queueNotifyDomain(di, B);
     di.backend->setNotified(di.id, di.serial);
   }
diff --git a/pdns/packetcache.cc b/pdns/packetcache.cc
deleted file mode 100644 (file)
index 295f4fc..0000000
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * This file is part of PowerDNS or dnsdist.
- * Copyright -- PowerDNS.COM B.V. and its contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * In addition, for the avoidance of any doubt, permission is granted to
- * link this program with OpenSSL and to (re)distribute the binaries
- * produced as the result of such linking.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include "utility.hh"
-#include "packetcache.hh"
-#include "logger.hh"
-#include "arguments.hh"
-#include "statbag.hh"
-#include <map>
-#include <boost/algorithm/string.hpp>
-
-const unsigned int PacketCache::s_mincleaninterval, PacketCache::s_maxcleaninterval;
-
-extern StatBag S;
-
-PacketCache::PacketCache()
-{
-  d_ops=0;
-  d_maps.resize(1024);
-  for(auto& mc : d_maps) {
-    pthread_rwlock_init(&mc.d_mut, 0);
-  }
-
-  d_ttl=-1;
-
-  d_lastclean=time(0);
-  d_cleanskipped=false;
-  d_nextclean=d_cleaninterval=4096;
-
-  S.declare("packetcache-hit");
-  S.declare("packetcache-miss");
-  S.declare("packetcache-size");
-
-  d_statnumhit=S.getPointer("packetcache-hit");
-  d_statnummiss=S.getPointer("packetcache-miss");
-  d_statnumentries=S.getPointer("packetcache-size");
-}
-
-PacketCache::~PacketCache()
-{
-  try {
-    //  WriteLock l(&d_mut);
-    vector<WriteLock*> locks;
-    for(auto& mc : d_maps) {
-      locks.push_back(new WriteLock(&mc.d_mut));
-    }
-    for(auto wl : locks) {
-      delete wl;
-    }
-  }
-  catch(...) {
-  }
-}
-
-
-
-int PacketCache::get(DNSPacket *p, DNSPacket *cached)
-{
-  extern StatBag S;
-
-  if(d_ttl<0) 
-    getTTLS();
-
-  cleanupIfNeeded();
-
-  if(!d_ttl) {
-    (*d_statnummiss)++;
-    return 0;
-  }
-    
-  if(ntohs(p->d.qdcount)!=1) // we get confused by packets with more than one question
-    return 0;
-
-  string value;
-  bool haveSomething;
-  {
-    auto& mc=getMap(p->qdomain);
-    TryReadLock l(&mc.d_mut); // take a readlock here
-    if(!l.gotIt()) {
-      S.inc("deferred-cache-lookup");
-      return 0;
-    }
-
-    uint16_t maxReplyLen = p->d_tcp ? 0xffff : p->getMaxReplyLen();
-    haveSomething=getEntryLocked(p->qdomain, p->qtype, PacketCache::PACKETCACHE, value, -1, maxReplyLen, p->d_dnssecOk, p->hasEDNS());
-  }
-  if(haveSomething) {
-    (*d_statnumhit)++;
-    if(cached->noparse(value.c_str(), value.size()) < 0)
-      return 0;
-    cached->spoofQuestion(p); // for correct case
-    cached->qdomain=p->qdomain;
-    cached->qtype=p->qtype;
-    return 1;
-  }
-
-  //  cerr<<"Packet cache miss for '"<<p->qdomain<<"'"<<endl;
-  (*d_statnummiss)++;
-  return 0; // bummer
-}
-
-void PacketCache::getTTLS()
-{
-  d_ttl=::arg().asNum("cache-ttl");
-}
-
-
-void PacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxttl)
-{
-  if(d_ttl < 0)
-    getTTLS();
-  
-  if(ntohs(q->d.qdcount)!=1) {
-    return; // do not try to cache packets with multiple questions
-  }
-
-  if(q->qclass != QClass::IN) // we only cache the INternet
-    return;
-
-  uint16_t maxReplyLen = q->d_tcp ? 0xffff : q->getMaxReplyLen();
-  unsigned int ourttl = d_ttl;
-  if(maxttl<ourttl)
-    ourttl=maxttl;
-  insert(q->qdomain, q->qtype, PacketCache::PACKETCACHE, r->getString(), ourttl, -1,
-    maxReplyLen, q->d_dnssecOk, q->hasEDNS());
-}
-
-// universal key appears to be: qname, qtype, kind (packet, query cache), optionally zoneid
-void PacketCache::insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const string& value, unsigned int ttl, int zoneID, 
-  unsigned int maxReplyLen, bool dnssecOk, bool EDNS)
-{
-  cleanupIfNeeded();
-
-  if(!ttl)
-    return;
-  
-  //cerr<<"Inserting qname '"<<qname<<"', cet: "<<(int)cet<<", qtype: "<<qtype.getName()<<", ttl: "<<ttl<<", maxreplylen: "<<maxReplyLen<<", hasEDNS: "<<EDNS<<endl;
-  CacheEntry val;
-  val.created=time(0);
-  val.ttd=val.created+ttl;
-  val.qname=qname;
-  val.qtype=qtype.getCode();
-  val.value=value;
-  val.ctype=cet;
-  val.maxReplyLen = maxReplyLen;
-  val.dnssecOk = dnssecOk;
-  val.zoneID = zoneID;
-  val.hasEDNS = EDNS;
-  
-  auto& mc = getMap(val.qname);
-
-  TryWriteLock l(&mc.d_mut);
-  if(l.gotIt()) { 
-    bool success;
-    cmap_t::iterator place;
-    tie(place, success)=mc.d_map.insert(val);
-
-    if(!success)
-      mc.d_map.replace(place, val);
-    else
-      (*d_statnumentries)++;
-  }
-  else 
-    S.inc("deferred-cache-inserts"); 
-}
-
-void PacketCache::insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const vector<DNSZoneRecord>& value, unsigned int ttl, int zoneID)
-{
-  cleanupIfNeeded();
-
-  if(!ttl)
-    return;
-  
-  //cerr<<"Inserting qname '"<<qname<<"', cet: "<<(int)cet<<", qtype: "<<qtype.getName()<<", ttl: "<<ttl<<", maxreplylen: "<<maxReplyLen<<", hasEDNS: "<<EDNS<<endl;
-  CacheEntry val;
-  val.created=time(0);
-  val.ttd=val.created+ttl;
-  val.qname=qname;
-  val.qtype=qtype.getCode();
-  val.drs=value;
-  val.ctype=cet;
-  val.maxReplyLen = 0;
-  val.dnssecOk = false;
-  val.zoneID = zoneID;
-  val.hasEDNS = false;
-  
-  auto& mc = getMap(val.qname);
-
-  TryWriteLock l(&mc.d_mut);
-  if(l.gotIt()) { 
-    bool success;
-    cmap_t::iterator place;
-    tie(place, success)=mc.d_map.insert(val);
-
-    if(!success)
-      mc.d_map.replace(place, val);
-    else
-      (*d_statnumentries)++;
-  }
-  else 
-    S.inc("deferred-cache-inserts"); 
-}
-
-
-/* clears the entire packetcache. */
-int PacketCache::purge()
-{
-  int delcount=0;
-  for(auto& mc : d_maps) {
-    WriteLock l(&mc.d_mut);
-    delcount+=mc.d_map.size();
-    mc.d_map.clear();
-  }
-  d_statnumentries->store(0);
-  return delcount;
-}
-
-int PacketCache::purgeExact(const DNSName& qname)
-{
-  int delcount=0;
-  auto& mc = getMap(qname);
-
-  WriteLock l(&mc.d_mut);
-  auto range = mc.d_map.equal_range(tie(qname));
-  if(range.first != range.second) {
-    delcount+=distance(range.first, range.second);
-    mc.d_map.erase(range.first, range.second);
-  }
-  *d_statnumentries-=delcount;
-  return delcount;
-}
-
-/* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
-int PacketCache::purge(const string &match)
-{
-  if(ends_with(match, "$")) {
-    int delcount=0;
-    string prefix(match);
-    prefix.resize(prefix.size()-1);
-    DNSName dprefix(prefix);
-    for(auto& mc : d_maps) {
-      WriteLock l(&mc.d_mut);
-      cmap_t::const_iterator iter = mc.d_map.lower_bound(tie(dprefix));
-      auto start=iter;
-
-      for(; iter != mc.d_map.end(); ++iter) {
-       if(!iter->qname.isPartOf(dprefix)) {
-         break;
-       }
-       delcount++;
-      }
-      mc.d_map.erase(start, iter);
-    }
-    *d_statnumentries-=delcount;
-    return delcount;
-  }
-  else {
-    return purgeExact(DNSName(match));
-  }
-}
-// called from ueberbackend
-bool PacketCache::getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSZoneRecord>& value, int zoneID)
-{
-  if(d_ttl<0) 
-    getTTLS();
-
-  cleanupIfNeeded();
-
-  auto& mc=getMap(qname);
-
-  TryReadLock l(&mc.d_mut); // take a readlock here
-  if(!l.gotIt()) {
-    S.inc( "deferred-cache-lookup");
-    return false;
-  }
-
-  return getEntryLocked(qname, qtype, cet, value, zoneID);
-}
-
-
-bool PacketCache::getEntryLocked(const DNSName &qname, const QType& qtype, CacheEntryType cet, string& value, int zoneID,
-  unsigned int maxReplyLen, bool dnssecOK, bool hasEDNS)
-{
-  uint16_t qt = qtype.getCode();
-  //cerr<<"Lookup for maxReplyLen: "<<maxReplyLen<<endl;
-  auto& mc=getMap(qname);
-  //  cmap_t::const_iterator i=mc.d_map.find(tie(qname, qt, cet, zoneID, maxReplyLen, dnssecOK, hasEDNS));
-
-  auto& idx = boost::multi_index::get<UnorderedNameTag>(mc.d_map);
-  auto range=idx.equal_range(tie(qname, qt, cet, zoneID));
-
-  if(range.first == range.second)
-    return false;
-  time_t now=time(0);
-  for(auto iter = range.first ; iter != range.second; ++iter) {
-    if(maxReplyLen == iter->maxReplyLen && dnssecOK == iter->dnssecOk && hasEDNS == iter->hasEDNS ) {
-      if(iter->ttd > now) {
-        value = iter->value;
-        return true;
-      }
-    }
-  }
-  
-  return false;
-}
-                          
-bool PacketCache::getEntryLocked(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSZoneRecord>& value, int zoneID)
-{
-  uint16_t qt = qtype.getCode();
-  //cerr<<"Lookup for maxReplyLen: "<<maxReplyLen<<endl;
-  auto& mc=getMap(qname);
-  auto& idx = boost::multi_index::get<UnorderedNameTag>(mc.d_map);
-  auto i=idx.find(tie(qname, qt, cet, zoneID));
-  if(i==idx.end())
-    return false;
-
-  time_t now=time(0);
-  if(i->ttd > now) {
-    value = i->drs;
-    return true;
-  }
-  return false;
-}
-
-
-map<char,int> PacketCache::getCounts()
-{
-  int packets=0, queryCacheEntries=0, negQueryCacheEntries=0;
-
-  for(auto& mc : d_maps) {
-    ReadLock l(&mc.d_mut);
-    
-    for(cmap_t::const_iterator iter = mc.d_map.begin() ; iter != mc.d_map.end(); ++iter) {
-      if(iter->ctype == PACKETCACHE)
-         packets++;
-      else if(iter->ctype == QUERYCACHE) {
-       if(iter->value.empty())
-         negQueryCacheEntries++;
-       else
-         queryCacheEntries++;
-      }
-    }
-  }
-  map<char,int> ret;
-
-  ret['!']=negQueryCacheEntries;
-  ret['Q']=queryCacheEntries;
-  ret['p']=packets;
-  return ret;
-}
-
-
-void PacketCache::cleanup()
-{
-  unsigned int maxCached = ::arg().asNum("max-cache-entries");
-  unsigned long cacheSize = *d_statnumentries;
-
-  // two modes - if toTrim is 0, just look through 10%  of the cache and nuke everything that is expired
-  // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
-  unsigned int toTrim = 0, lookAt = 0;
-  if(maxCached && cacheSize > maxCached) {
-    toTrim = cacheSize - maxCached;
-    lookAt = 5 * toTrim;
-  } else {
-    lookAt = cacheSize / 10;
-  }
-
-  DLOG(L<<"Starting cache clean, cacheSize: "<<cacheSize<<", lookAt: "<<lookAt<<", toTrim: "<<toTrim<<endl);
-
-  time_t now = time(0);
-  unsigned int totErased = 0;
-  for(auto& mc : d_maps) {
-    WriteLock wl(&mc.d_mut);
-    auto& sidx = boost::multi_index::get<SequenceTag>(mc.d_map);
-    unsigned int erased = 0, lookedAt = 0;
-    for(auto i = sidx.begin(); i != sidx.end(); lookedAt++) {
-      if(i->ttd < now) {
-        i = sidx.erase(i);
-        erased++;
-      } else {
-        ++i;
-      }
-
-      if(toTrim && erased > toTrim / d_maps.size())
-        break;
-
-      if(lookedAt > lookAt / d_maps.size())
-        break;
-    }
-    totErased += erased;
-  }
-  *d_statnumentries -= totErased;
-
-  DLOG(L<<"Done with cache clean, cacheSize: "<<*d_statnumentries<<", totErased"<<totErased<<endl);
-}
-
-/* the logic:
-   after d_nextclean operations, we clean. We also adjust the cleaninterval
-   a bit so we slowly move it to a value where we clean roughly every 30 seconds.
-
-   If d_nextclean has reached its maximum value, we also test if we were called
-   within 30 seconds, and if so, we skip cleaning. This means that under high load,
-   we will not clean more often than every 30 seconds anyhow.
-*/
-
-void PacketCache::cleanupIfNeeded()
-{
-  if (d_ops++ == d_nextclean) {
-    int timediff = max((int)(time(0) - d_lastclean), 1);
-
-    DLOG(L<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
-
-    if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
-      d_cleanskipped = true;
-      d_nextclean += d_cleaninterval;
-
-      DLOG(L<<"cleaning skipped, timediff: "<<timediff<<endl);
-
-      return;
-    }
-
-    if(!d_cleanskipped) {
-      d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
-      d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
-      d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
-
-      DLOG(L<<"new cleaninterval: "<<d_cleaninterval<<endl);
-    } else {
-      d_cleanskipped = false;
-    }
-
-    d_nextclean += d_cleaninterval;
-    d_lastclean=time(0);
-    cleanup();
-  }
-}
index 357acedd1aa2f2ea0f5dc38f6fc1c0d9bd1e4be1..b0785fd9d96dfbf8c5b9bd585bb8faef5ffc9a2a 100644 (file)
 #ifndef PACKETCACHE_HH
 #define PACKETCACHE_HH
 
-#include <string>
-#include <utility>
-#include <map>
-#include <map>
-#include "dns.hh"
-#include <boost/version.hpp>
-#include "namespaces.hh"
-using namespace ::boost::multi_index;
-
-#include "namespaces.hh"
-#include <boost/multi_index/hashed_index.hpp> 
-#include "dnspacket.hh"
-#include "lock.hh"
-#include "statbag.hh"
-
-/** This class performs 'whole packet caching'. Feed it a question packet and it will
-    try to find an answer. If you have an answer, insert it to have it cached for later use. 
-    Take care not to replace existing cache entries. While this works, it is wasteful. Only
-    insert packets that where not found by get()
-
-    Locking! 
-
-    The cache itself is protected by a read/write lock. Because deleting is a two step process, which 
-    first marks and then sweeps, a second lock is present to prevent simultaneous inserts and deletes.
-*/
+#include "ednsoptions.hh"
+#include "misc.hh"
+#include "iputils.hh"
 
 class PacketCache : public boost::noncopyable
 {
-public:
-  PacketCache();
-  ~PacketCache();
-  enum CacheEntryType { PACKETCACHE, QUERYCACHE};
-
-  void insert(DNSPacket *q, DNSPacket *r, unsigned int maxttl=UINT_MAX);  //!< We copy the contents of *p into our cache. Do not needlessly call this to insert questions already in the cache as it wastes resources
-
-  void insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const string& value, unsigned int ttl, int zoneID=-1,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool EDNS=false);
-
-  void insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const vector<DNSZoneRecord>& content, unsigned int ttl, int zoneID=-1);
-
-  int get(DNSPacket *p, DNSPacket *q); //!< We return a dynamically allocated copy out of our cache. You need to delete it. You also need to spoof in the right ID with the DNSPacket.spoofID() method.
-  bool getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, string& entry, int zoneID=-1,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool hasEDNS=false);
-  bool getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSZoneRecord>& entry, int zoneID=-1);
-  
-
-  int size() { return *d_statnumentries; } //!< number of entries in the cache
-  void cleanupIfNeeded();
-  void cleanup(); //!< force the cache to preen itself from expired packets
-  int purge();
-  int purge(const std::string& match); // could be $ terminated. Is not a dnsname!
-  int purgeExact(const DNSName& qname); // no wildcard matching here
-
-  map<char,int> getCounts();
-private:
-  bool getEntryLocked(const DNSName &content, const QType& qtype, CacheEntryType cet, string& entry, int zoneID=-1,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool hasEDNS=false);
-  bool getEntryLocked(const DNSName &content, const QType& qtype, CacheEntryType cet, vector<DNSZoneRecord>& entry, int zoneID=-1);
-
-
-  struct CacheEntry
+protected:
+  static uint32_t canHashPacket(const std::string& packet, bool skipECS=true)
   {
-    CacheEntry() { qtype = ctype = 0; zoneID = -1; dnssecOk=false; hasEDNS=false; created=0; ttd=0; maxReplyLen=512;}
-
-    DNSName qname;
-    string value;
-    vector<DNSZoneRecord> drs;
-    time_t created;
-    time_t ttd;
-
-    uint16_t qtype;
-    uint16_t ctype;
-    int zoneID;
-    unsigned int maxReplyLen;
-
-    bool dnssecOk;
-    bool hasEDNS;
-  };
-
-  void getTTLS();
-
-  struct UnorderedNameTag{};
-  struct SequenceTag{};
-  typedef multi_index_container<
-    CacheEntry,
-    indexed_by <
-                ordered_unique<
-                      composite_key< 
-                        CacheEntry,
-                        member<CacheEntry,DNSName,&CacheEntry::qname>,
-                        member<CacheEntry,uint16_t,&CacheEntry::qtype>,
-                        member<CacheEntry,uint16_t, &CacheEntry::ctype>,
-                        member<CacheEntry,int, &CacheEntry::zoneID>,
-                        member<CacheEntry,unsigned int, &CacheEntry::maxReplyLen>,
-                        member<CacheEntry,bool, &CacheEntry::dnssecOk>,
-                        member<CacheEntry,bool, &CacheEntry::hasEDNS>
-                        >,
-                      composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<uint16_t>, std::less<int>,
-                          std::less<unsigned int>, std::less<bool>, std::less<bool> >
-                       >,
-      hashed_non_unique<tag<UnorderedNameTag>, composite_key<CacheEntry,
-                                                             member<CacheEntry,DNSName,&CacheEntry::qname>,
-                                                             member<CacheEntry,uint16_t,&CacheEntry::qtype>,
-                                                             member<CacheEntry,uint16_t, &CacheEntry::ctype>,
-                                                             member<CacheEntry,int, &CacheEntry::zoneID> > > ,
-      sequenced<tag<SequenceTag>>
-                           >
-  > cmap_t;
-
-
-  struct MapCombo
-  {
-    pthread_rwlock_t d_mut;    
-    cmap_t d_map;
-  };
-
-  vector<MapCombo> d_maps;
-  MapCombo& getMap(const DNSName& qname) 
-  {
-    return d_maps[qname.hash() % d_maps.size()];
+    uint32_t ret = 0;
+    ret=burtle((const unsigned char*)packet.c_str() + 2, 10, ret); // rest of dnsheader, skip id
+    size_t packetSize = packet.size();
+    size_t pos = 12;
+    const char* end = packet.c_str() + packetSize;
+    const char* p = packet.c_str() + pos;
+
+    for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
+      const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
+      ret=burtle(&l, 1, ret);
+    }                           // XXX the embedded 0 in the qname will break the subnet stripping
+
+    struct dnsheader* dh = (struct dnsheader*)packet.c_str();
+    const char* skipBegin = p;
+    const char* skipEnd = p;
+    /* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
+       + OPT root label (1), type (2), class (2) and ttl (4)
+       + the OPT RR rdlen (2)
+       = 16
+    */
+    if(skipECS && ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
+      char* optionBegin = nullptr;
+      size_t optionLen = 0;
+      /* skip the final empty label (1), the qtype (2), qclass (2) */
+      /* root label (1), type (2), class (2) and ttl (4) */
+      int res = getEDNSOption((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
+      if (res == 0) {
+        skipBegin = optionBegin;
+        skipEnd = optionBegin + optionLen;
+      }
+    }
+    if (skipBegin > p) {
+      // cerr << "Hashing from " << (p-packet.c_str()) << " for " << skipBegin-p << "bytes, end is at "<< end-packet.c_str() << endl;
+      ret = burtle((const unsigned char*)p, skipBegin-p, ret);
+    }
+    if (skipEnd < end) {
+      // cerr << "Hashing from " << (skipEnd-packet.c_str()) << " for " << end-skipEnd << "bytes, end is at " << end-packet.c_str() << endl;
+      ret = burtle((const unsigned char*) skipEnd, end-skipEnd, ret);
+    }
+
+    return ret;
   }
-
-  AtomicCounter d_ops;
-  time_t d_lastclean; // doesn't need to be atomic
-  unsigned long d_nextclean;
-  unsigned int d_cleaninterval;
-  bool d_cleanskipped;
-  AtomicCounter *d_statnumhit;
-  AtomicCounter *d_statnummiss;
-  AtomicCounter *d_statnumentries;
-
-  int d_ttl;
-
-  static const unsigned int s_mincleaninterval=1000, s_maxcleaninterval=300000;
 };
 
-
-
 #endif /* PACKETCACHE_HH */
index 05f206d4b9be6b9598eb3ec3d98be67c0c8e3266..52bea5841dc4a6174e58eb207874307bd48e1fd2 100644 (file)
@@ -1484,7 +1484,7 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p)
     
     for(const auto& rr: r->getRRS()) {
       if(rr.scopeMask) {
-        noCache=1;
+        noCache=true;
         break;
       }
     }
@@ -1492,7 +1492,7 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p)
       addRRSigs(d_dk, B, authSet, r->getRRS());
       
     r->wrapup(); // needed for inserting in cache
-    if(!noCache)
+    if(!noCache && p->couldBeCached())
       PC.insert(p, r, r->getMinTTL()); // in the packet cache
   }
   catch(DBException &e) {
index 7157b29a18a96d62e4b44ef13f554e547f5876e5..949009f2d140834103d7ac8fc3e3c7074a998f23 100644 (file)
@@ -13,7 +13,8 @@
 #include "dnsbackend.hh"
 #include "ueberbackend.hh"
 #include "arguments.hh"
-#include "packetcache.hh"
+#include "auth-packetcache.hh"
+#include "auth-querycache.hh"
 #include "zoneparser-tng.hh"
 #include "signingpipe.hh"
 #include "dns_random.hh"
@@ -29,7 +30,8 @@
 #endif
 
 StatBag S;
-PacketCache PC;
+AuthPacketCache PC;
+AuthQueryCache QC;
 
 namespace po = boost::program_options;
 po::variables_map g_vm;
@@ -101,12 +103,7 @@ void loadMainConfig(const std::string& configdir)
   //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
 
   S.declare("qsize-q","Number of questions waiting for database attention");
-    
-  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
-  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
           
-  S.declare("query-cache-hit","Number of hits on the query cache");
-  S.declare("query-cache-miss","Number of misses on the query cache");
   ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
   ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";              
   ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
@@ -2076,7 +2073,7 @@ try
     return 0;
   }
 
-loadMainConfig(g_vm["config-dir"].as<string>());
+  loadMainConfig(g_vm["config-dir"].as<string>());
 
 #ifdef HAVE_LIBSODIUM
   if (sodium_init() == -1) {
index 22c6cb8272feaf7bdb6ad07b179fadbdf6bb9b5c..6186caad14c1ecd3e5ef4dcbe9c156efbc63e560 100644 (file)
@@ -49,49 +49,6 @@ static bool qrMatch(const DNSName& qname, uint16_t qtype, uint16_t qclass, const
   return qname==rname && rtype == qtype && rclass == qclass;
 }
 
-uint32_t RecursorPacketCache::canHashPacket(const std::string& origPacket)
-{
-  //  return 42; // should still work if you do this!
-  uint32_t ret=0;
-  ret=burtle((const unsigned char*)origPacket.c_str() + 2, 10, ret); // rest of dnsheader, skip id
-  const char* end = origPacket.c_str() + origPacket.size();
-  const char* p = origPacket.c_str() + 12;
-
-  for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
-    const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
-    ret=burtle(&l, 1, ret);
-  }                           // XXX the embedded 0 in the qname will break the subnet stripping
-
-  struct dnsheader* dh = (struct dnsheader*)origPacket.c_str();
-  const char* skipBegin = p;
-  const char* skipEnd = p;
-  /* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
-     + OPT root label (1), type (2), class (2) and ttl (4)
-     + the OPT RR rdlen (2)
-     = 16
-  */
-  if(ntohs(dh->arcount)==1 && (p+16) < end) {
-    char* optionBegin = nullptr;
-    size_t optionLen = 0;
-    /* skip the final empty label (1), the qtype (2), qclass (2) */
-    /* root label (1), type (2), class (2) and ttl (4) */
-    int res = getEDNSOption((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
-    if (res == 0) {
-      skipBegin = optionBegin;
-      skipEnd = optionBegin + optionLen;
-    }
-  }
-  if (skipBegin > p) {
-    //cout << "Hashing from " << (p-origPacket.c_str()) << " for " << skipBegin-p << "bytes, end is at "<< end-origPacket.c_str() << endl;
-    ret = burtle((const unsigned char*)p, skipBegin-p, ret);
-  }
-  if (skipEnd < end) {
-    //cout << "Hashing from " << (skipEnd-origPacket.c_str()) << " for " << end-skipEnd << "bytes, end is at " << end-origPacket.c_str() << endl;
-    ret = burtle((const unsigned char*) skipEnd, end-skipEnd, ret);
-  }
-  return ret;
-}
-
 bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, RecProtoBufMessage* protobufMessage)
 {
   for(auto iter = range.first ; iter != range.second ; ++ iter) {
@@ -148,7 +105,7 @@ bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string&
 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
                                             std::string* responsePacket, uint32_t* age, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
 {
-  *qhash = canHashPacket(queryPacket);
+  *qhash = canHashPacket(queryPacket, true);
   const auto& idx = d_packetCache.get<HashTag>();
   auto range = idx.equal_range(tie(tag,*qhash));
 
@@ -163,7 +120,7 @@ bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string&
 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
                                             std::string* responsePacket, uint32_t* age, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
 {
-  *qhash = canHashPacket(queryPacket);
+  *qhash = canHashPacket(queryPacket, true);
   const auto& idx = d_packetCache.get<HashTag>();
   auto range = idx.equal_range(tie(tag,*qhash));
 
@@ -191,12 +148,9 @@ void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash,
   auto iter = range.first;
 
   for( ; iter != range.second ; ++iter)  {
-    if(iter->d_type != qtype || iter->d_class != qclass)
-      continue;
-    // this only happens on insert which is relatively rare and does not need to be super fast
-    DNSName respname(iter->d_packet.c_str(), iter->d_packet.length(), sizeof(dnsheader), false, 0, 0, 0);
-    if(qname != respname)
+    if(iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname)
       continue;
+
     moveCacheItemToBack(d_packetCache, iter);
     iter->d_packet = responsePacket;
     iter->d_ttd = now + ttl;
index ca8c5d38fa2e2689d002923aed458234ef4067e0..51f4d642aab0a2761cf66a4e9bc69c6d522e7f24 100644 (file)
@@ -33,6 +33,8 @@
 #include <boost/tuple/tuple_comparison.hpp>
 #include <boost/multi_index/sequenced_index.hpp>
 
+#include "packetcache.hh"
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -46,7 +48,7 @@ using namespace ::boost::multi_index;
    you can use a query as a key too. But query and answer must compare as identical! 
    
    This precludes doing anything smart with EDNS directly from the packet */
-class RecursorPacketCache
+class RecursorPacketCache: public PacketCache
 {
 public:
   RecursorPacketCache();
@@ -88,7 +90,7 @@ private:
       return d_ttd;
     }
   };
-  uint32_t canHashPacket(const std::string& origPacket);
+
   typedef multi_index_container<
     Entry,
     indexed_by  <
index ddfb76bf9b661d29440a160a5bd8051718a031da..91f5b98ce727c201795c8910757cae24c7bc9b2b 100644 (file)
@@ -102,6 +102,7 @@ pdns_recursor_SOURCES = \
        namespaces.hh \
        nsecrecords.cc \
        opensslsigners.cc opensslsigners.hh \
+       packetcache.hh \
        pdns_recursor.cc \
        pdnsexception.hh \
        protobuf.cc protobuf.hh \
diff --git a/pdns/recursordist/packetcache.hh b/pdns/recursordist/packetcache.hh
new file mode 120000 (symlink)
index 0000000..b50f901
--- /dev/null
@@ -0,0 +1 @@
+../packetcache.hh
\ No newline at end of file
index 67c1b789946006766b5436ddb3871dc7b3f8ad70..588610f64dd8e38ba2e21eaa5e9c1e432a3adfa1 100644 (file)
@@ -4,7 +4,8 @@
 #include "packethandler.hh"
 #include "qtype.hh"
 #include "dnspacket.hh"
-#include "packetcache.hh"
+#include "auth-caches.hh"
+#include "statbag.hh"
 #include "dnsseckeeper.hh"
 #include "base64.hh"
 #include "base32.hh"
@@ -15,7 +16,6 @@
 #include "dns_random.hh"
 #include "backends/gsql/ssql.hh"
 
-extern PacketCache PC;
 extern StatBag S;
 
 pthread_mutex_t PacketHandler::s_rfc2136lock=PTHREAD_MUTEX_INITIALIZER;
@@ -966,7 +966,7 @@ int PacketHandler::processUpdate(DNSPacket *p) {
       // Purge the records!
       string zone(di.zone.toString());
       zone.append("$");
-      PC.purge(zone);
+      purgeAuthCaches(zone);
 
       L<<Logger::Info<<msgPrefix<<"Update completed, "<<changedRecords<<" changed records committed."<<endl;
     } else {
index 03cf47ffe1510eb22537330ed8bab573e3d3ef56..3bbf0b01150719555aa1b37817e760e717edd6c0 100644 (file)
@@ -23,7 +23,7 @@
 #include "config.h"
 #endif
 #include <boost/algorithm/string.hpp>
-#include "packetcache.hh"
+#include "auth-packetcache.hh"
 #include "utility.hh"
 #include "dnssecinfra.hh"
 #include "dnsseckeeper.hh"
@@ -56,7 +56,7 @@
 #include "namespaces.hh"
 #include "signingpipe.hh"
 #include "stubresolver.hh"
-extern PacketCache PC;
+extern AuthPacketCache PC;
 extern StatBag S;
 
 /**
index ca245f6a2694ebcdd95157ff26ad033ce6fee5cf..c3868a6fc9a8b9a73a8ad003a189b0cb2c0742ac 100644 (file)
@@ -9,63 +9,58 @@
 #include "iputils.hh"
 #include "nameserver.hh"
 #include "statbag.hh"
-#include "packetcache.hh"
+#include "auth-packetcache.hh"
+#include "auth-querycache.hh"
 #include "arguments.hh"
 #include <utility>
 extern StatBag S;
 
 BOOST_AUTO_TEST_SUITE(packetcache_cc)
 
-BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
-  PacketCache PC;
+BOOST_AUTO_TEST_CASE(test_AuthQueryCacheSimple) {
+  AuthQueryCache QC;
+  QC.setMaxEntries(1000000);
 
-  ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
-  ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
-  ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
-  ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
+  vector<DNSZoneRecord> records;
 
-  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
-  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
+  BOOST_CHECK_EQUAL(QC.size(), 0);
+  QC.insert(DNSName("hello"), QType(QType::A), records, 3600, 1);
+  BOOST_CHECK_EQUAL(QC.size(), 1);
+  BOOST_CHECK_EQUAL(QC.purge(), 1);
+  BOOST_CHECK_EQUAL(QC.size(), 0);
 
-
-  BOOST_CHECK_EQUAL(PC.size(), 0);
-  PC.insert(DNSName("hello"), QType(QType::A), PacketCache::QUERYCACHE, "something", 3600, 1);
-  BOOST_CHECK_EQUAL(PC.size(), 1);
-  PC.purge();
-  BOOST_CHECK_EQUAL(PC.size(), 0);
-
-  int counter=0;
+  uint64_t counter=0;
   try {
     for(counter = 0; counter < 100000; ++counter) {
       DNSName a=DNSName("hello ")+DNSName(std::to_string(counter));
       BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
 
-      PC.insert(a, QType(QType::A), PacketCache::QUERYCACHE, "something", 3600, 1);
-      if(!PC.purge(a.toString()))
-       BOOST_FAIL("Could not remove entry we just added to packet cache!");
-      PC.insert(a, QType(QType::A), PacketCache::QUERYCACHE, "something", 3600, 1);
+      QC.insert(a, QType(QType::A), records, 3600, 1);
+      if(!QC.purge(a.toString()))
+       BOOST_FAIL("Could not remove entry we just added to the query cache!");
+      QC.insert(a, QType(QType::A), records, 3600, 1);
     }
 
-    BOOST_CHECK_EQUAL(PC.size(), counter);
-    
-    int delcounter=0;
+    BOOST_CHECK_EQUAL(QC.size(), counter);
+
+    uint64_t delcounter=0;
     for(delcounter=0; delcounter < counter/100; ++delcounter) {
       DNSName a=DNSName("hello ")+DNSName(std::to_string(delcounter));
-      PC.purge(a.toString());
+      BOOST_CHECK_EQUAL(QC.purge(a.toString()), 1);
     }
-    
-    BOOST_CHECK_EQUAL(PC.size(), counter-delcounter);
-    
-    int matches=0;
+
+    BOOST_CHECK_EQUAL(QC.size(), counter-delcounter);
+
+    uint64_t matches=0;
     vector<DNSZoneRecord> entry;
-    int expected=counter-delcounter;
+    int64_t expected=counter-delcounter;
     for(; delcounter < counter; ++delcounter) {
-      if(PC.getEntry(DNSName("hello ")+DNSName(std::to_string(delcounter)), QType(QType::A), PacketCache::QUERYCACHE, entry, 1)) {
+      if(QC.getEntry(DNSName("hello ")+DNSName(std::to_string(delcounter)), QType(QType::A), entry, 1)) {
        matches++;
       }
     }
     BOOST_CHECK_EQUAL(matches, expected);
-    //    BOOST_CHECK_EQUAL(entry, "something");
+    BOOST_CHECK_EQUAL(entry.size(), records.size());
   }
   catch(PDNSException& e) {
     cerr<<"Had error: "<<e.reason<<endl;
@@ -74,14 +69,16 @@ BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
 
 }
 
-static PacketCache* g_PC;
+static AuthQueryCache* g_QC;
+static AtomicCounter g_QCmissing;
 
-static void *threadMangler(void* a)
+static void *threadQCMangler(void* a)
 try
 {
+  vector<DNSZoneRecord> records;
   unsigned int offset=(unsigned int)(unsigned long)a;
   for(unsigned int counter=0; counter < 100000; ++counter)
-    g_PC->insert(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), PacketCache::QUERYCACHE, "something", 3600, 1);    
+    g_QC->insert(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), records, 3600, 1);
   return 0;
 }
  catch(PDNSException& e) {
@@ -89,53 +86,152 @@ try
    throw;
  }
 
-AtomicCounter g_missing;
-
-static void *threadReader(void* a)
+static void *threadQCReader(void* a)
 try
 {
   unsigned int offset=(unsigned int)(unsigned long)a;
   vector<DNSZoneRecord> entry;
   for(unsigned int counter=0; counter < 100000; ++counter)
-    if(!g_PC->getEntry(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), PacketCache::QUERYCACHE, entry, 1)) {
-       g_missing++;
+    if(!g_QC->getEntry(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), entry, 1)) {
+      g_QCmissing++;
     }
   return 0;
 }
 catch(PDNSException& e) {
-  cerr<<"Had error in threadReader: "<<e.reason<<endl;
+  cerr<<"Had error in threadQCReader: "<<e.reason<<endl;
   throw;
 }
 
+BOOST_AUTO_TEST_CASE(test_QueryCacheThreaded) {
+  try {
+    AuthQueryCache QC;
+    QC.setMaxEntries(1000000);
+    g_QC=&QC;
+    pthread_t tid[4];
+    for(int i=0; i < 4; ++i)
+      pthread_create(&tid[i], 0, threadQCMangler, (void*)(i*1000000UL));
+    void* res;
+    for(int i=0; i < 4 ; ++i)
+      pthread_join(tid[i], &res);
+
+    BOOST_CHECK_EQUAL(QC.size() + S.read("deferred-cache-inserts"), 400000);
+    BOOST_CHECK_SMALL(1.0*S.read("deferred-cache-inserts"), 10000.0);
+
+    for(int i=0; i < 4; ++i)
+      pthread_create(&tid[i], 0, threadQCReader, (void*)(i*1000000UL));
+    for(int i=0; i < 4 ; ++i)
+      pthread_join(tid[i], &res);
+
+    BOOST_CHECK(S.read("deferred-cache-inserts") + S.read("deferred-cache-lookup") >= g_QCmissing);
+    //    BOOST_CHECK_EQUAL(S.read("deferred-cache-lookup"), 0); // cache cleaning invalidates this
+  }
+  catch(PDNSException& e) {
+    cerr<<"Had error: "<<e.reason<<endl;
+    throw;
+  }
+
+}
+
+static AuthPacketCache* g_PC;
+static AtomicCounter g_PCmissing;
+
+static void *threadPCMangler(void* a)
+try
+{
+  unsigned int offset=(unsigned int)(unsigned long)a;
+  for(unsigned int counter=0; counter < 100000; ++counter) {
+    vector<uint8_t> pak;
+    DNSName qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
+
+    DNSPacketWriter pw(pak, qname, QType::A);
+    DNSPacket q(true);
+    q.parse((char*)&pak[0], pak.size());
+
+    pak.clear();
+    DNSPacketWriter pw2(pak, qname, QType::A);
+    pw2.startRecord(qname, QType::A, 16, 1, DNSResourceRecord::ANSWER);
+    pw2.xfrIP(htonl(0x7f000001));
+    pw2.commit();
+
+    DNSPacket r(false);
+    r.parse((char*)&pak[0], pak.size());
+
+    /* this step is necessary to get a valid hash */
+    DNSPacket cached(false);
+    g_PC->get(&q, &cached);
+
+    g_PC->insert(&q, &r, 10);
+  }
+
+  return 0;
+}
+ catch(PDNSException& e) {
+   cerr<<"Had error: "<<e.reason<<endl;
+   throw;
+ }
+
+static void *threadPCReader(void* a)
+try
+{
+  unsigned int offset=(unsigned int)(unsigned long)a;
+  vector<DNSZoneRecord> entry;
+  for(unsigned int counter=0; counter < 100000; ++counter) {
+    vector<uint8_t> pak;
+    DNSName qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
+
+    DNSPacketWriter pw(pak, qname, QType::A);
+    DNSPacket q(true);
+    q.parse((char*)&pak[0], pak.size());
+    DNSPacket r(false);
+
+    if(!g_PC->get(&q, &r)) {
+      g_PCmissing++;
+    }
+  }
 
+  return 0;
+}
+catch(PDNSException& e) {
+  cerr<<"Had error in threadPCReader: "<<e.reason<<endl;
+  throw;
+}
 
 BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded) {
   try {
-    PacketCache PC;
+    AuthPacketCache PC;
+    PC.setMaxEntries(1000000);
+    PC.setTTL(20);
+
     g_PC=&PC;
     pthread_t tid[4];
-    for(int i=0; i < 4; ++i) 
-      pthread_create(&tid[i], 0, threadMangler, (void*)(i*1000000UL));
+    for(int i=0; i < 4; ++i)
+      pthread_create(&tid[i], 0, threadPCMangler, (void*)(i*1000000UL));
     void* res;
     for(int i=0; i < 4 ; ++i)
       pthread_join(tid[i], &res);
-    
-    BOOST_CHECK_EQUAL(PC.size() + S.read("deferred-cache-inserts"), 400000);
-    BOOST_CHECK_SMALL(1.0*S.read("deferred-cache-inserts"), 10000.0);
 
-    for(int i=0; i < 4; ++i) 
-      pthread_create(&tid[i], 0, threadReader, (void*)(i*1000000UL));
+    BOOST_CHECK_EQUAL(PC.size() + S.read("deferred-packetcache-inserts"), 400000);
+    BOOST_CHECK_SMALL(1.0*S.read("deferred-packetcache-inserts"), 10000.0);
+
+    for(int i=0; i < 4; ++i)
+      pthread_create(&tid[i], 0, threadPCReader, (void*)(i*1000000UL));
     for(int i=0; i < 4 ; ++i)
       pthread_join(tid[i], &res);
 
-    BOOST_CHECK(S.read("deferred-cache-inserts") + S.read("deferred-cache-lookup") >= g_missing);
-    //    BOOST_CHECK_EQUAL(S.read("deferred-cache-lookup"), 0); // cache cleaning invalidates this
+/*
+    cerr<<"Misses: "<<S.read("packetcache-miss")<<endl;
+    cerr<<"Hits: "<<S.read("packetcache-hit")<<endl;
+    cerr<<"Deferred inserts: "<<S.read("deferred-packetcache-inserts")<<endl;
+    cerr<<"Deferred lookups: "<<S.read("deferred-packetcache-lookup")<<endl;
+*/
+    BOOST_CHECK_EQUAL(g_PCmissing + S.read("packetcache-hit"), 400000);
+    BOOST_CHECK_GT(S.read("deferred-packetcache-inserts") + S.read("deferred-packetcache-lookup"), g_PCmissing);
   }
   catch(PDNSException& e) {
     cerr<<"Had error: "<<e.reason<<endl;
     throw;
   }
-  
+
 }
 
 bool g_stopCleaning;
@@ -143,34 +239,34 @@ static void *cacheCleaner(void*)
 try
 {
   while(!g_stopCleaning) {
-    g_PC->cleanup();
+    g_QC->cleanup();
   }
 
   return 0;
 }
 catch(PDNSException& e) {
-  cerr<<"Had error in threadReader: "<<e.reason<<endl;
+  cerr<<"Had error in cacheCleaner: "<<e.reason<<endl;
   throw;
 }
 
-BOOST_AUTO_TEST_CASE(test_PacketCacheClean) {
+BOOST_AUTO_TEST_CASE(test_QueryCacheClean) {
   try {
-    PacketCache PC;
+    AuthQueryCache QC;
+    QC.setMaxEntries(10000);
+    vector<DNSZoneRecord> records;
 
     for(unsigned int counter = 0; counter < 1000000; ++counter) {
-      PC.insert(DNSName("hello ")+DNSName(std::to_string(counter)), QType(QType::A), PacketCache::QUERYCACHE, "something", 1, 1);
+      QC.insert(DNSName("hello ")+DNSName(std::to_string(counter)), QType(QType::A), records, 1, 1);
     }
 
     sleep(1);
-    
-    g_PC=&PC;
-    pthread_t tid[4];
 
-    ::arg().set("max-cache-entries")="10000";
+    g_QC=&QC;
+    pthread_t tid[4];
 
-    pthread_create(&tid[0], 0, threadReader, (void*)(0*1000000UL));
-    pthread_create(&tid[1], 0, threadReader, (void*)(1*1000000UL));
-    pthread_create(&tid[2], 0, threadReader, (void*)(2*1000000UL));
+    pthread_create(&tid[0], 0, threadQCReader, (void*)(0*1000000UL));
+    pthread_create(&tid[1], 0, threadQCReader, (void*)(1*1000000UL));
+    pthread_create(&tid[2], 0, threadQCReader, (void*)(2*1000000UL));
     //    pthread_create(&tid[2], 0, threadMangler, (void*)(0*1000000UL));
     pthread_create(&tid[3], 0, cacheCleaner, 0);
 
@@ -181,71 +277,187 @@ BOOST_AUTO_TEST_CASE(test_PacketCacheClean) {
     pthread_join(tid[3], &res);
   }
   catch(PDNSException& e) {
-    cerr<<"Had error in threadReader: "<<e.reason<<endl;
+    cerr<<"Had error in test_QueryCacheClean: "<<e.reason<<endl;
     throw;
   }
 }
 
-BOOST_AUTO_TEST_CASE(test_PacketCachePacket) {
+BOOST_AUTO_TEST_CASE(test_AuthPacketCache) {
   try {
     ::arg().setSwitch("no-shuffle","Set this to prevent random shuffling of answers - for regression testing")="off";
 
-    PacketCache PC;
+    AuthPacketCache PC;
+    PC.setTTL(20);
+    PC.setMaxEntries(100000);
+
     vector<uint8_t> pak;
-    vector<pair<uint16_t,string > > opts;
+    DNSPacket q(true), differentIDQ(true), ednsQ(true), ednsVersion42(true), ednsDO(true), ecs1(true), ecs2(true), ecs3(true);
+    DNSPacket r(false), r2(false);
 
-    DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
-    DNSPacket q(true), r(false), r2(false);
-    q.parse((char*)&pak[0], pak.size());
+    {
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      q.parse((char*)&pak[0], pak.size());
 
-    pak.clear();
-    DNSPacketWriter pw2(pak, DNSName("www.powerdns.com"), QType::A);
-    pw2.startRecord(DNSName("www.powerdns.com"), QType::A, 16, 1, DNSResourceRecord::ANSWER);
-    pw2.xfrIP(htonl(0x7f000001));
-    pw2.commit();
+      differentIDQ.parse((char*)&pak[0], pak.size());
+      differentIDQ.setID(4242);
 
-    r.parse((char*)&pak[0], pak.size());
+      pw.addOpt(512, 0, 0);
+      pw.commit();
+      ednsQ.parse((char*)&pak[0], pak.size());
+
+      pak.clear();
+    }
+
+    DNSPacketWriter::optvect_t opts;
+    EDNSSubnetOpts ecsOpts;
+    {
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      pw.addOpt(512, 0, 0, DNSPacketWriter::optvect_t(), 42);
+      pw.commit();
+      ednsVersion42.parse((char*)&pak[0], pak.size());
+      pak.clear();
+    }
+
+    {
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      pw.addOpt(512, 0, EDNSOpts::DNSSECOK);
+      pw.commit();
+      ednsDO.parse((char*)&pak[0], pak.size());
+      pak.clear();
+    }
+
+    {
+      ecsOpts.source = Netmask(ComboAddress("192.0.2.1"), 32);
+      opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(ecsOpts)));
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      pw.addOpt(512, 0, 0, opts);
+      pw.commit();
+      ecs1.parse((char*)&pak[0], pak.size());
+      pak.clear();
+      opts.clear();
+    }
+
+    {
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      ecsOpts.source = Netmask(ComboAddress("192.0.2.2"), 32);
+      opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(ecsOpts)));
+      pw.addOpt(512, 0, 0, opts);
+      pw.commit();
+      ecs2.parse((char*)&pak[0], pak.size());
+      pak.clear();
+      opts.clear();
+    }
+
+    {
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      ecsOpts.source = Netmask(ComboAddress("192.0.2.3"), 16);
+      opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(ecsOpts)));
+      pw.addOpt(512, 0, 0, opts);
+      pw.commit();
+      ecs3.parse((char*)&pak[0], pak.size());
+      pak.clear();
+      opts.clear();
+    }
+
+    {
+      DNSPacketWriter pw(pak, DNSName("www.powerdns.com"), QType::A);
+      pw.startRecord(DNSName("www.powerdns.com"), QType::A, 16, 1, DNSResourceRecord::ANSWER);
+      pw.xfrIP(htonl(0x7f000001));
+      pw.commit();
+
+      r.parse((char*)&pak[0], pak.size());
+    }
+
+    /* this call is required so the correct hash is set into q->d_hash */
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
 
     PC.insert(&q, &r, 3600);
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
+    BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
+
+    /* different QID, still should match */
+    BOOST_CHECK_EQUAL(PC.get(&differentIDQ, &r2), true);
+    BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
+
+    /* with EDNS, should not match */
+    BOOST_CHECK_EQUAL(PC.get(&ednsQ, &r2), false);
+    /* inserting the EDNS-enabled one too */
+    PC.insert(&ednsQ, &r, 3600);
+    BOOST_CHECK_EQUAL(PC.size(), 2);
+
+    /* different EDNS versions, should not match */
+    BOOST_CHECK_EQUAL(PC.get(&ednsVersion42, &r2), false);
+
+    /* EDNS DO set, should not match */
+    BOOST_CHECK_EQUAL(PC.get(&ednsDO, &r2), false);
 
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 1);
+    /* EDNS Client Subnet set, should not match
+       since not only we don't skip the actual option, but the
+       total EDNS opt RR is still different. */
+    BOOST_CHECK_EQUAL(PC.get(&ecs1, &r2), false);
+
+    /* inserting the version with ECS Client Subnet set,
+     it should NOT replace the existing EDNS one. */
+    PC.insert(&ecs1, &r, 3600);
+    BOOST_CHECK_EQUAL(PC.size(), 3);
+
+    /* different subnet of same size, should NOT match
+     since we don't skip the option */
+    BOOST_CHECK_EQUAL(PC.get(&ecs2, &r2), false);
     BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
 
-    PC.purge("www.powerdns.com");
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 0);
+    /* different subnet of different size, should NOT match. */
+    BOOST_CHECK_EQUAL(PC.get(&ecs3, &r2), false);
+
+    BOOST_CHECK_EQUAL(PC.purge("www.powerdns.com"), 3);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
+    BOOST_CHECK_EQUAL(PC.size(), 0);
 
     PC.insert(&q, &r, 3600);
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 1);
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
     BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
-    PC.purge("com$");
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 0);
+    BOOST_CHECK_EQUAL(PC.purge("com$"), 1);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
+    BOOST_CHECK_EQUAL(PC.size(), 0);
 
     PC.insert(&q, &r, 3600);
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 1);
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
     BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
-    PC.purge("powerdns.com$");
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 0);
+    BOOST_CHECK_EQUAL(PC.purge("powerdns.com$"), 1);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
+    BOOST_CHECK_EQUAL(PC.size(), 0);
 
     PC.insert(&q, &r, 3600);
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 1);
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
     BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
-    PC.purge("www.powerdns.com$");
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 0);
+    BOOST_CHECK_EQUAL(PC.purge("www.powerdns.com$"), 1);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), false);
+    BOOST_CHECK_EQUAL(PC.size(), 0);
 
     PC.insert(&q, &r, 3600);
-    PC.purge("www.powerdns.net");
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 1);
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+    BOOST_CHECK_EQUAL(PC.purge("www.powerdns.net"), 0);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
     BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
-    PC.purge("net$");
-    BOOST_CHECK_EQUAL(PC.get(&q, &r2), 1);
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+
+    BOOST_CHECK_EQUAL(PC.purge("net$"), 0);
+    BOOST_CHECK_EQUAL(PC.get(&q, &r2), true);
     BOOST_CHECK_EQUAL(r2.qdomain, r.qdomain);
-    PC.purge("www.powerdns.com$");
+    BOOST_CHECK_EQUAL(PC.size(), 1);
+
+    BOOST_CHECK_EQUAL(PC.purge("www.powerdns.com$"), 1);
     BOOST_CHECK_EQUAL(PC.size(), 0);
   }
   catch(PDNSException& e) {
-    cerr<<"Had error in threadReader: "<<e.reason<<endl;
+    cerr<<"Had error in AuthPacketCache: "<<e.reason<<endl;
     throw;
   }
-} 
+}
 
 BOOST_AUTO_TEST_SUITE_END()
index b3ff81535e999fb1f1a7dc8ba4d6654670f87306..b4e55adbf58141c8e5c0f22d04701ef1ec2814fa 100644 (file)
@@ -6,8 +6,10 @@
 #include "config.h"
 #endif
 #include <boost/test/unit_test.hpp>
-#include "packetcache.hh"
+#include "auth-packetcache.hh"
+#include "auth-querycache.hh"
+#include "statbag.hh"
 StatBag S;
-PacketCache PC;
-
+AuthPacketCache PC;
+AuthQueryCache QC;
 
index 4a7a55783c7aaa8dc9f8d5e1c974abc97d3500be..258c85dc28d4622c275cbea54e2585932f654f44 100644 (file)
@@ -25,7 +25,7 @@
 #include <boost/archive/binary_iarchive.hpp>
 #include <boost/archive/binary_oarchive.hpp>
 
-#include "packetcache.hh"
+#include "auth-querycache.hh"
 #include "utility.hh"
 
 
@@ -444,30 +444,22 @@ void UeberBackend::cleanup()
   for_each(backends.begin(),backends.end(),del);
 }
 
-// silly Solaris fix
-#undef PC
-
 // returns -1 for miss, 0 for negative match, 1 for hit
 int UeberBackend::cacheHas(const Question &q, vector<DNSZoneRecord> &rrs)
 {
-  extern PacketCache PC;
-  static AtomicCounter *qcachehit=S.getPointer("query-cache-hit");
-  static AtomicCounter *qcachemiss=S.getPointer("query-cache-miss");
+  extern AuthQueryCache QC;
 
   if(!d_cache_ttl && ! d_negcache_ttl) {
-    (*qcachemiss)++;
     return -1;
   }
 
   rrs.clear();
   //  L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
 
-  bool ret=PC.getEntry(q.qname, q.qtype, PacketCache::QUERYCACHE, rrs, q.zoneId);   // think about lowercasing here
+  bool ret=QC.getEntry(q.qname, q.qtype, rrs, q.zoneId);   // think about lowercasing here
   if(!ret) {
-    (*qcachemiss)++;
     return -1;
   }
-  (*qcachehit)++;
   if(rrs.empty()) // negatively cached
     return 0;
   
@@ -476,16 +468,16 @@ int UeberBackend::cacheHas(const Question &q, vector<DNSZoneRecord> &rrs)
 
 void UeberBackend::addNegCache(const Question &q)
 {
-  extern PacketCache PC;
+  extern AuthQueryCache QC;
   if(!d_negcache_ttl)
     return;
   // we should also not be storing negative answers if a pipebackend does scopeMask, but we can't pass a negative scopeMask in an empty set!
-  PC.insert(q.qname, q.qtype, PacketCache::QUERYCACHE, vector<DNSZoneRecord>(), d_negcache_ttl, q.zoneId);
+  QC.insert(q.qname, q.qtype, vector<DNSZoneRecord>(), d_negcache_ttl, q.zoneId);
 }
 
 void UeberBackend::addCache(const Question &q, const vector<DNSZoneRecord> &rrs)
 {
-  extern PacketCache PC;
+  extern AuthQueryCache QC;
 
   if(!d_cache_ttl)
     return;
@@ -498,7 +490,7 @@ void UeberBackend::addCache(const Question &q, const vector<DNSZoneRecord> &rrs)
      return;
   }
 
-  PC.insert(q.qname, q.qtype, PacketCache::QUERYCACHE, rrs, store_ttl, q.zoneId);
+  QC.insert(q.qname, q.qtype, rrs, store_ttl, q.zoneId);
 }
 
 void UeberBackend::alsoNotifies(const DNSName &domain, set<string> *ips)
index 3bbe54ed6db4bf47d4b354c02246fb48e11cd657..2fc237d615b986f61f474ce1b6e72714943633ef 100644 (file)
@@ -28,7 +28,6 @@
 #include "json.hh"
 #include "webserver.hh"
 #include "logger.hh"
-#include "packetcache.hh"
 #include "statbag.hh"
 #include "misc.hh"
 #include "arguments.hh"
 #include <iomanip>
 #include "zoneparser-tng.hh"
 #include "common_startup.hh"
-
+#include "auth-caches.hh"
 
 using json11::Json;
 
 extern StatBag S;
-extern PacketCache PC;
 
 static void patchZone(HttpRequest* req, HttpResponse* resp);
 static void storeChangedPTRs(UeberBackend& B, vector<DNSResourceRecord>& new_ptrs);
@@ -1352,7 +1350,7 @@ static void storeChangedPTRs(UeberBackend& B, vector<DNSResourceRecord>& new_ptr
     }
 
     sd.db->commitTransaction();
-    PC.purgeExact(rr.qname);
+    purgeAuthCachesExact(rr.qname);
   }
 }
 
@@ -1479,7 +1477,7 @@ static void patchZone(HttpRequest* req, HttpResponse* resp) {
   }
   di.backend->commitTransaction();
 
-  PC.purgeExact(zonename);
+  purgeAuthCachesExact(zonename);
 
   // now the PTRs
   storeChangedPTRs(B, new_ptrs);
@@ -1578,10 +1576,10 @@ void apiServerCacheFlush(HttpRequest* req, HttpResponse* resp) {
 
   DNSName canon = apiNameToDNSName(req->getvars["domain"]);
 
-  int count = PC.purgeExact(canon);
+  uint64_t count = purgeAuthCachesExact(canon);
   resp->setBody(Json::object {
-    { "count", count },
-    { "result", "Flushed cache." }
+      { "count", (int) count },
+      { "result", "Flushed cache." }
   });
 }
 
index c68b39f9b1313e4c552d198519ce4be53e00ac88..00a483345102dbd851e3c280d3e2cacf49dfeeae 100644 (file)
@@ -2,6 +2,8 @@
 corrupt-packets=0
 deferred-cache-inserts=0
 deferred-cache-lookup=0
+deferred-packetcache-inserts=0
+deferred-packetcache-lookup=0
 dnsupdate-answers=0
 dnsupdate-changes=0
 dnsupdate-queries=0
@@ -10,8 +12,9 @@ incoming-notifications=0
 key-cache-size=0
 meta-cache-size=1
 overload-drops=0
-packetcache-size=8
+packetcache-size=4
 qsize-q=0
+query-cache-size=4
 rd-queries=0
 recursing-answers=0
 recursing-questions=0