]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
auth: implement producer freshness check and a lot of cleanup/speedup
authorKees Monshouwer <mind04@monshouwer.org>
Mon, 27 Jun 2022 20:01:56 +0000 (22:01 +0200)
committermind04 <mind04@monshouwer.org>
Fri, 8 Jul 2022 10:31:19 +0000 (12:31 +0200)
31 files changed:
modules/bindbackend/bindbackend2.cc
modules/bindbackend/bindbackend2.hh
modules/gmysqlbackend/gmysqlbackend.cc
modules/godbcbackend/godbcbackend.cc
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gsqlite3backend/gsqlite3backend.cc
modules/ldapbackend/ldapbackend.hh
modules/ldapbackend/master.cc
modules/lmdbbackend/lmdbbackend.cc
modules/lmdbbackend/lmdbbackend.hh
modules/remotebackend/remotebackend.cc
modules/remotebackend/remotebackend.hh
modules/remotebackend/test-remotebackend.cc
modules/tinydnsbackend/tinydnsbackend.cc
modules/tinydnsbackend/tinydnsbackend.hh
pdns/Makefile.am
pdns/auth-catalogzone.cc [new file with mode: 0644]
pdns/auth-catalogzone.hh [new file with mode: 0644]
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/communicator.hh
pdns/digests.hh
pdns/dnsbackend.hh
pdns/dnssecsigner.cc
pdns/mastercommunicator.cc
pdns/sha.hh
pdns/test-digests_hh.cc
pdns/test-ueberbackend_cc.cc
pdns/ueberbackend.cc
pdns/ueberbackend.hh
regression-tests/backends/gmysql-master

index 43e2034999fd08ea258a0724a6c7c32fe0934c26..641dba5c5ba3d9cefcb0e8d22e7a29baae34d683 100644 (file)
@@ -318,7 +318,7 @@ bool Bind2Backend::feedRecord(const DNSResourceRecord& rr, const DNSName& ordern
   return true;
 }
 
-void Bind2Backend::getUpdatedMasters(vector<DomainInfo>* changedDomains)
+void Bind2Backend::getUpdatedMasters(vector<DomainInfo>& changedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
 {
   vector<DomainInfo> consider;
   {
@@ -356,7 +356,7 @@ void Bind2Backend::getUpdatedMasters(vector<DomainInfo>* changedDomains)
       }
       if (di.notified_serial) { // don't do notification storm on startup
         di.serial = soadata.serial;
-        changedDomains->push_back(std::move(di));
+        changedDomains.push_back(std::move(di));
       }
     }
   }
index dc461e9a3be4dffb74d18c1148c9afec82a7cf5b..e09e7d753af29e51be18f2e25eba255ff42cc965 100644 (file)
@@ -184,7 +184,7 @@ public:
   Bind2Backend(const string& suffix = "", bool loadZones = true);
   ~Bind2Backend();
   void getUnfreshSlaveInfos(vector<DomainInfo>* unfreshDomains) override;
-  void getUpdatedMasters(vector<DomainInfo>* changedDomains) override;
+  void getUpdatedMasters(vector<DomainInfo>& changedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
   bool getDomainInfo(const DNSName& domain, DomainInfo& di, bool getSerial = true) override;
   time_t getCtime(const string& fname);
   // DNSSEC
index af3bd54ea2440190fc6d988328c1a78d5da1a3d5..d0ff7295bc844ab02a0ad2f8ce6482d79a168f13 100644 (file)
@@ -103,7 +103,7 @@ public:
 
     declare(suffix, "info-zone-query", "", "select id,name,master,last_check,notified_serial,type,options,catalog,account from domains where name=?");
 
-    declare(suffix, "info-all-slaves-query", "", "select id,name,master,last_check from domains where type='SLAVE'");
+    declare(suffix, "info-all-slaves-query", "", "select domains.id, domains.name, domains.type, domains.master, domains.last_check, records.content from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name where domains.type in ('SLAVE', 'CONSUMER')");
     declare(suffix, "supermaster-query", "", "select account from supermasters where ip=? and nameserver=?");
     declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?");
     declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values (?,?,?)");
@@ -132,7 +132,7 @@ public:
     declare(suffix, "update-account-query", "", "update domains set account=? where name=?");
     declare(suffix, "update-serial-query", "", "update domains set notified_serial=? where id=?");
     declare(suffix, "update-lastcheck-query", "", "update domains set last_check=? where id=?");
-    declare(suffix, "info-all-master-query", "", "select d.id, d.name, d.notified_serial, r.content from records r join domains d on r.domain_id=d.id and r.name=d.name where r.type='SOA' and r.disabled=0 and d.type='MASTER'");
+    declare(suffix, "info-all-master-query", "", "select d.id, d.name, d.type, d.notified_serial,d.options, d.catalog,r.content from records r join domains d on r.domain_id=d.id and r.name=d.name where r.type='SOA' and r.disabled=0 and d.type in ('MASTER', 'PRODUCER')");
     declare(suffix, "delete-domain-query", "", "delete from domains where name=?");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=?");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=? and name=? and type=?");
index 60572cf5acd87e922d20ac9aeaa5f1dc76d94628..0a750930688a3e685d3c9b578f1495546759fd7b 100644 (file)
@@ -83,7 +83,7 @@ public:
 
     declare(suffix, "info-zone-query", "", "select id,name,master,last_check,notified_serial,type,options,catalog,account from domains where name=?");
 
-    declare(suffix, "info-all-slaves-query", "", "select id,name,master,last_check from domains where type='SLAVE'");
+    declare(suffix, "info-all-slaves-query", "", "select domains.id, domains.name, domains.type, domains.master, domains.last_check, records.content from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name where domains.type in ('SLAVE', 'CONSUMER')");
     declare(suffix, "supermaster-query", "", "select account from supermasters where ip=? and nameserver=?");
     declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?");
     declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values (?,?,?)");
@@ -112,7 +112,7 @@ public:
     declare(suffix, "update-account-query", "", "update domains set account=? where name=?");
     declare(suffix, "update-serial-query", "", "update domains set notified_serial=? where id=?");
     declare(suffix, "update-lastcheck-query", "", "update domains set last_check=? where id=?");
-    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.notified_serial, records.content from records join domains on records.domain_id=domains.id and records.name=domains.name where records.type='SOA' and records.disabled=0 and domains.type='MASTER'");
+    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.type, domains.notified_serial, domains.options, domains.catalog, records.content from records join domains on records.domain_id=domains.id and records.name=domains.name where records.type='SOA' and records.disabled=0 and domains.type in ('MASTER', 'PRODUCER')");
     declare(suffix, "delete-domain-query", "", "delete from domains where name=?");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=?");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=? and name=? and type=?");
