#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)
#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);
#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"];
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()