]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
auth: implement producer axfr
authorKees Monshouwer <mind04@monshouwer.org>
Sat, 2 Jul 2022 00:23:08 +0000 (02:23 +0200)
committermind04 <mind04@monshouwer.org>
Fri, 8 Jul 2022 10:31:19 +0000 (12:31 +0200)
15 files changed:
modules/gmysqlbackend/gmysqlbackend.cc
modules/godbcbackend/godbcbackend.cc
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gsqlite3backend/gsqlite3backend.cc
modules/lmdbbackend/lmdbbackend.cc
modules/lmdbbackend/lmdbbackend.hh
pdns/auth-catalogzone.cc
pdns/auth-catalogzone.hh
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/dnsbackend.hh
pdns/mastercommunicator.cc
pdns/recursordist/Makefile.am
pdns/recursordist/auth-catalogzone.hh [new symlink]
pdns/tcpreceiver.cc

index d0ff7295bc844ab02a0ad2f8ce6482d79a168f13..05bf4a349ade3e0703e473883b096e3d2c139f25 100644 (file)
@@ -133,6 +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, "delete-domain-query", "", "delete from domains where name=?");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=?");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=? and name=? and type=?");
index 0a750930688a3e685d3c9b578f1495546759fd7b..f37f47dfd32d07772c939029498880180fa71037 100644 (file)
@@ -113,6 +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, "delete-domain-query", "", "delete from domains where name=?");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=?");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=? and name=? and type=?");
index 409e83fe2e8e0f263cd09071478ef7cc1deacca9..6edd00fdf457762b42c1b9985093b79c55d78d1b 100644 (file)
@@ -140,6 +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, "delete-domain-query", "", "delete from domains where name=$1");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=$1");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=$1 and name=$2 and type=$3");
index b3103f8b54cd7c3e14a0e536186821bb5a25d055..abdf3286c9ffa888b84878bcfabce83924c508e2 100644 (file)
@@ -126,6 +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, "delete-domain-query", "", "delete from domains where name=:domain");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=:domain_id");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=:domain_id and name=:qname and type=:qtype");
index b617aee34afe7146e972c736daf9d998f1f0008f..5c1a3a7289c0b4581cc6dd5eac4cb2e3fdae6d7c 100644 (file)
@@ -23,7 +23,6 @@
 #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"
@@ -978,20 +977,6 @@ bool LMDBBackend::setKind(const DNSName& domain, const DomainInfo::DomainKind ki
   });
 }
 
-bool LMDBBackend::setOptions(const DNSName& domain, const std::string& options)
-{
-  return genChangeDomain(domain, [options](DomainInfo& di) {
-    di.options = options;
-  });
-}
-
-bool LMDBBackend::setCatalog(const DNSName& domain, const DNSName& catalog)
-{
-  return genChangeDomain(domain, [catalog](DomainInfo& di) {
-    di.catalog = catalog;
-  });
-}
-
 bool LMDBBackend::setAccount(const DNSName& domain, const std::string& account)
 {
   return genChangeDomain(domain, [account](DomainInfo& di) {
@@ -1054,7 +1039,7 @@ void LMDBBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
 
   auto txn = d_tdomains->getROTransaction();
   for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
-    if (iter->kind != DomainInfo::Slave && iter->kind != DomainInfo::Consumer) {
+    if (!iter->isSecondaryType()) {
       continue;
     }
 
@@ -1103,7 +1088,7 @@ void LMDBBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::uno
 
   auto txn = d_tdomains->getROTransaction();
   for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
-    if (iter->kind != DomainInfo::Master) {
+    if (!iter->isPrimaryType()) {
       continue;
     }
 
@@ -1132,6 +1117,44 @@ void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
   });
 }
 
+bool LMDBBackend::getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type)
+{
+  auto txn = d_tdomains->getROTransaction();
+  for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
+    if ((type == CatalogInfo::CatalogType::Producer && iter->kind != DomainInfo::Master) || (type == CatalogInfo::CatalogType::Consumer && iter->kind != DomainInfo::Slave) || iter->catalog != catalog) {
+      continue;
+    }
+
+    CatalogInfo ci;
+    ci.d_id = iter->id;
+    ci.d_zone = iter->zone;
+    try {
+      ci.fromJson(iter->options, type);
+    }
+    catch (const std::runtime_error& e) {
+      g_log << Logger::Warning << __PRETTY_FUNCTION__ << " options '" << iter->options << "' for zone '" << iter->zone << "' is no valid JSON: " << e.what() << endl;
+      members.clear();
+      return false;
+    }
+    members.emplace_back(ci);
+  }
+  return true;
+}
+
+bool LMDBBackend::setOptions(const DNSName& domain, const std::string& options)
+{
+  return genChangeDomain(domain, [options](DomainInfo& di) {
+    di.options = options;
+  });
+}
+
+bool LMDBBackend::setCatalog(const DNSName& domain, const DNSName& catalog)
+{
+  return genChangeDomain(domain, [catalog](DomainInfo& di) {
+    di.catalog = catalog;
+  });
+}
+
 bool LMDBBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
 {
   meta.clear();
index f395a85480cf5cd1ad2afee1fb185d31b3e99332..1bc63e5c4d3219bc7bcccdaf3a54e32da9c75c9f 100644 (file)
@@ -89,6 +89,11 @@ public:
   void getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
   void setNotified(uint32_t id, uint32_t serial) override;
 
+  // catalog zones
+  bool getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type) override;
+  bool setOptions(const DNSName& domain, const std::string& options) override;
+  bool setCatalog(const DNSName& domain, const DNSName& options) override;
+
   bool setMasters(const DNSName& domain, const vector<ComboAddress>& masters) override;
   bool setKind(const DNSName& domain, const DomainInfo::DomainKind kind) override;
   bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta) override;
