]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Cleanup the auth and forward zones a bit 5236/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 11 Apr 2017 12:36:39 +0000 (14:36 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Sun, 14 May 2017 12:30:55 +0000 (14:30 +0200)
pdns/pdns_recursor.cc
pdns/recursordist/test-syncres_cc.cc
pdns/reczones.cc
pdns/syncres.cc
pdns/syncres.hh

index 90d30ea35e758f6bf06adf19c341f5ce91547e29..26908b4cb670693a176849edcf1f2bfe7735d17d 100644 (file)
@@ -105,7 +105,7 @@ static thread_local std::unique_ptr<tcpClientCounts_t> t_tcpClientCounts;
 thread_local std::unique_ptr<MT_t> MT; // the big MTasker
 thread_local std::unique_ptr<MemRecursorCache> t_RC;
 thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
-thread_local FDMultiplexer* t_fdm;
+thread_local FDMultiplexer* t_fdm{nullptr};
 thread_local std::unique_ptr<addrringbuf_t> t_remotes, t_servfailremotes, t_largeanswerremotes;
 thread_local std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > > t_queryring, t_servfailqueryring;
 thread_local std::shared_ptr<NetmaskGroup> t_allowFrom;
@@ -2043,9 +2043,9 @@ static void doStats(void)
 
 static void houseKeeping(void *)
 {
-  static __thread time_t last_stat, last_rootupdate, last_prune, last_secpoll;
-  static __thread int cleanCounter=0;
-  static __thread bool s_running;  // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
+  static thread_local time_t last_stat, last_rootupdate, last_prune, last_secpoll;
+  static thread_local int cleanCounter=0;
+  static thread_local bool s_running;  // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
   try {
     if(s_running)
       return;
index d0d51ded547431c11512a75bbd17cf69fd558b02..e2338783b243aa5367b5a648a351f4f2ada204d0 100644 (file)
@@ -414,6 +414,40 @@ BOOST_AUTO_TEST_CASE(test_tc_fallback_to_tcp) {
   BOOST_CHECK_EQUAL(res, 0);
 }
 
+BOOST_AUTO_TEST_CASE(test_tc_over_tcp) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  size_t tcpQueriesCount = 0;
+
+  sr->setAsyncCallback([&tcpQueriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      if (!doTCP) {
+        setLWResult(res, 0, true, true, false);
+        return 1;
+      }
+
+      /* first TCP query is answered with a TC response */
+      tcpQueriesCount++;
+      if (tcpQueriesCount == 1) {
+        setLWResult(res, 0, true, true, false);
+      }
+      else {
+        setLWResult(res, 0, true, false, false);
+      }
+
+      addRecordToLW(res, domain, QType::A, "192.0.2.1");
+      return 1;
+    });
+
+  primeHints();
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(tcpQueriesCount, 2);
+}
+
 BOOST_AUTO_TEST_CASE(test_all_nss_down) {
   std::unique_ptr<SyncRes> sr;
   init();
@@ -1562,6 +1596,42 @@ BOOST_AUTO_TEST_CASE(test_flawed_nsset) {
   BOOST_CHECK_EQUAL(ret.size(), 1);
 }
 
+BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  const DNSName target("powerdns.com.");
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([&queriesCount,target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      if (isRootServer(ip) && domain == target) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        return 1;
+      } else if (domain == DNSName("pdns-public-ns2.powerdns.com.") || domain == DNSName("pdns-public-ns3.powerdns.com.")){
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::ServFail);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+  /* one query to get NSs, then A and AAAA for each NS */
+  BOOST_CHECK_EQUAL(queriesCount, 5);
+}
+
 BOOST_AUTO_TEST_CASE(test_cache_hit) {
   std::unique_ptr<SyncRes> sr;
   init();
@@ -2482,6 +2552,529 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_oob) {
   BOOST_CHECK(sr->wasOutOfBand());
 }
 
+BOOST_AUTO_TEST_CASE(test_auth_zone) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("powerdns.com.");
+  const ComboAddress addr("192.0.2.5");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = target;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(addr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[target] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+      setLWResult(res, 0, true, false, true);
+      addRecordToLW(res, domain, QType::A, "192.0.2.42");
+      return 1;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::A);
+  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), addr.toString());
+  BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_cname_lead_to_oob) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("powerdns.com.");
+  const DNSName authZone("internal.powerdns.com.");
+  const ComboAddress addr("192.0.2.5");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(addr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount,target,authZone](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      if (domain == target) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, target, QType::CNAME, authZone.toString(), DNSResourceRecord::ANSWER, 3600);
+        return 1;
+      }
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 2);
+  BOOST_CHECK(ret[0].d_type == QType::CNAME);
+  BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget().toString(), authZone.toString());
+  BOOST_CHECK(ret[1].d_type == QType::A);
+  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[1])->getCA().toString(), addr.toString());
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_oob_lead_to_outgoing_queryb) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("powerdns.com.");
+  const DNSName externalCNAME("www.open-xchange.com.");
+  const ComboAddress addr("192.0.2.5");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = target;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::CNAME;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<CNAMERecordContent>(externalCNAME);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[target] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount,externalCNAME,addr](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      if (domain == externalCNAME) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, externalCNAME, QType::A, addr.toString(), DNSResourceRecord::ANSWER, 3600);
+        return 1;
+      }
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 2);
+  BOOST_CHECK(ret[0].d_type == QType::CNAME);
+  BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget().toString(), externalCNAME.toString());
+  BOOST_CHECK(ret[1].d_type == QType::A);
+  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[1])->getCA().toString(), addr.toString());
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_nodata) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("nodata.powerdns.com.");
+  const DNSName authZone("powerdns.com");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(ComboAddress("192.0.2.1"));
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::SOA);
+  BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_nx) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("nx.powerdns.com.");
+  const DNSName authZone("powerdns.com");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = DNSName("powerdns.com.");
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::SOA);
+  BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_delegation) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("www.test.powerdns.com.");
+  const ComboAddress targetAddr("192.0.2.2");
+  const DNSName ns("ns1.test.powerdns.com.");
+  const ComboAddress nsAddr("192.0.2.1");
+  const DNSName authZone("powerdns.com");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = DNSName("test.powerdns.com.");
+  dr.d_type = QType::NS;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<NSRecordContent>(ns);
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = ns;
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(nsAddr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount,target,targetAddr,nsAddr](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+      if (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+        return 1;
+      }
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::A);
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_point) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("test.powerdns.com.");
+  const ComboAddress targetAddr("192.0.2.2");
+  const DNSName ns("ns1.test.powerdns.com.");
+  const ComboAddress nsAddr("192.0.2.1");
+  const DNSName authZone("powerdns.com");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = DNSName("test.powerdns.com.");
+  dr.d_type = QType::NS;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<NSRecordContent>(ns);
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = ns;
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(nsAddr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount,nsAddr,target,targetAddr](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      if (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+        return 1;
+      }
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::A);
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("test.powerdns.com.");
+  const ComboAddress targetAddr("192.0.2.2");
+  const DNSName authZone("powerdns.com");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = DNSName("*.powerdns.com.");
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(targetAddr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::A);
+  BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard_nodata) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("test.powerdns.com.");
+  const ComboAddress targetAddr("192.0.2.2");
+  const DNSName authZone("powerdns.com");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = authZone;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = authZone;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = DNSName("*.powerdns.com.");
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(targetAddr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[authZone] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+
+      return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_REQUIRE_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::SOA);
+  BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_cache_only) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  size_t queriesCount = 0;
+  const DNSName target("powerdns.com.");
+  const ComboAddress addr("192.0.2.5");
+
+  SyncRes::AuthDomain ad;
+  ad.d_name = target;
+  DNSRecord dr;
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::SOA;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+  ad.d_records.insert(dr);
+
+  dr.d_place = DNSResourceRecord::ANSWER;
+  dr.d_name = target;
+  dr.d_type = QType::A;
+  dr.d_ttl = 3600;
+  dr.d_content = std::make_shared<ARecordContent>(addr);
+  ad.d_records.insert(dr);
+
+  auto map = std::make_shared<SyncRes::domainmap_t>();
+  (*map)[target] = ad;
+  SyncRes::setDomainMap(map);
+
+  sr->setAsyncCallback([&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+      queriesCount++;
+      setLWResult(res, 0, true, false, true);
+      addRecordToLW(res, domain, QType::A, "192.0.2.42");
+      return 1;
+  });
+
+  /* simulate a no-RD query */
+  sr->setCacheOnly();
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  BOOST_CHECK(ret[0].d_type == QType::A);
+  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), addr.toString());
+  BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
 /*
 // cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
 
index cd7ac7996d7aa2032fb282e1c980b41199e4d7c4..772decded2892066dcb2b94c1f6846f7bdfc2099 100644 (file)
@@ -126,7 +126,8 @@ static void makeNameToIPZone(std::shared_ptr<SyncRes::domainmap_t> newMap, const
   }
   else {
     L<<Logger::Warning<<"Inserting forward zone '"<<dr.d_name<<"' based on hosts file"<<endl;
-    (*newMap)[dr.d_name]=ad;
+    ad.d_name=dr.d_name;
+    (*newMap)[ad.d_name]=ad;
   }
 }
 
@@ -172,7 +173,8 @@ static void makeIPToNamesZone(std::shared_ptr<SyncRes::domainmap_t> newMap, cons
   else {
     if(ipparts.size()==4)
       L<<Logger::Warning<<"Inserting reverse zone '"<<dr.d_name<<"' based on hosts file"<<endl;
-    (*newMap)[dr.d_name]=ad;
+    ad.d_name = dr.d_name;
+    (*newMap)[ad.d_name]=ad;
   }
 }
 
@@ -454,8 +456,9 @@ std::shared_ptr<SyncRes::domainmap_t> parseAuthAndForwards()
           ad.d_rdForward = true;
         }
       }
-      
-      (*newMap)[DNSName(headers.first)]=ad; 
+
+      ad.d_name = DNSName(headers.first);
+      (*newMap)[ad.d_name]=ad;
     }
   }
   
@@ -502,7 +505,8 @@ std::shared_ptr<SyncRes::domainmap_t> parseAuthAndForwards()
         throw PDNSException("Conversion error parsing line "+std::to_string(linenum)+" of " +::arg()["forward-zones-file"]);
       }
 
-      (*newMap)[DNSName(domain)]=ad;
+      ad.d_name = DNSName(domain);
+      (*newMap)[ad.d_name]=ad;
     }
     L<<Logger::Warning<<"Done parsing " << newMap->size() - before<<" forwarding instructions from file '"<<::arg()["forward-zones-file"]<<"'"<<endl;
   }
index e43c91525bd99f8247fc8f57bbd69c781c3a61ca..4652fb1754f885f3032f6abd9e4728e85c42ebf2 100644 (file)
 
 thread_local SyncRes::ThreadLocalStorage SyncRes::t_sstorage;
 
+std::unordered_set<DNSName> SyncRes::s_delegationOnly;
+std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
+NetmaskGroup SyncRes::s_ednssubnets;
+SuffixMatchNode SyncRes::s_ednsdomains;
+string SyncRes::s_serverID;
+SyncRes::LogMode SyncRes::s_lm;
+
 unsigned int SyncRes::s_maxnegttl;
 unsigned int SyncRes::s_maxcachettl;
+unsigned int SyncRes::s_maxqperq;
+unsigned int SyncRes::s_maxtotusec;
+unsigned int SyncRes::s_maxdepth;
+unsigned int SyncRes::s_minimumTTL;
 unsigned int SyncRes::s_packetcachettl;
 unsigned int SyncRes::s_packetcacheservfailttl;
 unsigned int SyncRes::s_serverdownmaxfails;
@@ -54,24 +65,13 @@ std::atomic<uint64_t> SyncRes::s_nodelegated;
 std::atomic<uint64_t> SyncRes::s_unreachables;
 uint8_t SyncRes::s_ecsipv4limit;
 uint8_t SyncRes::s_ecsipv6limit;
-unsigned int SyncRes::s_minimumTTL;
 bool SyncRes::s_doIPv6;
 bool SyncRes::s_nopacketcache;
 bool SyncRes::s_rootNXTrust;
-unsigned int SyncRes::s_maxqperq;
-unsigned int SyncRes::s_maxtotusec;
-unsigned int SyncRes::s_maxdepth;
-string SyncRes::s_serverID;
-SyncRes::LogMode SyncRes::s_lm;
-std::unordered_set<DNSName> SyncRes::s_delegationOnly;
-std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
-NetmaskGroup SyncRes::s_ednssubnets;
-SuffixMatchNode SyncRes::s_ednsdomains;
+bool SyncRes::s_noEDNS;
 
 #define LOG(x) if(d_lm == Log) { L <<Logger::Warning << x; } else if(d_lm == Store) { d_trace << x; }
 
-bool SyncRes::s_noEDNS;
-
 static void accountAuthLatency(int usec, int family)
 {
   if(family == AF_INET) {
@@ -200,113 +200,133 @@ bool SyncRes::doSpecialNamesResolve(const DNSName &qname, const QType &qtype, co
 
 
 //! This is the 'out of band resolver', in other words, the authoritative server
-bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int& res)
+void SyncRes::AuthDomain::addSOA(std::vector<DNSRecord>& records) const
 {
-  string prefix;
-  if(doLog()) {
-    prefix=d_prefix;
-    prefix.append(depth, ' ');
+  SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(boost::make_tuple(getName(), QType::SOA));
+  if (ziter != d_records.end()) {
+    DNSRecord dr = *ziter;
+    dr.d_place = DNSResourceRecord::AUTHORITY;
+    records.push_back(dr);
+  }
+  else {
+    // cerr<<qname<<": can't find SOA record '"<<getName()<<"' in our zone!"<<endl;
   }
+}
 
-  LOG(prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl);
-  DNSName authdomain(qname);
+int SyncRes::AuthDomain::getRecords(const DNSName& qname, uint16_t qtype, std::vector<DNSRecord>& records) const
+{
+  int result = RCode::NoError;
+  records.clear();
 
-  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
-  if(iter==t_sstorage.domainmap->end()) {
-    LOG(prefix<<qname<<": auth storage has no zone for this query!"<<endl);
-    return false;
-  }
-  LOG(prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl);
-  pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range;
-
-  range=iter->second.d_records.equal_range(tie(qname)); // partial lookup
-
-  ret.clear();
-  AuthDomain::records_t::const_iterator ziter;
-  bool somedata=false;
-  for(ziter=range.first; ziter!=range.second; ++ziter) {
-    somedata=true;
-    if(qtype.getCode()==QType::ANY || ziter->d_type==qtype.getCode() || ziter->d_type==QType::CNAME)  // let rest of nameserver do the legwork on this one
-      ret.push_back(*ziter);
-    else if(ziter->d_type == QType::NS && ziter->d_name.countLabels() > authdomain.countLabels()) { // we hit a delegation point!
-      DNSRecord dr=*ziter;
+  // partial lookup
+  std::pair<records_t::const_iterator,records_t::const_iterator> range = d_records.equal_range(tie(qname));
+
+  SyncRes::AuthDomain::records_t::const_iterator ziter;
+  bool somedata = false;
+
+  for(ziter = range.first; ziter != range.second; ++ziter) {
+    somedata = true;
+
+    if(qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) {
+      // let rest of nameserver do the legwork on this one
+      records.push_back(*ziter);
+    }
+    else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) {
+      // we hit a delegation point!
+      DNSRecord dr = *ziter;
       dr.d_place=DNSResourceRecord::AUTHORITY;
-      ret.push_back(dr);
+      records.push_back(dr);
     }
   }
-  if(!ret.empty()) {
-    LOG(prefix<<qname<<": exact match in zone '"<<authdomain<<"'"<<endl);
-    res=0;
-    return true;
+
+  if (!records.empty()) {
+    /* We have found an exact match, we're done */
+    // cerr<<qname<<": exact match in zone '"<<getName()<<"'"<<endl;
+    return result;
   }
