]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
auth: add a cache of existing domains
authorChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Mon, 19 Oct 2020 14:21:17 +0000 (16:21 +0200)
committerChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Sat, 15 May 2021 22:45:45 +0000 (00:45 +0200)
28 files changed:
docs/settings.rst
modules/bindbackend/bindbackend2.cc
modules/lmdbbackend/lmdbbackend.cc
modules/lmdbbackend/lmdbbackend.hh
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
pdns/Makefile.am
pdns/auth-domaincache.cc [new file with mode: 0644]
pdns/auth-domaincache.hh [new file with mode: 0644]
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/common_startup.cc
pdns/common_startup.hh
pdns/dnsbackend.hh
pdns/pdnsutil.cc
pdns/receiver.cc
pdns/test-ueberbackend_cc.cc
pdns/testrunner.cc
pdns/ueberbackend.cc
pdns/ueberbackend.hh
regression-tests/backends/ldap-master
regression-tests/tests/bind-add-zone/command
regression-tests/tests/bind-add-zone/expected_result.bind

index 07fb6c2b3ae786f27152ba47e2e6670eed88d969..9a3774aeb91e657465acf25156ffbcbf424bca77 100644 (file)
@@ -171,6 +171,18 @@ Maximum time in seconds for inbound AXFR to start or be idle after starting.
 
 Also AXFR a zone from a master with a lower serial.
 
+.. _setting-domain-cache-ttl:
+
+``domain-cache-ttl``
+--------------------
+
+-  Integer
+-  Default: 0
+
+Seconds to cache a list of all known domains. A value of 0 will disable the cache.
+
+If your backends do not respond to unknown domains, it is suggested to enable :ref:`setting-consistent-backends` and set this option to `60`.
+
 .. _setting-cache-ttl:
 
 ``cache-ttl``
index 1d3312ed2d1c620b5abecc1ec01b011bb6ae7ad9..3f826feb847ef52de8ce4d7677d4d11e6da0108f 100644 (file)
@@ -53,6 +53,9 @@
 #include "pdns/misc.hh"
 #include "pdns/dynlistener.hh"
 #include "pdns/lock.hh"