@@ -110,8 +115,6 @@ public:
   }
 
   bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta) 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;
   bool deleteDomain(const DNSName& domain) override;
 
index a34851aaeb5ca776beb5f3c62ecc36700557478d..03c25b75d221ae485d929793ab926ad69397d700 100644 (file)
@@ -24,7 +24,7 @@
 #include "config.h"
 #endif
 
-#include "auth-catalogzone.hh"
+#include "dnsbackend.hh"
 
 void CatalogInfo::fromJson(const std::string& json, CatalogType type)
 {
@@ -40,21 +40,25 @@ void CatalogInfo::fromJson(const std::string& json, CatalogType type)
   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());
+      if (!items["coo"].is_null()) {
+        if (items["coo"].is_string()) {
+          if (!items["coo"].string_value().empty()) {
+            d_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 'coo' is not a string");
         }
       }
-      else {
-        throw std::out_of_range("Key 'unique' is not a string");
+      if (!items["unique"].is_null()) {
+        if (items["unique"].is_string()) {
+          if (!items["unique"].string_value().empty()) {
+            d_unique = DNSName(items["unique"].string_value());
+          }
+        }
+        else {
+          throw std::out_of_range("Key 'unique' is not a string");
+        }
       }
     }
   }
@@ -69,11 +73,11 @@ std::string CatalogInfo::toJson() const
     throw std::runtime_error("CatalogType is set to None");
   }
   json11::Json::object object;
-  if (!coo.empty()) {
-    object["coo"] = coo.toString();
+  if (!d_coo.empty()) {
+    object["coo"] = d_coo.toString();
   }
-  if (!unique.empty()) {
-    object["unique"] = unique.toString();
+  if (!d_unique.empty()) {
+    object["unique"] = d_unique.toString();
   }
   auto tmp = d_doc.object_items();
   tmp[getTypeString(d_type)] = object;
@@ -83,5 +87,42 @@ std::string CatalogInfo::toJson() const
 
 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());
+  hashes[di.catalog].process(static_cast<char>(di.id) + di.zone.toLogString() + "\0" + d_coo.toLogString() + "\0" + d_unique.toLogString());
+}
+
+DNSZoneRecord CatalogInfo::getCatalogVersionRecord(const DNSName& zone)
+{
+  DNSZoneRecord dzr;
+  dzr.dr.d_name = DNSName("version") + zone;
+  dzr.dr.d_ttl = 0;
+  dzr.dr.d_type = QType::TXT;
+  dzr.dr.d_content = std::make_shared<TXTRecordContent>("2");
+  return dzr;
+}
+
+void CatalogInfo::toDNSZoneRecords(const DNSName& zone, vector<DNSZoneRecord>& dzrs) const
+{
+  DNSName prefix;
+  if (d_unique.empty()) {
+    prefix = getUnique();
+  }
+  else {
+    prefix = d_unique;
+  }
+  prefix += DNSName("zones") + zone;
+
+  DNSZoneRecord dzr;
+  dzr.dr.d_name = prefix;
+  dzr.dr.d_ttl = 0;
+  dzr.dr.d_type = QType::PTR;
+  dzr.dr.d_content = std::make_shared<PTRRecordContent>(d_zone.toString());
+  dzrs.emplace_back(dzr);
+
+  if (!d_coo.empty()) {
+    dzr.dr.d_name = DNSName("coo") + prefix;
+    dzr.dr.d_ttl = 0;
+    dzr.dr.d_type = QType::PTR;
+    dzr.dr.d_content = std::make_shared<PTRRecordContent>(d_coo);
+    dzrs.emplace_back(dzr);
+  }
 }
