]> granicus.if.org Git - pdns/commitdiff
rec: Add unit tests for `SyncRes`
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 16 Mar 2017 16:13:36 +0000 (17:13 +0100)
committerPieter Lexis <pieter.lexis@powerdns.com>
Tue, 4 Apr 2017 15:11:25 +0000 (17:11 +0200)
pdns/pdns_recursor.cc
pdns/recursordist/Makefile.am
pdns/recursordist/test-syncres_cc.cc [new file with mode: 0644]
pdns/root-addresses.hh
pdns/root-dnssec.hh
pdns/secpoll-recursor.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/validate-recursor.cc

index a29a97186c69241f712e8cc8196c1d828d761a93..01ae81906e1671c481e92537b255dfcb5eaf6b27 100644 (file)
@@ -232,6 +232,11 @@ unsigned int getRecursorThreadId()
   return t_id;
 }
 
+int getMTaskerTID()
+{
+  return MT->getTid();
+}
+
 static void handleTCPClientWritable(int fd, FDMultiplexer::funcparam_t& var);
 
 // -1 is error, 0 is timeout, 1 is success
@@ -675,8 +680,8 @@ static void protobufLogResponse(const std::shared_ptr<RemoteLogger>& logger, con
 static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRes& sr, int& res, vector<DNSRecord>& ret)
 {
   if (spoofed.d_type == QType::CNAME) {
-    bool oldWantsRPZ = sr.d_wantsRPZ;
-    sr.d_wantsRPZ = false;
+    bool oldWantsRPZ = sr.getWantsRPZ();
+    sr.setWantsRPZ(false);
     vector<DNSRecord> ans;
     res = sr.beginResolve(DNSName(spoofed.d_content->getZoneRepresentation()), qtype, 1, ans);
     for (const auto& rec : ans) {
@@ -685,7 +690,7 @@ static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRe
       }
     }
     // Reset the RPZ state of the SyncRes
-    sr.d_wantsRPZ = oldWantsRPZ;
+    sr.setWantsRPZ(oldWantsRPZ);
   }
 }
 
@@ -757,7 +762,7 @@ static void startDoResolve(void *p)
     }
 
     if(g_dnssecmode != DNSSECMode::Off) {
-      sr.d_doDNSSEC=true;
+      sr.setDoDNSSEC(true);
 
       // Does the requestor want DNSSEC records?
       if(edo.d_Z & EDNSOpts::DNSSECOK) {
@@ -769,12 +774,12 @@ static void startDoResolve(void *p)
       pw.getHeader()->cd=0;
     }
 #ifdef HAVE_PROTOBUF
-    sr.d_initialRequestId = dc->d_uuid;
+    sr.setInitialRequestId(dc->d_uuid);
 #endif
     if (g_useIncomingECS) {
-      sr.d_incomingECSFound = dc->d_ecsFound;
+      sr.setIncomingECSFound(dc->d_ecsFound);
       if (dc->d_ecsFound) {
-        sr.d_incomingECS = dc->d_ednssubnet;
+        sr.setIncomingECS(dc->d_ednssubnet);
       }
     }
 
@@ -835,7 +840,7 @@ static void startDoResolve(void *p)
     // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
     if(!t_pdl->get() || !(*t_pdl)->preresolve(dq, res)) {
 
-      sr.d_wantsRPZ = wantsRPZ;
+      sr.setWantsRPZ(wantsRPZ);
       if(wantsRPZ) {
         switch(appliedPolicy.d_kind) {
           case DNSFilterEngine::PolicyKind::NoAction:
@@ -1221,7 +1226,7 @@ static void startDoResolve(void *p)
     }
 
     sr.d_outqueries ? t_RC->cacheMisses++ : t_RC->cacheHits++;
-    float spent=makeFloat(sr.d_now-dc->d_now);
+    float spent=makeFloat(sr.getNow()-dc->d_now);
     if(spent < 0.001)
       g_stats.answers0_1++;
     else if(spent < 0.010)
@@ -2073,7 +2078,7 @@ static void houseKeeping(void *)
     }
 
     if(now.tv_sec - last_rootupdate > 7200) {
-      int res = getRootNS();
+      int res = SyncRes::getRootNS(g_now, nullptr);
       if (!res)
         last_rootupdate=now.tv_sec;
     }
@@ -3300,43 +3305,3 @@ int main(int argc, char **argv)
 
   return ret;
 }
-
-int getRootNS(void) {
-  SyncRes sr(g_now);
-  sr.setDoEDNS0(true);
-  sr.setNoCache();
-  sr.d_doDNSSEC = (g_dnssecmode != DNSSECMode::Off);
-
-  vector<DNSRecord> ret;
-  int res=-1;
-  try {
-    res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret);
-    if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
-      ResolveContext ctx;
-      auto state = validateRecords(ctx, ret);
-      if (state == Bogus)
-        throw PDNSException("Got Bogus validation result for .|NS");
-    }
-    return res;
-  }
-  catch(PDNSException& e)
-  {
-    L<<Logger::Error<<"Failed to update . records, got an exception: "<<e.reason<<endl;
-  }
-
-  catch(std::exception& e)
-  {
-    L<<Logger::Error<<"Failed to update . records, got an exception: "<<e.what()<<endl;
-  }
-
-  catch(...)
-  {
-    L<<Logger::Error<<"Failed to update . records, got an exception"<<endl;
-  }
-  if(!res) {
-    L<<Logger::Notice<<"Refreshed . records"<<endl;
-  }
-  else
-    L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
-  return res;
-}
index eb473e9c423ccc10f697dba14fcb754f4f7dcc15..72a0c59899671f033f88a72e1641be773b168316 100644 (file)
@@ -179,16 +179,18 @@ testrunner_SOURCES = \
        base32.cc \
        base64.cc base64.hh \
        dns.cc dns.hh \