+#include "pdns/auth-domaincache.hh"
+
+extern AuthDomainCache g_domainCache;
 
 /* 
    All instances of this backend share one s_state, which is indexed by zone name and zone id.
@@ -703,6 +706,8 @@ string Bind2Backend::DLAddDomainHandler(const vector<string>& parts, Utility::pi
 
   safePutBBDomainInfo(bbd);
 
+  g_domainCache.add(domainname, bbd.d_id);  // make new domain visible
+
   g_log << Logger::Warning << "Zone " << domainname << " loaded" << endl;
   return "Loaded zone " + domainname.toLogString() + " from " + filename;
 }
index 03b0d5438a84cd22666d79e8e99d5820fe1e08d8..832beb667f5f6078a9eb8b92e7360f76652bbbaa 100644 (file)
@@ -953,22 +953,29 @@ bool LMDBBackend::setMasters(const DNSName& domain, const vector<ComboAddress>&
   });
 }
 
-bool LMDBBackend::createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& masters, const string& account)
+bool LMDBBackend::createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& masters, const string& account, int* zoneId)
 {
   DomainInfo di;
 
-  auto txn = d_tdomains->getRWTransaction();
-  if (txn.get<0>(domain, di)) {
-    throw DBException("Domain '" + domain.toLogString() + "' exists already");
-  }
+  {
+    auto txn = d_tdomains->getRWTransaction();
+    if (txn.get<0>(domain, di)) {
+      throw DBException("Domain '" + domain.toLogString() + "' exists already");
+    }
+
+    di.zone = domain;
+    di.kind = kind;
+    di.masters = masters;
+    di.account = account;
 
-  di.zone = domain;
-  di.kind = kind;
-  di.masters = masters;
-  di.account = account;
+    txn.put(di);
+    txn.commit();
+  }
 
-  txn.put(di);
-  txn.commit();
+  if (zoneId != nullptr) {
+    auto txn = d_tdomains->getROTransaction();
+    *zoneId = txn.get<0>(domain, di);
+  }
 
   return true;
 }
index 9951841d09800af1d57fc42a6ad2cda0741069bb..f9aca4e702e64fabb33c6494ad1fffa8f6701b66 100644 (file)
@@ -59,7 +59,7 @@ public:
   bool list(const DNSName& target, int id, bool include_disabled) override;
 
   bool getDomainInfo(const DNSName& domain, DomainInfo& di, bool getserial = true) override;
-  bool createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& masters, const string& account) override;
+  bool createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& masters, const string& account, int* zoneId) override;
 
   bool startTransaction(const DNSName& domain, int domain_id = -1) override;
   bool commitTransaction() override;
index bfc378f3f3c3175db9c6c9d4f20cf7df1af462a1..f0218cfc440f7f904f55299f4ebdf3851e6ce898 100644 (file)
@@ -95,6 +95,7 @@ BUILT_SOURCES = ../../pdns/dnslabeltext.cc
 
 libtestremotebackend_la_SOURCES = \
        ../../pdns/arguments.hh ../../pdns/arguments.cc \
+       ../../pdns/auth-domaincache.cc ../../pdns/auth-domaincache.hh \
        ../../pdns/auth-packetcache.cc ../../pdns/auth-packetcache.hh \
        ../../pdns/auth-querycache.cc ../../pdns/auth-querycache.hh \
        ../../pdns/base32.cc \
index 796737dc3848a40820c75895e2ddba66bca6e0bc..babf1359906851ffd6628c45bc894914019d7470 100644 (file)
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
+#include "pdns/auth-domaincache.hh"
 #include "pdns/auth-packetcache.hh"
 #include "pdns/auth-querycache.hh"
 
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 ArgvMap& arg()
index 26f905c47615eb459c786895e1f4bf0f6312b179..87a8234ef12f7577ce10bf285e652433597381af 100644 (file)
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
+#include "pdns/auth-domaincache.hh"
 #include "pdns/auth-packetcache.hh"
 #include "pdns/auth-querycache.hh"
 
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 ArgvMap& arg()
index 11883b104ef9f01aa9e276c6c27a3e11a2f26863..a32dfd0019d8540a43ba63ba1cd4b122f6e6bde0 100644 (file)
 #include "pdns/dnsrecords.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
+#include "pdns/auth-domaincache.hh"
 #include "pdns/auth-packetcache.hh"
 #include "pdns/auth-querycache.hh"
 
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 ArgvMap& arg()
index 899b51dbb98ba0162dfd116aba3401d2273946c8..0f80df4851a24a32b63c5894aef6681a5373d612 100644 (file)
 #include "pdns/arguments.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
+#include "pdns/auth-domaincache.hh"
 #include "pdns/auth-packetcache.hh"
 #include "pdns/auth-querycache.hh"
 
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 ArgvMap& arg()
index c2be2f3972284937ef6d25d4ad824e0680c1f5cb..7a0142aa75c0b69373c5ad75296d487b60c1e34a 100644 (file)
 #include "pdns/dnsrecords.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
+#include "pdns/auth-domaincache.hh"
 #include "pdns/auth-packetcache.hh"
 #include "pdns/auth-querycache.hh"
 
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 ArgvMap& arg()
index c5e5396edc89ea17a04a0c22f5f5ece276213e3c..01fed7f67cbc4757532030581d8febc415c30c74 100644 (file)
 #include "pdns/dnsrecords.hh"
 #include "pdns/json.hh"
 #include "pdns/statbag.hh"
+#include "pdns/auth-domaincache.hh"
 #include "pdns/auth-packetcache.hh"
 #include "pdns/auth-querycache.hh"
 
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 ArgvMap& arg()
index 97499ab22482dc034374478e39a09fcc8743e999..c97adcf5fa298309d86f3ec092a96b9453060cb5 100644 (file)
@@ -185,6 +185,7 @@ pdns_server_SOURCES = \
        ascii.hh \
        auth-caches.cc auth-caches.hh \
        auth-carbon.cc \
+       auth-domaincache.cc auth-domaincache.hh \
        auth-packetcache.cc auth-packetcache.hh \
        auth-querycache.cc auth-querycache.hh \
        axfr-retriever.cc axfr-retriever.hh \
@@ -321,6 +322,7 @@ endif
 pdnsutil_SOURCES = \
        arguments.cc \
        auth-caches.cc auth-caches.hh \
+       auth-domaincache.cc auth-domaincache.hh \
        auth-packetcache.cc auth-packetcache.hh \
        auth-querycache.cc auth-querycache.hh \
        backends/gsql/gsqlbackend.cc backends/gsql/gsqlbackend.hh \
@@ -1304,6 +1306,7 @@ pdns.conf-dist: pdns_server
 testrunner_SOURCES = \
        arguments.cc \
        auth-caches.cc auth-caches.hh \
+       auth-domaincache.cc auth-domaincache.hh \
        auth-packetcache.cc auth-packetcache.hh \
        auth-querycache.cc auth-querycache.hh \
        base32.cc \
diff --git a/pdns/auth-domaincache.cc b/pdns/auth-domaincache.cc
new file mode 100644 (file)
index 0000000..d530272
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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-domaincache.hh"
+#include "logger.hh"
+#include "statbag.hh"
+#include "arguments.hh"
+#include "cachecleaner.hh"
+extern StatBag S;
+
+AuthDomainCache::AuthDomainCache(size_t mapsCount): d_maps(mapsCount)
+{
+  S.declare("domain-cache-hit", "Number of hits on the domain cache");
+  S.declare("domain-cache-miss", "Number of misses on the domain cache");
+  S.declare("domain-cache-size", "Number of entries in the domain cache", StatType::gauge);
+
+  d_statnumhit=S.getPointer("domain-cache-hit");
+  d_statnummiss=S.getPointer("domain-cache-miss");
+  d_statnumentries=S.getPointer("domain-cache-size");
+
+  d_ttl = 0;
+}
+
+AuthDomainCache::~AuthDomainCache()
+{
+  try {
+    vector<WriteLock> locks;
+    for(auto& mc : d_maps) {
+      locks.push_back(WriteLock(mc.d_mut));
+    }
+    locks.clear();
+  }
+  catch(...) {
+  }
+}
+
+bool AuthDomainCache::getEntry(const DNSName &domain, int& zoneId)
+{
+  auto& mc = getMap(domain);
+  bool found = false;
+  {
+    ReadLock rl(mc.d_mut);
+    auto iter = mc.d_map.find(domain);
+    if (iter != mc.d_map.end()) {
+      found = true;
+      zoneId = iter->second.zoneId;
+    }
+  }
+
+  if (found) {
+    (*d_statnumhit)++;
+  } else {
+    (*d_statnummiss)++;
+  }
+  return found;
+}
+
+bool AuthDomainCache::isEnabled() const
+{
+  return d_ttl > 0;
+}
+
+void AuthDomainCache::clear()
+{
+  purgeLockedCollectionsVector(d_maps);
+}
+
+void AuthDomainCache::replace(const vector<tuple<DNSName, int>> &domain_indices)
+{
+  if(!d_ttl)
+    return;
+
+  size_t count = domain_indices.size();
+  vector<MapCombo> newMaps(d_maps.size());
+
+  // build new maps
+  for(const tuple<DNSName, int>& tup: domain_indices) {
+    const DNSName& domain = tup.get<0>();
+    CacheValue val;
+    val.zoneId = tup.get<1>();
+    auto& mc = newMaps[getMapIndex(domain)];
+    mc.d_map.emplace(domain, val);
+  }
+
+  for(size_t mapIndex = 0; mapIndex < d_maps.size(); mapIndex++)
+  {
+    auto& mc = d_maps[mapIndex];
+    WriteLock l(mc.d_mut);
+    mc.d_map = newMaps[mapIndex].d_map;
+  }
+
+  d_statnumentries->store(count);
+}
+
+void AuthDomainCache::add(const DNSName& domain, const int zoneId)
+{
+  if(!d_ttl)
+    return;
+
+  CacheValue val;
+  val.zoneId = zoneId;
+
+  int mapIndex = getMapIndex(domain);
+  {
+    auto& mc = d_maps[mapIndex];
+    WriteLock l(mc.d_mut);
+    mc.d_map.emplace(domain, val);
+  }
+}
diff --git a/pdns/auth-domaincache.hh b/pdns/auth-domaincache.hh
new file mode 100644 (file)
index 0000000..bee0202
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+#pragma once
+#include <string>
+#include <map>
+#include "dns.hh"
+
+#include <unordered_map>
+
+#include "dns.hh"
+#include "dnspacket.hh"
+#include "lock.hh"
+
+class AuthDomainCache : public boost::noncopyable
+{
+public:
+  AuthDomainCache(size_t mapsCount=1024);
+  ~AuthDomainCache();
+
+  void replace(const vector<tuple<DNSName, int>> &domains);
+  void add(const DNSName& domain, const int zoneId);
+
+  bool getEntry(const DNSName &domain, int &zoneId);
+
+  size_t size() { return *d_statnumentries; } //!< number of entries in the cache
+
+  uint32_t getTTL() const {
+    return d_ttl;
+  }
+
+  void setTTL(uint32_t ttl) {
+    d_ttl = ttl;
+  }
+
+  bool isEnabled() const;
+
+  void clear();
+
+private:
+  struct CacheValue
+  {
+    int zoneId{-1};
+  };
+
+  typedef std::unordered_map<DNSName, CacheValue, std::hash<DNSName>> cmap_t;
+
+  struct MapCombo
+  {
+    MapCombo() { }
+    ~MapCombo() { }
+    MapCombo(const MapCombo &) = delete;
+    MapCombo & operator=(const MapCombo &) = delete;
+
+    ReadWriteLock d_mut;
+    cmap_t d_map;
+  };
+
+  vector<MapCombo> d_maps;
+  size_t getMapIndex(const DNSName& domain)
+  {
+    return domain.hash() % d_maps.size();
+  }
+  MapCombo& getMap(const DNSName& qname)
+  {
+    return d_maps[getMapIndex(qname)];
+  }
+
+  AtomicCounter d_ops{0};
+  AtomicCounter *d_statnumhit;
+  AtomicCounter *d_statnummiss;
+  AtomicCounter *d_statnumentries;
+
+  time_t d_ttl;
+};
index 0956808db0ca68c66f3456b0c88b1a76c30664c1..5ad65bd863e9d717773d18c745b9f5ff7b45022a 100644 (file)
@@ -1289,7 +1289,7 @@ bool GSQLBackend::superMasterBackend(const string &ip, const DNSName &domain, co
   return false;
 }
 
-bool GSQLBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
+bool GSQLBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account, int* zoneId)
 {
   vector<string> masters_s;
   masters_s.reserve(masters.size());
@@ -1307,6 +1307,20 @@ bool GSQLBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKi
       bind("account", account)->
       execute()->
       reset();
+
+    if (zoneId != nullptr) {
+      // XXX: needs its own stmt as godbc has a table name in it
+      d_GetLastInsertedKeyIdQuery_stmt->execute();
+      if (!d_GetLastInsertedKeyIdQuery_stmt->hasNextRow()) {
+        return false;
+      }
+      SSqlStatement::row_t row;
+      d_GetLastInsertedKeyIdQuery_stmt->nextRow(row);
+      ASSERT_ROW_COLUMNS("get-last-inserted-key-id-query", row, 1);
+      *zoneId = std::stoi(row[0]);
+      d_GetLastInsertedKeyIdQuery_stmt->reset();
+    }
+    return true;
   }
   catch(SSqlException &e) {
     throw PDNSException("Database error trying to insert new domain '"+domain.toLogString()+"': "+ e.txtReason());
@@ -1340,7 +1354,7 @@ bool GSQLBackend::createSlaveDomain(const string &ip, const DNSName &domain, con
         masters = tmp;
       }
     }
-    createDomain(domain, DomainInfo::Slave, masters, account);
+    createDomain(domain, DomainInfo::Slave, masters, account, nullptr);
   }
   catch(SSqlException &e) {
     throw PDNSException("Database error trying to insert new slave domain '"+domain.toLogString()+"': "+ e.txtReason());
index 0aa79f54a63c970e0a2bea96a31195cb436e2c19..396ff7e0c7896ac979335884a0e6a33f7785a4d4 100644 (file)
@@ -193,7 +193,7 @@ public:
   bool feedRecord(const DNSResourceRecord &r, const DNSName &ordername, bool ordernameIsNSEC3=false) override;
   bool feedEnts(int domain_id, map<DNSName,bool>& nonterm) override;
   bool feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) override;
-  bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account) override;
+  bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account, int* zoneId=nullptr) override;
   bool createSlaveDomain(const string &ip, const DNSName &domain, const string &nameserver, const string &account) override;
   bool deleteDomain(const DNSName &domain) override;
   bool superMasterAdd(const string &ip, const string &nameserver, const string &account) override; 
index 60c81cad963c7e684bfe3ba9b99622f15888b9bc..111a75a39c39a694849316e3bcbc394e95895621 100644 (file)
@@ -55,6 +55,7 @@ ArgvMap theArg;
 StatBag S;  //!< Statistics are gathered across PDNS via the StatBag class S
 AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads
 AuthQueryCache QC;
+AuthDomainCache g_domainCache;
 std::unique_ptr<DNSProxy> DP{nullptr};
 std::unique_ptr<DynListener> dl{nullptr};
 CommunicatorClass Communicator;
@@ -180,6 +181,7 @@ void declareArguments()
   ::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";
+  ::arg().set("domain-cache-ttl","Seconds to cache list of known domains")="0";
   ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname - disabled or custom")="";
   ::arg().set("default-soa-content","Default SOA content")="a.misconfigured.dns.server.invalid hostmaster.@ 0 10800 3600 604800 3600";
   ::arg().set("default-soa-edit","Default SOA-EDIT value")="";
@@ -676,12 +678,28 @@ void mainthread()
     sd_notify(0, "READY=1");
 #endif
 
+  const uint32_t secpollInterval = 1800;
+  uint32_t secpollSince = 0;
+  uint32_t domainCacheUpdateSince = 0;
   for(;;) {
-    sleep(1800);
-    try {
-      doSecPoll(false);
+    const uint32_t slept = g_domainCache.getTTL() == 0 ? secpollInterval : std::min(secpollInterval, g_domainCache.getTTL());
+    sleep(slept);  // if any signals arrive, we might run more often than expected.
+
+    domainCacheUpdateSince += slept;
+    if (domainCacheUpdateSince >= g_domainCache.getTTL()) {
+      domainCacheUpdateSince = 0;
+      UeberBackend B;
+      B.updateDomainCache();
+    }
+
+    secpollSince += slept;
+    if (secpollSince >= secpollInterval) {
+      secpollSince = 0;
+      try {
+        doSecPoll(false);
+      }
+      catch(...){}
     }
-    catch(...){}
   }
   
   g_log<<Logger::Error<<"Mainthread exiting - should never happen"<<endl;
index 737ebb00f53af0b7a0593c8d1ec38a6c179df5cd..4e82e8f03210493a4c4180d6c9eb7ce1c219b373 100644 (file)
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 #pragma once
+#include "auth-domaincache.hh"
 #include "auth-packetcache.hh"
 #include "auth-querycache.hh"
 #include "utility.hh"
@@ -38,6 +39,7 @@ extern ArgvMap theArg;
 extern StatBag S;  //!< Statistics are gathered across PDNS via the StatBag class S
 extern AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads
 extern AuthQueryCache QC;
+extern AuthDomainCache g_domainCache;
 extern std::unique_ptr<DNSProxy> DP;
 extern std::unique_ptr<DynListener> dl;
 extern CommunicatorClass Communicator;
index f200a0526a6436706f23e61f33ae78bd781fe1a7..ee9ddca5e9159f579916a96784155a6711d7f07a 100644 (file)
@@ -356,7 +356,7 @@ public:
   }
 
   //! called by PowerDNS to create a new domain
-  virtual bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
+  virtual bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account, int* zoneId = nullptr)
   {
     return false;
   }
index 6d6dcd33cb28064773beac280aa7075222bba724..c2a7ac1f107dc0263994e5660645ba0867315751 100644 (file)
@@ -18,6 +18,7 @@
 #include "dnsbackend.hh"
 #include "ueberbackend.hh"
 #include "arguments.hh"
+#include "auth-domaincache.hh"
 #include "auth-packetcache.hh"
 #include "auth-querycache.hh"
 #include "zoneparser-tng.hh"
@@ -40,6 +41,7 @@
 StatBag S;
 AuthPacketCache PC;
 AuthQueryCache QC;
+AuthDomainCache g_domainCache;
 
 namespace po = boost::program_options;
 po::variables_map g_vm;
index dcc8051a24641c8a472835c57cba28bffbb3a7d7..edadbbc3abb5edeb97d51b4b855ff7af45adb5f2 100644 (file)
@@ -624,6 +624,12 @@ int main(int argc, char **argv)
       }
     }
 
+    g_domainCache.setTTL(::arg().asNum("domain-cache-ttl"));
+    {
+      UeberBackend B;
+      B.updateDomainCache();
+    }
+
     UeberBackend::go();
     N=std::make_shared<UDPNameserver>(); // this fails when we are not root, throws exception
     g_udpReceivers.push_back(N);
index 3921b02fa760232e0a323b5c633792445621a1df..c8fc32708e1dd24a8739d7235c39451dc2537b72 100644 (file)
@@ -16,6 +16,7 @@
 #include <boost/multi_index/key_extractors.hpp>
 
 #include "arguments.hh"
+#include "auth-domaincache.hh"
 #include "auth-querycache.hh"
 #include "ueberbackend.hh"
 
@@ -332,11 +333,14 @@ public:
 
 struct UeberBackendSetupArgFixture {
   UeberBackendSetupArgFixture() {
+    extern AuthDomainCache g_domainCache;
     extern AuthQueryCache QC;
     ::arg().set("query-cache-ttl")="0";
     ::arg().set("negquery-cache-ttl")="0";
     ::arg().set("consistent-backends")="no";
     QC.cleanup();
+    ::arg().set("domain-cache-ttl")="0";
+    g_domainCache.clear();
     BackendMakers().clear();
     SimpleBackend::s_zones.clear();
     SimpleBackend::s_metadata.clear();
@@ -345,6 +349,7 @@ struct UeberBackendSetupArgFixture {
 
 static void testWithoutThenWithCache(std::function<void(UeberBackend& ub)> func)
 {
+  extern AuthDomainCache g_domainCache;
   extern AuthQueryCache QC;
 
   {
@@ -352,6 +357,8 @@ static void testWithoutThenWithCache(std::function<void(UeberBackend& ub)> func)
     ::arg().set("query-cache-ttl")="0";
     ::arg().set("negquery-cache-ttl")="0";
     QC.cleanup();
+    ::arg().set("domain-cache-ttl")="0";
+    g_domainCache.clear();
 
     UeberBackend ub;
     func(ub);
@@ -362,8 +369,11 @@ static void testWithoutThenWithCache(std::function<void(UeberBackend& ub)> func)
     ::arg().set("query-cache-ttl")="20";
     ::arg().set("negquery-cache-ttl")="60";
     QC.cleanup();
+    ::arg().set("domain-cache-ttl")="60";
+    g_domainCache.clear();
 
     UeberBackend ub;
+    ub.updateDomainCache();
     /* a first time to fill the cache */
     func(ub);
     /* a second time to make sure every call has been tried with the cache filled */
