From: Otto Moerbeek Date: Mon, 6 Feb 2023 07:57:31 +0000 (+0100) Subject: Refactor of root priming code X-Git-Tag: rec-4.9.0-alpha1~12^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5c7e7d4880494ddfa280d3b50857fee721cd32fa;p=thirdparty%2Fpdns.git Refactor of root priming code --- diff --git a/pdns/recursordist/reczones.cc b/pdns/recursordist/reczones.cc index 94181f7517..81e6774bd9 100644 --- a/pdns/recursordist/reczones.cc +++ b/pdns/recursordist/reczones.cc @@ -41,138 +41,162 @@ static void putIntoCache(time_t now, QType qtype, vState state, const ComboAddre auto records = allRecords.equal_range(name); vector 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>(), vector>(), 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& seenA, set& seenAAAA, set& seenNS, std::multimap& aRecords, std::multimap& aaaaRecords, vector& 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& names, const set& 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& nsvec) { - // prime root cache - const vState validationState = vState::Insecure; const ComboAddress from("255.255.255.255"); - vector nsvec; + set seenNS; + set seenA; + set seenAAAA; + + std::multimap aRecords; + std::multimap 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(DNSName(templ))); - arr.setContent(std::make_shared(ComboAddress(rootIps4[c - 'a']))); - vector 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& 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(letter + 'a'); + aaaarr.d_name = arr.d_name = DNSName(templ); + nsrr.setContent(std::make_shared(DNSName(templ))); + nsvec.push_back(nsrr); + + if (!rootIps4.at(letter).empty()) { + arr.setContent(std::make_shared(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>(), vector>(), false, g_rootdnsname, boost::none, boost::none, validationState, from); // auth, nuke it all - if (rootIps6[c - 'a'] != NULL) { - aaaarr.setContent(std::make_shared(ComboAddress(rootIps6[c - 'a']))); - - vector aaaaset; - aaaaset.push_back(aaaarr); - g_recCache->replace(now, DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), 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 seenNS; - set seenA; - set seenAAAA; - - std::multimap aRecords; - std::multimap 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(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 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) diff --git a/pdns/recursordist/root-addresses.hh b/pdns/recursordist/root-addresses.hh index 3455b9bcfe..e766fd9490 100644 --- a/pdns/recursordist/root-addresses.hh +++ b/pdns/recursordist/root-addresses.hh @@ -21,7 +21,10 @@ */ #pragma once -static const char* const rootIps4[] = { +#include +#include + +const std::array 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 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); diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 52b773c765..ae574ebe03 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -101,7 +101,7 @@ bool primeHints(time_t now) vector aset; aset.push_back(arr); g_recCache->replace(now, DNSName(templ), QType(QType::A), aset, vector>(), vector>(), false, g_rootdnsname); - if (rootIps6[c - 'a'] != NULL) { + if (!rootIps6[c - 'a'].empty()) { aaaarr.setContent(std::make_shared(ComboAddress(rootIps6[c - 'a']))); vector 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; }