]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Refactor of root priming code
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 6 Feb 2023 07:57:31 +0000 (08:57 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 3 Apr 2023 09:28:43 +0000 (11:28 +0200)
pdns/recursordist/reczones.cc
pdns/recursordist/root-addresses.hh
pdns/recursordist/test-syncres_cc.cc

index 94181f7517519ec218013fe059b36bab529fe3fe..81e6774bd9d04abbdfac317c89c8aa3d0074360a 100644 (file)
@@ -41,138 +41,162 @@ static void putIntoCache(time_t now, QType qtype, vState state, const ComboAddre
     auto records = allRecords.equal_range(name);
     vector<DNSRecord> aset;
     for (auto elem = records.first; elem != records.second; ++elem) {
-      aset.push_back(elem->second);
+      aset.emplace_back(elem->second);
     }
-    g_recCache->replace(now, name, qtype, aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, g_rootdnsname, boost::none, boost::none, state, from); // auth, etc see above
+    // Put non-default root hints into cache as authoritative.  As argued below in
+    // putDefaultHintsIntoCache, this is actually wrong, but people might depend on it by having
+    // root-hints that refer to servers that aren't actually capable or willing to serve root data.
+    g_recCache->replace(now, name, qtype, aset, {}, {}, true, g_rootdnsname, boost::none, boost::none, state, from);
   }
 }
 