index bbd39645e434ba69ba05ff3ce65d84dd25d37baa..409e83fe2e8e0f263cd09071478ef7cc1deacca9 100644 (file)
@@ -110,7 +110,7 @@ public:
 
     declare(suffix, "info-zone-query", "", "select id,name,master,last_check,notified_serial,type,options,catalog,account from domains where name=$1");
 
-    declare(suffix, "info-all-slaves-query", "", "select id,name,master,last_check from domains where type='SLAVE'");
+    declare(suffix, "info-all-slaves-query", "", "select domains.id, domains.name, domains.type, domains.master, domains.last_check, records.content from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name where domains.type in ('SLAVE', 'CONSUMER')");
     declare(suffix, "supermaster-query", "", "select account from supermasters where ip=$1 and nameserver=$2");
     declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=$1 and account=$2");
     declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values ($1,$2,$3)");
@@ -139,7 +139,7 @@ public:
     declare(suffix, "update-account-query", "", "update domains set account=$1 where name=$2");
     declare(suffix, "update-serial-query", "", "update domains set notified_serial=$1 where id=$2");
     declare(suffix, "update-lastcheck-query", "", "update domains set last_check=$1 where id=$2");
-    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.notified_serial, records.content from records join domains on records.domain_id=domains.id and records.name=domains.name where records.type='SOA' and records.disabled=false and domains.type='MASTER'");
+    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.type, domains.notified_serial, domains.options, domains.catalog, records.content from records join domains on records.domain_id=domains.id and records.name=domains.name where records.type='SOA' and records.disabled=false and domains.type in ('MASTER', 'PRODUCER')");
     declare(suffix, "delete-domain-query", "", "delete from domains where name=$1");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=$1");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=$1 and name=$2 and type=$3");
index a0ff8a0b8b15ed815139d70d0a1c3ca796ce7e40..b3103f8b54cd7c3e14a0e536186821bb5a25d055 100644 (file)
@@ -96,7 +96,7 @@ public:
 
     declare(suffix, "info-zone-query", "", "select id,name,master,last_check,notified_serial,type,options,catalog,account from domains where name=:domain");
 
-    declare(suffix, "info-all-slaves-query", "", "select id,name,master,last_check from domains where type='SLAVE'");
+    declare(suffix, "info-all-slaves-query", "", "select domains.id, domains.name, domains.type, domains.master, domains.last_check, records.content from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name where domains.type in ('SLAVE', 'CONSUMER')");
     declare(suffix, "supermaster-query", "", "select account from supermasters where ip=:ip and nameserver=:nameserver");
     declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=:nameserver and account=:account");
     declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values (:ip,:nameserver,:account)");
@@ -125,7 +125,7 @@ public:
     declare(suffix, "update-account-query", "", "update domains set account=:account where name=:domain");
     declare(suffix, "update-serial-query", "", "update domains set notified_serial=:serial where id=:domain_id");
     declare(suffix, "update-lastcheck-query", "", "update domains set last_check=:last_check where id=:domain_id");
-    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.notified_serial, records.content from records join domains on records.domain_id=domains.id and records.name=domains.name where records.type='SOA' and records.disabled=0 and domains.type='MASTER'");
+    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.type, domains.notified_serial, domains.options, domains.catalog, records.content from records join domains on records.domain_id=domains.id and records.name=domains.name where records.type='SOA' and records.disabled=0 and domains.type in ('MASTER', 'PRODUCER')");
     declare(suffix, "delete-domain-query", "", "delete from domains where name=:domain");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=:domain_id");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=:domain_id and name=:qname and type=:qtype");
index 747c6c7e8e1b01b64f2f23c886347e659e5cf358..23e77c8795af892b29a5ec806b1a82684f935cfb 100644 (file)
@@ -177,6 +177,6 @@ public:
   bool getDomainInfo(const DNSName& domain, DomainInfo& di, bool getSerial = true) override;
 
   // Master backend
-  void getUpdatedMasters(vector<DomainInfo>* domains) override;
+  void getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
   void setNotified(uint32_t id, uint32_t serial) override;
 };
index 9a4847803c2f4af27a2d23893789441b00d400ff..1ab28f0d3ad79b20ab6991960611604a7108b296 100644 (file)
@@ -24,7 +24,7 @@
 #include "ldapbackend.hh"
 #include <cstdlib>
 
-void LdapBackend::getUpdatedMasters(vector<DomainInfo>* domains)
+void LdapBackend::getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
 {
   string filter;
   PowerLDAP::SearchResult::Ptr search;
@@ -45,7 +45,7 @@ void LdapBackend::getUpdatedMasters(vector<DomainInfo>* domains)
   catch (LDAPNoConnection& lnc) {
     g_log << Logger::Warning << d_myname << " Connection to LDAP lost, trying to reconnect" << endl;
     if (reconnect())
-      this->getUpdatedMasters(domains);
+      this->getUpdatedMasters(domains, catalogs, catalogHashes);
     else
       throw PDNSException("Failed to reconnect to LDAP server");
   }
@@ -66,7 +66,7 @@ void LdapBackend::getUpdatedMasters(vector<DomainInfo>* domains)
       continue;
 
     if (di.notified_serial < di.serial)
-      domains->push_back(di);
+      domains.push_back(di);
   }
 }
 
index 018faf820f07f56f9a550eb1846d802304a79326..b617aee34afe7146e972c736daf9d998f1f0008f 100644 (file)
@@ -23,6 +23,7 @@
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+#include "pdns/auth-catalogzone.hh"
 #include "pdns/utility.hh"
 #include "pdns/dnsbackend.hh"
 #include "pdns/dns.hh"
@@ -998,27 +999,6 @@ bool LMDBBackend::setAccount(const DNSName& domain, const std::string& account)
   });
 }
 