+       dns_random.cc dns_random.hh \
        dnslabeltext.cc \
        dnsname.cc dnsname.hh \
        dnsparser.hh dnsparser.cc \
-       dns_random.cc dns_random.hh \
        dnsrecords.cc \
        dnssecinfra.cc \
        dnswriter.cc dnswriter.hh \
        ednscookies.cc ednscookies.hh \
+       ecs.cc \
        ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
+       filterpo.cc filterpo.hh \
        gettime.cc gettime.hh \
        gss_context.cc gss_context.hh \
        iputils.cc iputils.hh \
@@ -199,13 +201,17 @@ testrunner_SOURCES = \
        pdnsexception.hh \
        protobuf.cc protobuf.hh \
        qtype.cc qtype.hh \
+       randomhelper.cc \
        rcpgenerator.cc \
-       recpacketcache.cc recpacketcache.hh \
        rec-protobuf.cc rec-protobuf.hh \
+       recpacketcache.cc recpacketcache.hh \
+       recursor_cache.cc recursor_cache.hh \
        responsestats.cc \
+       root-dnssec.hh \
        sillyrecords.cc \
        sholder.hh \
        sstuff.hh \
+       syncres.cc syncres.hh \
        test-arguments_cc.cc \
        test-base32_cc.cc \
        test-base64_cc.cc \
@@ -220,9 +226,12 @@ testrunner_SOURCES = \
        test-nmtree.cc \
        test-rcpgenerator_cc.cc \
        test-recpacketcache_cc.cc \
+       test-syncres_cc.cc \
        test-tsig.cc \
        testrunner.cc \
        unix_utility.cc \
+       validate.cc validate.hh \
+       validate-recursor.cc validate-recursor.hh \
        zoneparser-tng.cc zoneparser-tng.hh
 
 testrunner_LDFLAGS = \
diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc
new file mode 100644 (file)
index 0000000..7f531e0
--- /dev/null
@@ -0,0 +1,608 @@
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+#include <boost/test/unit_test.hpp>
+
+#include "arguments.hh"
+#include "lua-recursor4.hh"
+#include "namespaces.hh"
+#include "rec-lua-conf.hh"
+#include "root-dnssec.hh"
+#include "syncres.hh"
+#include "validate-recursor.hh"
+
+std::unordered_set<DNSName> g_delegationOnly;
+RecursorStats g_stats;
+GlobalStateHolder<LuaConfigItems> g_luaconfs;
+NetmaskGroup* g_dontQuery{nullptr};
+__thread MemRecursorCache* t_RC{nullptr};
+SyncRes::domainmap_t* g_initialDomainMap{nullptr};
+unsigned int g_numThreads = 1;
+
+/* Fake some required functions we didn't want the trouble to
+   link with */
+ArgvMap &arg()
+{
+  static ArgvMap theArg;
+  return theArg;
+}
+
+int getMTaskerTID()
+{
+  return 0;
+}
+
+bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret)
+{
+  return false;
+}
+
+int asyncresolve(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)
+{
+  return 0;
+}
+
+/* primeHints() is only here for now because it
+   was way too much trouble to link with the real one.
+   We should fix this, empty functions are one thing, but this is
+   bad.
+*/
+
+#include "root-addresses.hh"
+
+void primeHints(void)
+{
+  vector<DNSRecord> nsset;
+  if(!t_RC)
+    t_RC = new MemRecursorCache();
+
+  DNSRecord arr, aaaarr, nsrr;
+  nsrr.d_name=g_rootdnsname;
+  arr.d_type=QType::A;
+  aaaarr.d_type=QType::AAAA;
+  nsrr.d_type=QType::NS;
+  arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(nullptr)+3600000;
+
+  for(char c='a';c<='m';++c) {
+    static char templ[40];
+    strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1);
+    templ[sizeof(templ)-1] = '\0';
+    *templ=c;
+    aaaarr.d_name=arr.d_name=DNSName(templ);
+    nsrr.d_content=std::make_shared<NSRecordContent>(DNSName(templ));
+    arr.d_content=std::make_shared<ARecordContent>(ComboAddress(rootIps4[c-'a']));
+    vector<DNSRecord> aset;
+    aset.push_back(arr);
+    t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), true); // auth, nuke it all
+    if (rootIps6[c-'a'] != NULL) {
+      aaaarr.d_content=std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c-'a']));
+
+      vector<DNSRecord> aaaaset;
+      aaaaset.push_back(aaaarr);
+      t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), true);
+    }
+
+    nsset.push_back(nsrr);
+  }
+  t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), false); // and stuff in the cache
+}
+
+LuaConfigItems::LuaConfigItems()
+{
+  for (const auto &dsRecord : rootDSs) {
+    auto ds=unique_ptr<DSRecordContent>(dynamic_cast<DSRecordContent*>(DSRecordContent::make(dsRecord)));
+    dsAnchors[g_rootdnsname].insert(*ds);
+  }
+}
+
+/* Some helpers functions */
+
+static void init(bool debug=false)
+{
+  if (debug) {
+    L.setName("test");
+    L.setLoglevel((Logger::Urgency)(6)); // info and up
+    L.disableSyslog(true);
+    L.toConsole(Logger::Info);
+  }
+
+  seedRandom("/dev/urandom");
+
+  if (g_dontQuery)
+    delete g_dontQuery;
+  g_dontQuery = new NetmaskGroup();
+
+  if (t_RC)
+    delete t_RC;
+  t_RC = new MemRecursorCache();
+
+  if (g_initialDomainMap)
+    delete g_initialDomainMap;
+  g_initialDomainMap = new SyncRes::domainmap_t(); // new threads needs this to be setup
+
+  SyncRes::s_maxqperq = 50;
+  SyncRes::s_maxtotusec = 1000*7000;
+  SyncRes::s_maxdepth = 40;
+  SyncRes::s_maxnegttl = 3600;
+  SyncRes::s_maxcachettl = 86400;
+  SyncRes::s_packetcachettl = 3600;
+  SyncRes::s_packetcacheservfailttl = 60;
+  SyncRes::s_serverdownmaxfails = 64;
+  SyncRes::s_serverdownthrottletime = 60;
+  SyncRes::s_doIPv6 = true;
+
+  ::arg().set("ecs-ipv4-bits", "24");
+  ::arg().set("ecs-ipv6-bits", "56");
+}
+
+static void initSR(std::unique_ptr<SyncRes>& sr, bool edns0, bool dnssec)
+{
+  struct timeval now;
+  Utility::gettimeofday(&now, 0);
+  sr = std::unique_ptr<SyncRes>(new SyncRes(now));
+  sr->setDoEDNS0(edns0);
+  sr->setDoDNSSEC(dnssec);
+  t_sstorage->domainmap = g_initialDomainMap;
+  t_sstorage->negcache.clear();
+  t_sstorage->nsSpeeds.clear();
+  t_sstorage->ednsstatus.clear();
+  t_sstorage->throttle.clear();
+  t_sstorage->fails.clear();
+  t_sstorage->dnssecmap.clear();
+}
+
+static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false, bool edns=false)
+{
+  res->d_rcode = rcode;
+  res->d_aabit = aa;
+  res->d_tcbit = tc;
+  res->d_haveEDNS = edns;
+}
+
+static void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60)
+{
+  DNSRecord rec;
+  rec.d_place = place;
+  rec.d_name = name;
+  rec.d_type = type;
+  rec.d_ttl = ttl;
+
+  if (type == QType::NS) {
+    rec.d_content = std::make_shared<NSRecordContent>(DNSName(content));
+  }
+  else if (type == QType::A) {
+    rec.d_content = std::make_shared<ARecordContent>(ComboAddress(content));
+  }
+  else if (QType::AAAA)
+  {
+    rec.d_content = std::make_shared<AAAARecordContent>(ComboAddress(content));
+  }
+  else {
+    rec.d_content = shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(type, QClass::IN, content));
+  }
+
+  res->d_records.push_back(rec);
+}
+
+static void addRecordToLW(LWResult* res, const std::string& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60)
+{
+  addRecordToLW(res, DNSName(name), type, content, place, ttl);
+}
+
+static bool isRootServer(const ComboAddress& ip)
+{
+  for (size_t idx = 0; idx < rootIps4Count; idx++) {
+    if (ip.toString() == rootIps4[idx]) {
+      return true;
+    }
+  }
+
+  for (size_t idx = 0; idx < rootIps6Count; idx++) {
+    if (ip.toString() == rootIps6[idx]) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/* Real tests */
+
+BOOST_AUTO_TEST_SUITE(syncres_cc)
+
+BOOST_AUTO_TEST_CASE(test_root_primed) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  /* we are primed, we should be able to resolve NS . without any query */
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 13);
+}
+
+BOOST_AUTO_TEST_CASE(test_root_not_primed) {
+  std::unique_ptr<SyncRes> sr;
+  init(false);
+  initSR(sr, true, false);
+
+  size_t queriesCount = 0;
+
+  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++;
+
+      if (domain == g_rootdnsname && type == QType::NS) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, g_rootdnsname, QType::NS, "a.root-servers.net.", DNSResourceRecord::ANSWER, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  /* we are not primed yet, so SyncRes will have to call primeHints()
+     then call getRootNS(), for which at least one of the root servers needs to answer */
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  BOOST_CHECK_EQUAL(queriesCount, 2);
+}
+
+BOOST_AUTO_TEST_CASE(test_root_not_primed_and_no_response) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+  std::set<ComboAddress> downServers;
+
+  /* we are not primed yet, so SyncRes will have to call primeHints()
+     then call getRootNS(), for which at least one of the root servers needs to answer.
+     None will, so it should ServFail.
+  */
+  sr->setAsyncCallback([&downServers](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) {
+
+      downServers.insert(ip);
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 2);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+  BOOST_CHECK(downServers.size() > 0);
+  /* we explicitly refuse to mark the root servers down */
+  for (const auto& server : downServers) {
+    BOOST_CHECK_EQUAL(t_sstorage->fails.value(server), 0);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_edns_formerr_fallback) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  ComboAddress noEDNSServer;
+  size_t queriesWithEDNS = 0;
+  size_t queriesWithoutEDNS = 0;
+
+  sr->setAsyncCallback([&queriesWithEDNS, &queriesWithoutEDNS, &noEDNSServer](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 (EDNS0Level != 0) {
+        queriesWithEDNS++;
+        noEDNSServer = ip;
+
+        setLWResult(res, RCode::FormErr);
+        return 1;
+      }
+
+      queriesWithoutEDNS++;
+
+      if (domain == DNSName("powerdns.com") && type == QType::A && !doTCP) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::A, "192.0.2.1");
+        return 1;
+      }
+
+      return 0;
+    });
+
+  primeHints();
+
+  /* fake that the root NS doesn't handle EDNS, check that we fallback */
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  BOOST_CHECK_EQUAL(queriesWithEDNS, 1);
+  BOOST_CHECK_EQUAL(queriesWithoutEDNS, 1);
+  BOOST_CHECK_EQUAL(t_sstorage->ednsstatus.size(), 1);
+  BOOST_CHECK_EQUAL(t_sstorage->ednsstatus[noEDNSServer].mode, SyncRes::EDNSStatus::NOEDNS);
+}
+
+BOOST_AUTO_TEST_CASE(test_edns_notimp_fallback) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  size_t queriesWithEDNS = 0;
+  size_t queriesWithoutEDNS = 0;
+
+  sr->setAsyncCallback([&queriesWithEDNS, &queriesWithoutEDNS](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 (EDNS0Level != 0) {
+        queriesWithEDNS++;
+        setLWResult(res, RCode::NotImp);
+        return 1;
+      }
+
+      queriesWithoutEDNS++;
+
+      if (domain == DNSName("powerdns.com") && type == QType::A && !doTCP) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::A, "192.0.2.1");
+        return 1;
+      }
+
+      return 0;
+    });
+
+  primeHints();
+
+  /* fake that the NS doesn't handle EDNS, check that we fallback */
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  BOOST_CHECK_EQUAL(queriesWithEDNS, 1);
+  BOOST_CHECK_EQUAL(queriesWithoutEDNS, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_tc_fallback_to_tcp) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  sr->setAsyncCallback([](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, false, true, false);
+        return 1;
+      }
+      if (domain == DNSName("powerdns.com") && type == QType::A && doTCP) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::A, "192.0.2.1");
+        return 1;
+      }
+
+      return 0;
+    });
+
+  primeHints();
+
+  /* fake that the NS truncates every request over UDP, we should fallback to TCP */
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_all_nss_down) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+  std::set<ComboAddress> downServers;
+
+  primeHints();
+
+  sr->setAsyncCallback([&downServers](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 (isRootServer(ip)) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
+        return 1;
+      }
+      else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
+        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
+        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
+        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
+        return 1;
+      }
+      else {
+        downServers.insert(ip);
+        return 0;
+      }
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 2);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+  BOOST_CHECK_EQUAL(downServers.size(), 4);
+
+  for (const auto& server : downServers) {
+    BOOST_CHECK_EQUAL(t_sstorage->fails.value(server), 1);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_glued_referral) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  const DNSName target("powerdns.com.");
+
+  sr->setAsyncCallback([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) {
+      /* this will cause issue with qname minimization if we ever implement it */
+      if (domain != target) {
+        return 0;
+      }
+
+      if (isRootServer(ip)) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
+        return 1;
+      }
+      else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
+        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
+        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
+        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
+        return 1;
+      }
+      else if (ip == ComboAddress("192.0.2.2:53") || ip == ComboAddress("192.0.2.3:53") || ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("[2001:DB8::3]:53")) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, target, QType::A, "192.0.2.4");
+        return 1;
+      }
+      else {
+        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_EQUAL(ret[0].d_type, QType::A);
+  BOOST_CHECK_EQUAL(ret[0].d_name, target);
+}
+
+BOOST_AUTO_TEST_CASE(test_glueless_referral) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, false);
+
+  primeHints();
+
+  const DNSName target("powerdns.com.");
+
+  sr->setAsyncCallback([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) {
+
+      if (isRootServer(ip)) {
+        setLWResult(res, 0, true, false, true);
+
+        if (domain.isPartOf(DNSName("com."))) {
+          addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+        } else if (domain.isPartOf(DNSName("org."))) {
+          addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+        }
+        else {
+          setLWResult(res, RCode::NXDomain, false, false, true);
+          return 1;
+        }
+
+        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
+        return 1;
+      }
+      else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
+        if (domain == target) {
+          setLWResult(res, 0, true, false, true);
+          addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
+          addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
+          return 1;
+        }
+        else if (domain == DNSName("pdns-public-ns1.powerdns.org.")) {
+          setLWResult(res, 0, true, false, true);
+          addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::A, "192.0.2.2");
+          addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::AAAA, "2001:DB8::2");
+          return 1;
+        }
+        else if (domain == DNSName("pdns-public-ns2.powerdns.org.")) {
+          setLWResult(res, 0, true, false, true);
+          addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::A, "192.0.2.3");
+          addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::AAAA, "2001:DB8::3");
+          return 1;
+        }
+
+        setLWResult(res, RCode::NXDomain, false, false, true);
+        return 1;
+      }
+      else if (ip == ComboAddress("192.0.2.2:53") || ip == ComboAddress("192.0.2.3:53") || ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("[2001:DB8::3]:53")) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, target, QType::A, "192.0.2.4");
+        return 1;
+      }
+      else {
+        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_EQUAL(ret[0].d_type, QType::A);
+  BOOST_CHECK_EQUAL(ret[0].d_name, target);
+}
+
+/*
+  TODO:
+
+//      cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
+
+check RPZ (nameservers name blocked, name server IP blocked)
+check that wantsRPZ false skip the RPZ checks
+
+check that we are asking the more specific nameservers
+
+check that we correctly ignore unauth data?
+
+check out of band support
+
+check throttled
+
+check blocked
+
+check we query the fastest auth available first?
+
+check we correctly store slow servers
+
+check EDNS subnetmask
+
+if possible, check preoutquery
+
+check depth limit
+
+check time limit
+
+check we correctly populate the cache from the results
+
+check we correctly populate the negcache
+
+check we honor s_minimumTTL and s_maxcachettl
+
+check we follow a CNAME
+
+check we follow a CNAME, but not a loop!
+
+check we follow referral
+
+check delegation only
+
+check root NX trust
+
+check direct answer (done I think?)
+
+check incomplete CNAME answer
+
+complete CNAME answer
+
+nxdomain answer
+nodata answer
+*/
+
+BOOST_AUTO_TEST_SUITE_END()
index f9ec172d45310469f95934d4189f26920fd87e45..3ab5263dca599786a3b8fd98212eb5526ea4dec3 100644 (file)
  */
 #pragma once
 
