From: Kees Monshouwer Date: Mon, 27 Jun 2022 20:01:56 +0000 (+0200) Subject: auth: implement producer freshness check and a lot of cleanup/speedup X-Git-Tag: auth-4.8.0-alpha0~7^2~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ddeea7a6b1ef34509e7740c325c681aca1e6d4ae;p=thirdparty%2Fpdns.git auth: implement producer freshness check and a lot of cleanup/speedup --- diff --git a/modules/bindbackend/bindbackend2.cc b/modules/bindbackend/bindbackend2.cc index 43e2034999..641dba5c5b 100644 --- a/modules/bindbackend/bindbackend2.cc +++ b/modules/bindbackend/bindbackend2.cc @@ -318,7 +318,7 @@ bool Bind2Backend::feedRecord(const DNSResourceRecord& rr, const DNSName& ordern return true; } -void Bind2Backend::getUpdatedMasters(vector* changedDomains) +void Bind2Backend::getUpdatedMasters(vector& changedDomains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) { vector consider; { @@ -356,7 +356,7 @@ void Bind2Backend::getUpdatedMasters(vector* 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)); } } } diff --git a/modules/bindbackend/bindbackend2.hh b/modules/bindbackend/bindbackend2.hh index dc461e9a3b..e09e7d753a 100644 --- a/modules/bindbackend/bindbackend2.hh +++ b/modules/bindbackend/bindbackend2.hh @@ -184,7 +184,7 @@ public: Bind2Backend(const string& suffix = "", bool loadZones = true); ~Bind2Backend(); void getUnfreshSlaveInfos(vector* unfreshDomains) override; - void getUpdatedMasters(vector* changedDomains) override; + void getUpdatedMasters(vector& changedDomains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) override; bool getDomainInfo(const DNSName& domain, DomainInfo& di, bool getSerial = true) override; time_t getCtime(const string& fname); // DNSSEC diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc index af3bd54ea2..d0ff7295bc 100644 --- a/modules/gmysqlbackend/gmysqlbackend.cc +++ b/modules/gmysqlbackend/gmysqlbackend.cc @@ -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=?"); diff --git a/modules/godbcbackend/godbcbackend.cc b/modules/godbcbackend/godbcbackend.cc index 60572cf5ac..0a75093068 100644 --- a/modules/godbcbackend/godbcbackend.cc +++ b/modules/godbcbackend/godbcbackend.cc @@ -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=?"); diff --git a/modules/gpgsqlbackend/gpgsqlbackend.cc b/modules/gpgsqlbackend/gpgsqlbackend.cc index bbd39645e4..409e83fe2e 100644 --- a/modules/gpgsqlbackend/gpgsqlbackend.cc +++ b/modules/gpgsqlbackend/gpgsqlbackend.cc @@ -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"); diff --git a/modules/gsqlite3backend/gsqlite3backend.cc b/modules/gsqlite3backend/gsqlite3backend.cc index a0ff8a0b8b..b3103f8b54 100644 --- a/modules/gsqlite3backend/gsqlite3backend.cc +++ b/modules/gsqlite3backend/gsqlite3backend.cc @@ -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"); diff --git a/modules/ldapbackend/ldapbackend.hh b/modules/ldapbackend/ldapbackend.hh index 747c6c7e8e..23e77c8795 100644 --- a/modules/ldapbackend/ldapbackend.hh +++ b/modules/ldapbackend/ldapbackend.hh @@ -177,6 +177,6 @@ public: bool getDomainInfo(const DNSName& domain, DomainInfo& di, bool getSerial = true) override; // Master backend - void getUpdatedMasters(vector* domains) override; + void getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) override; void setNotified(uint32_t id, uint32_t serial) override; }; diff --git a/modules/ldapbackend/master.cc b/modules/ldapbackend/master.cc index 9a4847803c..1ab28f0d3a 100644 --- a/modules/ldapbackend/master.cc +++ b/modules/ldapbackend/master.cc @@ -24,7 +24,7 @@ #include "ldapbackend.hh" #include -void LdapBackend::getUpdatedMasters(vector* domains) +void LdapBackend::getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) { string filter; PowerLDAP::SearchResult::Ptr search; @@ -45,7 +45,7 @@ void LdapBackend::getUpdatedMasters(vector* 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* domains) continue; if (di.notified_serial < di.serial) - domains->push_back(di); + domains.push_back(di); } } diff --git a/modules/lmdbbackend/lmdbbackend.cc b/modules/lmdbbackend/lmdbbackend.cc index 018faf820f..b617aee34a 100644 --- a/modules/lmdbbackend/lmdbbackend.cc +++ b/modules/lmdbbackend/lmdbbackend.cc @@ -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& masters) { return genChangeDomain(domain, [&masters](DomainInfo& di) { @@ -1067,44 +1047,89 @@ void LMDBBackend::getAllDomains(vector* domains, bool doSerial, bool void LMDBBackend::getUnfreshSlaveInfos(vector* domains) { - // cout<<"Start of getUnfreshSlaveInfos"<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(), 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 <<" + " < = " << now << "\n"; serial = ntohl(st.serial); } else { - // cout << "Could not find SOA for "<zone<<" with id "<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& updatedDomains, std::unordered_set& 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"<>& meta) diff --git a/modules/lmdbbackend/lmdbbackend.hh b/modules/lmdbbackend/lmdbbackend.hh index 34a4eb95ac..f395a85480 100644 --- a/modules/lmdbbackend/lmdbbackend.hh +++ b/modules/lmdbbackend/lmdbbackend.hh @@ -80,7 +80,14 @@ public: bool get(DNSResourceRecord& rr) override; bool get(DNSZoneRecord& dzr) override; + // secondary support void getUnfreshSlaveInfos(vector* domains) override; + void setStale(uint32_t domain_id) override; + void setFresh(uint32_t domain_id) override; + + // primary support + void getUpdatedMasters(vector& updatedDomains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) override; + void setNotified(uint32_t id, uint32_t serial) override; bool setMasters(const DNSName& domain, const vector& 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& 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; diff --git a/modules/remotebackend/remotebackend.cc b/modules/remotebackend/remotebackend.cc index 380d956442..cbbe2f9fb3 100644 --- a/modules/remotebackend/remotebackend.cc +++ b/modules/remotebackend/remotebackend.cc @@ -885,7 +885,7 @@ void RemoteBackend::alsoNotifies(const DNSName& domain, set* ips) ips->insert(meta.begin(), meta.end()); } -void RemoteBackend::getUpdatedMasters(vector* domains) +void RemoteBackend::getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) { Json query = Json::object{ {"method", "getUpdatedMasters"}, @@ -902,7 +902,7 @@ void RemoteBackend::getUpdatedMasters(vector* domains) for (const auto& row : answer["result"].array_items()) { DomainInfo di; this->parseDomainInfo(row, di); - domains->push_back(di); + domains.push_back(di); } } diff --git a/modules/remotebackend/remotebackend.hh b/modules/remotebackend/remotebackend.hh index 08811fe078..d103331c17 100644 --- a/modules/remotebackend/remotebackend.hh +++ b/modules/remotebackend/remotebackend.hh @@ -199,7 +199,7 @@ public: bool searchRecords(const string& pattern, int maxResults, vector& result) override; bool searchComments(const string& pattern, int maxResults, vector& result) override; void getAllDomains(vector* domains, bool getSerial, bool include_disabled) override; - void getUpdatedMasters(vector* domains) override; + void getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) override; void alsoNotifies(const DNSName& domain, set* ips) override; void getUnfreshSlaveInfos(vector* domains) override; void setStale(uint32_t domain_id) override; diff --git a/modules/remotebackend/test-remotebackend.cc b/modules/remotebackend/test-remotebackend.cc index 06ebeb596e..c7386748aa 100644 --- a/modules/remotebackend/test-remotebackend.cc +++ b/modules/remotebackend/test-remotebackend.cc @@ -370,8 +370,10 @@ BOOST_AUTO_TEST_CASE(test_method_getUpdatedMasters) DomainInfo di; BOOST_TEST_MESSAGE("Testing getUpdatedMasters method"); vector result; + std::unordered_set catalogs; + CatalogHashMap hashes; - be->getUpdatedMasters(&result); + be->getUpdatedMasters(result, catalogs, hashes); BOOST_CHECK(result.size() > 0); diff --git a/modules/tinydnsbackend/tinydnsbackend.cc b/modules/tinydnsbackend/tinydnsbackend.cc index ae35c0feaa..c1b02a1ec1 100644 --- a/modules/tinydnsbackend/tinydnsbackend.cc +++ b/modules/tinydnsbackend/tinydnsbackend.cc @@ -88,7 +88,7 @@ TinyDNSBackend::TinyDNSBackend(const string& suffix) d_isWildcardQuery = false; } -void TinyDNSBackend::getUpdatedMasters(vector* retDomains) +void TinyDNSBackend::getUpdatedMasters(vector& retDomains, std::unordered_set& 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* 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); } } } diff --git a/modules/tinydnsbackend/tinydnsbackend.hh b/modules/tinydnsbackend/tinydnsbackend.hh index 10ee552782..29d1e64798 100644 --- a/modules/tinydnsbackend/tinydnsbackend.hh +++ b/modules/tinydnsbackend/tinydnsbackend.hh @@ -73,7 +73,7 @@ public: void getAllDomains(vector* domains, bool getSerial, bool include_disabled) override; //Master mode operation - void getUpdatedMasters(vector* domains) override; + void getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) override; void setNotified(uint32_t id, uint32_t serial) override; private: diff --git a/pdns/Makefile.am b/pdns/Makefile.am index 2ef1f85b02..2a5ed88521 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -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 index 0000000000..a34851aaeb --- /dev/null +++ b/pdns/auth-catalogzone.cc @@ -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(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 index 0000000000..f483fab68d --- /dev/null +++ b/pdns/auth-catalogzone.hh @@ -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; +}; diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index bf15ea450f..c18900c4d7 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -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 *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 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<(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< 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<(slave.last_check + sdata.refresh) < time(nullptr)) { - slave.serial=sdata.serial; - unfreshDomains->push_back(slave); - } - } - catch(const std::exception& exp) { - g_log<emplace_back(di); } } -void GSQLBackend::getUpdatedMasters(vector *updatedDomains) +void GSQLBackend::getUpdatedMasters(vector& updatedDomains, std::unordered_set& 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(); - vectorparts; + 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(parts[2]) : 0; - auto notified_serial = pdns::checked_stoi(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); + } } } diff --git a/pdns/backends/gsql/gsqlbackend.hh b/pdns/backends/gsql/gsqlbackend.hh index e7ffdb3370..395fafdcf0 100644 --- a/pdns/backends/gsql/gsqlbackend.hh +++ b/pdns/backends/gsql/gsqlbackend.hh @@ -211,7 +211,7 @@ public: void setStale(uint32_t domain_id) override; void setFresh(uint32_t domain_id) override; void getUnfreshSlaveInfos(vector *domains) override; - void getUpdatedMasters(vector *updatedDomains) override; + void getUpdatedMasters(vector& updatedDomains, std::unordered_set& 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 &masters) override; diff --git a/pdns/communicator.hh b/pdns/communicator.hh index b799e0159e..78746ffe00 100644 --- a/pdns/communicator.hh +++ b/pdns/communicator.hh @@ -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& domains, const std::unordered_set& 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 diff --git a/pdns/digests.hh b/pdns/digests.hh index 0f30ba8306..14a926c368 100644 --- a/pdns/digests.hh +++ b/pdns/digests.hh @@ -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) { diff --git a/pdns/dnsbackend.hh b/pdns/dnsbackend.hh index e48c0ca5ac..9c0f678f38 100644 --- a/pdns/dnsbackend.hh +++ b/pdns/dnsbackend.hh @@ -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 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* domains) + virtual void getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) { } diff --git a/pdns/dnssecsigner.cc b/pdns/dnssecsigner.cc index cdf265e074..bc7f992239 100644 --- a/pdns/dnssecsigner.cc +++ b/pdns/dnssecsigner.cc @@ -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); } } diff --git a/pdns/mastercommunicator.cc b/pdns/mastercommunicator.cc index efee904399..29c46f8487 100644 --- a/pdns/mastercommunicator.cc +++ b/pdns/mastercommunicator.cc @@ -136,30 +136,74 @@ void NotificationQueue::dump() } } +void CommunicatorClass::getUpdatedProducers(UeberBackend* B, vector& domains, const std::unordered_set& 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(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 cmdomains; - B->getUpdatedMasters(&cmdomains); - + std::unordered_set catalogs; + CatalogHashMap catalogHashes; + B->getUpdatedMasters(cmdomains, catalogs, catalogHashes); + getUpdatedProducers(B, cmdomains, catalogs, catalogHashes); + if(cmdomains.empty()) { - if(d_masterschanged) - g_log<1 ? "s" : "")<<" for which we are master need"<< - (cmdomains.size()>1 ? "" : "s")<< - " notifications"<(EVP_MD_CTX_new(), EVP_MD_CTX_free)) diff --git a/pdns/test-digests_hh.cc b/pdns/test-digests_hh.cc index c061a8c204..79ab4d73ba 100644 --- a/pdns/test-digests_hh.cc +++ b/pdns/test-digests_hh.cc @@ -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); } diff --git a/pdns/test-ueberbackend_cc.cc b/pdns/test-ueberbackend_cc.cc index dc6c147bad..6e35d384d5 100644 --- a/pdns/test-ueberbackend_cc.cc +++ b/pdns/test-ueberbackend_cc.cc @@ -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({"value3"}))); + BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-a", std::vector({"value4"}))); + BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-b", std::vector({"value5"}))); } // check the updated values diff --git a/pdns/ueberbackend.cc b/pdns/ueberbackend.cc index 84243afdc4..e099ce9ca9 100644 --- a/pdns/ueberbackend.cc +++ b/pdns/ueberbackend.cc @@ -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 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& 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 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* domains) } } - - -void UeberBackend::getUpdatedMasters(vector* domains) +void UeberBackend::getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes) { for (auto & backend : backends) { - backend->getUpdatedMasters( domains ); + backend->getUpdatedMasters(domains, catalogs, catalogHashes); } } diff --git a/pdns/ueberbackend.hh b/pdns/ueberbackend.hh index 3ee7364427..413b957fb3 100644 --- a/pdns/ueberbackend.hh +++ b/pdns/ueberbackend.hh @@ -106,7 +106,7 @@ public: void getAllDomains(vector* domains, bool getSerial, bool include_disabled); void getUnfreshSlaveInfos(vector* domains); - void getUpdatedMasters(vector* domains); + void getUpdatedMasters(vector& domains, std::unordered_set& catalogs, CatalogHashMap& catalogHashes); bool getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial=true); bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector &masters, const string &account); @@ -115,7 +115,9 @@ public: bool getDomainKeys(const DNSName& name, std::vector& keys); bool getAllDomainMetadata(const DNSName& name, std::map >& meta); bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector& 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& 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); diff --git a/regression-tests/backends/gmysql-master b/regression-tests/backends/gmysql-master index 8a31c1c242..14f36144f5 100644 --- a/regression-tests/backends/gmysql-master +++ b/regression-tests/backends/gmysql-master @@ -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