]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Move domain-map related helper functions for testability and add tests
authorFred Morcos <fred.morcos@open-xchange.com>
Tue, 31 May 2022 11:37:30 +0000 (13:37 +0200)
committerFred Morcos <fred.morcos@open-xchange.com>
Mon, 13 Jun 2022 12:24:24 +0000 (14:24 +0200)
This also adds tests for loading entries from an etc-hosts-like file.

pdns/recursordist/Makefile.am
pdns/recursordist/reczones-helpers.cc [new symlink]
pdns/recursordist/reczones-helpers.hh [new symlink]
pdns/recursordist/test-reczones-helpers.cc [new file with mode: 0644]
pdns/reczones-helpers.cc [new file with mode: 0644]
pdns/reczones-helpers.hh [new file with mode: 0644]
pdns/reczones.cc

index 743a17b991d7bed06eacda748920190c9a94402d..df74be989aa5d0ded9055e8918e944125bf9d6ad 100644 (file)
@@ -175,6 +175,7 @@ pdns_recursor_SOURCES = \
        rec_channel_rec.cc \
        recpacketcache.cc recpacketcache.hh \
        recursor_cache.cc recursor_cache.hh \
+       reczones-helpers.cc reczones-helpers.hh \
        reczones.cc \
        remote_logger.cc remote_logger.hh \
        resolve-context.hh \
@@ -290,6 +291,7 @@ testrunner_SOURCES = \
        rec-zonetocache.cc rec-zonetocache.hh \
        recpacketcache.cc recpacketcache.hh \
        recursor_cache.cc recursor_cache.hh \
+       reczones-helpers.cc reczones-helpers.hh \
        resolver.hh resolver.cc \
        responsestats.cc \
        root-dnssec.hh \
@@ -329,6 +331,7 @@ testrunner_SOURCES = \
        test-rec-zonetocache.cc \
        test-recpacketcache_cc.cc \
        test-recursorcache_cc.cc \
+       test-reczones-helpers.cc \
        test-rpzloader_cc.cc \
        test-secpoll_cc.cc \
        test-signers.cc \