-static const char*rootIps4[]={"198.41.0.4",             // a.root-servers.net.
-                              "192.228.79.201",         // b.root-servers.net.
-                              "192.33.4.12",            // c.root-servers.net.
-                              "199.7.91.13",            // d.root-servers.net.
-                              "192.203.230.10",         // e.root-servers.net.
-                              "192.5.5.241",            // f.root-servers.net.
-                              "192.112.36.4",           // g.root-servers.net.
-                              "198.97.190.53",          // h.root-servers.net.
-                              "192.36.148.17",          // i.root-servers.net.
-                              "192.58.128.30",          // j.root-servers.net.
-                              "193.0.14.129",           // k.root-servers.net.
-                              "199.7.83.42",            // l.root-servers.net.
-                              "202.12.27.33"            // m.root-servers.net.
-                              };
+static const char* const rootIps4[]={"198.41.0.4",             // a.root-servers.net.
+                                     "192.228.79.201",         // b.root-servers.net.
+                                     "192.33.4.12",            // c.root-servers.net.
+                                     "199.7.91.13",            // d.root-servers.net.
+                                     "192.203.230.10",         // e.root-servers.net.
+                                     "192.5.5.241",            // f.root-servers.net.
+                                     "192.112.36.4",           // g.root-servers.net.
+                                     "198.97.190.53",          // h.root-servers.net.
+                                     "192.36.148.17",          // i.root-servers.net.
+                                     "192.58.128.30",          // j.root-servers.net.
+                                     "193.0.14.129",           // k.root-servers.net.
+                                     "199.7.83.42",            // l.root-servers.net.
+                                     "202.12.27.33"            // m.root-servers.net.
+                                    };
+static size_t const rootIps4Count = sizeof(rootIps4) / sizeof(*rootIps4);
 