-void LMDBBackend::setStale(uint32_t domain_id)
-{
-  genChangeDomain(domain_id, [](DomainInfo& di) {
-    di.last_check = 0;
-  });
-}
-
-void LMDBBackend::setFresh(uint32_t domain_id)
-{
-  genChangeDomain(domain_id, [](DomainInfo& di) {
-    di.last_check = time(0);
-  });
-}
-
-void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
-{
-  genChangeDomain(domain_id, [serial](DomainInfo& di) {
-    di.serial = serial;
-  });
-}
-
 bool LMDBBackend::setMasters(const DNSName& domain, const vector<ComboAddress>& masters)
 {
   return genChangeDomain(domain, [&masters](DomainInfo& di) {
@@ -1067,44 +1047,89 @@ void LMDBBackend::getAllDomains(vector<DomainInfo>* domains, bool doSerial, bool
 
 void LMDBBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
 {
-  //  cout<<"Start of getUnfreshSlaveInfos"<<endl;
-  domains->clear();
-  auto txn = d_tdomains->getROTransaction();
-
+  uint32_t serial;
   time_t now = time(0);
+  LMDBResourceRecord lrr;
+  soatimes st;
+
+  auto txn = d_tdomains->getROTransaction();
   for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
-    if (iter->kind != DomainInfo::Slave)
+    if (iter->kind != DomainInfo::Slave && iter->kind != DomainInfo::Consumer) {
       continue;
+    }
 
     auto txn2 = getRecordsROTransaction(iter.getID());
     compoundOrdername co;
     MDBOutVal val;
-    uint32_t serial = 0;
     if (!txn2->txn->get(txn2->db->dbi, co(iter.getID(), g_rootdnsname, QType::SOA), val)) {
-      LMDBResourceRecord lrr;
       serFromString(val.get<string_view>(), lrr);
-      struct soatimes st;
-
       memcpy(&st, &lrr.content[lrr.content.size() - sizeof(soatimes)], sizeof(soatimes));
-
-      if ((time_t)(iter->last_check + ntohl(st.refresh)) >= now) { // still fresh
-        continue; // try next domain
+      if ((time_t)(iter->last_check + ntohl(st.refresh)) > now) { // still fresh
+        continue;
       }
-      //      cout << di.last_check <<" + " <<sdata.refresh<<" > = " << now << "\n";
       serial = ntohl(st.serial);
     }
     else {
-      //      cout << "Could not find SOA for "<<iter->zone<<" with id "<<iter.getID()<<endl;
       serial = 0;
     }
-    DomainInfo di = *iter;
+
+    DomainInfo di(*iter);
     di.id = iter.getID();
     di.serial = serial;
     di.backend = this;
 
-    domains->push_back(di);
+    domains->emplace_back(di);
+  }
+}
+
+void LMDBBackend::setStale(uint32_t domain_id)
+{
+  genChangeDomain(domain_id, [](DomainInfo& di) {
+    di.last_check = 0;
+  });
+}
+
+void LMDBBackend::setFresh(uint32_t domain_id)
+{
+  genChangeDomain(domain_id, [](DomainInfo& di) {
+    di.last_check = time(nullptr);
+  });
+}
+
+void LMDBBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
+{
+  DomainInfo di;
+  CatalogInfo ci;
+
+  auto txn = d_tdomains->getROTransaction();
+  for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
+    if (iter->kind != DomainInfo::Master) {
+      continue;
+    }
+
+    if (iter->kind == DomainInfo::Producer) {
+      catalogs.insert(iter->zone);
+      catalogHashes[iter->zone].process("\0");
+      continue; // Producer fresness check is performed elsewhere
+    }
+
+    if (!iter->catalog.empty()) {
+      ci.fromJson(iter->options, CatalogInfo::CatalogType::Producer);
+      ci.updateHash(catalogHashes, *iter);
+    }
+
+    di = *iter;
+    if (getSerial(di) && di.serial != di.notified_serial) {
+      updatedDomains.emplace_back(di);
+    }
   }
-  //  cout<<"END of getUnfreshSlaveInfos"<<endl;
+}
+
+void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
+{
+  genChangeDomain(domain_id, [serial](DomainInfo& di) {
+    di.serial = serial;
+  });
 }
 
 bool LMDBBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
index 34a4eb95ac54a9416e320656ee3ed442ce06e34f..f395a85480cf5cd1ad2afee1fb185d31b3e99332 100644 (file)
@@ -80,7 +80,14 @@ public:
   bool get(DNSResourceRecord& rr) override;
   bool get(DNSZoneRecord& dzr) override;
 
+  // secondary support
   void getUnfreshSlaveInfos(vector<DomainInfo>* domains) override;
+  void setStale(uint32_t domain_id) override;
+  void setFresh(uint32_t domain_id) override;
+
+  // primary support
+  void getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
+  void setNotified(uint32_t id, uint32_t serial) override;
 
   bool setMasters(const DNSName& domain, const vector<ComboAddress>& masters) override;
   bool setKind(const DNSName& domain, const DomainInfo::DomainKind kind) override;
@@ -103,9 +110,6 @@ public:
   }
 
   bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta) override;
-  void setStale(uint32_t domain_id) override;
-  void setFresh(uint32_t domain_id) override;
-  void setNotified(uint32_t id, uint32_t serial) override;
   bool setOptions(const DNSName& domain, const std::string& options) override;
   bool setCatalog(const DNSName& domain, const DNSName& options) override;
   bool setAccount(const DNSName& domain, const std::string& account) override;
index 380d956442de3440fe63523461c1f04e1199b1df..cbbe2f9fb3c02f9ada4bef5ff973353be7aad7dc 100644 (file)
@@ -885,7 +885,7 @@ void RemoteBackend::alsoNotifies(const DNSName& domain, set<string>* ips)
   ips->insert(meta.begin(), meta.end());
 }
 
-void RemoteBackend::getUpdatedMasters(vector<DomainInfo>* domains)
+void RemoteBackend::getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
 {
   Json query = Json::object{
     {"method", "getUpdatedMasters"},
@@ -902,7 +902,7 @@ void RemoteBackend::getUpdatedMasters(vector<DomainInfo>* domains)
   for (const auto& row : answer["result"].array_items()) {
     DomainInfo di;
     this->parseDomainInfo(row, di);
-    domains->push_back(di);
+    domains.push_back(di);
   }
 }
 
index 08811fe078ea7e6036272d93ed1242fd1a857b1f..d103331c17381db01083c01f1f237535c2763056 100644 (file)
@@ -199,7 +199,7 @@ public:
   bool searchRecords(const string& pattern, int maxResults, vector<DNSResourceRecord>& result) override;
   bool searchComments(const string& pattern, int maxResults, vector<Comment>& result) override;
   void getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool include_disabled) override;
-  void getUpdatedMasters(vector<DomainInfo>* domains) override;
+  void getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
   void alsoNotifies(const DNSName& domain, set<string>* ips) override;
   void getUnfreshSlaveInfos(vector<DomainInfo>* domains) override;
   void setStale(uint32_t domain_id) override;
