This also adds tests for loading entries from an etc-hosts-like file.
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 \
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 \
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 \
--- /dev/null
+../reczones-helpers.cc
\ No newline at end of file
--- /dev/null
+../reczones-helpers.hh
\ No newline at end of file
--- /dev/null
+#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()
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
#include "config.h"
#endif
+#include "reczones-helpers.hh"
#include "syncres.hh"
#include "arguments.hh"
#include "zoneparser-tng.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
-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;
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);
}
}
}