-static const char*rootIps6[]={"2001:503:ba3e::2:30",    // a.root-servers.net.
-                              "2001:500:84::b",         // b.root-servers.net.
-                              "2001:500:2::c",          // c.root-servers.net.
-                              "2001:500:2d::d",         // d.root-servers.net.
-                              "2001:500:a8::e",         // e.root-servers.net.
-                              "2001:500:2f::f",         // f.root-servers.net.
-                              "2001:500:12::d0d",       // g.root-servers.net.
-                              "2001:500:1::53",         // h.root-servers.net.
-                              "2001:7fe::53",           // i.root-servers.net.
-                              "2001:503:c27::2:30",     // j.root-servers.net.
-                              "2001:7fd::1",            // k.root-servers.net.
-                              "2001:500:9f::42",        // l.root-servers.net.
-                              "2001:dc3::35"            // m.root-servers.net.
-                              };
+static const char* const rootIps6[]={"2001:503:ba3e::2:30",    // a.root-servers.net.
+                                     "2001:500:84::b",         // b.root-servers.net.
+                                     "2001:500:2::c",          // c.root-servers.net.
+                                     "2001:500:2d::d",         // d.root-servers.net.
+                                     "2001:500:a8::e",         // e.root-servers.net.
+                                     "2001:500:2f::f",         // f.root-servers.net.
+                                     "2001:500:12::d0d",       // g.root-servers.net.
+                                     "2001:500:1::53",         // h.root-servers.net.
+                                     "2001:7fe::53",           // i.root-servers.net.
+                                     "2001:503:c27::2:30",     // j.root-servers.net.
+                                     "2001:7fd::1",            // k.root-servers.net.
+                                     "2001:500:9f::42",        // l.root-servers.net.
+                                     "2001:dc3::35"            // m.root-servers.net.
+                                    };
+static size_t const rootIps6Count = sizeof(rootIps6) / sizeof(*rootIps6);
index 1f5bb37fe7ed6084d56243ec7c41c759846c5c7c..415c74522fa51c9f9dffb2243168220286ffea28 100644 (file)
@@ -22,5 +22,5 @@
 
 #pragma once
 