index 06ebeb596eb207a205b178e8cddb6d36ab759209..c7386748aa9d4be002a363ce905ead687d6a6aa0 100644 (file)
@@ -370,8 +370,10 @@ BOOST_AUTO_TEST_CASE(test_method_getUpdatedMasters)
   DomainInfo di;
   BOOST_TEST_MESSAGE("Testing getUpdatedMasters method");
   vector<DomainInfo> result;
+  std::unordered_set<DNSName> catalogs;
+  CatalogHashMap hashes;
 
-  be->getUpdatedMasters(&result);
+  be->getUpdatedMasters(result, catalogs, hashes);
 
   BOOST_CHECK(result.size() > 0);
 
index ae35c0feaaad9acb75f15962f635e6889c1606de..c1b02a1ec194b74613355738d87afd383eb12b0e 100644 (file)
@@ -88,7 +88,7 @@ TinyDNSBackend::TinyDNSBackend(const string& suffix)
   d_isWildcardQuery = false;
 }
 
-void TinyDNSBackend::getUpdatedMasters(vector<DomainInfo>* retDomains)
+void TinyDNSBackend::getUpdatedMasters(vector<DomainInfo>& retDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
 {
   auto domainInfo = s_domainInfo.lock(); //TODO: We could actually lock less if we do it per suffix.
   if (!domainInfo->count(d_suffix)) {
@@ -120,13 +120,13 @@ void TinyDNSBackend::getUpdatedMasters(vector<DomainInfo>* retDomains)
 
       di->id = s_lastId;
       if (di->notified_serial > 0) {
-        retDomains->push_back(*di);
+        retDomains.push_back(*di);
       }
     }
     else {
       if (itByZone->notified_serial < di->serial) {
         di->id = itByZone->id;
-        retDomains->push_back(*di);
+        retDomains.push_back(*di);
       }
     }
   }
index 10ee5527825c8e7564989af73b1ffa51d47c05f8..29d1e64798549aa01b2d5d06e94e8312ae92d243 100644 (file)
@@ -73,7 +73,7 @@ public:
   void getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool include_disabled) override;
 
   //Master mode operation
-  void getUpdatedMasters(vector<DomainInfo>* domains) override;
+  void getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
   void setNotified(uint32_t id, uint32_t serial) override;
 
 private:
index 2ef1f85b023170c98014d8ee6e7d27c8f1e0deaf..2a5ed8852114e245a64c5720127f2ad08190567d 100644 (file)
@@ -188,6 +188,7 @@ pdns_server_SOURCES = \
        arguments.cc arguments.hh \
        auth-caches.cc auth-caches.hh \
        auth-carbon.cc \
+       auth-catalogzone.cc auth-catalogzone.hh \
        auth-packetcache.cc auth-packetcache.hh \
        auth-querycache.cc auth-querycache.hh \
        auth-zonecache.cc auth-zonecache.hh \
@@ -331,6 +332,7 @@ endif
 pdnsutil_SOURCES = \
        arguments.cc \
        auth-caches.cc auth-caches.hh \
+       auth-catalogzone.cc auth-catalogzone.hh \
        auth-packetcache.cc auth-packetcache.hh \
        auth-querycache.cc auth-querycache.hh \
        auth-zonecache.cc auth-zonecache.hh \
diff --git a/pdns/auth-catalogzone.cc b/pdns/auth-catalogzone.cc
new file mode 100644 (file)
index 0000000..a34851a
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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-catalogzone.hh"
+
+void CatalogInfo::fromJson(const std::string& json, CatalogType type)
+{
+  d_type = type;
+  if (d_type == CatalogType::None) {
+    throw std::runtime_error("CatalogType is set to None");
+  }
+  if (json.empty()) {
+    return;
+  }
+  std::string err;
+  d_doc = json11::Json::parse(json, err);
+  if (!d_doc.is_null()) {
+    if (!d_doc[getTypeString(d_type)].is_null()) {
+      auto items = d_doc[getTypeString(type)].object_items();
+      if (items["coo"].is_string()) {
+        if (!items["coo"].string_value().empty()) {
+          this->coo = DNSName(items["coo"].string_value());
+        }
+      }
+      else {
+        throw std::out_of_range("Key 'coo' is not a string");
+      }
+      if (items["unique"].is_string()) {
+        if (!items["uniq"].string_value().empty()) {
+          this->unique = DNSName(items["unique"].string_value());
+        }
+      }
+      else {
+        throw std::out_of_range("Key 'unique' is not a string");
+      }
+    }
+  }
+  else {
+    throw std::runtime_error("Parsing of JSON options failed: " + err);
+  }
+}
+
+std::string CatalogInfo::toJson() const
+{
+  if (d_type == CatalogType::None) {
+    throw std::runtime_error("CatalogType is set to None");
+  }
+  json11::Json::object object;
+  if (!coo.empty()) {
+    object["coo"] = coo.toString();
+  }
+  if (!unique.empty()) {
+    object["unique"] = unique.toString();
+  }
+  auto tmp = d_doc.object_items();
+  tmp[getTypeString(d_type)] = object;
+  const json11::Json ret = tmp;
+  return ret.dump();
+}
+
+void CatalogInfo::updateHash(CatalogHashMap& hashes, const DomainInfo& di) const
+{
+  hashes[di.catalog].process(static_cast<char>(di.id) + di.zone.toLogString() + "\0" + this->coo.toLogString() + "\0" + this->unique.toLogString());
+}
diff --git a/pdns/auth-catalogzone.hh b/pdns/auth-catalogzone.hh
new file mode 100644 (file)
index 0000000..f483fab
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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 "ext/json11/json11.hpp"
+#include "dnsbackend.hh"
+
+class CatalogInfo
+{
+public:
+  enum CatalogType : uint8_t
+  {
+    None,
+    Producer,
+    Consumer
+  };
+
+  static const char* getTypeString(enum CatalogType type)
+  {
+    const char* types[] = {"none", "producer", "consumer"};
+    return types[type];
+  }
+
+  CatalogInfo() :
+    d_type(CatalogType::None) {}
+  CatalogInfo(const DNSName& zone, CatalogType type)
+  {
+    this->zone = zone;
+    d_type = type;
+  }
+
+  void setType(CatalogType type) { d_type = type; }
+
+  void fromJson(const std::string& json, CatalogType type);
+  std::string toJson() const;
+
+  void updateHash(CatalogHashMap& hashes, const DomainInfo& di) const;
+
+  bool operator<(const CatalogInfo& rhs) const
+  {
+    return zone < rhs.zone;
+  }
+
+  DNSName coo, unique, zone;
+
+private:
+  CatalogType d_type;
+  json11::Json d_doc;
+};
index bf15ea450fa718c3c9af33038e6b84ce7475a14f..c18900c4d7e19d33384759c927acc32956e1c79c 100644 (file)
@@ -22,6 +22,7 @@
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+#include "pdns/auth-catalogzone.hh"
 #include "pdns/dns.hh"
 #include "pdns/dnsbackend.hh"
 #include "gsqlbackend.hh"