diff --git a/pdns/recursordist/reczones-helpers.cc b/pdns/recursordist/reczones-helpers.cc
new file mode 120000 (symlink)
index 0000000..b77778d
--- /dev/null
@@ -0,0 +1 @@
+../reczones-helpers.cc
\ No newline at end of file
diff --git a/pdns/recursordist/reczones-helpers.hh b/pdns/recursordist/reczones-helpers.hh
new file mode 120000 (symlink)
index 0000000..6851251
--- /dev/null
@@ -0,0 +1 @@
+../reczones-helpers.hh
\ No newline at end of file
diff --git a/pdns/recursordist/test-reczones-helpers.cc b/pdns/recursordist/test-reczones-helpers.cc
new file mode 100644 (file)
index 0000000..9d14258
--- /dev/null
@@ -0,0 +1,163 @@
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+
+#include <stdio.h>
+
+#include "test-syncres_cc.hh"
+#include "reczones-helpers.hh"
+
+BOOST_AUTO_TEST_SUITE(reczones_helpers)
+
+static const std::array<std::string, 5> 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",
+};
+
+struct Fixture
+{
+  static std::shared_ptr<DNSRecordContent> makeLocalhostRootDRC()
+  {
+    return DNSRecordContent::mastermake(QType::SOA, QClass::IN, "localhost. root 1 604800 86400 2419200 604800");
+  }
+
+  static std::shared_ptr<DNSRecordContent> makeLocalhostDRC()
+  {
+    return DNSRecordContent::mastermake(QType::NS, QClass::IN, "localhost.");
+  }
+
+  static std::shared_ptr<DNSRecordContent> makePtrDRC(const std::string& name)
+  {
+    return DNSRecordContent::mastermake(QType::PTR, QClass::IN, name);
+  }
+
+  void addDomainMapFixtureEntry(const std::string& name, const SyncRes::AuthDomain::records_t& records)
+  {
+    domainMapFixture[DNSName{name}] = SyncRes::AuthDomain{
+      .d_records = records,
+      .d_servers = {},
+      .d_name = DNSName{name},
+      .d_rdForward = false,
+    };
+  }
+
+  void addDomainMapFixtureEntry(const std::string& name, const QType type, const std::string& address)
+  {
+    domainMapFixture[DNSName{name}] = SyncRes::AuthDomain{
+      .d_records = {
+        DNSRecord(name, DNSRecordContent::mastermake(type, QClass::IN, address), type),
+        DNSRecord(name, makeLocalhostDRC(), QType::NS),
+        DNSRecord(name, makeLocalhostRootDRC(), QType::SOA),
+      },
+      .d_servers = {},
+      .d_name = DNSName{name},
+      .d_rdForward = false,
+    };
+  }
+
+  Fixture()
+  {
+    addDomainMapFixtureEntry("foo", QType::A, "192.168.0.1");
+    addDomainMapFixtureEntry("bar", QType::A, "192.168.0.1");
+    addDomainMapFixtureEntry("dupfoo", QType::A, "192.168.0.1");
+    addDomainMapFixtureEntry(
+      "1.0.168.192.in-addr.arpa",
+      {
+        DNSRecord("1.0.168.192.in-addr.arpa", makeLocalhostDRC(), QType::NS),
+        DNSRecord("1.0.168.192.in-addr.arpa", makeLocalhostRootDRC(), QType::SOA),
+        DNSRecord("1.0.168.192.in-addr.arpa", makePtrDRC("foo."), QType::PTR),
+        DNSRecord("1.0.168.192.in-addr.arpa", makePtrDRC("bar."), QType::PTR),
+      });
+    addDomainMapFixtureEntry("baz", QType::A, "192.168.0.2");
+    addDomainMapFixtureEntry(
+      "2.0.168.192.in-addr.arpa",
+      {
+        DNSRecord("2.0.168.192.in-addr.arpa", makeLocalhostDRC(), QType::NS),
+        DNSRecord("2.0.168.192.in-addr.arpa", makeLocalhostRootDRC(), QType::SOA),
+        DNSRecord("2.0.168.192.in-addr.arpa", makePtrDRC("baz."), QType::PTR),
+      });
+    addDomainMapFixtureEntry("fancy", QType::A, "1.1.1.1");
+    addDomainMapFixtureEntry(
+      "1.1.1.1.in-addr.arpa",
+      {
+        DNSRecord("1.1.1.1.in-addr.arpa", makeLocalhostDRC(), QType::NS),
+        DNSRecord("1.1.1.1.in-addr.arpa", makeLocalhostRootDRC(), QType::SOA),
+        DNSRecord("1.1.1.1.in-addr.arpa", makePtrDRC("fancy."), QType::PTR),
+      });
+    addDomainMapFixtureEntry("more.fancy", QType::A, "2.2.2.2");
+    addDomainMapFixtureEntry(
+      "2.2.2.2.in-addr.arpa",
+      {
+        DNSRecord("2.2.2.2.in-addr.arpa", makeLocalhostDRC(), QType::NS),
+        DNSRecord("2.2.2.2.in-addr.arpa", makeLocalhostRootDRC(), QType::SOA),
+        DNSRecord("2.2.2.2.in-addr.arpa", makePtrDRC("more.fancy."), QType::PTR),
+      });
+  }
+
+  using DomainMapEntry = std::pair<DNSName, SyncRes::AuthDomain>;
+
+  static std::vector<DomainMapEntry> sortDomainMap(const SyncRes::domainmap_t& domainMap)
+  {
+    std::vector<DomainMapEntry> sorted{};
+    sorted.reserve(domainMap.size());
+    for (const auto& pair : domainMap) {
+      sorted.emplace_back(pair.first, pair.second);
+    }
+    std::stable_sort(std::begin(sorted), std::end(sorted), [](const DomainMapEntry& a, const DomainMapEntry& b) {
+      return a.first < b.first && a.second.d_name < b.second.d_name;
+    });
+    return sorted;
+  }
+
+  static std::string printDomainMap(const std::vector<DomainMapEntry>& domainMap)
+  {
+    std::stringstream s{};
+    for (const auto& entry : domainMap) {
+      s << "Entry `" << entry.first << "` {" << std::endl;
+      s << entry.second.print("  ");
+      s << "}" << std::endl;
+    }
+    return s.str();
+  }
+
+  std::vector<DomainMapEntry> getDomainMapFixture() const
+  {
+    return sortDomainMap(domainMapFixture);
+  }
+
+private:
+  SyncRes::domainmap_t domainMapFixture{};
+};
+
+BOOST_FIXTURE_TEST_CASE(test_loading_etc_hosts, Fixture)
+{
+  auto log = g_slog->withName("config");
+
+  auto domainMap = std::make_shared<SyncRes::domainmap_t>();
+  std::vector<std::string> parts{};
+  for (auto line : hostLines) {
+    BOOST_REQUIRE(parseEtcHostsLine(parts, line));
+    addForwardAndReverseLookupEntries(domainMap, "", parts, log);
+  }
+
+  auto actual = sortDomainMap(*domainMap);
+  BOOST_TEST_MESSAGE("Actual:");
+  BOOST_TEST_MESSAGE(printDomainMap(actual));
+
+  auto expected = getDomainMapFixture();
+  BOOST_TEST_MESSAGE("Expected:");
+  BOOST_TEST_MESSAGE(printDomainMap(expected));
+
+  BOOST_CHECK_EQUAL(actual.size(), expected.size());
+  for (std::vector<DomainMapEntry>::size_type i = 0; i < actual.size(); i++) {
+    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
new file mode 100644 (file)
index 0000000..a96c421
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#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)
+{
+  SyncRes::AuthDomain ad;
+  ad.d_rdForward = false;
+
+  DNSRecord dr;
+  dr.d_name = hostname;
+  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");
+
+  ad.d_records.insert(dr);
+
+  dr.d_type = QType::NS;
+  dr.d_content = std::make_shared<NSRecordContent>("localhost.");
+
+  ad.d_records.insert(dr);
+
+  dr.d_type = QType::A;
+  dr.d_content = DNSRecordContent::mastermake(QType::A, 1, ip);
+  ad.d_records.insert(dr);
+
+  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)));
+  }
+  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;
+  }
+}
+
+//! 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)
+{
+  string address = parts[0];
+  vector<string> ipParts;
+  stringtok(ipParts, address, ".");
+
+  SyncRes::AuthDomain ad;
+  ad.d_rdForward = false;
+
+  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.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."));
+
+  ad.d_records.insert(dr);
+  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);
+    }
+  }
+
+  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;
+  }
+}
+
+void addForwardAndReverseLookupEntries(const std::shared_ptr<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);
+    }
+    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);
+      }
+    }
+  }
+  makeIPToNamesZone(newMap, parts, log);
+}
+
+bool parseEtcHostsLine(std::vector<std::string>& parts, std::string& line)
+{
+  const string::size_type pos = line.find('#');
+  if (pos != string::npos) {
+    line.resize(pos);
+  }
+  boost::trim(line);
+  if (line.empty()) {
+    return false;
+  }
+  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
new file mode 100644 (file)
index 0000000..f6900e7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string>
+#include <vector>
+#include <memory>
+#include "syncres.hh"
+#include "logger.hh"
+
+void makeIPToNamesZone(const std::shared_ptr<SyncRes::domainmap_t>& newMap,
+                       const vector<string>& parts,
+                       Logr::log_t log);
+
+//! 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,
+                                       const std::string& searchSuffix,
+                                       const std::vector<std::string>& parts,
+                                       Logr::log_t log);
index d81678ffc0ecc807b0107e382783999c33829b06..141c308b74e7140deb818bb5fc9207cf158431fe 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #endif
 