-static const char*rootDSs[]={"19036 8 2 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5",
-                             "20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d"};
+static const char* const rootDSs[]={"19036 8 2 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5",
+                                    "20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d"};
index c7fa5b817942a40f0fc495a1b8a4fbbfc59dd33a..e1190985a96335ffb4678026cde724433d5be3a8 100644 (file)
@@ -26,7 +26,7 @@ void doSecPoll(time_t* last_secpoll)
   gettimeofday(&now, 0);
   SyncRes sr(now);
   if (g_dnssecmode != DNSSECMode::Off)
-    sr.d_doDNSSEC=true;
+    sr.setDoDNSSEC(true);
   vector<DNSRecord> ret;
 
   string version = "recursor-" +pkgv;
index 22b5bafd87e87fa896c263b2bad36bc7165900c3..f88900e41265aa539759bd885bac1b8b591b406f 100644 (file)
@@ -111,8 +111,8 @@ static void accountAuthLatency(int usec, int family)
 
 
 SyncRes::SyncRes(const struct timeval& now) :  d_outqueries(0), d_tcpoutqueries(0), d_throttledqueries(0), d_timeouts(0), d_unreachables(0),
-                                              d_totUsec(0), d_doDNSSEC(false), d_now(now),
-                                              d_cacheonly(false), d_nocache(false), d_doEDNS0(false), d_lm(s_lm)
+                                              d_totUsec(0), d_now(now),
+                                              d_cacheonly(false), d_nocache(false), d_doDNSSEC(false), d_doEDNS0(false), d_lm(s_lm)
                                                  
 { 
   if(!t_sstorage) {
@@ -377,7 +377,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
   */
 
   SyncRes::EDNSStatus* ednsstatus;
-  ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port? 
+  ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port? YES
 
   if(ednsstatus->modeSetAt && ednsstatus->modeSetAt + 3600 < d_now.tv_sec) {
     *ednsstatus=SyncRes::EDNSStatus();
@@ -403,8 +403,13 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
     }
     else if(ednsMANDATORY || mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode==EDNSStatus::EDNSIGNORANT)
       EDNSLevel = 1;
-    
-    ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res);
+
+    if (d_asyncResolve) {
+      ret = d_asyncResolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res);
+    }
+    else {
+      ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res);
+    }
     if(ret < 0) {
       return ret; // transport error, nothing to learn here
     }
