]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Convert the handling of addresses from etc-hosts file to ComboAddress
authorFred Morcos <fred.morcos@open-xchange.com>
Mon, 23 May 2022 13:06:43 +0000 (15:06 +0200)
committerFred Morcos <fred.morcos@open-xchange.com>
Mon, 13 Jun 2022 12:24:24 +0000 (14:24 +0200)
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.

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

index 9d142581dc3858679dd49b17f9d536b4f501eeca..f089c7cb0f341fcc4b112a7934fa94abcd368025 100644 (file)
@@ -8,12 +8,16 @@
 
 BOOST_AUTO_TEST_SUITE(reczones_helpers)
 
-static const std::array<std::string, 5> hostLines = {
+static const std::array<std::string, 9> 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<DNSName, SyncRes::AuthDomain>;
@@ -139,7 +179,7 @@ BOOST_FIXTURE_TEST_CASE(test_loading_etc_hosts, Fixture)
   std::vector<std::string> 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()
index a96c4213e84584a60709068a5cc7a73f9c9ae7de..c71d2db1ab84ae76bed31976ebde99292a7b2051 100644 (file)
 #include "syncres.hh"
 #include "reczones-helpers.hh"
 
-static void makeNameToIPZone(const std::shared_ptr<SyncRes::domainmap_t>& newMap,
-                             const DNSName& hostname,
-                             const string& ip,
-                             Logr::log_t log)
+template <typename T>
+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<NSRecordContent>("localhost.");
-
+  dr.d_content = std::make_shared<NSRecordContent>(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<SyncRes::domainmap_t>& newMap,
-                       const vector<string>& 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<string> 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<std::string>& 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<NSRecordContent>(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<const char*> 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<SyncRes::domainmap_t>& newMap,
+void addForwardAndReverseLookupEntries(SyncRes::domainmap_t& newMap,
                                        const std::string& searchSuffix,
                                        const std::vector<std::string>& 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<std::string>& parts, std::string& line)
@@ -150,8 +164,5 @@ bool parseEtcHostsLine(std::vector<std::string>& parts, std::string& line)
   }
   parts.clear();
   stringtok(parts, line, "\t\r\n ");
-  if (parts[0].find(':') != string::npos) {
-    return false;
-  }
   return parts.size() >= 2;
 }
index f6900e7cc975deff90c6285b576cd061af5f75f3..e68fed2ec08a8f6ddb09bd8c39a44fe863cbdef3 100644 (file)
@@ -39,7 +39,11 @@ void makeIPToNamesZone(const std::shared_ptr<SyncRes::domainmap_t>& newMap,
 //! A return value `false` means that the line cannot be parsed (e.g. unsupported IPv6).
 bool parseEtcHostsLine(std::vector<std::string>& parts, std::string& line);
 
-void addForwardAndReverseLookupEntries(const std::shared_ptr<SyncRes::domainmap_t>& newMap,
+void makePartialIPZone(SyncRes::domainmap_t& newMap,
+                       std::initializer_list<const char*> labels,
+                       Logr::log_t log);
+
+void addForwardAndReverseLookupEntries(SyncRes::domainmap_t& newMap,
                                        const std::string& searchSuffix,
                                        const std::vector<std::string>& parts,
                                        Logr::log_t log);
index 141c308b74e7140deb818bb5fc9207cf158431fe..a4b24972ccbcef7dba5911f457849850cdf3e5e6 100644 (file)
@@ -465,22 +465,34 @@ std::tuple<std::shared_ptr<SyncRes::domainmap_t>, std::shared_ptr<notifyset_t>>
   }
 
   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<SyncRes::domainmap_t>, std::shared_ptr<notifyset_t>>
   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);
     }
   }