-  if(somedata) {
-    LOG(prefix<<qname<<": found record in '"<<authdomain<<"', but nothing of the right type, sending SOA"<<endl);
-    ziter=iter->second.d_records.find(boost::make_tuple(authdomain, QType::SOA));
-    if(ziter!=iter->second.d_records.end()) {
-      DNSRecord dr=*ziter;
-      dr.d_place=DNSResourceRecord::AUTHORITY;
-      ret.push_back(dr);
-    }
-    else
-      LOG(prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl);
-    res=RCode::NoError;
-    return true;
+
+  if (somedata) {
+    /* We have records for that name, but not of the wanted qtype */
+    // cerr<<qname<<": found record in '"<<getName()<<"', but nothing of the right type, sending SOA"<<endl;
+    addSOA(records);
+
+    return result;
   }
 
-  LOG(prefix<<qname<<": nothing found so far in '"<<authdomain<<"', trying wildcards"<<endl);
+  // cerr<<qname<<": nothing found so far in '"<<getName()<<"', trying wildcards"<<endl;
   DNSName wcarddomain(qname);
-  while(wcarddomain != iter->first && wcarddomain.chopOff()) {
-    LOG(prefix<<qname<<": trying '*."<<wcarddomain<<"' in "<<authdomain<<endl);
-    range=iter->second.d_records.equal_range(boost::make_tuple(g_wildcarddnsname+wcarddomain));
-    if(range.first==range.second)
+  while(wcarddomain != getName() && wcarddomain.chopOff()) {
+    // cerr<<qname<<": trying '*."<<wcarddomain<<"' in "<<getName()<<endl;
+    range = d_records.equal_range(boost::make_tuple(g_wildcarddnsname + wcarddomain));
+    if (range.first==range.second)
       continue;
 
-    for(ziter=range.first; ziter!=range.second; ++ziter) {
-      DNSRecord dr=*ziter;
+    for(ziter = range.first; ziter != range.second; ++ziter) {
+      DNSRecord dr = *ziter;
       // if we hit a CNAME, just answer that - rest of recursor will do the needful & follow
-      if(dr.d_type == qtype.getCode() || qtype.getCode() == QType::ANY || dr.d_type == QType::CNAME) {
+      if(dr.d_type == qtype || qtype == QType::ANY || dr.d_type == QType::CNAME) {
         dr.d_name = qname;
-        dr.d_place=DNSResourceRecord::ANSWER;
-        ret.push_back(dr);
+        dr.d_place = DNSResourceRecord::ANSWER;
+        records.push_back(dr);
       }
     }
-    LOG(prefix<<qname<<": in '"<<authdomain<<"', had wildcard match on '*."<<wcarddomain<<"'"<<endl);
-    res=RCode::NoError;
-    return true;
+
+    if (records.empty()) {
+      addSOA(records);
+    }
+
+    // cerr<<qname<<": in '"<<getName()<<"', had wildcard match on '*."<<wcarddomain<<"'"<<endl;
+    return result;
   }
 
+  /* Nothing for this name, no wildcard, let's see if there is some NS */
   DNSName nsdomain(qname);
-
-  while(nsdomain.chopOff() && nsdomain != iter->first) {
-    range=iter->second.d_records.equal_range(boost::make_tuple(nsdomain,QType::NS));
-    if(range.first==range.second)
+  while (nsdomain.chopOff() && nsdomain != getName()) {
+    range = d_records.equal_range(boost::make_tuple(nsdomain,QType::NS));
+    if(range.first == range.second)
       continue;
 
-    for(ziter=range.first; ziter!=range.second; ++ziter) {
-      DNSRecord dr=*ziter;
-      dr.d_place=DNSResourceRecord::AUTHORITY;
-      ret.push_back(dr);
+    for(ziter = range.first; ziter != range.second; ++ziter) {
+      DNSRecord dr = *ziter;
+      dr.d_place = DNSResourceRecord::AUTHORITY;
+      records.push_back(dr);
     }
   }
-  if(ret.empty()) {
-    LOG(prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl);
-    ziter=iter->second.d_records.find(boost::make_tuple(authdomain, QType::SOA));
-    if(ziter!=iter->second.d_records.end()) {
-      DNSRecord dr=*ziter;
-      dr.d_place=DNSResourceRecord::AUTHORITY;
-      ret.push_back(dr);
-    }
-    else {
-      LOG(prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl);
-    }
-    res=RCode::NXDomain;
+
+  if(records.empty()) {
+    // cerr<<qname<<": no NS match in zone '"<<getName()<<"' either, handing out SOA"<<endl;
+    addSOA(records);
+    result = RCode::NXDomain;
   }
-  else
-    res=0;
 
+  return result;
+}
+
+bool SyncRes::doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int& res) const
+{
+  res = domain.getRecords(qname, qtype.getCode(), ret);
   return true;
 }
 
+bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int& res)
+{
+  string prefix;
+  if(doLog()) {
+    prefix=d_prefix;
+    prefix.append(depth, ' ');
+  }
+
+  DNSName authdomain(qname);
+  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
+  if(iter==t_sstorage.domainmap->end() || !iter->second.isAuth()) {
+    LOG(prefix<<qname<<": auth storage has no zone for this query!"<<endl);
+    return false;
+  }
+
+  LOG(prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl);
+  return doOOBResolve(iter->second, qname, qtype, ret, res);
+}
+
 void SyncRes::doEDNSDumpAndClose(int fd)
 {
   FILE* fp=fdopen(fd, "w");
@@ -486,13 +506,13 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
       DNSName authname(qname);
       domainmap_t::const_iterator iter=getBestAuthZone(&authname);
       if(iter != t_sstorage.domainmap->end()) {
-        const vector<ComboAddress>& servers = iter->second.d_servers;
-        if(servers.empty()) {
+        if(iter->second.isAuth()) {
           ret.clear();
           d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, res);
           return res;
         }
         else {
+          const vector<ComboAddress>& servers = iter->second.d_servers;
           const ComboAddress remoteIP = servers.front();
           LOG(prefix<<qname<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname<<"'"<<endl);
 
@@ -732,7 +752,7 @@ DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtyp
 
   domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
   if(iter!=t_sstorage.domainmap->end()) {
-    if( iter->second.d_servers.empty() )
+    if( iter->second.isAuth() )
       // this gets picked up in doResolveAt, the empty DNSName, combined with the
       // empty vector means 'we are auth for this zone'
       nsset.insert({DNSName(), {{}, false}});
@@ -740,7 +760,7 @@ DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtyp
       // Again, picked up in doResolveAt. An empty DNSName, combined with a
       // non-empty vector of ComboAddresses means 'this is a forwarded domain'
       // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
-      nsset.insert({DNSName(), {iter->second.d_servers, iter->second.d_rdForward}});
+      nsset.insert({DNSName(), {iter->second.d_servers, iter->second.shouldRecurse() }});
     }
     return authdomain;
   }
index c8a3a9b3c4af629f5b17f21e45bf6d1a1695b2f1..e069eafc13e032785a22d92407c849dd49cded89 100644 (file)
@@ -328,10 +328,9 @@ public:
   typedef map<DNSName, DecayingEwmaCollection> nsspeeds_t;
   typedef map<ComboAddress, EDNSStatus> ednsstatus_t;
 
-  struct AuthDomain
+  class AuthDomain
   {
-    vector<ComboAddress> d_servers;
-    bool d_rdForward;
+  public:
     typedef multi_index_container <
       DNSRecord,
       indexed_by <
@@ -344,7 +343,32 @@ public:
         >
       >
     > records_t;
+
     records_t d_records;
+    vector<ComboAddress> d_servers;
+    DNSName d_name;
+    bool d_rdForward{false};
+
+    int getRecords(const DNSName& qname, uint16_t qtype, std::vector<DNSRecord>& records) const;
+    bool isAuth() const
+    {
+      return d_servers.empty();
+    }
+    bool isForward() const
+    {
+      return !isAuth();
+    }
+    bool shouldRecurse() const
+    {
+      return d_rdForward;
+    }
+    const DNSName& getName() const
+    {
+      return d_name;
+    }
+
+  private:
+    void addSOA(std::vector<DNSRecord>& records) const;
   };
 
   typedef map<DNSName, AuthDomain> domainmap_t;
@@ -676,6 +700,7 @@ private:
   bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional<Netmask> ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode);
 
   int doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere);
+  bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int& res) const;
   bool doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int &res);
   domainmap_t::const_iterator getBestAuthZone(DNSName* qname) const;
   bool doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int &res);