index f483fab68d80c752d31adec919f8350827616da0..e4862f282a5b615875ce0d8929d68da86eb904ab 100644 (file)
 #pragma once
 
 #include "ext/json11/json11.hpp"
+#include "base32.hh"
 #include "dnsbackend.hh"
+#include "dnssecinfra.hh"
+
+struct DomainInfo;
+
+typedef map<DNSName, pdns::SHADigest> CatalogHashMap;
 
 class CatalogInfo
 {
@@ -42,26 +48,29 @@ public:
   }
 
   CatalogInfo() :
-    d_type(CatalogType::None) {}
-  CatalogInfo(const DNSName& zone, CatalogType type)
+    d_id(0), d_type(CatalogType::None) {}
+  CatalogInfo(uint32_t id, const DNSName& zone, const std::string& options, CatalogType type)
   {
-    this->zone = zone;
-    d_type = type;
+    d_id = id;
+    d_zone = zone;
+    fromJson(options, 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;
+  DNSName getUnique() const { return DNSName(toBase32Hex(hashQNameWithSalt(std::to_string(d_id), 0, d_zone))); } // salt with domain id to detect recreated zones
+  static DNSZoneRecord getCatalogVersionRecord(const DNSName& zone);
+  void toDNSZoneRecords(const DNSName& zone, vector<DNSZoneRecord>& dzrs) const;
 
   bool operator<(const CatalogInfo& rhs) const
   {
-    return zone < rhs.zone;
+    return d_zone < rhs.d_zone;
   }
 
-  DNSName coo, unique, zone;
+  uint32_t d_id;
+  DNSName d_zone, d_coo, d_unique;
 
 private:
   CatalogType d_type;
index c18900c4d7e19d33384759c927acc32956e1c79c..f589aa3e5ea3a8d86bef47802b041e73d55c6c02 100644 (file)
@@ -22,7 +22,6 @@
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include "pdns/auth-catalogzone.hh"
 #include "pdns/dns.hh"
 #include "pdns/dnsbackend.hh"
 #include "gsqlbackend.hh"
@@ -84,6 +83,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
   d_UpdateCatalogOfZoneQuery = getArg("update-catalog-query");
   d_UpdateAccountOfZoneQuery=getArg("update-account-query");
   d_InfoOfAllMasterDomainsQuery=getArg("info-all-master-query");
+  d_InfoProducerMembersQuery = getArg("info-producer-members-query");
+  d_InfoConsumerMembersQuery = getArg("info-consumer-members-query");
   d_DeleteDomainQuery=getArg("delete-domain-query");
   d_DeleteZoneQuery=getArg("delete-zone-query");
   d_DeleteRRSetQuery=getArg("delete-rrset-query");
@@ -159,6 +160,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
   d_UpdateCatalogOfZoneQuery_stmt = nullptr;
   d_UpdateAccountOfZoneQuery_stmt = nullptr;
   d_InfoOfAllMasterDomainsQuery_stmt = nullptr;
+  d_InfoProducerMembersQuery_stmt = nullptr;
+  d_InfoConsumerMembersQuery_stmt = nullptr;
   d_DeleteDomainQuery_stmt = nullptr;
   d_DeleteZoneQuery_stmt = nullptr;
   d_DeleteRRSetQuery_stmt = nullptr;
@@ -418,7 +421,7 @@ void GSQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
 
   SOAData sd;
   DomainInfo di;
-  for (const auto& row : d_result) { // id, name, type, master, last_check, content
+  for (const auto& row : d_result) { // id, name, type, master, last_check, catalog, content
     ASSERT_ROW_COLUMNS("info-all-slaves-query", row, 6);
 
     try {
@@ -570,7 +573,7 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::uno
 
     try {
       if (!row[5].empty()) {
-        ci.fromJson(row[3], CatalogInfo::CatalogType::Producer);
+        ci.fromJson(row[4], CatalogInfo::CatalogType::Producer);
         ci.updateHash(catalogHashes, di);
       }
     }
@@ -580,7 +583,7 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::uno
     }
 
     try {
-      pdns::checked_stoi_into(di.id, row[4]);
+      pdns::checked_stoi_into(di.id, row[3]);
     }
     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;
@@ -591,11 +594,11 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::uno
       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;
+      g_log << Logger::Warning << "Error while parsing SOA content '" << row[6] << "' for zone '" << di.zone << "': " << exp.what() << endl;
       continue;
     }
     catch (...) {
-      g_log << Logger::Warning << "Error while parsing SOA data for zone '" << di.zone << endl;
+      g_log << Logger::Warning << "Error while parsing SOA content '" << row[6] << "' for zone '" << di.zone << endl;
       continue;
     }
 
@@ -609,6 +612,79 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::uno
   }
 }
 
+bool GSQLBackend::getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type)
+{
+  try {
+    reconnectIfNeeded();
+
+    if (type == CatalogInfo::CatalogType::Producer) {
+      // clang-format off
+      d_InfoProducerMembersQuery_stmt->
+        bind("catalog", catalog)->
+        execute()->
+        getResult(d_result)->
+        reset();
+      // clang-format on
+    }
+    else if (type == CatalogInfo::CatalogType::Consumer) {
+      // clang-format off
+      d_InfoProducerMembersQuery_stmt->
+        bind("catalog", catalog)->
+        execute()->
+        getResult(d_result)->
+        reset();
+      // clang-format on
+    }
+    else {
+      PDNSException(std::string(__PRETTY_FUNCTION__) + " unknown type '" + CatalogInfo::getTypeString(type) + "'");
+    }
+  }
+  catch (SSqlException& e) {
+    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);
+
+    CatalogInfo ci;
+
+    try {
+      ci.d_zone = DNSName(row[1]);
+    }
+    catch (const std::runtime_error& e) {
+      g_log << Logger::Warning << __PRETTY_FUNCTION__ << " zone name '" << row[1] << "' is not a valid DNS name: " << e.what() << endl;
+      members.clear();
+      return false;
+    }
+    catch (PDNSException& ae) {
+      g_log << Logger::Warning << __PRETTY_FUNCTION__ << " zone name '" << row[1] << "' is not a valid DNS name: " << ae.reason << endl;
+      members.clear();
+      return false;
+    }
+
+    try {
+      pdns::checked_stoi_into(ci.d_id, row[0]);
+    }
+    catch (const std::exception& e) {
+      g_log << Logger::Warning << __PRETTY_FUNCTION__ << " could not convert id '" << row[0] << "' for zone '" << ci.d_zone << "' into an integer: " << e.what() << endl;
+      members.clear();
+      return false;
+    }
+
+    try {
+      ci.fromJson(row[2], type);
+    }
+    catch (const std::runtime_error& e) {
+      g_log << Logger::Warning << __PRETTY_FUNCTION__ << " options '" << row[2] << "' for zone '" << ci.d_zone << "' is no valid JSON: " << e.what() << endl;
+      members.clear();
+      return false;
+    }
+
+    members.emplace_back(ci);
+  }
+  return true;
+}
+
 bool GSQLBackend::updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype)
 {
   if(!d_dnssecQueries)
index 395fafdcf055e26585d94eb900d34d8736fd6d9a..724f050d9132a5d0afeaca13dcf5d3ba7d696f34 100644 (file)
@@ -80,6 +80,8 @@ protected:
       d_UpdateSerialOfZoneQuery_stmt = d_db->prepare(d_UpdateSerialOfZoneQuery, 2);
       d_UpdateLastCheckOfZoneQuery_stmt = d_db->prepare(d_UpdateLastCheckOfZoneQuery, 2);
       d_InfoOfAllMasterDomainsQuery_stmt = d_db->prepare(d_InfoOfAllMasterDomainsQuery, 0);
+      d_InfoProducerMembersQuery_stmt = d_db->prepare(d_InfoProducerMembersQuery, 1);
+      d_InfoConsumerMembersQuery_stmt = d_db->prepare(d_InfoConsumerMembersQuery, 1);
       d_DeleteDomainQuery_stmt = d_db->prepare(d_DeleteDomainQuery, 1);
       d_DeleteZoneQuery_stmt = d_db->prepare(d_DeleteZoneQuery, 1);
       d_DeleteRRSetQuery_stmt = d_db->prepare(d_DeleteRRSetQuery, 3);
@@ -148,6 +150,8 @@ protected:
     d_UpdateSerialOfZoneQuery_stmt.reset();
     d_UpdateLastCheckOfZoneQuery_stmt.reset();
     d_InfoOfAllMasterDomainsQuery_stmt.reset();
+    d_InfoProducerMembersQuery_stmt.reset();
+    d_InfoConsumerMembersQuery_stmt.reset();
     d_DeleteDomainQuery_stmt.reset();
     d_DeleteZoneQuery_stmt.reset();
     d_DeleteRRSetQuery_stmt.reset();
@@ -212,6 +216,7 @@ public:
   void setFresh(uint32_t domain_id) override;
   void getUnfreshSlaveInfos(vector<DomainInfo> *domains) override;
   void getUpdatedMasters(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes) override;
+  bool getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type) 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;
@@ -315,6 +320,8 @@ private:
   string d_UpdateSerialOfZoneQuery;
   string d_UpdateLastCheckOfZoneQuery;
   string d_InfoOfAllMasterDomainsQuery;
