return true;
}
-void Bind2Backend::getUpdatedMasters(vector<DomainInfo>* changedDomains)
+void Bind2Backend::getUpdatedMasters(vector<DomainInfo>& changedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
{
vector<DomainInfo> consider;
{
}
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));
}
}
}
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
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 (?,?,?)");
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=?");
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 (?,?,?)");
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=?");
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)");
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");
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)");
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");
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;
};
#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;
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");
}
continue;
if (di.notified_serial < di.serial)
- domains->push_back(di);
+ domains.push_back(di);
}
}
#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"
});
}
-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) {
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)
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;
}
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;
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"},
for (const auto& row : answer["result"].array_items()) {
DomainInfo di;
this->parseDomainInfo(row, di);
- domains->push_back(di);
+ domains.push_back(di);
}
}
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;
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);
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)) {
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);
}
}
}
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:
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 \
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 \
--- /dev/null
+/*
+ * 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());
+}
--- /dev/null
+/*
+ * 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;
+};
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include "pdns/auth-catalogzone.hh"
#include "pdns/dns.hh"
#include "pdns/dnsbackend.hh"
#include "gsqlbackend.hh"
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);
+ }
}
}
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;
CommunicatorClass()
{
d_tickinterval=60;
- d_masterschanged=d_slaveschanged=true;
+ d_slaveschanged = true;
d_nsock4 = -1;
d_nsock6 = -1;
d_preventSelfNotification = false;
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;
NotificationQueue d_nq;
time_t d_tickinterval;
- bool d_masterschanged, d_slaveschanged;
+ bool d_slaveschanged;
bool d_preventSelfNotification;
struct Data
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) {
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) {
#include "dnsname.hh"
#include "dnsrecords.hh"
#include "iputils.hh"
+#include "sha.hh"
class DNSBackend;
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);
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];
}
{
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) {
};
+typedef map<DNSName, pdns::SHADigest> CatalogHashMap;
+
struct TSIGKey {
DNSName name;
DNSName algorithm;
}
//! 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)
{
}
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);
}
}
}
}
+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);
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))
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);
}
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);
}
{
// 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
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) {
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) {
}
}
-
-
-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);
}
}
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);
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);
any-to-tcp=no
zone-cache-refresh-interval=0
+primary=yes
+xfr-cycle-interval=10
__EOF__
gsql_master gmysql dyndns