From: Kees Monshouwer Date: Wed, 6 Jul 2022 16:48:37 +0000 (+0200) Subject: auth: implement consumer support X-Git-Tag: auth-4.8.0-alpha0~7^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=73f9bdd8a3c7971b5fddd45beb7cea5a4902fa91;p=thirdparty%2Fpdns.git auth: implement consumer support --- diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc index 05bf4a349a..b908df5c68 100644 --- a/modules/gmysqlbackend/gmysqlbackend.cc +++ b/modules/gmysqlbackend/gmysqlbackend.cc @@ -133,8 +133,8 @@ public: 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.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, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.catalog=? and records.type='SOA' and records.disabled=0"); - declare(suffix, "info-consumer-members-query", "", "select domains.id, domains.name, domains.options from domains left join records ON records.domain_id=domains.id and records.type='SOA' and records.name=domains.name where domains.catalog=?'"); + declare(suffix, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.type='MASTER' and domains.catalog=? and records.type='SOA' and records.disabled=0"); + declare(suffix, "info-consumer-members-query", "", "select id, name, options, master from domains where type='SLAVE' and catalog=?"); 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 f37f47dfd3..6d0cc6b585 100644 --- a/modules/godbcbackend/godbcbackend.cc +++ b/modules/godbcbackend/godbcbackend.cc @@ -113,8 +113,8 @@ public: 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.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, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.catalog=? and records.type='SOA' and records.disabled=0"); - declare(suffix, "info-consumer-members-query", "", "select domains.id, domains.name, domains.options from domains left join records ON records.domain_id=domains.id and records.type='SOA' and records.name=domains.name where domains.catalog=?'"); + declare(suffix, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.type='MASTER' and domains.catalog=? and records.type='SOA' and records.disabled=0"); + declare(suffix, "info-consumer-members-query", "", "select id, name, options, master from domains where type='SLAVE' and catalog=?"); 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 6edd00fdf4..9160b64e90 100644 --- a/modules/gpgsqlbackend/gpgsqlbackend.cc +++ b/modules/gpgsqlbackend/gpgsqlbackend.cc @@ -140,8 +140,8 @@ public: 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.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, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.catalog=$1 and records.type='SOA' and records.disabled=0"); - declare(suffix, "info-consumer-members-query", "", "select domains.id, domains.name, domains.options from domains left join records ON records.domain_id=domains.id and records.type='SOA' and records.name=domains.name where domains.catalog=$1'"); + declare(suffix, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.type='MASTER' and domains.catalog=$1 and records.type='SOA' and records.disabled=0"); + declare(suffix, "info-consumer-members-query", "", "select id, name, options, master from domains where type='SLAVE' and catalog=$1"); 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 abdf3286c9..df3aff2714 100644 --- a/modules/gsqlite3backend/gsqlite3backend.cc +++ b/modules/gsqlite3backend/gsqlite3backend.cc @@ -126,8 +126,8 @@ public: 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.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, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.catalog:catalog and records.type='SOA' and records.disabled=0"); - declare(suffix, "info-consumer-members-query", "", "select domains.id, domains.name, domains.options from domains left join records ON records.domain_id=domains.id and records.type='SOA' and records.name=domains.name where domains.catalog:catalog'"); + declare(suffix, "info-producer-members-query", "", "select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.type='MASTER' and domains.catalog:catalog and records.type='SOA' and records.disabled=0"); + declare(suffix, "info-consumer-members-query", "", "select id, name, options, master from domains where type='SLAVE' and catalog:catalog"); 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/lmdbbackend/lmdbbackend.cc b/modules/lmdbbackend/lmdbbackend.cc index 5c1a3a7289..514cbccacb 100644 --- a/modules/lmdbbackend/lmdbbackend.cc +++ b/modules/lmdbbackend/lmdbbackend.cc @@ -679,8 +679,12 @@ bool LMDBBackend::deleteDomain(const DNSName& domain) id = txn.get<0>(domain, di); } - if (!d_rwtxn || d_transactiondomainid != id) { - throw DBException(std::string(__PRETTY_FUNCTION__) + " called without a proper transaction"); + if (!d_rwtxn) { + throw DBException(std::string(__PRETTY_FUNCTION__) + " called without a transaction"); + } + if (d_transactiondomainid != id) { + commitTransaction(); + startTransaction(domain); } { // Remove metadata @@ -1128,6 +1132,7 @@ bool LMDBBackend::getCatalogMembers(const DNSName& catalog, vector& CatalogInfo ci; ci.d_id = iter->id; ci.d_zone = iter->zone; + ci.d_primaries = iter->masters; try { ci.fromJson(iter->options, type); } diff --git a/pdns/auth-catalogzone.hh b/pdns/auth-catalogzone.hh index adfb9fc3b1..26805e71f2 100644 --- a/pdns/auth-catalogzone.hh +++ b/pdns/auth-catalogzone.hh @@ -58,6 +58,7 @@ public: void fromJson(const std::string& json, CatalogType type); std::string toJson() const; + void setType(CatalogType type) { d_type = type; } void updateHash(CatalogHashMap& hashes, const DomainInfo& di) const; DNSName getUnique() const { return DNSName(toBase32Hex(hashQNameWithSalt(std::to_string(d_id), 0, d_zone))); } // salt with domain id to detect recreated zones @@ -72,6 +73,7 @@ public: uint32_t d_id; DNSName d_zone, d_coo, d_unique; std::set d_group; + vector d_primaries; private: CatalogType d_type; diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index f589aa3e5e..90b1be27e7 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -628,7 +628,7 @@ bool GSQLBackend::getCatalogMembers(const DNSName& catalog, vector& } else if (type == CatalogInfo::CatalogType::Consumer) { // clang-format off - d_InfoProducerMembersQuery_stmt-> + d_InfoConsumerMembersQuery_stmt-> bind("catalog", catalog)-> execute()-> getResult(d_result)-> @@ -643,8 +643,13 @@ bool GSQLBackend::getCatalogMembers(const DNSName& catalog, vector& throw PDNSException(std::string(__PRETTY_FUNCTION__) + " unable to retrieve list of member zones: " + e.txtReason()); } - for (const auto& row : d_result) { // id, zone, options - ASSERT_ROW_COLUMNS("info-producer/consumer-members-query", row, 3); + for (const auto& row : d_result) { // id, zone, options, [master] + if (type == CatalogInfo::CatalogType::Producer) { + ASSERT_ROW_COLUMNS("info-producer/consumer-members-query", row, 3); + } + else { + ASSERT_ROW_COLUMNS("info-producer/consumer-members-query", row, 4); + } CatalogInfo ci; @@ -680,6 +685,21 @@ bool GSQLBackend::getCatalogMembers(const DNSName& catalog, vector& return false; } + if (row.size() >= 4) { // Consumer only + vector masters; + stringtok(masters, row[3], ", \t"); + for (const auto& m : masters) { + try { + ci.d_primaries.emplace_back(m, 53); + } + catch (const PDNSException& e) { + g_log << Logger::Warning << "Could not parse master address '" << m << "' for zone '" << ci.d_zone << "': " << e.reason << endl; + members.clear(); + return false; + } + } + } + members.emplace_back(ci); } return true; diff --git a/pdns/slavecommunicator.cc b/pdns/slavecommunicator.cc index 79b0d39550..c76d6dbfe3 100644 --- a/pdns/slavecommunicator.cc +++ b/pdns/slavecommunicator.cc @@ -86,6 +86,311 @@ struct ZoneStatus int numDeltas{0}; }; +static bool catalogDiff(const DomainInfo& di, vector& fromXFR, vector& fromDB, const string& logPrefix) +{ + extern CommunicatorClass Communicator; + + bool doTransaction{true}; + bool inTransaction{false}; + bool doOptions{false}; + CatalogInfo ciCreate, ciRemove; + vector retrieve; + + try { + sort(fromXFR.begin(), fromXFR.end()); + sort(fromDB.begin(), fromDB.end()); + + auto xfr = fromXFR.cbegin(); + auto db = fromDB.cbegin(); + + while (xfr != fromXFR.end() || db != fromDB.end()) { + bool create{false}; + bool remove{false}; + + if ((xfr != fromXFR.end() && db == fromDB.end()) || *xfr < *db) { // create + ciCreate = *xfr; + create = true; + ++xfr; + } + else if ((db != fromDB.end() && xfr == fromXFR.end()) || *db < *xfr) { // remove + ciRemove = *db; + remove = true; + ++db; + } + else { + CatalogInfo ciXFR = *xfr; + CatalogInfo ciDB = *db; + if (ciXFR.d_unique == ciDB.d_unique) { // update + + if (ciXFR.d_coo != ciDB.d_coo) { // update coo + g_log << Logger::Warning << logPrefix << "update coo for zone '" << ciXFR.d_zone << "' to '" << ciXFR.d_coo << "'" << endl; + ciDB.d_coo = ciXFR.d_coo; + doOptions = true; + } + + if (ciXFR.d_group != ciDB.d_group) { // update group + g_log << Logger::Warning << logPrefix << "update group for zone '" << ciXFR.d_zone << "' to '" << boost::join(ciXFR.d_group, ", ") << "'" << endl; + ciDB.d_group = ciXFR.d_group; + doOptions = true; + } + + if (doOptions) { // update zone options + if (doTransaction && (inTransaction = di.backend->startTransaction(di.zone))) { + g_log << Logger::Warning << logPrefix << "backend transaction started" << endl; + doTransaction = false; + } + + g_log << Logger::Warning << logPrefix << "update options for zone '" << ciXFR.d_zone << "'" << endl; + di.backend->setOptions(ciXFR.d_zone, ciDB.toJson()); + } + + if (di.masters != ciDB.d_primaries) { // update primaries + if (doTransaction && (inTransaction = di.backend->startTransaction(di.zone))) { + g_log << Logger::Warning << logPrefix << "backend transaction started" << endl; + doTransaction = false; + } + + vector primaries; + for (const auto& primary : di.masters) { + primaries.push_back(primary.toStringWithPortExcept(53)); + } + g_log << Logger::Warning << logPrefix << "update primaries for zone '" << ciXFR.d_zone << "' to '" << boost::join(primaries, ", ") << "'" << endl; + di.backend->setMasters(ciXFR.d_zone, di.masters); + + retrieve.emplace_back(ciXFR); + } + } + else { // reset + ciCreate = *xfr; + ciRemove = *db; + create = true; + remove = true; + } + ++xfr; + ++db; + } + + DomainInfo d; + if (create && remove) { + g_log << Logger::Warning << logPrefix << "zone '" << ciCreate.d_zone << "' state reset" << endl; + } + else if (create && di.backend->getDomainInfo(ciCreate.d_zone, d)) { // detect clash + CatalogInfo ci; + ci.fromJson(d.options, CatalogInfo::CatalogType::Consumer); + + if (di.zone != d.catalog && di.zone == ci.d_coo) { + if (ciCreate.d_unique == ci.d_unique) { + g_log << Logger::Warning << logPrefix << "zone '" << d.zone << "' owner change without state reset, old catalog '" << d.catalog << "', new catalog '" << di.zone << "'" << endl; + + if (doTransaction && (inTransaction = di.backend->startTransaction(di.zone))) { + g_log << Logger::Warning << logPrefix << "backend transaction started" << endl; + doTransaction = false; + } + + di.backend->setMasters(ciCreate.d_zone, di.masters); + di.backend->setOptions(ciCreate.d_zone, ciCreate.toJson()); + di.backend->setCatalog(ciCreate.d_zone, di.zone); + + retrieve.emplace_back(ciCreate); + continue; + } + g_log << Logger::Warning << logPrefix << "zone '" << d.zone << "' owner change with state reset, old catalog '" << d.catalog << "', new catalog '" << di.zone << "'" << endl; + + ciRemove.d_zone = d.zone; + remove = true; + } + else { + g_log << Logger::Warning << logPrefix << "zone '" << d.zone << "' already exists"; + if (!d.catalog.empty()) { + g_log << " in catalog '" << d.catalog; + } + g_log << "', create skipped" << endl; + continue; + } + } + + if (remove) { // delete zone + if (doTransaction && (inTransaction = di.backend->startTransaction(di.zone))) { + g_log << Logger::Warning << logPrefix << "backend transaction started" << endl; + doTransaction = false; + } + + g_log << Logger::Warning << logPrefix << "delete zone '" << ciRemove.d_zone << "'" << endl; + di.backend->deleteDomain(ciRemove.d_zone); + + if (g_zoneCache.isEnabled()) { + g_zoneCache.remove(ciRemove.d_zone); + } + } + + if (create) { // create zone + if (doTransaction && (inTransaction = di.backend->startTransaction(ciCreate.d_zone))) { + g_log << Logger::Warning << logPrefix << "backend transaction started" << endl; + doTransaction = false; + } + + g_log << Logger::Warning << logPrefix << "create zone '" << ciCreate.d_zone << "'" << endl; + di.backend->createDomain(ciCreate.d_zone, DomainInfo::Slave, ciCreate.d_primaries, ""); + + di.backend->setMasters(ciCreate.d_zone, di.masters); + di.backend->setOptions(ciCreate.d_zone, ciCreate.toJson()); + di.backend->setCatalog(ciCreate.d_zone, di.zone); + + retrieve.emplace_back(ciCreate); + + if (g_zoneCache.isEnabled()) { + if (di.backend->getDomainInfo(ciCreate.d_zone, d)) { + g_zoneCache.add(ciCreate.d_zone, d.id); + } + else { + g_log << Logger::Error << logPrefix << "new zone '" << ciCreate.d_zone << "' does not exists and was not inserted in the zone-cache" << endl; + } + } + } + } + + if (inTransaction && di.backend->commitTransaction()) { + g_log << Logger::Warning << logPrefix << "backend transaction committed" << endl; + } + + // retrieve new and updated zones with new primaries + auto masters = di.masters; + if (!masters.empty()) { + for (auto& ret : retrieve) { + shuffle(masters.begin(), masters.end(), pdns::dns_random_engine()); + const auto& master = masters.front(); + Communicator.addSuckRequest(ret.d_zone, master, SuckRequest::Notify); + } + } + + return true; + } + catch (DBException& re) { + g_log << Logger::Error << logPrefix << "DBException " << re.reason << endl; + } + catch (PDNSException& pe) { + g_log << Logger::Error << logPrefix << "PDNSException " << pe.reason << endl; + } + catch (std::exception& re) { + g_log << Logger::Error << logPrefix << "std::exception " << re.what() << endl; + } + + if (di.backend && inTransaction) { + g_log << Logger::Info << logPrefix << "aborting possible open transaction" << endl; + di.backend->abortTransaction(); + } + + return false; +} + +static bool catalogProcess(const DomainInfo& di, vector& rrs, string logPrefix) +{ + logPrefix += "Catalog-Zone "; + + vector fromXFR, fromDB; + std::unordered_set dupcheck; + + // From XFR + bool hasSOA{false}; + bool hasVersion{false}; + bool zoneInvalid{false}; + + CatalogInfo ci; + + vector ret; + + const auto compare = [](const DNSResourceRecord& a, const DNSResourceRecord& b) { return a.qname == b.qname ? a.qtype < b.qtype : a.qname.canonCompare(b.qname); }; + sort(rrs.begin(), rrs.end(), compare); + + DNSName rel; + DNSName unique; + for (auto& rr : rrs) { + if (di.zone == rr.qname) { + if (rr.qtype == QType::SOA) { + hasSOA = true; + continue; + } + } + + else if (rr.qname == DNSName("version") + di.zone && rr.qtype == QType::TXT) { + if (rr.content == "\"2\"") { + hasVersion = true; + continue; + } + else { + g_log << Logger::Warning << logPrefix << "zone '" << di.zone << "', unsupported catalog zone schema version " << rr.content << ", aborting" << endl; + return false; + } + } + + else if (rr.qname.isPartOf(DNSName("zones") + di.zone)) { + if (rel.empty() && !hasVersion) { + g_log << Logger::Warning << logPrefix << "zone '" << di.zone << "', catalog zone schema version missing, aborting" << endl; + return false; + } + + rel = rr.qname.makeRelative(DNSName("zones") + di.zone); + + if (rel.countLabels() == 1 && rr.qtype == QType::PTR) { + if (!unique.empty()) { + if (rel != unique) { + fromXFR.emplace_back(ci); + } + else { + g_log << Logger::Warning << logPrefix << "zone '" << di.zone << "', duplicate unique '" << unique << "'" << endl; + zoneInvalid = true; + } + } + + unique = rel; + + ci = {}; + ci.setType(CatalogInfo::CatalogType::Consumer); + ci.d_zone = DNSName(rr.content); + ci.d_unique = unique; + + if (!dupcheck.insert(ci.d_zone).second) { + g_log << Logger::Warning << logPrefix << "zone '" << di.zone << "', duplicate member zone'" << ci.d_zone << "'" << endl; + zoneInvalid = true; + } + } + + else if (rel == (DNSName("coo") + unique) && rr.qtype == QType::PTR) { + if (!ci.d_coo.empty()) { + g_log << Logger::Warning << logPrefix << "zone '" << di.zone << "', duplicate COO for unique '" << unique << "'" << endl; + zoneInvalid = true; + } + else { + ci.d_coo = DNSName(rr.content); + } + } + else if (rel == (DNSName("group") + unique) && rr.qtype == QType::TXT) { + std::string content = rr.content; + if (content.length() >= 2 && content.at(0) == '\"' && content.at(content.length() - 1) == '\"') { // TXT pain + content = content.substr(1, content.length() - 2); + } + ci.d_group.insert(content); + } + } + rr.disabled = true; + } + if (!ci.d_zone.empty()) { + fromXFR.emplace_back(ci); + } + + if (!hasSOA || !hasVersion || zoneInvalid) { + g_log << Logger::Warning << logPrefix << "zone '" << di.zone << "' is invalid, skip updates" << endl; + return false; + } + + // Get catalog ifo from db + if (!di.backend->getCatalogMembers(di.zone, fromDB, CatalogInfo::CatalogType::Consumer)) { + return false; + } + + // Process + return catalogDiff(di, fromXFR, fromDB, logPrefix); +} void CommunicatorClass::ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, const ComboAddress& laddr, const ComboAddress& remote, unique_ptr& pdl, ZoneStatus& zs, vector* axfr) @@ -327,9 +632,9 @@ void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote, DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper bool wrongDomainKind = false; // this checks three error conditions & sets wrongDomainKind if we hit the third - if(!B.getDomainInfo(domain, di) || !di.backend || (wrongDomainKind = true, !force && di.kind != DomainInfo::Slave)) { // di.backend and B are mostly identical + if (!B.getDomainInfo(domain, di) || !di.backend || (wrongDomainKind = true, !force && !di.isSecondaryType())) { // di.backend and B are mostly identical if(wrongDomainKind) - g_log<> pdns-gmysql2.conf fi + zones=0 for zone in $(grep 'zone ' named.conf | cut -f2 -d\" | perl -e 'print reverse ') do - mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQL2HOST" \ - "$GMYSQL2DB" -e "INSERT INTO domains (name, type, master) VALUES('$zone','SLAVE','127.0.0.1:$port')" + zones=$((zones+1)) + if [ "$zone" = "example.com" ]; then + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone $zone 127.0.0.1:$port + fi + if [ "$zone" = "test.com" ]; then + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone $zone 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-catalog $zone other-catalog.invalid + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-options $zone '{"consumer":{"coo":"catalog.invalid","unique":"42"}}' + fi if [ "$zone" = "tsig.com" ]; then + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone $zone 127.0.0.2:$port + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-catalog $zone catalog.invalid + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-options $zone "{\"consumer\":{\"unique\":\"$($SAXFR 127.0.0.1 $port catalog.invalid | grep $zone | grep PTR | cut -d'.' -f1)\"}}" $PDNSUTIL --config-dir=. --config-name=gmysql2 import-tsig-key test $ALGORITHM $KEY $PDNSUTIL --config-dir=. --config-name=gmysql2 activate-tsig-key tsig.com test slave fi if [ "$zone" = "stest.com" ]; then + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone $zone 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-catalog $zone other-catalog.invalid + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-options $zone "{\"consumer\":{\"coo\":\"catalog.invalid\",\"unique\":\"$($SAXFR 127.0.0.1 $port catalog.invalid | grep $zone | grep PTR | cut -d'.' -f1)\"}}" if [[ $skipreasons != *nolua* ]]; then $PDNSUTIL --config-dir=. --config-name=gmysql2 set-meta stest.com AXFR-SOURCE 127.0.0.2 fi fi + if [ "$zone" = "." ]; then + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone $zone 127.0.0.1:$port + fi done + # setup catalog zone + if [ $zones -ne 1 ] # detect root tests + then + zones=$((zones+1)) + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone catalog.invalid 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-kind catalog.invalid consumer + + $PDNSUTIL --config-dir=. --config-name=gmysql2 create-slave-zone remove.invalid 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=gmysql2 set-catalog remove.invalid catalog.invalid + fi + port=$((port+100)) $RUNWRAPPER $PDNS2 --daemon=no --local-port=$port --config-dir=. \ @@ -54,15 +82,15 @@ __EOF__ while [ $loopcount -lt 30 ] do sleep 5 - todo=$(mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQL2HOST" \ - "$GMYSQL2DB" -ss -e 'SELECT COUNT(id) FROM domains WHERE last_check IS NULL') - if [ $todo = 0 ] + present=$(mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQL2HOST" \ + "$GMYSQL2DB" -ss -e "SELECT COUNT(DISTINCT(name)) FROM records WHERE type='SOA'") + if [ $present -eq $zones ] then break fi let loopcount=loopcount+1 done - if [ $todo -ne 0 ] + if [ $present -ne $zones ] then echo "AXFR FAILED" >> failed_tests exit diff --git a/regression-tests/backends/godbc_sqlite3-master b/regression-tests/backends/godbc_sqlite3-master index 3efedc66a7..89c11cb218 100644 --- a/regression-tests/backends/godbc_sqlite3-master +++ b/regression-tests/backends/godbc_sqlite3-master @@ -41,9 +41,11 @@ godbc-get-tsig-key-query=select algorithm, secret from tsigkeys where name=? godbc-get-tsig-keys-query=select name,algorithm, secret from tsigkeys godbc-publish-domain-key-query=update cryptokeys set published=1 where domain_id=(select id from domains where name=?) and cryptokeys.id=? godbc-id-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and type=? and name=? and domain_id=? -godbc-info-all-master-query=select id,name,master,last_check,notified_serial,type from domains where type='MASTER' -godbc-info-all-slaves-query=select id,name,master,last_check from domains where type='SLAVE' -godbc-info-zone-query=select id,name,master,last_check,notified_serial,type,account from domains where name=? +godbc-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') +godbc-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') +godbc-info-zone-query=select id,name,master,last_check,notified_serial,type,options,catalog,account from domains where name=? +godbc-info-producer-members-query=select domains.id, domains.name, domains.options from records join domains on records.domain_id=domains.id and records.name=domains.name where domains.type='MASTER' and domains.catalog=? and records.type='SOA' and records.disabled=0 +godbc-info-consumer-members-query=select id, name, options, master from domains where type='SLAVE' and catalog=? godbc-insert-comment-query=INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES (?, ?, ?, ?, ?, ?) godbc-insert-empty-non-terminal-order-query=insert into records (type,domain_id,disabled,name,ordername,auth,ttl,prio,content) values (null,?,0,?,?,?,null,null,null) godbc-insert-record-query=insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (?,?,?,?,?,?,?,?,?) diff --git a/regression-tests/backends/lmdb-slave b/regression-tests/backends/lmdb-slave index 992a8bcf4a..7981a654c2 100644 --- a/regression-tests/backends/lmdb-slave +++ b/regression-tests/backends/lmdb-slave @@ -9,19 +9,46 @@ __EOF__ zones=0 for zone in $(grep 'zone ' named.conf | cut -f2 -d\" | grep -v '^nztest.com$' | perl -e 'print reverse ') do - let zones=zones+1 - $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone $zone 127.0.0.1:$port + zones=$((zones+1)) + if [ "$zone" = "example.com" ]; then + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone $zone 127.0.0.1:$port + fi + if [ "$zone" = "test.com" ]; then + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone $zone 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-catalog $zone other-catalog.invalid + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-options $zone '{"consumer":{"coo":"catalog.invalid","unique":"42"}}' + fi if [ "$zone" = "tsig.com" ]; then + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone $zone 127.0.0.2:$port + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-catalog $zone catalog.invalid + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-options $zone "{\"consumer\":{\"unique\":\"$($SAXFR 127.0.0.1 $port catalog.invalid | grep $zone | grep PTR | cut -d'.' -f1)\"}}" $PDNSUTIL --config-dir=. --config-name=lmdb2 import-tsig-key test $ALGORITHM $KEY $PDNSUTIL --config-dir=. --config-name=lmdb2 activate-tsig-key tsig.com test slave fi if [ "$zone" = "stest.com" ]; then + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone $zone 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-catalog $zone other-catalog.invalid + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-options $zone "{\"consumer\":{\"coo\":\"catalog.invalid\",\"unique\":\"$($SAXFR 127.0.0.1 $port catalog.invalid | grep $zone | grep PTR | cut -d'.' -f1)\"}}" if [[ $skipreasons != *nolua* ]]; then $PDNSUTIL --config-dir=. --config-name=lmdb2 set-meta stest.com AXFR-SOURCE 127.0.0.2 fi fi + if [ "$zone" = "." ]; then + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone $zone 127.0.0.1:$port + fi done + # setup catalog zone + if [ $zones -ne 1 ] # detect root tests + then + zones=$((zones+1)) + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone catalog.invalid 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-kind catalog.invalid consumer + + $PDNSUTIL --config-dir=. --config-name=lmdb2 create-slave-zone remove.invalid 127.0.0.1:$port + $PDNSUTIL --config-dir=. --config-name=lmdb2 set-catalog remove.invalid catalog.invalid + fi + port=$((port+100)) $RUNWRAPPER $PDNS2 --daemon=no --local-port=$port --config-dir=. \