+#include "reczones-helpers.hh"
 #include "syncres.hh"
 #include "arguments.hh"
 #include "zoneparser-tng.hh"
@@ -199,93 +200,6 @@ void primeRootNSZones(DNSSECMode mode, unsigned int depth)
   }
 }
 
-static void makeNameToIPZone(const std::shared_ptr<SyncRes::domainmap_t>& newMap, const DNSName& hostname, const string& ip, Logr::log_t log)
-{
-  SyncRes::AuthDomain ad;
-  ad.d_rdForward = false;
-
-  DNSRecord dr;
-  dr.d_name = hostname;
-  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");
-
-  ad.d_records.insert(dr);
-
-  dr.d_type = QType::NS;
-  dr.d_content = std::make_shared<NSRecordContent>("localhost.");
-
-  ad.d_records.insert(dr);
-
-  dr.d_type = QType::A;
-  dr.d_content = DNSRecordContent::mastermake(QType::A, 1, ip);
-  ad.d_records.insert(dr);
-
-  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)));
-  }
-  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;
-  }
-}
-
-//! parts[0] must be an IP address, the rest must be host names
-static void makeIPToNamesZone(const std::shared_ptr<SyncRes::domainmap_t>& newMap, const vector<string>& parts, Logr::log_t log)
-{
-  string address = parts[0];
-  vector<string> ipParts;
-  stringtok(ipParts, address, ".");
-
-  SyncRes::AuthDomain ad;
-  ad.d_rdForward = false;
-
-  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.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."));
-
-  ad.d_records.insert(dr);
-  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);
-    }
-  }
-
-  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;
-  }
-}
-
 static void convertServersForAD(const std::string& zone, const std::string& input, SyncRes::AuthDomain& ad, const char* sepa, Logr::log_t log, bool verbose = true)
 {
   vector<string> servers;
@@ -560,34 +474,13 @@ std::tuple<std::shared_ptr<SyncRes::domainmap_t>, std::shared_ptr<notifyset_t>>
            log->error(Logr::Warning, "Could not open file for reading", "file", Logging::Loggable(fname)));
     }
     else {
-      string searchSuffix = ::arg()["export-etc-hosts-search-suffix"];
-      string::size_type pos = 0;
       while (getline(ifs, line)) {
-        pos = line.find('#');
-        if (pos != string::npos) {
-          line.resize(pos);
-        }
-        boost::trim(line);
-        if (line.empty()) {
+        if (!parseEtcHostsLine(parts, line)) {
           continue;
         }
-        parts.clear();
-        stringtok(parts, line, "\t\r\n ");
-        if (parts[0].find(':') != string::npos)
-          continue;
 
-        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);
-          }
-          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);
-            }
-          }
-        }
-        makeIPToNamesZone(newMap, parts, log);
+        string searchSuffix = ::arg()["export-etc-hosts-search-suffix"];
+        addForwardAndReverseLookupEntries(newMap, searchSuffix, parts, log);
       }
     }
   }