index 7db0337ae21f382349cef12d30934fbd9fa5f434..91b481169d5479816a71ce0b1bff37bb5fd2e0dd 100644 (file)
@@ -5,10 +5,12 @@
 #endif
 #include <boost/test/unit_test.hpp>
 #include "arguments.hh"
+#include "auth-domaincache.hh"
 #include "auth-packetcache.hh"
 #include "auth-querycache.hh"
 #include "statbag.hh"
 StatBag S;
+AuthDomainCache g_domainCache;
 AuthPacketCache PC;
 AuthQueryCache QC;
 
index 8dc738265887ad9e14cd6993736afb5fceda66fc..e34b875066a4f494a8922919db1d2176966a3c7d 100644 (file)
@@ -26,6 +26,7 @@
 #include <boost/archive/binary_oarchive.hpp>
 
 #include "auth-querycache.hh"
+#include "auth-domaincache.hh"
 #include "utility.hh"
 
 
@@ -47,6 +48,7 @@
 #include "logger.hh"
 #include "statbag.hh"
 
+extern AuthDomainCache g_domainCache;
 extern StatBag S;
 
 vector<UeberBackend *>UeberBackend::instances;
@@ -120,12 +122,18 @@ bool UeberBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool get
 
 bool UeberBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
 {
+  bool success = false;
+  int zoneId;
   for(DNSBackend* mydb :  backends) {
-    if(mydb->createDomain(domain, kind, masters, account)) {
-      return true;
+    if(mydb->createDomain(domain, kind, masters, account, &zoneId)) {
+      success = true;
+      break;
     }
   }
-  return false;
+  if (success) {
+    g_domainCache.add(domain, zoneId);  // make new domain visible
+  }
+  return success;
 }
 
 bool UeberBackend::doesDNSSEC()
@@ -273,9 +281,26 @@ void UeberBackend::reload()
   }
 }
 