-bool primeHints(time_t ignored)
+static void parseHintFile(time_t now, const std::string& hintfile, set<DNSName>& seenA, set<DNSName>& seenAAAA, set<DNSName>& seenNS, std::multimap<DNSName, DNSRecord>& aRecords, std::multimap<DNSName, DNSRecord>& aaaaRecords, vector<DNSRecord>& nsvec)
+{
+  ZoneParserTNG zpt(hintfile);
+  zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
+  zpt.setMaxIncludes(::arg().asNum("max-include-depth"));
+  DNSResourceRecord rrecord;
+
+  while (zpt.get(rrecord)) {
+    rrecord.ttl += now;
+    switch (rrecord.qtype) {
+    case QType::A:
+      seenA.insert(rrecord.qname);
+      aRecords.emplace(rrecord.qname, DNSRecord(rrecord));
+      break;
+    case QType::AAAA:
+      seenAAAA.insert(rrecord.qname);
+      aaaaRecords.emplace(rrecord.qname, DNSRecord(rrecord));
+      break;
+    case QType::NS:
+      seenNS.emplace(rrecord.content);
+      rrecord.content = toLower(rrecord.content);
+      nsvec.emplace_back(rrecord);
+      break;
+    }
+  }
+}
+
+static bool determineReachable(const set<DNSName>& names, const set<DNSName>& nameservers)
+{
+  bool reachable = false;
+  for (auto const& record : names) {
+    if (nameservers.count(record) != 0) {
+      reachable = true;
+      break;
+    }
+  }
+  return reachable;
+}
+
+static bool readHintsIntoCache(time_t now, const std::string& hintfile, std::vector<DNSRecord>& nsvec)
 {
-  // prime root cache
-  const vState validationState = vState::Insecure;
   const ComboAddress from("255.255.255.255");
-  vector<DNSRecord> nsvec;
+  set<DNSName> seenNS;
+  set<DNSName> seenA;
+  set<DNSName> seenAAAA;
+
+  std::multimap<DNSName, DNSRecord> aRecords;
+  std::multimap<DNSName, DNSRecord> aaaaRecords;
 
-  time_t now = time(nullptr);
+  parseHintFile(now, hintfile, seenA, seenAAAA, seenNS, aRecords, aaaaRecords, nsvec);
+
+  putIntoCache(now, QType::A, vState::Insecure, from, seenA, aRecords);
+  putIntoCache(now, QType::AAAA, vState::Insecure, from, seenAAAA, aaaaRecords);
+
+  bool reachableA = determineReachable(seenA, seenNS);
+  bool reachableAAAA = determineReachable(seenAAAA, seenNS);
 
   auto log = g_slog->withName("config");
-  const string hintfile = ::arg()["hint-file"];
-  if (hintfile == "no") {
-    SLOG(g_log << Logger::Debug << "Priming root disabled by hint-file=no" << endl,
-         log->info(Logr::Debug, "Priming root disabled by hint-file=no"));
-    return true;
+  if (SyncRes::s_doIPv4 && !SyncRes::s_doIPv6 && !reachableA) {
+    SLOG(g_log << Logger::Error << "Running IPv4 only but no IPv4 root hints" << endl,
+         log->info(Logr::Error, "Running IPv4 only but no IPv4 root hints"));
+    return false;
   }
-  if (hintfile.empty()) {
-    DNSRecord arr, aaaarr, nsrr;
-    nsrr.d_name = g_rootdnsname;
-    arr.d_type = QType::A;
-    aaaarr.d_type = QType::AAAA;
-    nsrr.d_type = QType::NS;
-
-    arr.d_ttl = aaaarr.d_ttl = nsrr.d_ttl = now + 3600000;
-
-    for (char c = 'a'; c <= 'm'; ++c) {
-      char templ[40];
-      strncpy(templ, "a.root-servers.net.", sizeof(templ) - 1);
-      templ[sizeof(templ) - 1] = '\0';
-      *templ = c;
-      aaaarr.d_name = arr.d_name = DNSName(templ);
-      nsrr.setContent(std::make_shared<NSRecordContent>(DNSName(templ)));
-      arr.setContent(std::make_shared<ARecordContent>(ComboAddress(rootIps4[c - 'a'])));
-      vector<DNSRecord> aset;
-      aset.push_back(arr);
+  if (!SyncRes::s_doIPv4 && SyncRes::s_doIPv6 && !reachableAAAA) {
+    SLOG(g_log << Logger::Error << "Running IPv6 only but no IPv6 root hints" << endl,
+         log->info(Logr::Error, "Running IPv6 only but no IPv6 root hints"));
+    return false;
+  }
+  if (SyncRes::s_doIPv4 && SyncRes::s_doIPv6 && !reachableA && !reachableAAAA) {
+    SLOG(g_log << Logger::Error << "No valid root hints" << endl,
+         log->info(Logr::Error, "No valid root hints"));
+    return false;
+  }
+  return true;
+}
+
+static void putDefaultHintsIntoCache(time_t now, std::vector<DNSRecord>& nsvec)
+{
+  const ComboAddress from("255.255.255.255");
+
+  DNSRecord arr;
+  DNSRecord aaaarr;
+  DNSRecord nsrr;
+
+  nsrr.d_name = g_rootdnsname;
+  arr.d_type = QType::A;
+  aaaarr.d_type = QType::AAAA;
+  nsrr.d_type = QType::NS;
+  arr.d_ttl = aaaarr.d_ttl = nsrr.d_ttl = now + 3600000;
+
+  string templ = "a.root-servers.net.";
+
+  static_assert(rootIps4.size() == rootIps6.size());
+
+  for (size_t letter = 0; letter < rootIps4.size(); ++letter) {
+    templ.at(0) = static_cast<char>(letter + 'a');
+    aaaarr.d_name = arr.d_name = DNSName(templ);
+    nsrr.setContent(std::make_shared<NSRecordContent>(DNSName(templ)));
+    nsvec.push_back(nsrr);
+
+    if (!rootIps4.at(letter).empty()) {
+      arr.setContent(std::make_shared<ARecordContent>(ComboAddress(rootIps4.at(letter))));
       /*
-       * Originally the hint records were inserted with the auth flag set, with the consequence that data from AUTHORITY and
-       * ADDITIONAL sections (as seen in a . NS response) were not used. This (together with the long ttl) caused outdated
-       * hint to be kept in cache. So insert as non-auth, and the extra sections in the . NS refreshing cause the cached
-       * records to be updated with up-to-date information received from a real root server.
+       * Originally the hint records were inserted with the auth flag set, with the consequence that
+       * data from AUTHORITY and ADDITIONAL sections (as seen in a . NS response) were not used. This
+       * (together with the long ttl) caused outdated hint to be kept in cache. So insert as non-auth,
+       * and the extra sections in the . NS refreshing cause the cached records to be updated with
+       * up-to-date information received from a real root server.
        *
-       * Note that if a user query is done for one of the root-server.net names, it will be inserted into the cache with the
-       * auth bit set. Further NS refreshes will not update that entry. If all root names are queried at the same time by a user,
-       * all root-server.net names will be marked auth and will expire at the same time. A re-prime is then triggered,
-       * as before, when the records were inserted with the auth bit set and the TTD comes.
+       * Note that if a user query is done for one of the root-server.net names, it will be inserted
+       * into the cache with the auth bit set. Further NS refreshes will not update that entry. If all
+       * root names are queried at the same time by a user, all root-server.net names will be marked
+       * auth and will expire at the same time. A re-prime is then triggered, as before, when the
+       * records were inserted with the auth bit set and the TTD comes.
        */
-      g_recCache->replace(now, DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<const RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false, g_rootdnsname, boost::none, boost::none, validationState, from); // auth, nuke it all
-      if (rootIps6[c - 'a'] != NULL) {
-        aaaarr.setContent(std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c - 'a'])));
-
-        vector<DNSRecord> aaaaset;
-        aaaaset.push_back(aaaarr);
-        g_recCache->replace(now, DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<const RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false, g_rootdnsname, boost::none, boost::none, validationState, from);
-      }
-
-      nsvec.push_back(nsrr);
+      g_recCache->replace(now, DNSName(templ), QType::A, {arr}, {}, {}, false, g_rootdnsname, boost::none, boost::none, vState::Insecure, from);
     }
-  }
-  else {
-    ZoneParserTNG zpt(hintfile);
-    zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
-    zpt.setMaxIncludes(::arg().asNum("max-include-depth"));
-    DNSResourceRecord rr;
-    set<DNSName> seenNS;
-    set<DNSName> seenA;
-    set<DNSName> seenAAAA;
-
-    std::multimap<DNSName, DNSRecord> aRecords;
-    std::multimap<DNSName, DNSRecord> aaaaRecords;
-
-    while (zpt.get(rr)) {
-      rr.ttl += now;
-      switch (rr.qtype) {
-      case QType::A:
-        seenA.insert(rr.qname);
-        aRecords.insert({rr.qname, DNSRecord(rr)});
-        break;
-      case QType::AAAA:
-        seenAAAA.insert(rr.qname);
-        aaaaRecords.insert({rr.qname, DNSRecord(rr)});
-        break;
-      case QType::NS:
-        seenNS.insert(DNSName(rr.content));
-        rr.content = toLower(rr.content);
-        nsvec.emplace_back(rr);
-        break;
-      }
+    if (!rootIps6.at(letter).empty()) {
+      aaaarr.setContent(std::make_shared<AAAARecordContent>(ComboAddress(rootIps6.at(letter))));
+      g_recCache->replace(now, DNSName(templ), QType::AAAA, {aaaarr}, {}, {}, false, g_rootdnsname, boost::none, boost::none, vState::Insecure, from);
     }
+  }
+}
 
-    putIntoCache(now, QType::A, validationState, from, seenA, aRecords);
-    putIntoCache(now, QType::AAAA, validationState, from, seenAAAA, aaaaRecords);
+bool primeHints(time_t now)
+{
+  const string hintfile = ::arg()["hint-file"];
+  vector<DNSRecord> nsvec;
+  bool ret = true;
 
-    bool reachableA = false;
-    for (auto const& record : seenA) {
-      if (seenNS.count(record) != 0) {
-        reachableA = true;
-        break;
-      }
-    }
-    bool reachableAAAA = false;
-    for (auto const& record : seenAAAA) {
-      if (seenNS.count(record) != 0) {
-        reachableAAAA = true;
-        break;
-      }
-    }
-    if (SyncRes::s_doIPv4 && !SyncRes::s_doIPv6 && !reachableA) {
-      SLOG(g_log << Logger::Error << "Running IPv4 only but no IPv4 root hints" << endl,
-           log->info(Logr::Error, "Running IPv4 only but no IPv4 root hints"));
-      return false;
-    }
-    if (!SyncRes::s_doIPv4 && SyncRes::s_doIPv6 && !reachableAAAA) {
-      SLOG(g_log << Logger::Error << "Running IPv6 only but no IPv6 root hints" << endl,
-           log->info(Logr::Error, "Running IPv6 only but no IPv6 root hints"));
-      return false;
-    }
-    if (SyncRes::s_doIPv4 && SyncRes::s_doIPv6 && !reachableA && !reachableAAAA) {
-      SLOG(g_log << Logger::Error << "No valid root hints" << endl,
-           log->info(Logr::Error, "No valid root hints"));
-      return false;
-    }
+  if (hintfile == "no") {
+    auto log = g_slog->withName("config");
+    SLOG(g_log << Logger::Debug << "Priming root disabled by hint-file=no" << endl,
+         log->info(Logr::Debug, "Priming root disabled by hint-file=no"));
+    return ret;
+  }
+
+  if (hintfile.empty()) {
+    putDefaultHintsIntoCache(now, nsvec);
+  }
+  else {
+    ret = readHintsIntoCache(now, hintfile, nsvec);
   }
 
   g_recCache->doWipeCache(g_rootdnsname, false, QType::NS);
-  g_recCache->replace(now, g_rootdnsname, QType(QType::NS), nsvec, {}, {}, false, g_rootdnsname, boost::none, boost::none, validationState, from); // and stuff in the cache
-  return true;
+  g_recCache->replace(now, g_rootdnsname, QType::NS, nsvec, {}, {}, false, g_rootdnsname, boost::none, boost::none, vState::Insecure, ComboAddress("255.255.255.255")); // and stuff in the cache
+  return ret;
 }
 
 static void convertServersForAD(const std::string& zone, const std::string& input, SyncRes::AuthDomain& ad, const char* sepa, Logr::log_t log, bool verbose = true)
index 3455b9bcfea82a44309dce184adc210e6e753694..e766fd9490ada18d5ce4057b19dc635d67746f83 100644 (file)
  */
 #pragma once
 
-static const char* const rootIps4[] = {
+#include <array>
+#include <string>
+
+const std::array<const std::string, 13> rootIps4 = {
   "198.41.0.4", // a.root-servers.net.
   "199.9.14.201", // b.root-servers.net.
   "192.33.4.12", // c.root-servers.net.
@@ -36,9 +39,8 @@ static const char* const rootIps4[] = {
   "199.7.83.42", // l.root-servers.net.
   "202.12.27.33" // m.root-servers.net.
 };
-static size_t const rootIps4Count = sizeof(rootIps4) / sizeof(*rootIps4);
 
-static const char* const rootIps6[] = {
+const std::array<const std::string, 13> rootIps6 = {
   "2001:503:ba3e::2:30", // a.root-servers.net.
   "2001:500:200::b", // b.root-servers.net.
   "2001:500:2::c", // c.root-servers.net.
@@ -53,4 +55,3 @@ static const char* const rootIps6[] = {
   "2001:500:9f::42", // l.root-servers.net.
   "2001:dc3::35" // m.root-servers.net.
 };
-static size_t const rootIps6Count = sizeof(rootIps6) / sizeof(*rootIps6);
index 52b773c7658d160c498609a87c873e98bdbc6aae..ae574ebe0383930fbf7398c6b2822cdf136e8d4c 100644 (file)
@@ -101,7 +101,7 @@ bool primeHints(time_t now)
     vector<DNSRecord> aset;
     aset.push_back(arr);
     g_recCache->replace(now, DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<const RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false, g_rootdnsname);
-    if (rootIps6[c - 'a'] != NULL) {
+    if (!rootIps6[c - 'a'].empty()) {
       aaaarr.setContent(std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c - 'a'])));
 
       vector<DNSRecord> aaaaset;
@@ -273,14 +273,14 @@ void addRecordToLW(LWResult* res, const std::string& name, uint16_t type, const
 bool isRootServer(const ComboAddress& ip)
 {
   if (ip.isIPv4()) {
-    for (size_t idx = 0; idx < rootIps4Count; idx++) {
+    for (size_t idx = 0; idx < rootIps4.size(); idx++) {
       if (ip.toString() == rootIps4[idx]) {
         return true;
       }
     }
   }
   else {
-    for (size_t idx = 0; idx < rootIps6Count; idx++) {
+    for (size_t idx = 0; idx < rootIps6.size(); idx++) {
       if (ip.toString() == rootIps6[idx]) {
         return true;
       }