@@ -685,7 +690,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
       // We lost the root NS records
       primeHints();
       LOG(prefix<<qname<<": reprimed the root"<<endl);
-      getRootNS();
+      getRootNS(d_now, d_asyncResolve);
     }
   }while(subdomain.chopOff());
 }
@@ -1591,3 +1596,46 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<D
   
   return res;
 }
+
+#include "validate-recursor.hh"
+
+int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback) {
+  SyncRes sr(now);
+  sr.setDoEDNS0(true);
+  sr.setNoCache();
+  sr.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
+  sr.setAsyncCallback(asyncCallback);
+
+  vector<DNSRecord> ret;
+  int res=-1;
+  try {
+    res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret);
+    if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
+      ResolveContext ctx;
+      auto state = validateRecords(ctx, ret);
+      if (state == Bogus)
+        throw PDNSException("Got Bogus validation result for .|NS");
+    }
+    return res;
+  }
+  catch(PDNSException& e)
+  {
+    L<<Logger::Error<<"Failed to update . records, got an exception: "<<e.reason<<endl;
+  }
+
+  catch(std::exception& e)
+  {
+    L<<Logger::Error<<"Failed to update . records, got an exception: "<<e.what()<<endl;
+  }
+
+  catch(...)
+  {
+    L<<Logger::Error<<"Failed to update . records, got an exception"<<endl;
+  }
+  if(!res) {
+    L<<Logger::Notice<<"Refreshed . records"<<endl;
+  }
+  else
+    L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
+  return res;
+}
index 84d0c64d932100440a0d025efc448e87bf2162d9..d32e913c3ada4b66d8cc5ef9046b3a2ca06b2fa7 100644 (file)
@@ -59,7 +59,7 @@
 #endif
 
 void primeHints(void);
-int getRootNS(void);
+
 class RecursorLua4;
 
 struct BothRecordsAndSignatures
@@ -135,10 +135,15 @@ public:
       d_cont[t]=e;
   }
 
-  unsigned int size()
+  unsigned int size() const
   {
     return (unsigned int)d_cont.size();
   }
+
+  void clear()
+  {
+    d_cont.clear();
+  }
 private:
   unsigned int d_limit;
   time_t d_ttl;
@@ -210,7 +215,7 @@ public:
     return d_val*=factor;
   }
 
-  double peek(void)
+  double peek(void) const
   {
     return d_val;
   }
@@ -233,9 +238,9 @@ public:
   Counters()
   {
   }
-  unsigned long value(const Thing& t)
+  unsigned long value(const Thing& t) const
   {
-    typename cont_t::iterator i=d_cont.find(t);
+    typename cont_t::const_iterator i=d_cont.find(t);
 
     if(i==d_cont.end()) {
       return 0;
@@ -274,7 +279,11 @@ public:
       d_cont.erase(i);
     }
   }
-  size_t size()
+  void clear()
+  {
+    d_cont.clear();
+  }
+  size_t size() const
   {
     return d_cont.size();
   }
@@ -291,6 +300,8 @@ public:
 
   explicit SyncRes(const struct timeval& now);
 
+  typedef std::function<int(const ComboAddress& ip, const DNSName& qdomain, int qtype, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult *lwr)> asyncresolve_t;
+
   int beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret);
   void setId(int id)
   {
@@ -326,6 +337,26 @@ public:
     d_doEDNS0=state;
   }
 
+  void setDoDNSSEC(bool state=true)
+  {
+    d_doDNSSEC=state;
+  }
+
+  void setWantsRPZ(bool state=true)
+  {
+    d_wantsRPZ=state;
+  }
+
+  bool getWantsRPZ() const
+  {
+    return d_wantsRPZ;
+  }
+
+  void setIncomingECSFound(bool state=true)
+  {
+    d_incomingECSFound=state;
+  }
+
   string getTrace() const
   {
     return d_trace.str();
@@ -346,14 +377,35 @@ public:
     return d_wasOutOfBand;
   }
 
+  struct timeval getNow() const
+  {
+    return d_now;
+  }
+
   void setSkipCNAMECheck(bool skip = false)
   {
     d_skipCNAMECheck = skip;
   }
 
