]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add test, with some reorganization as reczones is not linked into testrunner. 12655/head
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 3 Apr 2023 10:18:02 +0000 (12:18 +0200)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 3 Apr 2023 10:26:58 +0000 (12:26 +0200)
So move a few functions to reczones-helpers.cc

pdns/recursordist/reczones-helpers.cc
pdns/recursordist/reczones-helpers.hh
pdns/recursordist/reczones.cc
pdns/recursordist/test-reczones-helpers.cc

index 41a36dc7f0214cf49655fba999b1af9a18530bf0..721a8d2e7db42f44259556470773a6d4372cae06 100644 (file)
 #include "config.h"
 #endif
 
+#include "arguments.hh"
 #include "syncres.hh"
 #include "reczones-helpers.hh"
+#include "root-addresses.hh"
+#include "zoneparser-tng.hh"
+
+static void putIntoCache(time_t now, QType qtype, vState state, const ComboAddress& from, const set<DNSName>& seen, const std::multimap<DNSName, DNSRecord>& allRecords)
+{
+  for (const auto& name : seen) {
+    auto records = allRecords.equal_range(name);
+    vector<DNSRecord> aset;
+    for (auto elem = records.first; elem != records.second; ++elem) {
+      aset.emplace_back(elem->second);
+    }
+    // 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);
+  }
+}
+
+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;
+}
+
+bool readHintsIntoCache(time_t now, const std::string& hintfile, std::vector<DNSRecord>& nsvec)
+{
+  const ComboAddress from("255.255.255.255");
+  set<DNSName> seenNS;
+  set<DNSName> seenA;
+  set<DNSName> seenAAAA;
+
+  std::multimap<DNSName, DNSRecord> aRecords;
+  std::multimap<DNSName, DNSRecord> aaaaRecords;
+
+  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");
+  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;
+  }
+  return true;
+}
+
+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.
+       *
+       * 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::A, {arr}, {}, {}, false, g_rootdnsname, boost::none, boost::none, vState::Insecure, from);
+    }
+    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);
+    }
+  }
+}
 
 template <typename T>
 static SyncRes::AuthDomain makeSOAAndNSNodes(DNSRecord& dr, T content)
index e68fed2ec08a8f6ddb09bd8c39a44fe863cbdef3..dafeee4daf9f93e91412dd20563613710fa90147 100644 (file)
@@ -32,6 +32,9 @@
 #include "syncres.hh"
 #include "logger.hh"
 
+bool readHintsIntoCache(time_t now, const std::string& hintfile, std::vector<DNSRecord>& nsvec);
+void putDefaultHintsIntoCache(time_t now, std::vector<DNSRecord>& nsvec);
+
 void makeIPToNamesZone(const std::shared_ptr<SyncRes::domainmap_t>& newMap,
                        const vector<string>& parts,
                        Logr::log_t log);
index 81e6774bd9d04abbdfac317c89c8aa3d0074360a..ba658b19c2be49bded83a19cda84c2a88632dc76 100644 (file)
 #endif
 
 #include "reczones-helpers.hh"
-#include "syncres.hh"
 #include "arguments.hh"
-#include "zoneparser-tng.hh"
-#include "logger.hh"
 #include "dnsrecords.hh"
-#include "root-addresses.hh"
+#include "logger.hh"
+#include "syncres.hh"
+#include "zoneparser-tng.hh"
 
 extern int g_argc;
 extern char** g_argv;
 
-static void putIntoCache(time_t now, QType qtype, vState state, const ComboAddress& from, const set<DNSName>& seen, const std::multimap<DNSName, DNSRecord>& allRecords)
-{
-  for (const auto& name : seen) {
-    auto records = allRecords.equal_range(name);
-    vector<DNSRecord> aset;
-    for (auto elem = records.first; elem != records.second; ++elem) {
-      aset.emplace_back(elem->second);
-    }
-    // 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);
-  }
-}
-
-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)
-{
-  const ComboAddress from("255.255.255.255");
-  set<DNSName> seenNS;
-  set<DNSName> seenA;
-  set<DNSName> seenAAAA;
-
-  std::multimap<DNSName, DNSRecord> aRecords;
-  std::multimap<DNSName, DNSRecord> aaaaRecords;
-
-  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");
-  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;
-  }
-  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.
-       *
-       * 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::A, {arr}, {}, {}, false, g_rootdnsname, boost::none, boost::none, vState::Insecure, from);
-    }
-    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);
-    }
-  }
-}
-
 bool primeHints(time_t now)
 {
   const string hintfile = ::arg()["hint-file"];
index 4a7aa30e78d0b34930678e9ce7bfc370d282412c..921c7d1f11c4e016f2921d306362e4caa155df04 100644 (file)
@@ -258,4 +258,48 @@ BOOST_FIXTURE_TEST_CASE(test_loading_etc_hosts, Fixture)
   BOOST_TEST_MESSAGE("-----------------------------------------------------");
 }
 
+const std::string hints = ". 3600 IN NS ns.\n"
+                          ". 3600 IN NS ns1.\n"
+                          "ns. 3600 IN A 192.168.178.16\n"
+                          "ns. 3600 IN A 192.168.178.17\n"
+                          "ns. 3600 IN A 192.168.178.18\n"
+                          "ns. 3600 IN AAAA 1::2\n"
+                          "ns. 3600 IN AAAA 1::3\n"
+                          "ns1. 3600 IN A 192.168.178.18\n";
+
+BOOST_AUTO_TEST_CASE(test_UserHints)
+{
+
+  g_recCache = make_unique<MemRecursorCache>();
+
+  ::arg().set("max-generate-steps") = "0";
+  ::arg().set("max-include-depth") = "0";
+  char temp[] = "/tmp/hintsXXXXXXXXXX";
+  int fd = mkstemp(temp);
+  BOOST_REQUIRE(fd > 0);
+  FILE* fp = fdopen(fd, "w");
+  BOOST_REQUIRE(fp != nullptr);
+  size_t written = fwrite(hints.data(), 1, hints.length(), fp);
+  BOOST_REQUIRE(written == hints.length());
+  BOOST_REQUIRE(fclose(fp) == 0);
+
+  time_t now = time(nullptr);
+  std::vector<DNSRecord> nsvec;
+
+  auto ok = readHintsIntoCache(now, std::string(temp), nsvec);
+  BOOST_CHECK(ok);
+  BOOST_CHECK_EQUAL(nsvec.size(), 2U);
+
+  const MemRecursorCache::Flags flags = 0;
+
+  BOOST_CHECK(g_recCache->get(now, DNSName("ns"), QType::A, flags, &nsvec, ComboAddress()) > 0);
+  BOOST_CHECK_EQUAL(nsvec.size(), 3U);
+
+  BOOST_CHECK(g_recCache->get(now, DNSName("ns"), QType::AAAA, flags, &nsvec, ComboAddress()) > 0);
+  BOOST_CHECK_EQUAL(nsvec.size(), 2U);
+
+  BOOST_CHECK(g_recCache->get(now, DNSName("ns1"), QType::A, flags, &nsvec, ComboAddress()) > 0);
+  BOOST_CHECK_EQUAL(nsvec.size(), 1U);
+}
+
 BOOST_AUTO_TEST_SUITE_END()