+void UeberBackend::updateDomainCache() {
+  if (!g_domainCache.isEnabled()) {
+    return;
+  }
+
+  vector<tuple<DNSName, int>> domain_indices;
+
+  for (vector<DNSBackend*>::iterator i = backends.begin(); i != backends.end(); ++i )
+  {
+    vector<DomainInfo> domains;
+    (*i)->getAllDomains(&domains, false);
+    for(const auto& di: domains) {
+      domain_indices.push_back({di.zone, (int)di.id});  // this cast should not be necessary
+    }
+  }
+  g_domainCache.replace(domain_indices);
+}
+
 void UeberBackend::rediscover(string *status)
 {
-  
   for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
   {
     string tmpstr;
@@ -283,6 +308,8 @@ void UeberBackend::rediscover(string *status)
     if(status) 
       *status+=tmpstr + (i!=backends.begin() ? "\n" : "");
   }
+
+  updateDomainCache();
 }
 
 
@@ -322,20 +349,39 @@ bool UeberBackend::getAuth(const DNSName &target, const QType& qtype, SOAData* s
   // of them has a more specific zone but don't bother asking this specific
   // backend again for b.c.example.com., c.example.com. and example.com.
   // If a backend has no match it may respond with an empty qname.
-
   bool found = false;
   int cstat;
   DNSName shorter(target);
   vector<pair<size_t, SOAData> > bestmatch (backends.size(), make_pair(target.wirelength()+1, SOAData()));
   do {
+    int zoneId{-1};
+    if(cachedOk && g_domainCache.isEnabled()) {
+      if (g_domainCache.getEntry(shorter, zoneId)) {
+        // Zone exists in domain cache, directly lookup SOA.
+        // XXX: this code path and the cache lookup below should be merged; but that needs the code path below to also use ANY.
+        // Or it should just also use lookup().
+        DNSZoneRecord zr;
+        lookup(QType(QType::SOA), shorter, zoneId, nullptr);
+        if (!get(zr)) {
+          throw PDNSException("Backend returned no SOA for existing domain '"+shorter.toLogString()+"'");
+        }
+        sd->qname = zr.dr.d_name;
+        fillSOAData(zr, *sd);
+        // leave database handle in a consistent state
+        while (get(zr))
+          ;
+        goto found;
+      }
+      // domain does not exist, try again with shorter name
+      continue;
+    }
 
     d_question.qtype = QType::SOA;
     d_question.qname = shorter;
-    d_question.zoneId = -1;
+    d_question.zoneId = zoneId;
 
     // Check cache
     if(cachedOk && (d_cache_ttl || d_negcache_ttl)) {
-
       cstat = cacheHas(d_question,d_answers);
 
       if(cstat == 1 && !d_answers.empty() && d_cache_ttl) {
@@ -400,7 +446,7 @@ bool UeberBackend::getAuth(const DNSName &target, const QType& qtype, SOAData* s
         DLOG(g_log<<Logger::Error<<"add pos cache entry: "<<sd->qname<<endl);
         d_question.qtype = QType::SOA;
         d_question.qname = sd->qname;
-        d_question.zoneId = -1;
+        d_question.zoneId = zoneId;
 
         DNSZoneRecord rr;
         rr.dr.d_name = sd->qname;
index 873b098f161455422df73a700cdbf59180b8f69f..92963176f1b484c5a424486ace5f44e72d26b487 100644 (file)
@@ -98,7 +98,6 @@ public:
 
   /** Determines if we are authoritative for a zone, and at what level */
   bool getAuth(const DNSName &target, const QType &qtype, SOAData* sd, bool cachedOk=true);
-
   /** Load SOA info from backends, ignoring the cache.*/
   bool getSOAUncached(const DNSName &domain, SOAData &sd);
   bool get(DNSZoneRecord &r);
@@ -133,6 +132,8 @@ public:
   bool searchRecords(const string &pattern, int maxResults, vector<DNSResourceRecord>& result);
   bool searchComments(const string &pattern, int maxResults, vector<Comment>& result);
 
+  void updateDomainCache();
+
   bool inTransaction();
 private:
   handle d_handle;
@@ -162,5 +163,4 @@ private:
   int cacheHas(const Question &q, vector<DNSZoneRecord> &rrs);
   void addNegCache(const Question &q);
   void addCache(const Question &q, vector<DNSZoneRecord>&& rrs);
-  
 };
index 7260c10163757d9da4901ae033cba632d369c9f4..c607fdf706cb5f8b7c988fd002cbc76f6b48bcc7 100644 (file)
@@ -31,6 +31,7 @@ __EOF__
                        --query-logging --dnsupdate=yes \
       --expand-alias=yes --outgoing-axfr-expand-alias=yes \
       --resolver=$RESOLVERIP \
+      --domain-cache-ttl=0 \
                        --cache-ttl=$cachettl --dname-processing $lua_prequery &
 
                skipreasons="nodnssec noent nodyndns nometa noaxfr"
index bb3bc17dba894a27b2b247fc0796f3a64c4045a3..1462e274ded35936de23fe286d63fdd479f57a96 100755 (executable)
@@ -7,7 +7,8 @@ fi
 cleandig ns1.addzone.com A
 cleandig ns1.test.com A
 $PDNSCONTROL --config-name=bind --socket-dir=. --no-config bind-add-zone addzone.com ${PWD}/zones/addzone.com >/dev/null 2>&1
-$PDNSCONTROL --config-name=bind --socket-dir=. --no-config purge addzone.com
+# output of purge changes if domain-cache-ttl is set
+$PDNSCONTROL --config-name=bind --socket-dir=. --no-config purge addzone.com >/dev/null
 sleep 1
 $PDNSCONTROL --config-name=bind --socket-dir=. --no-config bind-add-zone addzone.com ${PWD}/zones/addzone.com
 sleep 1
index 845856c4727609f0bb09bf2404e93e2af805367e..5650c31b2177c630ba95dbf48024fdb3f59aac64 100644 (file)
@@ -3,7 +3,6 @@ Reply to question for qname='ns1.addzone.com.', qtype=A
 0      ns1.test.com.   IN      A       3600    1.1.1.1
 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
 Reply to question for qname='ns1.test.com.', qtype=A
-1
 Already loaded
 0      ns1.addzone.com.        IN      A       3600    1.1.1.5
 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0