-  int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res) const;
+  void setIncomingECS(boost::optional<const EDNSSubnetOpts&> incomingECS)
+  {
+    d_incomingECS = incomingECS;
+  }
+
+#ifdef HAVE_PROTOBUF
+  void setInitialRequestId(boost::optional<const boost::uuids::uuid&> initialRequestId)
+  {
+    d_initialRequestId = initialRequestId;
+  }
+#endif
+
+  void setAsyncCallback(asyncresolve_t func)
+  {
+    d_asyncResolve = func;
+  }
 
   static void doEDNSDumpAndClose(int fd);
+  static int getRootNS(struct timeval now, asyncresolve_t asyncCallback);
 
   static std::atomic<uint64_t> s_queries;
   static std::atomic<uint64_t> s_outgoingtimeouts;
@@ -372,10 +424,6 @@ public:
   static unsigned int s_maxdepth;
   std::unordered_map<std::string,bool> d_discardedPolicies;
   DNSFilterEngine::Policy d_appliedPolicy;
-  boost::optional<const EDNSSubnetOpts&> d_incomingECS;
-#ifdef HAVE_PROTOBUF
-  boost::optional<const boost::uuids::uuid&> d_initialRequestId;
-#endif
   unsigned int d_outqueries;
   unsigned int d_tcpoutqueries;
   unsigned int d_throttledqueries;
@@ -383,14 +431,7 @@ public:
   unsigned int d_unreachables;
   unsigned int d_totUsec;
   ComboAddress d_requestor;
-  bool d_doDNSSEC;
-  
-  bool d_wasVariable{false};
-  bool d_wasOutOfBand{false};
-  bool d_wantsRPZ{true};
-  bool d_skipCNAMECheck{false};
-  bool d_incomingECSFound{false};
-  
+
   typedef multi_index_container <
     NegCacheEntry,
     indexed_by <
@@ -497,7 +538,6 @@ public:
 
   typedef Counters<ComboAddress> fails_t;
 
-  struct timeval d_now;
   static unsigned int s_maxnegttl;
   static unsigned int s_maxcachettl;
   static unsigned int s_packetcachettl;
@@ -543,8 +583,16 @@ private:
 
   bool doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t &qclass, vector<DNSRecord> &ret);
 
+  int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res) const;
+
   ostringstream d_trace;
   shared_ptr<RecursorLua4> d_pdl;
+  boost::optional<const EDNSSubnetOpts&> d_incomingECS;
+#ifdef HAVE_PROTOBUF
+  boost::optional<const boost::uuids::uuid&> d_initialRequestId;
+#endif
+  asyncresolve_t d_asyncResolve{nullptr};
+  struct timeval d_now;
   string d_prefix;
 
   /* When d_cacheonly is set to true, we will only check the cache.
@@ -555,7 +603,13 @@ private:
    * It forces us to not look in the cache or local auth.
    */
   bool d_nocache;
-  bool d_doEDNS0;
+  bool d_doDNSSEC;
+  bool d_doEDNS0{true};
+  bool d_incomingECSFound{false};
+  bool d_skipCNAMECheck{false};
+  bool d_wantsRPZ{true};
+  bool d_wasOutOfBand{false};
+  bool d_wasVariable{false};
 
   static LogMode s_lm;
   LogMode d_lm;
@@ -686,7 +740,7 @@ public:
   TCPConnection(int fd, const ComboAddress& addr);
   ~TCPConnection();
 
-  int getFD()
+  int getFD() const
   {
     return d_fd;
   }
index f6c9372e460593122ff2919a6a134f2e1e2dae3a..1af29ec0c08aadc9d5a0d5017ca99e399ff15e42 100644 (file)
@@ -6,6 +6,8 @@
 DNSSECMode g_dnssecmode{DNSSECMode::ProcessNoValidate};
 bool g_dnssecLogBogus;
 
+extern int getMTaskerTID();
+
 #define LOG(x) if(g_dnssecLOG) { L <<Logger::Warning << x; }
 
 class SRRecordOracle : public DNSRecordOracle
@@ -19,13 +21,13 @@ public:
     struct timeval tv;
     gettimeofday(&tv, 0);
     SyncRes sr(tv);
-    sr.setId(MT->getTid());
+    sr.setId(getMTaskerTID());
 #ifdef HAVE_PROTOBUF
-    sr.d_initialRequestId = d_ctx.d_initialRequestId;
+    sr.setInitialRequestId(d_ctx.d_initialRequestId);
 #endif
 
     vector<DNSRecord> ret;
-    sr.d_doDNSSEC=true;
+    sr.setDoDNSSEC(true);
     if (qtype == QType::DS || qtype == QType::DNSKEY || qtype == QType::NS)
       sr.setSkipCNAMECheck(true);
     sr.beginResolve(qname, QType(qtype), 1, ret);