+  string d_InfoProducerMembersQuery;
+  string d_InfoConsumerMembersQuery;
   string d_DeleteDomainQuery;
   string d_DeleteZoneQuery;
   string d_DeleteRRSetQuery;
@@ -390,6 +397,8 @@ private:
   unique_ptr<SSqlStatement> d_UpdateSerialOfZoneQuery_stmt;
   unique_ptr<SSqlStatement> d_UpdateLastCheckOfZoneQuery_stmt;
   unique_ptr<SSqlStatement> d_InfoOfAllMasterDomainsQuery_stmt;
+  unique_ptr<SSqlStatement> d_InfoProducerMembersQuery_stmt;
+  unique_ptr<SSqlStatement> d_InfoConsumerMembersQuery_stmt;
   unique_ptr<SSqlStatement> d_DeleteDomainQuery_stmt;
   unique_ptr<SSqlStatement> d_DeleteZoneQuery_stmt;
   unique_ptr<SSqlStatement> d_DeleteRRSetQuery_stmt;
index 9c0f678f38aaf55e87685be14f77187d3f63c728..ace60d7e589ab2e29432311a7a4ddfce8f538192 100644 (file)
@@ -43,6 +43,7 @@ class DNSPacket;
 #include "dnsrecords.hh"
 #include "iputils.hh"
 #include "sha.hh"
