From: Fred Morcos Date: Mon, 23 May 2022 13:06:43 +0000 (+0200) Subject: Convert the handling of addresses from etc-hosts file to ComboAddress X-Git-Tag: auth-4.8.0-alpha0~63^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9452c5e694ac3a11de3a2e41a9ad348687f319e5;p=thirdparty%2Fpdns.git Convert the handling of addresses from etc-hosts file to ComboAddress This enables almost-seamless IPv6 support and cleans up a few things e.g. avoid using strings when handling IP addresses. This commit also refactors the creation of forward and reverse lookup entries. --- diff --git a/pdns/recursordist/test-reczones-helpers.cc b/pdns/recursordist/test-reczones-helpers.cc index 9d142581dc..f089c7cb0f 100644 --- a/pdns/recursordist/test-reczones-helpers.cc +++ b/pdns/recursordist/test-reczones-helpers.cc @@ -8,12 +8,16 @@ BOOST_AUTO_TEST_SUITE(reczones_helpers) -static const std::array hostLines = { +static const std::array hostLines = { "192.168.0.1 foo bar\n", "192.168.0.1 dupfoo\n", "192.168.0.2 baz\n", "1.1.1.1 fancy\n", "2.2.2.2 more.fancy\n", + "2001:db8::567:89ab foo6 bar6\n", + "2001:db8::567:89ab dupfoo6\n", + "::1 localhost self\n", + "2001:db8::567:89ac some.address.somewhere some some.address\n", }; struct Fixture @@ -94,6 +98,42 @@ struct Fixture DNSRecord("2.2.2.2.in-addr.arpa", makeLocalhostRootDRC(), QType::SOA), DNSRecord("2.2.2.2.in-addr.arpa", makePtrDRC("more.fancy."), QType::PTR), }); + + addDomainMapFixtureEntry("foo6", QType::AAAA, "2001:db8::567:89ab"); + addDomainMapFixtureEntry("bar6", QType::AAAA, "2001:db8::567:89ab"); + addDomainMapFixtureEntry("dupfoo6", QType::AAAA, "2001:db8::567:89ab"); + addDomainMapFixtureEntry( + "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", + { + DNSRecord("b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makeLocalhostDRC(), QType::NS), + DNSRecord("b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makeLocalhostRootDRC(), QType::SOA), + DNSRecord("b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makePtrDRC("foo6."), QType::PTR), + DNSRecord("b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makePtrDRC("bar6."), QType::PTR), + }); + + addDomainMapFixtureEntry("localhost", QType::AAAA, "::1"); + addDomainMapFixtureEntry("self", QType::AAAA, "::1"); + addDomainMapFixtureEntry( + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", + { + DNSRecord("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", makeLocalhostDRC(), QType::NS), + DNSRecord("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", makeLocalhostRootDRC(), QType::SOA), + DNSRecord("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", makePtrDRC("localhost."), QType::PTR), + DNSRecord("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", makePtrDRC("self."), QType::PTR), + }); + + addDomainMapFixtureEntry("some", QType::AAAA, "2001:db8::567:89ac"); + addDomainMapFixtureEntry("some.address.somewhere", QType::AAAA, "2001:db8::567:89ac"); + addDomainMapFixtureEntry("some.address", QType::AAAA, "2001:db8::567:89ac"); + addDomainMapFixtureEntry( + "c.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", + { + DNSRecord("c.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makeLocalhostDRC(), QType::NS), + DNSRecord("c.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makeLocalhostRootDRC(), QType::SOA), + DNSRecord("c.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makePtrDRC("some.address.somewhere."), QType::PTR), + DNSRecord("c.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makePtrDRC("some."), QType::PTR), + DNSRecord("c.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa", makePtrDRC("some.address."), QType::PTR), + }); } using DomainMapEntry = std::pair; @@ -139,7 +179,7 @@ BOOST_FIXTURE_TEST_CASE(test_loading_etc_hosts, Fixture) std::vector parts{}; for (auto line : hostLines) { BOOST_REQUIRE(parseEtcHostsLine(parts, line)); - addForwardAndReverseLookupEntries(domainMap, "", parts, log); + addForwardAndReverseLookupEntries(*domainMap, "", parts, log); } auto actual = sortDomainMap(*domainMap); @@ -155,9 +195,6 @@ BOOST_FIXTURE_TEST_CASE(test_loading_etc_hosts, Fixture) BOOST_CHECK(actual[i].first == expected[i].first); BOOST_CHECK(actual[i].second == expected[i].second); } - - // BOOST_CHECK_EQUAL(actual, expected); - // BOOST_CHECK(actualSorted == expectedSorted); } BOOST_AUTO_TEST_SUITE_END() diff --git a/pdns/reczones-helpers.cc b/pdns/reczones-helpers.cc index a96c4213e8..c71d2db1ab 100644 --- a/pdns/reczones-helpers.cc +++ b/pdns/reczones-helpers.cc @@ -27,115 +27,129 @@ #include "syncres.hh" #include "reczones-helpers.hh" -static void makeNameToIPZone(const std::shared_ptr& newMap, - const DNSName& hostname, - const string& ip, - Logr::log_t log) +template +static SyncRes::AuthDomain makeSOAAndNSNodes(DNSRecord& dr, T content) { - SyncRes::AuthDomain ad; - ad.d_rdForward = false; - - DNSRecord dr; - dr.d_name = hostname; + dr.d_class = 1; dr.d_place = DNSResourceRecord::ANSWER; dr.d_ttl = 86400; dr.d_type = QType::SOA; - dr.d_class = 1; dr.d_content = DNSRecordContent::mastermake(QType::SOA, 1, "localhost. root 1 604800 86400 2419200 604800"); + SyncRes::AuthDomain ad; + ad.d_rdForward = false; ad.d_records.insert(dr); dr.d_type = QType::NS; - dr.d_content = std::make_shared("localhost."); - + dr.d_content = std::make_shared(content); ad.d_records.insert(dr); - dr.d_type = QType::A; - dr.d_content = DNSRecordContent::mastermake(QType::A, 1, ip); - ad.d_records.insert(dr); + return ad; +} - if (newMap->count(dr.d_name) != 0) { - SLOG(g_log << Logger::Warning << "Hosts file will not overwrite zone '" << dr.d_name << "' already loaded" << endl, - log->info(Logr::Warning, "Hosts file will not overwrite already loaded zone", "zone", Logging::Loggable(dr.d_name))); +static void addToDomainMap(SyncRes::domainmap_t& newMap, + SyncRes::AuthDomain ad, + DNSName& name, + Logr::log_t log, + const bool partial = false, + const bool reverse = false) +{ + if (newMap.count(name) != 0) { + SLOG(g_log << Logger::Warning << "Will not overwrite zone '" << name << "' already loaded" << endl, + log->info(Logr::Warning, "Will not overwrite already loaded zone", "zone", + Logging::Loggable(name))); } else { - SLOG(g_log << Logger::Warning << "Inserting forward zone '" << dr.d_name << "' based on hosts file" << endl, - log->info(Logr::Notice, "Inserting forward zone based on hosts file", "zone", Logging::Loggable(dr.d_name))); - ad.d_name = dr.d_name; - (*newMap)[ad.d_name] = ad; + if (!partial) { + const auto direction = reverse ? std::string{"reverse"} : std::string{"forward"}; + SLOG(g_log << Logger::Warning << "Inserting " << direction << " zone '" << name << "' based on hosts file" << endl, + log->info(Logr::Notice, "Inserting " + direction + " zone based on hosts file", "zone", Logging::Loggable(name))); + } + ad.d_name = name; + newMap[ad.d_name] = ad; } } -//! parts[0] must be an IP address, the rest must be host names -void makeIPToNamesZone(const std::shared_ptr& newMap, - const vector& parts, - Logr::log_t log) +static void makeNameToIPZone(SyncRes::domainmap_t& newMap, + const DNSName& hostname, + const ComboAddress& addr, + Logr::log_t log) { - string address = parts[0]; - vector ipParts; - stringtok(ipParts, address, "."); + DNSRecord dr; + dr.d_name = hostname; - SyncRes::AuthDomain ad; - ad.d_rdForward = false; + SyncRes::AuthDomain ad = makeSOAAndNSNodes(dr, "localhost."); + + auto recType = addr.isIPv6() ? QType::AAAA : QType::A; + dr.d_type = recType; + dr.d_content = DNSRecordContent::mastermake(recType, 1, addr.toStringNoInterface()); + ad.d_records.insert(dr); + addToDomainMap(newMap, ad, dr.d_name, log); +} + +static void makeIPToNamesZone(SyncRes::domainmap_t& newMap, + const ComboAddress& addr, + const std::vector& parts, + Logr::log_t log) +{ DNSRecord dr; - for (auto part = ipParts.rbegin(); part != ipParts.rend(); ++part) { - dr.d_name.appendRawLabel(*part); - } - dr.d_name.appendRawLabel("in-addr"); + dr.d_name = DNSName(addr.toStringReversed()); + dr.d_name.appendRawLabel(addr.isIPv4() ? "in-addr" : "ip6"); dr.d_name.appendRawLabel("arpa"); - dr.d_class = 1; - dr.d_place = DNSResourceRecord::ANSWER; - dr.d_ttl = 86400; - dr.d_type = QType::SOA; - dr.d_content = DNSRecordContent::mastermake(QType::SOA, 1, "localhost. root 1 604800 86400 2419200 604800"); - - ad.d_records.insert(dr); - dr.d_type = QType::NS; - dr.d_content = std::make_shared(DNSName("localhost.")); + SyncRes::AuthDomain ad = makeSOAAndNSNodes(dr, DNSName("localhost.")); - ad.d_records.insert(dr); + // Go over the hostname and aliases (parts[1], parts[2], etc...) and add PTR entries for + // reverse lookups. dr.d_type = QType::PTR; - - if (ipParts.size() == 4) { // otherwise this is a partial zone - for (unsigned int n = 1; n < parts.size(); ++n) { - dr.d_content = DNSRecordContent::mastermake(QType::PTR, 1, DNSName(parts[n]).toString()); // XXX FIXME DNSNAME PAIN CAN THIS BE RIGHT? - ad.d_records.insert(dr); - } + for (auto name = parts.cbegin() + 1; name != parts.cend(); ++name) { + dr.d_content = DNSRecordContent::mastermake(QType::PTR, 1, DNSName(*name).toString()); + ad.d_records.insert(dr); } - if (newMap->count(dr.d_name) != 0) { - SLOG(g_log << Logger::Warning << "Will not overwrite zone '" << dr.d_name << "' already loaded" << endl, - log->info(Logr::Warning, "Will not overwrite already loaded zone", "zone", Logging::Loggable(dr.d_name))); - } - else { - if (ipParts.size() == 4) { - SLOG(g_log << Logger::Warning << "Inserting reverse zone '" << dr.d_name << "' based on hosts file" << endl, - log->info(Logr::Notice, "Inserting reverse zone based on hosts file", "zone", Logging::Loggable(dr.d_name))); - } - ad.d_name = dr.d_name; - (*newMap)[ad.d_name] = ad; + addToDomainMap(newMap, ad, dr.d_name, log, false, true); +} + +void makePartialIPZone(SyncRes::domainmap_t& newMap, + std::initializer_list labels, + Logr::log_t log) +{ + DNSRecord dr; + for (auto label = std::rbegin(labels); label != std::rend(labels); ++label) { + dr.d_name.appendRawLabel(*label); } + dr.d_name.appendRawLabel("in-addr"); + dr.d_name.appendRawLabel("arpa"); + + SyncRes::AuthDomain ad = makeSOAAndNSNodes(dr, DNSName("localhost.")); + + addToDomainMap(newMap, ad, dr.d_name, log, true, true); } -void addForwardAndReverseLookupEntries(const std::shared_ptr& newMap, +void addForwardAndReverseLookupEntries(SyncRes::domainmap_t& newMap, const std::string& searchSuffix, const std::vector& parts, Logr::log_t log) { - for (unsigned int n = 1; n < parts.size(); ++n) { - if (searchSuffix.empty() || parts[n].find('.') != string::npos) { - makeNameToIPZone(newMap, DNSName(parts[n]), parts[0], log); + const ComboAddress address{parts[0]}; + + // Go over the hostname and aliases (parts[1], parts[2], etc...) and add entries + // for forward lookups. + for (auto name = parts.cbegin() + 1; name != parts.cend(); ++name) { + if (searchSuffix.empty() || name->find('.') != string::npos) { + makeNameToIPZone(newMap, DNSName(*name), address, log); } else { - DNSName canonic = toCanonic(DNSName(searchSuffix), parts[n]); /// XXXX DNSName pain - if (canonic != DNSName(parts[n])) { // XXX further DNSName pain - makeNameToIPZone(newMap, canonic, parts[0], log); + DNSName canonical = toCanonic(DNSName(searchSuffix), *name); + if (canonical != DNSName(*name)) { + makeNameToIPZone(newMap, canonical, address, log); } } } - makeIPToNamesZone(newMap, parts, log); + + // Add entries for reverse lookups. + makeIPToNamesZone(newMap, address, parts, log); } bool parseEtcHostsLine(std::vector& parts, std::string& line) @@ -150,8 +164,5 @@ bool parseEtcHostsLine(std::vector& parts, std::string& line) } parts.clear(); stringtok(parts, line, "\t\r\n "); - if (parts[0].find(':') != string::npos) { - return false; - } return parts.size() >= 2; } diff --git a/pdns/reczones-helpers.hh b/pdns/reczones-helpers.hh index f6900e7cc9..e68fed2ec0 100644 --- a/pdns/reczones-helpers.hh +++ b/pdns/reczones-helpers.hh @@ -39,7 +39,11 @@ void makeIPToNamesZone(const std::shared_ptr& newMap, //! A return value `false` means that the line cannot be parsed (e.g. unsupported IPv6). bool parseEtcHostsLine(std::vector& parts, std::string& line); -void addForwardAndReverseLookupEntries(const std::shared_ptr& newMap, +void makePartialIPZone(SyncRes::domainmap_t& newMap, + std::initializer_list labels, + Logr::log_t log); + +void addForwardAndReverseLookupEntries(SyncRes::domainmap_t& newMap, const std::string& searchSuffix, const std::vector& parts, Logr::log_t log); diff --git a/pdns/reczones.cc b/pdns/reczones.cc index 141c308b74..a4b24972cc 100644 --- a/pdns/reczones.cc +++ b/pdns/reczones.cc @@ -465,22 +465,34 @@ std::tuple, std::shared_ptr> } if (::arg().mustDo("export-etc-hosts")) { - string line; string fname = ::arg()["etc-hosts-file"]; - ifstream ifs(fname.c_str()); if (!ifs) { SLOG(g_log << Logger::Warning << "Could not open " << fname << " for reading" << endl, log->error(Logr::Warning, "Could not open file for reading", "file", Logging::Loggable(fname))); } else { + std::string line{}; while (getline(ifs, line)) { if (!parseEtcHostsLine(parts, line)) { continue; } - string searchSuffix = ::arg()["export-etc-hosts-search-suffix"]; - addForwardAndReverseLookupEntries(newMap, searchSuffix, parts, log); + try { + string searchSuffix = ::arg()["export-etc-hosts-search-suffix"]; + addForwardAndReverseLookupEntries(*newMap, searchSuffix, parts, log); + } + catch (const PDNSException& ex) { + SLOG(g_log << Logger::Warning + << "The line `" << line << "` " + << "in the provided etc-hosts file `" << fname << "` " + << "could not be added: " << ex.reason << ". Going to skip it." + << endl, + log->info(Logr::Notice, "Skipping line in etc-hosts file", + "line", Logging::Loggable(line), + "hosts-file", Logging::Loggable(fname), + "reason", Logging::Loggable(ex.reason))); + } } } } @@ -488,17 +500,13 @@ std::tuple, std::shared_ptr> if (::arg().mustDo("serve-rfc1918")) { SLOG(g_log << Logger::Warning << "Inserting rfc 1918 private space zones" << endl, log->info(Logr::Notice, "Inserting rfc 1918 private space zones")); - parts.clear(); - parts.push_back("127"); - makeIPToNamesZone(newMap, parts, log); - parts[0] = "10"; - makeIPToNamesZone(newMap, parts, log); - parts[0] = "192.168"; - makeIPToNamesZone(newMap, parts, log); + makePartialIPZone(*newMap, {"127"}, log); + makePartialIPZone(*newMap, {"10"}, log); + makePartialIPZone(*newMap, {"192", "168"}, log); + for (int n = 16; n < 32; n++) { - parts[0] = "172." + std::to_string(n); - makeIPToNamesZone(newMap, parts, log); + makePartialIPZone(*newMap, {"172", std::to_string(n).c_str()}, log); } }