@@ -396,141 +397,215 @@ bool GSQLBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool getS
 
 void GSQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
 {
-  /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
-     id,name,master IP,serial */
+  /*
+    list all domains that need refreshing for which we are secondary, and insert into
+    unfreshDomains: id, name, master, serial
+  */
+
   try {
     reconnectIfNeeded();
 
+    // clang-format off
     d_InfoOfAllSlaveDomainsQuery_stmt->
       execute()->
       getResult(d_result)->
       reset();
+    // clang-format on
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve list of slave domains: "+e.txtReason());
   }
 
-  vector<DomainInfo> allSlaves;
+  SOAData sd;
+  DomainInfo di;
+  for (const auto& row : d_result) { // id, name, type, master, last_check, content
+    ASSERT_ROW_COLUMNS("info-all-slaves-query", row, 6);
 
-  bool loggedAssertRowColumns = false;
-  for(const auto& row : d_result) { // id,name,master,last_check
-    DomainInfo sd;
     try {
-      ASSERT_ROW_COLUMNS("info-all-slaves-query", row, 4);
-    } catch(const PDNSException &e) {
-      if (!loggedAssertRowColumns) {
-        g_log<<Logger::Warning<<e.reason<<endl;
-      }
-      loggedAssertRowColumns = true;
+      di.zone = DNSName(row[1]);
+    }
+    catch (const std::runtime_error& e) {
+      g_log << Logger::Warning << "Zone name '" << row[1] << "' is not a valid DNS name: " << e.what() << endl;
       continue;
     }
-
-    try {
-      sd.zone = DNSName(row[1]);
-    } catch(const std::runtime_error &e) {
-      g_log<<Logger::Warning<<"Domain name '"<<row[1]<<"' is not a valid DNS name: "<<e.what()<<endl;
+    catch (PDNSException& ae) {
+      g_log << Logger::Warning << "Zone name '" << row[1] << "' is not a valid DNS name: " << ae.reason << endl;
       continue;
     }
 
+    if (!row[5].empty()) {
+      try {
+        fillSOAData(row[5], sd);
+      }
+      catch (const std::exception& exp) {
+        g_log << Logger::Warning << "Error while parsing SOA data for zone '" << di.zone << "': " << exp.what() << endl;
+        continue;
+      }
+      catch (...) {
+        g_log << Logger::Warning << "Error while parsing SOA data for zone '" << di.zone << endl;
+        continue;
+      }
+
+      uint32_t last_check;
+      try {
+        pdns::checked_stoi_into(last_check, row[4]);
+      }
+      catch (const std::exception& e) {
+        g_log << Logger::Warning << "Could not convert last_check '" << row[4] << "' for zone '" << di.zone << "' into an integer: " << e.what() << endl;
+        continue;
+      }
+
+      if (static_cast<time_t>(last_check + sd.refresh) < time(nullptr)) { // still fresh
+        continue;
+      }
+      di.serial = sd.serial;
+    }
+
     try {
-      pdns::checked_stoi_into(sd.id, row[0]);
+      pdns::checked_stoi_into(di.id, row[0]);
     } catch (const std::exception &e) {
-      g_log<<Logger::Warning<<"Could not convert id ("<<row[0]<<") for domain '"<<sd.zone<<"' into an integer: "<<e.what()<<endl;
+      g_log << Logger::Warning << "Could not convert id '" << row[0] << "' for zone '" << di.zone << "' into an integer: " << e.what() << endl;
       continue;
     }
 
     vector<string> masters;
-    stringtok(masters, row[2], ", \t");
+    stringtok(masters, row[3], ", \t");
     for(const auto& m : masters) {
       try {
-        sd.masters.emplace_back(m, 53);
+        di.masters.emplace_back(m, 53);
       } catch(const PDNSException &e) {
-        g_log<<Logger::Warning<<"Could not parse master address ("<<m<<") for zone '"<<sd.zone<<"': "<<e.reason<<endl;
+        g_log << Logger::Warning << "Could not parse master address '" << m << "' for zone '" << di.zone << "': " << e.reason << endl;
       }
     }
-    if (sd.masters.empty()) {
-      g_log<<Logger::Warning<<"No masters for slave zone '"<<sd.zone<<"' found in the database"<<endl;
+    if (di.masters.empty()) {
+      g_log << Logger::Warning << "No masters for secondary zone '" << di.zone << "' found in the database" << endl;
       continue;
     }
 
-    try {
-      pdns::checked_stoi_into(sd.last_check, row[3]);
-    } catch (const std::exception &e) {
-      g_log<<Logger::Warning<<"Could not convert last_check ("<<row[3]<<") for domain '"<<sd.zone<<"' into an integer: "<<e.what()<<endl;
-      continue;
+    if (pdns_iequals(row[2], "SLAVE")) {
+      di.kind = DomainInfo::Slave;
     }
-
-    sd.backend=this;
-    sd.kind=DomainInfo::Slave;
-    allSlaves.push_back(sd);
-  }
-
-  for (auto& slave : allSlaves) {
-    try {
-      SOAData sdata;
-      sdata.serial=0;
-      sdata.refresh=0;
-      getSOA(slave.zone, sdata);
-      if(static_cast<time_t>(slave.last_check + sdata.refresh) < time(nullptr)) {
-        slave.serial=sdata.serial;
-        unfreshDomains->push_back(slave);
-      }
-    }
-    catch(const std::exception& exp) {
-      g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone<<"': "<<exp.what()<<endl;
-      continue;
+    else if (pdns_iequals(row[2], "CONSUMER")) {
+      di.kind = DomainInfo::Consumer;
     }
-    catch(...) {
-      g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone<<"', skipping"<<endl;
-      continue;
+    else {
+      g_log << Logger::Warning << "Type '" << row[2] << "' for zone '" << di.zone << "' is no secondary type" << endl;
     }
+
+    di.backend = this;
+    unfreshDomains->emplace_back(di);
   }
 }
 
-void GSQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
+void GSQLBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
 {
-  /* list all domains that need notifications for which we are master, and insert into updatedDomains
-     id, name, notified_serial, serial */
+  /*
+    list all domains that need notifications for which we are promary, and insert into
+    updatedDomains: id, name, notified_serial, serial
+  */
+
   try {
     reconnectIfNeeded();
 
+    // clang-format off
     d_InfoOfAllMasterDomainsQuery_stmt->
       execute()->
       getResult(d_result)->
       reset();
+    // clang-format on
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e.txtReason());
   }
 
-  size_t numanswers=d_result.size();
-  vector<string>parts;
+  SOAData sd;
   DomainInfo di;
+  CatalogInfo ci;
 
-  di.backend = this;
-  di.kind = DomainInfo::Master;
+  for (const auto& row : d_result) { // id, name, type, notified_serial, options, catalog, content
+    ASSERT_ROW_COLUMNS("info-all-master-query", row, 7);
 
-  for( size_t n = 0; n < numanswers; ++n ) { // id, name, notified_serial, content
-    ASSERT_ROW_COLUMNS( "info-all-master-query", d_result[n], 4 );
+    di.backend = this;
 
-    parts.clear();
-    stringtok( parts, d_result[n][3] );
+    try {
+      pdns::checked_stoi_into(di.id, row[0]);
+    }
+    catch (const std::exception& e) {
+      g_log << Logger::Warning << "Could not convert id '" << row[0] << "' for zone '" << di.zone << "' into an integer: " << e.what() << endl;
+      continue;
+    }
 
     try {
-      uint32_t serial = parts.size() > 2 ? pdns::checked_stoi<uint32_t>(parts[2]) : 0;
-      auto notified_serial = pdns::checked_stoi<uint32_t>(d_result[n][2]);
+      di.zone = DNSName(row[1]);
+    }
+    catch (const std::runtime_error& e) {
+      g_log << Logger::Warning << "Zone name '" << row[1] << "' is not a valid DNS name: " << e.what() << endl;
+      continue;
+    }
+    catch (PDNSException& ae) {
+      g_log << Logger::Warning << "Zone name '" << row[1] << "' is not a valid DNS name: " << ae.reason << endl;
+      continue;
+    }
+
+    try {
+      di.catalog = DNSName(row[5]);
+    }
+    catch (const std::runtime_error& e) {
+      g_log << Logger::Warning << "Zone name '" << row[5] << "' is not a valid DNS name: " << e.what() << endl;
+      continue;
+    }
+    catch (PDNSException& ae) {
+      g_log << Logger::Warning << "Zone name '" << row[5] << "' is not a valid DNS name: " << ae.reason << endl;
+      continue;
+    }
 
-      if( serial != notified_serial ) {
-        pdns::checked_stoi_into(di.id, d_result[n][0]);
-        di.zone = DNSName( d_result[n][1] );
-        di.serial = serial;
-        di.notified_serial = notified_serial;
+    if (pdns_iequals(row[2], "PRODUCER")) {
+      catalogs.insert(di.zone);
+      catalogHashes[di.zone].process("\0");
+      continue; // Producer fresness check is performed elsewhere
+    }
+    else if (!pdns_iequals(row[2], "MASTER")) {
+      g_log << Logger::Warning << "Type '" << row[2] << "' for zone '" << di.zone << "' is no primary type" << endl;
+    }
 
-        updatedDomains->emplace_back(di);
+    try {
+      if (!row[5].empty()) {
+        ci.fromJson(row[3], CatalogInfo::CatalogType::Producer);
+        ci.updateHash(catalogHashes, di);
       }
-    } catch ( ... ) {
+    }
+    catch (const std::exception& e) {
+      g_log << Logger::Warning << "Catalog hash update failed'" << row[4] << "' for zone '" << di.zone << "' member of '" << di.catalog << "': " << e.what() << endl;
+      continue;
+    }
+
+    try {
+      pdns::checked_stoi_into(di.id, row[4]);
+    }
+    catch (const std::exception& e) {
+      g_log << Logger::Warning << "Could not convert notified_serial '" << row[4] << "' for zone '" << di.zone << "' into an integer: " << e.what() << endl;
       continue;
     }
+
+    try {
+      fillSOAData(row[6], sd);
+    }
+    catch (const std::exception& exp) {
+      g_log << Logger::Warning << "Error while parsing SOA data for zone '" << di.zone << "': " << exp.what() << endl;
+      continue;
+    }
+    catch (...) {
+      g_log << Logger::Warning << "Error while parsing SOA data for zone '" << di.zone << endl;
+      continue;
+    }
+
+    if (di.notified_serial != sd.serial) {
+      di.kind = DomainInfo::Master;
+      di.serial = sd.serial;
+      di.catalog.clear();
+
+      updatedDomains.emplace_back(di);
+    }
   }
 }
 
index e7ffdb3370e5fa078f5299dfe3923255b3ffbb91..395fafdcf055e26585d94eb900d34d8736fd6d9a 100644 (file)
@@ -211,7 +211,7 @@ public:
   void setStale(uint32_t domain_id) override;
   void setFresh(uint32_t domain_id) override;
   void getUnfreshSlaveInfos(vector<DomainInfo> *domains) override;
-  void getUpdatedMasters(vector<DomainInfo> *updatedDomains) override;
+  void getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
   bool getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial=true) override;
   void setNotified(uint32_t domain_id, uint32_t serial) override;
   bool setMasters(const DNSName &domain, const vector<ComboAddress> &masters) override;
index b799e0159e443e03eaf40a60999be0df09dccfbc..78746ffe00e05b7f110efd5e5ed756ae2996336e 100644 (file)
@@ -153,7 +153,7 @@ public:
   CommunicatorClass() 
   {
     d_tickinterval=60;
-    d_masterschanged=d_slaveschanged=true;
+    d_slaveschanged = true;
     d_nsock4 = -1;
     d_nsock6 = -1;
     d_preventSelfNotification = false;
@@ -187,6 +187,7 @@ private:
 
   void slaveRefresh(PacketHandler *P);
   void masterUpdateCheck(PacketHandler *P);
+  void getUpdatedProducers(UeberBackend* B, vector<DomainInfo>& domains, const std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes);
 
   Semaphore d_suck_sem;
   Semaphore d_any_sem;
@@ -196,7 +197,7 @@ private:
   NotificationQueue d_nq;
 
   time_t d_tickinterval;
-  bool d_masterschanged, d_slaveschanged;
+  bool d_slaveschanged;
   bool d_preventSelfNotification;
 
   struct Data
index 0f30ba8306ac1376cd3becb14c524f2a1e820159..14a926c368e5416b4aaddd83cb96a8d90cd0c84e 100644 (file)
@@ -60,7 +60,7 @@ inline std::string pdns_hash(const EVP_MD * md, const std::string& input)
   return result;
 }
 
-inline std::string pdns_md5sum(const std::string& input)
+inline std::string pdns_md5(const std::string& input)
 {
   const auto md = EVP_md5();
   if (md == nullptr) {
@@ -70,7 +70,7 @@ inline std::string pdns_md5sum(const std::string& input)
   return pdns_hash(md, input);
 }
 
-inline std::string pdns_sha1sum(const std::string& input)
+inline std::string pdns_sha1(const std::string& input)
 {
   const auto md = EVP_sha1();
   if (md == nullptr) {
index e48c0ca5ace8ef705de7eaa7f5f38d0bf1394161..9c0f678f38aaf55e87685be14f77187d3f63c728 100644 (file)
@@ -42,6 +42,7 @@ class DNSPacket;
 #include "dnsname.hh"
 #include "dnsrecords.hh"
 #include "iputils.hh"
+#include "sha.hh"
 
 class DNSBackend;  
 struct DomainInfo
@@ -62,13 +63,23 @@ struct DomainInfo
   bool receivedNotify;
 
   uint32_t serial;
-  enum DomainKind : uint8_t { Master, Slave, Native } kind;
-  
+
   bool operator<(const DomainInfo& rhs) const
   {
     return zone < rhs.zone;
   }
 
+  // Do not reorder (lmdbbackend)!!! One exception 'All' is always last.
+  enum DomainKind : uint8_t
+  {
+    Master,
+    Slave,
+    Native,
+    Producer,
+    Consumer,
+    All
+  } kind;
+
   const char *getKindString() const
   {
     return DomainInfo::getKindString(kind);
@@ -76,7 +87,7 @@ struct DomainInfo
 
   static const char *getKindString(enum DomainKind kind)
   {
-    const char *kinds[]={"Master", "Slave", "Native"};
+    const char* kinds[] = {"Master", "Slave", "Native", "Producer", "Consumer", "All"};
     return kinds[kind];
   }
 
@@ -84,12 +95,20 @@ struct DomainInfo
   {
     if (pdns_iequals(kind, "SECONDARY") || pdns_iequals(kind, "SLAVE"))
       return DomainInfo::Slave;
-    else if (pdns_iequals(kind, "PRIMARY") || pdns_iequals(kind, "MASTER"))
+    if (pdns_iequals(kind, "PRIMARY") || pdns_iequals(kind, "MASTER"))
       return DomainInfo::Master;
-    else
-      return DomainInfo::Native;
+    if (pdns_iequals(kind, "PRODUCER"))
+      return DomainInfo::Producer;
+    if (pdns_iequals(kind, "CONSUMER"))
+      return DomainInfo::Consumer;
+    // No "ALL" here please. Yes, I really mean it...
+    return DomainInfo::Native;
   }
 
+  bool isPrimaryType() const { return (kind == DomainInfo::Master || kind == DomainInfo::Producer); }
+  bool isSecondaryType() const { return (kind == DomainInfo::Slave || kind == DomainInfo::Consumer); }
+  bool isCatalogType() const { return (kind == DomainInfo::Producer || kind == DomainInfo::Consumer); }
+
   bool isMaster(const ComboAddress& ip) const
   {
     for( const auto& master: masters) {
@@ -101,6 +120,8 @@ struct DomainInfo
 
 };
 
+typedef map<DNSName, pdns::SHADigest> CatalogHashMap;
+
 struct TSIGKey {
    DNSName name;
    DNSName algorithm;
@@ -320,7 +341,7 @@ public:
   }
 
   //! get list of domains that have been changed since their last notification to slaves
-  virtual void getUpdatedMasters(vector<DomainInfo>* domains)
+  virtual void getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
   {
   }
 
index cdf265e074ac4f58071e3babee36e9e41f4b3e58..bc7f992239e44b0181972fa8a1c71e87cbcdb679 100644 (file)
@@ -43,10 +43,10 @@ AtomicCounter* g_signatureCount;
 static std::string getLookupKey(const std::string& msg)
 {
   try {
-    return pdns_md5sum(msg);
+    return pdns_md5(msg);
   }
   catch(const std::runtime_error& e) {
-    return pdns_sha1sum(msg);
+    return pdns_sha1(msg);
   }
 }
 
index efee904399bfcd6e8b8d9fcd54034ad7fa96d611..29c46f8487c75fc9271789134004781bcc4df473 100644 (file)
@@ -136,30 +136,74 @@ void NotificationQueue::dump()
   }
 }
 
+void CommunicatorClass::getUpdatedProducers(UeberBackend* B, vector<DomainInfo>& domains, const std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
+{
+  std::string metaHash;
+  std::string mapHash;
+  for (auto& ch : catalogHashes) {
+    g_log << Logger::Error << catalogs.size() << " " << ch.first << endl;
+    if (!catalogs.count(ch.first)) {
+      g_log << Logger::Warning << "orphaned member zones found with catalog '" << ch.first << "'" << endl;
+      continue;
+    }
+
+    if (!B->getDomainMetadata(ch.first, "CATALOG-HASH", metaHash)) {
+      metaHash.clear();
+    }
+
+    mapHash = Base64Encode(ch.second.digest());
+    if (mapHash != metaHash) {
+      DomainInfo di;
+      if (B->getDomainInfo(ch.first, di)) {
+        if (di.kind != DomainInfo::Producer) {
+          g_log << Logger::Warning << "zone '" << di.zone << "' is no producer zone" << endl;
+          continue;
+        }
+
+        B->setDomainMetadata(di.zone, "CATALOG-HASH", mapHash);
+
+        g_log << Logger::Warning << "new hash '" << mapHash << "' for zone '" << di.zone << "' REMOVE" << endl; // REMOVE
+
+        SOAData sd;
+        if (!B->getSOAUncached(di.zone, sd)) {
+          g_log << Logger::Warning << "SOA lookup failed for producer zone '" << di.zone << "'" << endl;
+          continue;
+        }
+
+        DNSResourceRecord rr;
+        makeIncreasedSOARecord(sd, "EPOCH", "", rr);
+        di.backend->startTransaction(sd.qname, -1);
+        if (!di.backend->replaceRRSet(di.id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
+          di.backend->abortTransaction();
+          throw PDNSException("backend hosting producer zone '" + sd.qname.toLogString() + "' does not support editing records");
+        }
+        di.backend->commitTransaction();
+
+        domains.emplace_back(di);
+      }
+    }
+  }
+}
+
 void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
 {
   if(!::arg().mustDo("primary"))
-    return; 
+    return;
 
   UeberBackend *B=P->getBackend();
   vector<DomainInfo> cmdomains;
-  B->getUpdatedMasters(&cmdomains);
-  
+  std::unordered_set<DNSName> catalogs;
+  CatalogHashMap catalogHashes;
+  B->getUpdatedMasters(cmdomains, catalogs, catalogHashes);
+  getUpdatedProducers(B, cmdomains, catalogs, catalogHashes);
+
   if(cmdomains.empty()) {
-    if(d_masterschanged)
-      g_log<<Logger::Info<<"No master domains need notifications"<<endl;
-    d_masterschanged=false;
+    g_log << Logger::Info << "no primary or producer domains need notifications" << endl;
   }
   else {
-    d_masterschanged=true;
-    g_log<<Logger::Notice<<cmdomains.size()<<" domain"<<(cmdomains.size()>1 ? "s" : "")<<" for which we are master need"<<
-      (cmdomains.size()>1 ? "" : "s")<<
-      " notifications"<<endl;
+    g_log << Logger::Info << cmdomains.size() << " domain" << addS(cmdomains.size()) << " for which we are primary or consumer need" << addS(cmdomains.size()) << " notifications" << endl;
   }
 
-  // figure out A records of everybody needing notification
-  // do this via the FindNS class, d_fns
-  
   for(auto& di : cmdomains) {
     purgeAuthCachesExact(di.zone);
     queueNotifyDomain(di, B);
index faef292f4705d92cbdb140ae9cf7cc848ec3065f..e1c1e733cd83979167206c9d3093257a5b53e600 100644 (file)
@@ -58,6 +58,8 @@ namespace pdns
 class SHADigest
 {
 public:
+  SHADigest() :
+    SHADigest(256) {}
   SHADigest(unsigned int bits) :
 #if defined(HAVE_EVP_MD_CTX_NEW) && defined(HAVE_EVP_MD_CTX_FREE)
     mdctx(std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), EVP_MD_CTX_free))
index c061a8c204736a9efcb920bca7eecea21e64edc7..79ab4d73ba7841a6e1b03aa0f91271521f99cbdb 100644 (file)
@@ -16,7 +16,7 @@ BOOST_AUTO_TEST_SUITE(test_digests_hh)
 BOOST_AUTO_TEST_CASE(test_pdns_md5sum)
 {
    std::string result = "a3 24 8c e3 1a 88 a6 40 e6 30 73 98 57 6d 06 9e ";
-   std::string sum = pdns_md5sum("a quick brown fox jumped over the lazy dog");
+   std::string sum = pdns_md5("a quick brown fox jumped over the lazy dog");
 
    BOOST_CHECK_EQUAL(makeHexDump(sum), result);
 }
@@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(test_pdns_md5sum)
 BOOST_AUTO_TEST_CASE(test_pdns_sha1sum)
 {
    std::string result = "b9 37 10 0d c9 57 b3 86 d9 cb 77 fc 90 c0 18 22 fd eb 6e 7f ";
-   std::string sum = pdns_sha1sum("a quick brown fox jumped over the lazy dog");
+   std::string sum = pdns_sha1("a quick brown fox jumped over the lazy dog");
 
    BOOST_CHECK_EQUAL(makeHexDump(sum), result);
 }
index dc6c147bad2ade3f38343c0f2de5747439483d13..6e35d384d53d27aa85a060b41f703ae8786524b6 100644 (file)
@@ -1132,9 +1132,9 @@ BOOST_AUTO_TEST_CASE(test_multi_backends_metadata) {
 
     {
       // update the values
-      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.com."), "test-data-a", { "value3" }));
-      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-a", { "value4" }));
-      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-b", { "value5" }));
+      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.com."), "test-data-a", std::vector<std::string>({"value3"})));
+      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-a", std::vector<std::string>({"value4"})));
+      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-b", std::vector<std::string>({"value5"})));
     }
 
     // check the updated values
index 84243afdc49b6b587f66f0aa1f53cca764efedb8..e099ce9ca918da81e226b38e8c5be810f0da1181 100644 (file)
@@ -173,6 +173,17 @@ bool UeberBackend::getDomainMetadata(const DNSName& name, const std::string& kin
   return false;
 }
 
+bool UeberBackend::getDomainMetadata(const DNSName& name, const std::string& kind, std::string& meta)
+{
+  bool ret;
+  meta.clear();
+  std::vector<string> tmp;
+  if ((ret = getDomainMetadata(name, kind, tmp)) && !tmp.empty()) {
+    meta = *tmp.begin();
+  }
+  return ret;
+}
+
 bool UeberBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
 {
   for(DNSBackend* db :  backends) {
@@ -182,6 +193,15 @@ bool UeberBackend::setDomainMetadata(const DNSName& name, const std::string& kin
   return false;
 }
 
+bool UeberBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::string& meta)
+{
+  std::vector<string> tmp;
+  if (!meta.empty()) {
+    tmp.push_back(meta);
+  }
+  return setDomainMetadata(name, kind, tmp);
+}
+
 bool UeberBackend::activateDomainKey(const DNSName& name, unsigned int id)
 {
   for(DNSBackend* db :  backends) {
@@ -314,13 +334,11 @@ void UeberBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
   }  
 }
 
-
-
-void UeberBackend::getUpdatedMasters(vector<DomainInfo>* domains)
+void UeberBackend::getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
 {
   for (auto & backend : backends)
   {
-    backend->getUpdatedMasters( domains );
+    backend->getUpdatedMasters(domains, catalogs, catalogHashes);
   }
 }
 
index 3ee73644276d827c72bdd15f91cdec1c97e4261c..413b957fb3add6671a0a345c2cd19903433d2a43 100644 (file)
@@ -106,7 +106,7 @@ public:
   void getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool include_disabled);
 
   void getUnfreshSlaveInfos(vector<DomainInfo>* domains);
-  void getUpdatedMasters(vector<DomainInfo>* domains);
+  void getUpdatedMasters(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes);
   bool getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial=true);
   bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account);
   
@@ -115,7 +115,9 @@ public:
   bool getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys);
   bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta);
   bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta);
+  bool getDomainMetadata(const DNSName& name, const std::string& kind, std::string& meta);
   bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta);
+  bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::string& meta);
 
   bool removeDomainKey(const DNSName& name, unsigned int id);
   bool activateDomainKey(const DNSName& name, unsigned int id);
index 8a31c1c242d26bbce7505eb2717574bca90775f1..14f36144f54f75d213e2f6131ed0b57994f3b295 100644 (file)
@@ -26,6 +26,8 @@ gmysql-password=$GMYSQLPASSWD
 
 any-to-tcp=no
 zone-cache-refresh-interval=0
+primary=yes
+xfr-cycle-interval=10
 __EOF__
 
                gsql_master gmysql dyndns