+#include "auth-catalogzone.hh"
 
 class DNSBackend;  
 struct DomainInfo
@@ -120,8 +121,6 @@ struct DomainInfo
 
 };
 
-typedef map<DNSName, pdns::SHADigest> CatalogHashMap;
-
 struct TSIGKey {
    DNSName name;
    DNSName algorithm;
@@ -345,6 +344,12 @@ public:
   {
   }
 
+  //! get list of all members in a catalog
+  virtual bool getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type)
+  {
+    return false;
+  }
+
   //! Called by PowerDNS to inform a backend that a domain need to be checked for freshness
   virtual void setStale(uint32_t domain_id)
   {
index 29c46f8487c75fc9271789134004781bcc4df473..332e40ce7fd39a4cb364114debda6d19c89b89bf 100644 (file)
@@ -141,7 +141,6 @@ void CommunicatorClass::getUpdatedProducers(UeberBackend* B, vector<DomainInfo>&
   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;
index 91d49d822b4f1476feb64fbc4ec004a363512c8a..00ad4cba671a9d07a8ff523a4d234ec132389b7b 100644 (file)
@@ -101,6 +101,7 @@ endif
 pdns_recursor_SOURCES = \
        aggressive_nsec.cc aggressive_nsec.hh \
        arguments.cc \
+       auth-catalogzone.hh \
        axfr-retriever.hh axfr-retriever.cc \
        base32.cc base32.hh \
        base64.cc base64.hh \
diff --git a/pdns/recursordist/auth-catalogzone.hh b/pdns/recursordist/auth-catalogzone.hh
new file mode 120000 (symlink)
index 0000000..6883b89
--- /dev/null
@@ -0,0 +1 @@
+../auth-catalogzone.hh
\ No newline at end of file
index 30cc461096555a16a7d7664574bba03dafc771f1..9eeff003692ba4b345e589a2c16007744d250a62 100644 (file)
@@ -562,7 +562,7 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
   g_log<<Logger::Warning<<logPrefix<<"transfer initiated"<<endl;
 
   // determine if zone exists and AXFR is allowed using existing backend before spawning a new backend.
-  SOAData sd;
+  DomainInfo di;
   {
     auto packetHandler = s_P.lock();
     DLOG(g_log<<logPrefix<<"looking for SOA"<<endl);    // find domain_id via SOA and list complete domain. No SOA, no AXFR
@@ -579,7 +579,7 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
       return 0;
     }
 
-    if(!(*packetHandler)->getBackend()->getSOAUncached(target, sd)) {
+    if (!(*packetHandler)->getBackend()->getDomainInfo(target, di, false)) {
       g_log<<Logger::Warning<<logPrefix<<"failed: not authoritative"<<endl;
       outpacket->setRcode(RCode::NotAuth);
       sendPacket(outpacket,outsock);
@@ -588,6 +588,7 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
   }
 
   UeberBackend db;
+  SOAData sd;
   if(!db.getSOAUncached(target, sd)) {
     g_log<<Logger::Warning<<logPrefix<<"failed: not authoritative in second instance"<<endl;
     outpacket->setRcode(RCode::NotAuth);
@@ -595,14 +596,20 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
     return 0;
   }
 
+  bool securedZone = false;
+  bool presignedZone = false;
+  bool NSEC3Zone = false;
+  bool narrow = false;
+
+  NSEC3PARAMRecordContent ns3pr;
+
   DNSSECKeeper dk(&db);
   DNSSECKeeper::clearCaches(target);
-  bool securedZone = dk.isSecuredZone(target);
-  bool presignedZone = dk.isPresigned(target);
+  if (!di.isCatalogType()) {
+    securedZone = dk.isSecuredZone(target);
+    presignedZone = dk.isPresigned(target);
+  }
 
-  NSEC3PARAMRecordContent ns3pr;
-  bool narrow;
-  bool NSEC3Zone=false;
   if(securedZone && dk.getNSEC3PARAM(target, &ns3pr, &narrow)) {
     NSEC3Zone=true;
     if(narrow) {
@@ -727,18 +734,46 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
     zrrs.push_back(zrr);
   }
 
+  const bool rectify = !(presignedZone || ::arg().mustDo("disable-axfr-rectify"));
+  set<DNSName> qnames, nsset, terms;
+
+  // Catalog zone start
+  if (di.kind == DomainInfo::Producer) {
+    // Ignore all records except NS at apex
+    sd.db->lookup(QType::NS, target, di.id);
+    while (sd.db->get(zrr)) {
+      zrrs.emplace_back(zrr);
+    }
+    if (zrrs.empty()) {
+      zrr.dr.d_name = target;
+      zrr.dr.d_ttl = 0;
+      zrr.dr.d_type = QType::NS;
+      zrr.dr.d_content = std::make_shared<TXTRecordContent>("invalid.");
+      zrrs.emplace_back(zrr);
+    }
+
+    zrrs.emplace_back(CatalogInfo::getCatalogVersionRecord(target));
+
+    vector<CatalogInfo> members;
+    sd.db->getCatalogMembers(target, members, CatalogInfo::CatalogType::Producer);
+    for (const auto& ci : members) {
+      ci.toDNSZoneRecords(target, zrrs);
+    }
+    if (members.empty()) {
+      g_log << Logger::Warning << logPrefix << "catalog zone '" << target << "' has no members" << endl;
+    }
+    goto send;
+  }
+  // Catalog zone end
+
   // now start list zone
-  if(!(sd.db->list(target, sd.domain_id))) {
+  if (!(sd.db->list(target, sd.domain_id, di.isCatalogType()))) {
     g_log<<Logger::Error<<logPrefix<<"backend signals error condition, aborting AXFR"<<endl;
     outpacket->setRcode(RCode::ServFail);
     sendPacket(outpacket,outsock);
     return 0;
   }
 
-
-  const bool rectify = !(presignedZone || ::arg().mustDo("disable-axfr-rectify"));
-  set<DNSName> qnames, nsset, terms;
-
   while(sd.db->get(zrr)) {
     if (!presignedZone) {
       if (zrr.dr.d_type == QType::RRSIG) {
@@ -905,6 +940,7 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
     }
   }
 
+send:
 
   /* now write all other records */