]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #5236 from rgacogne/rec-doresolveat-refactor
authorPieter Lexis <pieterlexis@users.noreply.github.com>
Thu, 25 May 2017 16:51:29 +0000 (18:51 +0200)
committerGitHub <noreply@github.com>
Thu, 25 May 2017 16:51:29 +0000 (18:51 +0200)
rec: Move to thread_local, refactor `SyncRes::doResolveAt()` and auth zones

14 files changed:
.travis.yml
build-scripts/travis.sh
pdns/ednssubnet.hh
pdns/pdns_recursor.cc
pdns/rec_channel_rec.cc
pdns/recursor_cache.cc
pdns/recursor_cache.hh
pdns/recursordist/Makefile.am
pdns/recursordist/ecs.cc [deleted file]
pdns/recursordist/test-syncres_cc.cc
pdns/reczones.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/ws-recursor.cc

index 5498bcab3de61fe4267f6dae8de472438651bd94..9260619702c857126dd9b48bc8e4ea43ab3562a2 100644 (file)
@@ -14,6 +14,14 @@ matrix:
   exclude:
     - compiler: clang
       env: PDNS_BUILD_PRODUCT=docs
+    - compiler: clang
+      env: PDNS_BUILD_PRODUCT=recursor
+  include:
+    - compiler: clang
+      env: PDNS_BUILD_PRODUCT=recursor COMPILER=clang++-3.6
+      addons:
+        apt:
+          packages: ['clang-3.6']
 
 before_script:
   - git describe --always --dirty=+
index bbeb86fd371b6fe7b1b905cac43182e37d686268..cc99ef03a2d8e3413fe048a95999a304e7537938 100755 (executable)
@@ -397,7 +397,7 @@ build_recursor() {
   run "tar xf pdns-recursor-*.tar.bz2"
   run "rm -f pdns-recursor-*.tar.bz2"
   run "cd pdns-recursor-*"
-  run "CFLAGS='-O1' CXXFLAGS='-O1' ./configure \
+  run "CFLAGS='-O1' CXXFLAGS='-O1' CXX=${COMPILER} ./configure \
     --prefix=$PDNS_RECURSOR_DIR \
     --enable-unit-tests \
     --disable-silent-rules"
index 0220bd0f52e76f255d83dcbfee19053bc1ffa6dd..eb87b488e6fa65daa8af49ae51c7f8845bdb95b3 100644 (file)
 #include "iputils.hh"
 #include "dnsname.hh"
 
-extern NetmaskGroup g_ednssubnets;
-extern SuffixMatchNode g_ednsdomains;
-extern bool g_useIncomingECS;
-
 struct EDNSSubnetOpts
 {
        Netmask source;
index 868a229813d428e10657bb2acdce8f12a5f1830a..26908b4cb670693a176849edcf1f2bfe7735d17d 100644 (file)
@@ -97,20 +97,20 @@ extern SortList g_sortlist;
 
 typedef map<ComboAddress, uint32_t, ComboAddress::addressOnlyLessThan> tcpClientCounts_t;
 
-static __thread shared_ptr<RecursorLua4>* t_pdl;
-static __thread unsigned int t_id;
-static __thread shared_ptr<Regex>* t_traceRegex;
-static __thread tcpClientCounts_t* t_tcpClientCounts;
-
-__thread MT_t* MT; // the big MTasker
-__thread MemRecursorCache* t_RC;
-__thread RecursorPacketCache* t_packetCache;
-__thread FDMultiplexer* t_fdm;
-__thread addrringbuf_t* t_remotes, *t_servfailremotes, *t_largeanswerremotes;
-__thread boost::circular_buffer<pair<DNSName, uint16_t> >* t_queryring, *t_servfailqueryring;
-__thread NetmaskGroup* t_allowFrom;
+static thread_local std::shared_ptr<RecursorLua4> t_pdl;
+static thread_local unsigned int t_id;
+static thread_local std::shared_ptr<Regex> t_traceRegex;
+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{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;
 #ifdef HAVE_PROTOBUF
-__thread boost::uuids::random_generator* t_uuidGenerator;
+thread_local std::unique_ptr<boost::uuids::random_generator> t_uuidGenerator;
 #endif
 __thread struct timeval g_now; // timestamp, updated (too) frequently
 
@@ -135,8 +135,8 @@ static std::unordered_map<unsigned int, deferredAdd_t> deferredAdds;
 static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism
 static vector<ComboAddress> g_localQueryAddresses4, g_localQueryAddresses6;
 static AtomicCounter counter;
-static SyncRes::domainmap_t* g_initialDomainMap; // new threads needs this to be setup
-static NetmaskGroup* g_initialAllowFrom; // new thread needs to be setup with this
+static std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
+static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
 static size_t g_tcpMaxQueriesPerConn;
 static uint64_t g_latencyStatSize;
 static uint32_t g_disthashseed;
@@ -156,11 +156,10 @@ static bool g_reusePort{false};
 static bool g_useOneSocketPerThread;
 static bool g_gettagNeedsEDNSOptions{false};
 static time_t g_statisticsInterval;
+static bool g_useIncomingECS;
 
-std::unordered_set<DNSName> g_delegationOnly;
 RecursorControlChannel s_rcc; // only active in thread 0
 RecursorStats g_stats;
-NetmaskGroup* g_dontQuery;
 string s_programname="pdns_recursor";
 string s_pidfname;
 unsigned int g_numThreads;
@@ -489,7 +488,7 @@ public:
   }
 };
 
-static __thread UDPClientSocks* t_udpclientsocks;
+static thread_local std::unique_ptr<UDPClientSocks> t_udpclientsocks;
 
 /* these two functions are used by LWRes */
 // -2 is OS error, -1 is error that depends on the remote, > 0 is success
@@ -760,7 +759,7 @@ static void startDoResolve(void *p)
     SyncRes sr(dc->d_now);
     bool DNSSECOK=false;
     if(t_pdl) {
-      sr.setLuaEngine(*t_pdl);
+      sr.setLuaEngine(t_pdl);
       sr.d_requestor=dc->d_remote;
     }
 
@@ -815,7 +814,7 @@ static void startDoResolve(void *p)
       goto sendit;
     }
 
-    if(t_traceRegex->get() && (*t_traceRegex)->match(dc->d_mdp.d_qname.toString())) {
+    if(t_traceRegex && t_traceRegex->match(dc->d_mdp.d_qname.toString())) {
       sr.setLogMode(SyncRes::Store);
       tracedQuery=true;
     }
@@ -834,8 +833,8 @@ static void startDoResolve(void *p)
     if(!dc->d_mdp.d_header.rd)
       sr.setCacheOnly();
 
-    if (t_pdl->get()) {
-      (*t_pdl)->prerpz(dq, res);
+    if (t_pdl) {
+      t_pdl->prerpz(dq, res);
     }
 
     // Check if the query has a policy attached to it
@@ -844,7 +843,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)) {
+    if(!t_pdl || !t_pdl->preresolve(dq, res)) {
 
       sr.setWantsRPZ(wantsRPZ);
       if(wantsRPZ) {
@@ -939,20 +938,20 @@ static void startDoResolve(void *p)
         appliedPolicy = luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies);
       }
 
-      if(t_pdl->get()) {
+      if(t_pdl) {
         if(res == RCode::NoError) {
                auto i=ret.cbegin();
                 for(; i!= ret.cend(); ++i)
                   if(i->d_type == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER)
                           break;
-                if(i == ret.cend() && (*t_pdl)->nodata(dq, res))
+                if(i == ret.cend() && t_pdl->nodata(dq, res))
                   shouldNotValidate = true;
 
        }
-       else if(res == RCode::NXDomain && (*t_pdl)->nxdomain(dq, res))
+       else if(res == RCode::NXDomain && t_pdl->nxdomain(dq, res))
           shouldNotValidate = true;
 
-       if((*t_pdl)->postresolve(dq, res))
+       if(t_pdl->postresolve(dq, res))
           shouldNotValidate = true;
       }
 
@@ -1384,7 +1383,7 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
     if(conn->bytesread==conn->qlen) {
       t_fdm->removeReadFD(fd); // should no longer awake ourselves when there is data to read
 
-      DNSComboWriter* dc=0;
+      DNSComboWriter* dc=nullptr;
       try {
         dc=new DNSComboWriter(conn->data, conn->qlen, g_now);
       }
@@ -1416,16 +1415,16 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
       }
 #endif
 
-      if(needECS || (t_pdl->get() && (*t_pdl)->d_gettag)) {
+      if(needECS || (t_pdl && t_pdl->d_gettag)) {
 
         try {
           std::map<uint16_t, EDNSOptionView> ednsOptions;
           dc->d_ecsParsed = true;
           dc->d_ecsFound = getQNameAndSubnet(std::string(conn->data, conn->qlen), &qname, &qtype, &qclass, &dc->d_ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr);
 
-          if(t_pdl->get() && (*t_pdl)->d_gettag) {
+          if(t_pdl && t_pdl->d_gettag) {
             try {
-              dc->d_tag = (*t_pdl)->gettag(conn->d_remote, dc->d_ednssubnet.source, dest, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId);
+              dc->d_tag = t_pdl->gettag(conn->d_remote, dc->d_ednssubnet.source, dest, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId);
             }
             catch(std::exception& e)  {
               if(g_logCommonErrors)
@@ -1526,7 +1525,7 @@ static void handleNewTCPQuestion(int fd, FDMultiplexer::funcparam_t& )
     }
 
     setNonBlocking(newsock);
-    shared_ptr<TCPConnection> tc(new TCPConnection(newsock, addr));
+    std::shared_ptr<TCPConnection> tc = std::make_shared<TCPConnection>(newsock, addr);
     tc->state=TCPConnection::BYTE0;
 
     t_fdm->addReadFD(tc->getFD(), handleRunningTCPQuestion, tc);
@@ -1591,16 +1590,16 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
     */
 #endif
 
-    if(needECS || (t_pdl->get() && (*t_pdl)->d_gettag)) {
+    if(needECS || (t_pdl && t_pdl->d_gettag)) {
       try {
         std::map<uint16_t, EDNSOptionView> ednsOptions;
         ecsFound = getQNameAndSubnet(question, &qname, &qtype, &qclass, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr);
         qnameParsed = true;
         ecsParsed = true;
 
-        if(t_pdl->get() && (*t_pdl)->d_gettag) {
+        if(t_pdl && t_pdl->d_gettag) {
           try {
-            ctag=(*t_pdl)->gettag(fromaddr, ednssubnet.source, destaddr, qname, qtype, &policyTags, data, ednsOptions, false, requestorId);
+            ctag=t_pdl->gettag(fromaddr, ednssubnet.source, destaddr, qname, qtype, &policyTags, data, ednsOptions, false, requestorId);
           }
           catch(std::exception& e)  {
             if(g_logCommonErrors)
@@ -1676,8 +1675,8 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
     return 0;
   }
 
-  if(t_pdl->get()) {
-    if((*t_pdl)->ipfilter(fromaddr, destaddr, *dh)) {
+  if(t_pdl) {
+    if(t_pdl->ipfilter(fromaddr, destaddr, *dh)) {
       if(!g_quiet)
        L<<Logger::Notice<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] DROPPED question from "<<fromaddr.toStringWithPort()<<" based on policy"<<endl;
       g_stats.policyDrops++;
@@ -2044,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;
@@ -2061,15 +2060,11 @@ static void houseKeeping(void *)
       t_RC->doPrune(); // this function is local to a thread, so fine anyhow
       t_packetCache->doPruneTo(::arg().asNum("max-packetcache-entries") / g_numWorkerThreads);
 
-      t_sstorage->negcache.prune(::arg().asNum("max-cache-entries") / (g_numWorkerThreads * 10));
+      SyncRes::pruneNegCache(::arg().asNum("max-cache-entries") / (g_numWorkerThreads * 10));
 
       if(!((cleanCounter++)%40)) {  // this is a full scan!
        time_t limit=now.tv_sec-300;
-       for(SyncRes::nsspeeds_t::iterator i = t_sstorage->nsSpeeds.begin() ; i!= t_sstorage->nsSpeeds.end(); )
-         if(i->second.stale(limit))
-           t_sstorage->nsSpeeds.erase(i++);
-         else
-           ++i;
+        SyncRes::pruneNSSpeeds(limit);
       }
       last_prune=time(0);
     }
@@ -2181,7 +2176,7 @@ void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
 
 static void handlePipeRequest(int fd, FDMultiplexer::funcparam_t& var)
 {
-  ThreadMSG* tmsg;
+  ThreadMSG* tmsg = nullptr;
 
   if(read(fd, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread
     unixDie("read from thread pipe returned wrong size or error");
@@ -2471,10 +2466,9 @@ retryWithName:
 FDMultiplexer* getMultiplexer()
 {
   FDMultiplexer* ret;
-  for(FDMultiplexer::FDMultiplexermap_t::const_iterator i = FDMultiplexer::getMultiplexerMap().begin();
-      i != FDMultiplexer::getMultiplexerMap().end(); ++i) {
+  for(const auto& i : FDMultiplexer::getMultiplexerMap()) {
     try {
-      ret=i->second();
+      ret=i.second();
       return ret;
     }
     catch(FDMultiplexerException &fe) {
@@ -2494,12 +2488,12 @@ static string* doReloadLuaScript()
   string fname= ::arg()["lua-dns-script"];
   try {
     if(fname.empty()) {
-      t_pdl->reset();
+      t_pdl.reset();
       L<<Logger::Error<<t_id<<" Unloaded current lua script"<<endl;
       return new string("unloaded\n");
     }
     else {
-      *t_pdl = shared_ptr<RecursorLua4>(new RecursorLua4(fname));
+      t_pdl = std::make_shared<RecursorLua4>(fname);
     }
   }
   catch(std::exception& e) {
@@ -2523,11 +2517,11 @@ static string* pleaseUseNewTraceRegex(const std::string& newRegex)
 try
 {
   if(newRegex.empty()) {
-    t_traceRegex->reset();
+    t_traceRegex.reset();
     return new string("unset\n");
   }
   else {
-    (*t_traceRegex) = shared_ptr<Regex>(new Regex(newRegex));
+    t_traceRegex = std::make_shared<Regex>(newRegex);
     return new string("ok\n");
   }
 }
@@ -2575,10 +2569,10 @@ static void checkOrFixFDS()
 
 static void* recursorThread(void*);
 
-static void* pleaseSupplantACLs(NetmaskGroup *ng)
+static void* pleaseSupplantACLs(std::shared_ptr<NetmaskGroup> ng)
 {
   t_allowFrom = ng;
-  return 0;
+  return nullptr;
 }
 
 int g_argc;
@@ -2613,13 +2607,13 @@ void parseACLs()
     ::arg().preParse(g_argc, g_argv, "allow-from");
   }
 
-  NetmaskGroup* oldAllowFrom = t_allowFrom, *allowFrom=new NetmaskGroup;
+  std::shared_ptr<NetmaskGroup> oldAllowFrom = t_allowFrom;
+  std::shared_ptr<NetmaskGroup> allowFrom = std::make_shared<NetmaskGroup>();
 
   if(!::arg()["allow-from-file"].empty()) {
     string line;
     ifstream ifs(::arg()["allow-from-file"].c_str());
     if(!ifs) {
-      delete allowFrom;
       throw runtime_error("Could not open '"+::arg()["allow-from-file"]+"': "+stringerror());
     }
 
@@ -2652,13 +2646,12 @@ void parseACLs()
   else {
     if(::arg()["local-address"]!="127.0.0.1" && ::arg().asNum("local-port")==53)
       L<<Logger::Error<<"WARNING: Allowing queries from all IP addresses - this can be a security risk!"<<endl;
-    delete allowFrom;
-    allowFrom = 0;
+    allowFrom = nullptr;
   }
 
   g_initialAllowFrom = allowFrom;
   broadcastFunction(boost::bind(pleaseSupplantACLs, allowFrom));
-  delete oldAllowFrom;
+  oldAllowFrom = nullptr;
 
   l_initialized = true;
 }
@@ -2669,7 +2662,7 @@ static void setupDelegationOnly()
   vector<string> parts;
   stringtok(parts, ::arg()["delegation-only"], ", \t");
   for(const auto& p : parts) {
-    g_delegationOnly.insert(DNSName(p));
+    SyncRes::addDelegationOnly(DNSName(p));
   }
 }
 
@@ -2748,7 +2741,6 @@ static int serviceMain(int argc, char*argv[])
   sortPublicSuffixList();
 
   if(!::arg()["dont-query"].empty()) {
-    g_dontQuery=new NetmaskGroup;
     vector<string> ips;
     stringtok(ips, ::arg()["dont-query"], ", ");
     ips.push_back("0.0.0.0");
@@ -2756,7 +2748,7 @@ static int serviceMain(int argc, char*argv[])
 
     L<<Logger::Warning<<"Will not send queries to: ";
     for(vector<string>::const_iterator i = ips.begin(); i!= ips.end(); ++i) {
-      g_dontQuery->addMask(*i);
+      SyncRes::addDontQuery(*i);
       if(i!=ips.begin())
         L<<Logger::Warning<<", ";
       L<<Logger::Warning<<*i;
@@ -2848,7 +2840,7 @@ static int serviceMain(int argc, char*argv[])
     makeTCPServerSockets(0);
   }
 
-  parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
+  SyncRes::parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
   g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
 
   int forks;
@@ -2952,24 +2944,22 @@ try
 {
   t_id=(int) (long) ptr;
   SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so..
-  t_sstorage->domainmap = g_initialDomainMap;
+  SyncRes::setDomainMap(g_initialDomainMap);
   t_allowFrom = g_initialAllowFrom;
-  t_udpclientsocks = new UDPClientSocks();
-  t_tcpClientCounts = new tcpClientCounts_t();
+  t_udpclientsocks = std::unique_ptr<UDPClientSocks>(new UDPClientSocks());
+  t_tcpClientCounts = std::unique_ptr<tcpClientCounts_t>(new tcpClientCounts_t());
   primeHints();
 
-  t_packetCache = new RecursorPacketCache();
+  t_packetCache = std::unique_ptr<RecursorPacketCache>(new RecursorPacketCache());
 
 #ifdef HAVE_PROTOBUF
-  t_uuidGenerator = new boost::uuids::random_generator();
+  t_uuidGenerator = std::unique_ptr<boost::uuids::random_generator>(new boost::uuids::random_generator());
 #endif
   L<<Logger::Warning<<"Done priming cache with root hints"<<endl;
 
-  t_pdl = new shared_ptr<RecursorLua4>();
-
   try {
     if(!::arg()["lua-dns-script"].empty()) {
-      *t_pdl = shared_ptr<RecursorLua4>(new RecursorLua4(::arg()["lua-dns-script"]));
+      t_pdl = std::make_shared<RecursorLua4>(::arg()["lua-dns-script"]);
       L<<Logger::Warning<<"Loaded 'lua' script from '"<<::arg()["lua-dns-script"]<<"'"<<endl;
     }
   }
@@ -2978,26 +2968,25 @@ try
     _exit(99);
   }
 
-  t_traceRegex = new shared_ptr<Regex>();
   unsigned int ringsize=::arg().asNum("stats-ringbuffer-entries") / g_numWorkerThreads;
   if(ringsize) {
-    t_remotes = new addrringbuf_t();
+    t_remotes = std::unique_ptr<addrringbuf_t>(new addrringbuf_t());
     if(g_weDistributeQueries)  // if so, only 1 thread does recvfrom
       t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries"));
     else
       t_remotes->set_capacity(ringsize);
-    t_servfailremotes = new addrringbuf_t();
+    t_servfailremotes = std::unique_ptr<addrringbuf_t>(new addrringbuf_t());
     t_servfailremotes->set_capacity(ringsize);
-    t_largeanswerremotes = new addrringbuf_t();
+    t_largeanswerremotes = std::unique_ptr<addrringbuf_t>(new addrringbuf_t());
     t_largeanswerremotes->set_capacity(ringsize);
 
-    t_queryring = new boost::circular_buffer<pair<DNSName, uint16_t> >();
+    t_queryring = std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > >(new boost::circular_buffer<pair<DNSName, uint16_t> >());
     t_queryring->set_capacity(ringsize);
-    t_servfailqueryring = new boost::circular_buffer<pair<DNSName, uint16_t> >();
+    t_servfailqueryring = std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > >(new boost::circular_buffer<pair<DNSName, uint16_t> >());
     t_servfailqueryring->set_capacity(ringsize);
   }
 
-  MT=new MTasker<PacketID,string>(::arg().asNum("stack-size"));
+  MT=std::unique_ptr<MTasker<PacketID,string> >(new MTasker<PacketID,string>(::arg().asNum("stack-size")));
 
   PacketID pident;
 
index e7d1731ce0c7b4eb905039c734147629fe5a9e2d..ce3a0d452358c3a6d6edfe44305bd9ddd93c76f3 100644 (file)
@@ -191,12 +191,12 @@ static uint64_t dumpNegCache(NegCache& negcache, int fd)
 
 static uint64_t* pleaseDump(int fd)
 {
-  return new uint64_t(t_RC->doDump(fd) + dumpNegCache(t_sstorage->negcache, fd) + t_packetCache->doDump(fd));
+  return new uint64_t(t_RC->doDump(fd) + dumpNegCache(SyncRes::t_sstorage.negcache, fd) + t_packetCache->doDump(fd));
 }
 
 static uint64_t* pleaseDumpNSSpeeds(int fd)
 {
-  return new uint64_t(t_RC->doDumpNSSpeeds(fd));
+  return new uint64_t(SyncRes::doDumpNSSpeeds(fd));
 }
 
 template<typename T>
@@ -274,7 +274,7 @@ uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree)
 
 uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree)
 {
-  uint64_t ret = t_sstorage->negcache.wipe(canon, subtree);
+  uint64_t ret = SyncRes::wipeNegCache(canon, subtree);
   return new uint64_t(ret);
 }
 
@@ -621,7 +621,7 @@ static string doCurrentQueries()
 
 uint64_t* pleaseGetThrottleSize()
 {
-  return new uint64_t(t_sstorage ? t_sstorage->throttle.size() : 0);
+  return new uint64_t(SyncRes::getThrottledServersSize());
 }
 
 static uint64_t getThrottleSize()
@@ -631,7 +631,7 @@ static uint64_t getThrottleSize()
 
 uint64_t* pleaseGetNegCacheSize()
 {
-  uint64_t tmp=(t_sstorage ? t_sstorage->negcache.size() : 0);
+  uint64_t tmp=(SyncRes::getNegCacheSize());
   return new uint64_t(tmp);
 }
 
@@ -642,7 +642,7 @@ uint64_t getNegCacheSize()
 
 uint64_t* pleaseGetFailedHostsSize()
 {
-  uint64_t tmp=(t_sstorage ? t_sstorage->fails.size() : 0);
+  uint64_t tmp=(SyncRes::getThrottledServersSize());
   return new uint64_t(tmp);
 }
 uint64_t getFailedHostsSize()
@@ -652,7 +652,7 @@ uint64_t getFailedHostsSize()
 
 uint64_t* pleaseGetNsSpeedsSize()
 {
-  return new uint64_t(t_sstorage ? t_sstorage->nsSpeeds.size() : 0);
+  return new uint64_t(SyncRes::getNSSpeedsSize());
 }
 
 uint64_t getNsSpeedsSize()
index b4995e44abfd5d4c20f93b7b1c490e5a5913a7fc..bc483e9df852e7c8a619dc66b32fa20fbbb266c8 100644 (file)
@@ -242,29 +242,6 @@ bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtyp
   return false;
 }
 
-uint64_t MemRecursorCache::doDumpNSSpeeds(int fd)
-{
-  FILE* fp=fdopen(dup(fd), "w");
-  if(!fp)
-    return 0;
-  fprintf(fp, "; nsspeed dump from thread follows\n;\n");
-  uint64_t count=0;
-
-  for(SyncRes::nsspeeds_t::iterator i = t_sstorage->nsSpeeds.begin() ; i!= t_sstorage->nsSpeeds.end(); ++i)
-  {
-    count++;
-    fprintf(fp, "%s -> ", i->first.toString().c_str());
-    for(SyncRes::DecayingEwmaCollection::collection_t::iterator j = i->second.d_collection.begin(); j!= i->second.d_collection.end(); ++j)
-    {
-      // typedef vector<pair<ComboAddress, DecayingEwma> > collection_t;
-      fprintf(fp, "%s/%f ", j->first.toString().c_str(), j->second.peek());
-    }
-    fprintf(fp, "\n");
-  }
-  fclose(fp);
-  return count;
-}
-
 uint64_t MemRecursorCache::doDump(int fd)
 {
   FILE* fp=fdopen(dup(fd), "w");
index baa988b3e22587ece94dc74a87fdb8097b544f05..8999335f9ab306e2ddd57300fc6f9d317bcb1539 100644 (file)
@@ -58,7 +58,6 @@ public:
   void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, bool auth, boost::optional<Netmask> ednsmask=boost::optional<Netmask>());
   void doPrune(void);
   uint64_t doDump(int fd);
-  uint64_t doDumpNSSpeeds(int fd);
 
   int doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff);
   bool doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL);
index d4e3fee05ac8255969eaa414dda54093e03beee1..72ab7451bbcb5cf7408a75f5dbc70401ac912269 100644 (file)
@@ -92,7 +92,6 @@ pdns_recursor_SOURCES = \
        dnssecinfra.hh dnssecinfra.cc \
        dnsseckeeper.hh \
        dnswriter.cc dnswriter.hh \
-       ecs.cc \
        ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        filterpo.cc filterpo.hh \
@@ -189,7 +188,6 @@ testrunner_SOURCES = \
        dnssecinfra.cc \
        dnswriter.cc dnswriter.hh \
        ednscookies.cc ednscookies.hh \
-       ecs.cc \
        ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        filterpo.cc filterpo.hh \
diff --git a/pdns/recursordist/ecs.cc b/pdns/recursordist/ecs.cc
deleted file mode 100644 (file)
index 7e9b717..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "syncres.hh"
-#include "arguments.hh"
-
-NetmaskGroup g_ednssubnets;
-SuffixMatchNode g_ednsdomains;
-bool g_useIncomingECS;
-
-void  parseEDNSSubnetWhitelist(const std::string& wlist)
-{
-  vector<string> parts;
-  stringtok(parts, wlist, ",; ");
-  for(const auto& a : parts) {
-    try {
-      Netmask nm(a);
-      g_ednssubnets.addMask(nm);
-    }
-    catch(...) {
-      g_ednsdomains.add(DNSName(a));
-    }
-  }
-}
index 7761a47ad62055258600c4b6838fb322621f0d9e..e2338783b243aa5367b5a648a351f4f2ada204d0 100644 (file)
@@ -8,14 +8,12 @@
 #include "rec-lua-conf.hh"
 #include "root-dnssec.hh"
 #include "syncres.hh"
+#include "utility.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};
+thread_local std::unique_ptr<MemRecursorCache> t_RC{nullptr};
 unsigned int g_numThreads = 1;
 
 /* Fake some required functions we didn't want the trouble to
@@ -53,7 +51,7 @@ void primeHints(void)
 {
   vector<DNSRecord> nsset;
   if(!t_RC)
-    t_RC = new MemRecursorCache();
+    t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
 
   DNSRecord arr, aaaarr, nsrr;
   nsrr.d_name=g_rootdnsname;
@@ -108,17 +106,7 @@ static void init(bool debug=false)
   seedRandom("/dev/urandom");
   reportAllTypes();
 
-  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
+  t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
 
   SyncRes::s_maxqperq = 50;
   SyncRes::s_maxtotusec = 1000*7000;
@@ -135,11 +123,19 @@ static void init(bool debug=false)
   SyncRes::s_rootNXTrust = true;
   SyncRes::s_minimumTTL = 0;
   SyncRes::s_serverID = "PowerDNS Unit Tests Server ID";
-
-  g_ednssubnets = NetmaskGroup();
-  g_ednsdomains = SuffixMatchNode();
-  g_useIncomingECS = false;
-  g_delegationOnly.clear();
+  SyncRes::clearEDNSSubnets();
+  SyncRes::clearEDNSDomains();
+  SyncRes::clearDelegationOnly();
+  SyncRes::clearDontQuery();
+
+  SyncRes::clearNSSpeeds();
+  BOOST_CHECK_EQUAL(SyncRes::getNSSpeedsSize(), 0);
+  SyncRes::clearEDNSStatuses();
+  BOOST_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), 0);
+  SyncRes::clearThrottle();
+  BOOST_CHECK_EQUAL(SyncRes::getThrottledServersSize(), 0);
+  SyncRes::clearFailedServers();
+  BOOST_CHECK_EQUAL(SyncRes::getFailedServersSize(), 0);
 
   auto luaconfsCopy = g_luaconfs.getCopy();
   luaconfsCopy.dfe.clear();
@@ -163,13 +159,8 @@ static void initSR(std::unique_ptr<SyncRes>& sr, bool edns0, bool dnssec, SyncRe
   sr->setDoEDNS0(edns0);
   sr->setDoDNSSEC(dnssec);
   sr->setLogMode(lm);
-  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();
+  SyncRes::setDomainMap(std::make_shared<SyncRes::domainmap_t>());
+  SyncRes::clearNegCache();
 }
 
 static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false, bool edns=false)
@@ -313,7 +304,7 @@ BOOST_AUTO_TEST_CASE(test_root_not_primed_and_no_response) {
   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_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0);
   }
 }
 
@@ -355,8 +346,8 @@ BOOST_AUTO_TEST_CASE(test_edns_formerr_fallback) {
   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_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), 1);
+  BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(noEDNSServer), SyncRes::EDNSStatus::NOEDNS);
 }
 
 BOOST_AUTO_TEST_CASE(test_edns_notimp_fallback) {
@@ -423,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();
@@ -465,8 +490,8 @@ BOOST_AUTO_TEST_CASE(test_all_nss_down) {
   BOOST_CHECK_EQUAL(downServers.size(), 4);
 
   for (const auto& server : downServers) {
-    BOOST_CHECK_EQUAL(t_sstorage->fails.value(server), 1);
-    BOOST_CHECK(t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(server, target, QType::A)));
+    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1);
+    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
   }
 }
 
@@ -513,8 +538,9 @@ BOOST_AUTO_TEST_CASE(test_all_nss_network_error) {
   BOOST_CHECK_EQUAL(downServers.size(), 4);
 
   for (const auto& server : downServers) {
-    BOOST_CHECK_EQUAL(t_sstorage->fails.value(server), 1);
-    BOOST_CHECK(t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(server, target, QType::A)));
+    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1);
+    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
+;
   }
 }
 
@@ -569,8 +595,8 @@ BOOST_AUTO_TEST_CASE(test_os_limit_errors) {
 
   /* Error is reported as "OS limit error" (-2) so the servers should _NOT_ be marked down */
   for (const auto& server : downServers) {
-    BOOST_CHECK_EQUAL(t_sstorage->fails.value(server), 0);
-    BOOST_CHECK(!t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(server, target, QType::A)));
+    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0);
+    BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), server, target, QType::A));
   }
 }
 
@@ -701,8 +727,7 @@ BOOST_AUTO_TEST_CASE(test_edns_submask_by_domain) {
   primeHints();
 
   const DNSName target("powerdns.com.");
-  g_useIncomingECS = true;
-  g_ednsdomains.add(target);
+  SyncRes::addEDNSDomain(target);
 
   EDNSSubnetOpts incomingECS;
   incomingECS.source = Netmask("192.0.2.128/32");
@@ -729,8 +754,7 @@ BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) {
   primeHints();
 
   const DNSName target("powerdns.com.");
-  g_useIncomingECS = true;
-  g_ednssubnets.addMask("192.0.2.1/32");
+  SyncRes::addEDNSSubnet(Netmask("192.0.2.1/32"));
 
   EDNSSubnetOpts incomingECS;
   incomingECS.source = Netmask("2001:DB8::FF/128");
@@ -1125,7 +1149,7 @@ BOOST_AUTO_TEST_CASE(test_throttled_server) {
     });
 
   /* mark ns as down */
-  t_sstorage->throttle.throttle(time(nullptr), boost::make_tuple(ns, "", 0), SyncRes::s_serverdownthrottletime, 10000);
+  SyncRes::doThrottle(time(nullptr), ns, SyncRes::s_serverdownthrottletime, 10000);
 
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
@@ -1146,14 +1170,14 @@ BOOST_AUTO_TEST_CASE(test_throttled_server_count) {
 
   const size_t blocks = 10;
   /* mark ns as down for 'blocks' queries */
-  t_sstorage->throttle.throttle(time(nullptr), boost::make_tuple(ns, "", 0), SyncRes::s_serverdownthrottletime, blocks);
+  SyncRes::doThrottle(time(nullptr), ns, SyncRes::s_serverdownthrottletime, blocks);
 
   for (size_t idx = 0; idx < blocks; idx++) {
-    BOOST_CHECK(t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(ns, "", 0)));
+    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), ns));
   }
 
   /* we have been throttled 'blocks' times, we should not be throttled anymore */
-  BOOST_CHECK(!t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(ns, "", 0)));
+  BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), ns));
 }
 
 BOOST_AUTO_TEST_CASE(test_throttled_server_time) {
@@ -1167,13 +1191,14 @@ BOOST_AUTO_TEST_CASE(test_throttled_server_time) {
 
   const size_t seconds = 1;
   /* mark ns as down for 'seconds' seconds */
-  t_sstorage->throttle.throttle(time(nullptr), boost::make_tuple(ns, "", 0), seconds, 10000);
-  BOOST_CHECK(t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(ns, "", 0)));
+  SyncRes::doThrottle(time(nullptr), ns, seconds, 10000);
+
+  BOOST_CHECK(SyncRes::isThrottled(time(nullptr), ns));
 
   sleep(seconds + 1);
 
   /* we should not be throttled anymore */
-  BOOST_CHECK(!t_sstorage->throttle.shouldThrottle(time(nullptr), boost::make_tuple(ns, "", 0)));
+  BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), ns));
 }
 
 BOOST_AUTO_TEST_CASE(test_dont_query_server) {
@@ -1209,7 +1234,7 @@ BOOST_AUTO_TEST_CASE(test_dont_query_server) {
     });
 
   /* prevent querying this NS */
-  g_dontQuery->addMask(Netmask(ns));
+  SyncRes::addDontQuery(Netmask(ns));
 
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
@@ -1264,14 +1289,14 @@ BOOST_AUTO_TEST_CASE(test_root_nx_trust) {
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
   BOOST_CHECK_EQUAL(ret.size(), 1);
   /* one for target1 and one for the entire TLD */
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 2);
+  BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2);
 
   ret.clear();
   res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
   BOOST_CHECK_EQUAL(ret.size(), 1);
   /* one for target1 and one for the entire TLD */
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 2);
+  BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2);
 
   /* we should have sent only one query */
   BOOST_CHECK_EQUAL(queriesCount, 1);
@@ -1327,7 +1352,7 @@ BOOST_AUTO_TEST_CASE(test_root_nx_trust_specific) {
 
   /* even with root-nx-trust on and a NX answer from the root,
      we should not have cached the entire TLD this time. */
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 1);
+  BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
 
   ret.clear();
   res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
@@ -1337,7 +1362,7 @@ BOOST_AUTO_TEST_CASE(test_root_nx_trust_specific) {
   BOOST_CHECK_EQUAL(ret[0].d_name, target2);
   BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress("192.0.2.2"));
 
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 1);
+  BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
 
   BOOST_CHECK_EQUAL(queriesCount, 3);
 }
@@ -1389,14 +1414,14 @@ BOOST_AUTO_TEST_CASE(test_root_nx_dont_trust) {
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
   BOOST_CHECK_EQUAL(ret.size(), 1);
   /* one for target1 */
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 1);
+  BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1);
 
   ret.clear();
   res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, 0);
   BOOST_CHECK_EQUAL(ret.size(), 1);
   /* one for target1 */
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 1);
+  BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1);
 
   /* we should have sent three queries */
   BOOST_CHECK_EQUAL(queriesCount, 3);
@@ -1412,8 +1437,7 @@ BOOST_AUTO_TEST_CASE(test_skip_negcache_for_variable_response) {
   const DNSName target("www.powerdns.com.");
   const DNSName cnameTarget("cname.powerdns.com.");
 
-  g_useIncomingECS = true;
-  g_ednsdomains.add(DNSName("powerdns.com."));
+  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
 
   EDNSSubnetOpts incomingECS;
   incomingECS.source = Netmask("192.0.2.128/32");
@@ -1456,7 +1480,7 @@ BOOST_AUTO_TEST_CASE(test_skip_negcache_for_variable_response) {
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
   BOOST_CHECK_EQUAL(ret.size(), 2);
   /* no negative cache entry because the response was variable */
-  BOOST_CHECK_EQUAL(t_sstorage->negcache.size(), 0);
+  BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(test_ns_speed) {
@@ -1514,12 +1538,12 @@ BOOST_AUTO_TEST_CASE(test_ns_speed) {
 
   /* make pdns-public-ns2.powerdns.com. the fastest NS, with its IPv6 address faster than the IPV4 one,
      then pdns-public-ns1.powerdns.com. on IPv4 */
-  t_sstorage->nsSpeeds[DNSName("pdns-public-ns1.powerdns.com.")].submit(ComboAddress("192.0.2.1:53"), 100, &now);
-  t_sstorage->nsSpeeds[DNSName("pdns-public-ns1.powerdns.com.")].submit(ComboAddress("[2001:DB8::1]:53"), 10000, &now);
-  t_sstorage->nsSpeeds[DNSName("pdns-public-ns2.powerdns.com.")].submit(ComboAddress("192.0.2.2:53"), 10, &now);
-  t_sstorage->nsSpeeds[DNSName("pdns-public-ns2.powerdns.com.")].submit(ComboAddress("[2001:DB8::2]:53"), 1, &now);
-  t_sstorage->nsSpeeds[DNSName("pdns-public-ns3.powerdns.com.")].submit(ComboAddress("192.0.2.3:53"), 10000, &now);
-  t_sstorage->nsSpeeds[DNSName("pdns-public-ns3.powerdns.com.")].submit(ComboAddress("[2001:DB8::3]:53"), 10000, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("192.0.2.1:53"), 100, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("[2001:DB8::1]:53"), 10000, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("192.0.2.2:53"), 10, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("[2001:DB8::2]:53"), 1, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("192.0.2.3:53"), 10000, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("[2001:DB8::3]:53"), 10000, &now);
 
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
@@ -1572,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();
@@ -1729,8 +1789,8 @@ BOOST_AUTO_TEST_CASE(test_delegation_only) {
   primeHints();
 
   /* Thanks, Verisign */
-  g_delegationOnly.insert(DNSName("com."));
-  g_delegationOnly.insert(DNSName("net."));
+  SyncRes::addDelegationOnly(DNSName("com."));
+  SyncRes::addDelegationOnly(DNSName("net."));
 
   const DNSName target("nx-powerdns.com.");
 
@@ -2305,11 +2365,13 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_nord) {
   SyncRes::AuthDomain ad;
   ad.d_rdForward = false;
   ad.d_servers.push_back(forwardedNS);
-  (*t_sstorage->domainmap)[target] = ad;
+  (*SyncRes::t_sstorage.domainmap)[target] = ad;
 
   sr->setAsyncCallback([forwardedNS](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 (ip == forwardedNS) {
+        BOOST_CHECK_EQUAL(sendRDQuery, false);
+
         setLWResult(res, 0, true, false, true);
         addRecordToLW(res, domain, QType::A, "192.0.2.42");
         return 1;
@@ -2341,11 +2403,13 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_rd) {
   SyncRes::AuthDomain ad;
   ad.d_rdForward = false;
   ad.d_servers.push_back(forwardedNS);
-  (*t_sstorage->domainmap)[target] = ad;
+  (*SyncRes::t_sstorage.domainmap)[target] = ad;
 
   sr->setAsyncCallback([forwardedNS](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 (ip == forwardedNS) {
+        BOOST_CHECK_EQUAL(sendRDQuery, false);
+
         setLWResult(res, 0, true, false, true);
         addRecordToLW(res, domain, QType::A, "192.0.2.42");
         return 1;
@@ -2374,11 +2438,13 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_nord) {
   SyncRes::AuthDomain ad;
   ad.d_rdForward = true;
   ad.d_servers.push_back(forwardedNS);
-  (*t_sstorage->domainmap)[target] = ad;
+  (*SyncRes::t_sstorage.domainmap)[target] = ad;
 
   sr->setAsyncCallback([forwardedNS](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 (ip == forwardedNS) {
+        BOOST_CHECK_EQUAL(sendRDQuery, false);
+
         setLWResult(res, 0, true, false, true);
         addRecordToLW(res, domain, QType::A, "192.0.2.42");
         return 1;
@@ -2410,11 +2476,13 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd) {
   SyncRes::AuthDomain ad;
   ad.d_rdForward = true;
   ad.d_servers.push_back(forwardedNS);
-  (*t_sstorage->domainmap)[target] = ad;
+  (*SyncRes::t_sstorage.domainmap)[target] = ad;
 
   sr->setAsyncCallback([forwardedNS](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 (ip == forwardedNS) {
+        BOOST_CHECK_EQUAL(sendRDQuery, true);
+
         setLWResult(res, 0, true, false, true);
         addRecordToLW(res, domain, QType::A, "192.0.2.42");
         return 1;
@@ -2459,7 +2527,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_oob) {
   dr.d_content = std::make_shared<ARecordContent>(nsAddr);
   ad.d_records.insert(dr);
 
-  (*t_sstorage->domainmap)[authZone] = ad;
+  (*SyncRes::t_sstorage.domainmap)[authZone] = ad;
 
   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++;
@@ -2484,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 845155b77581fa33690a1ba96a88cc1ecac98756..772decded2892066dcb2b94c1f6846f7bdfc2099 100644 (file)
@@ -41,7 +41,7 @@ void primeHints(void)
   // prime root cache
   vector<DNSRecord> nsset;
   if(!t_RC)
-    t_RC = new MemRecursorCache();
+    t_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
 
   if(::arg()["hint-file"].empty()) {
     DNSRecord arr, aaaarr, nsrr;
@@ -97,7 +97,7 @@ void primeHints(void)
   t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), false); // and stuff in the cache
 }
 
-static void makeNameToIPZone(SyncRes::domainmap_t* newMap, const DNSName& hostname, const string& ip)
+static void makeNameToIPZone(std::shared_ptr<SyncRes::domainmap_t> newMap, const DNSName& hostname, const string& ip)
 {
   SyncRes::AuthDomain ad;
   ad.d_rdForward=false;
@@ -126,12 +126,13 @@ static void makeNameToIPZone(SyncRes::domainmap_t* newMap, const DNSName& hostna
   }
   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;
   }
 }
 
 //! parts[0] must be an IP address, the rest must be host names
-static void makeIPToNamesZone(SyncRes::domainmap_t* newMap, const vector<string>& parts) 
+static void makeIPToNamesZone(std::shared_ptr<SyncRes::domainmap_t> newMap, const vector<string>& parts)
 {
   string address=parts[0];
   vector<string> ipparts;
@@ -172,7 +173,8 @@ static void makeIPToNamesZone(SyncRes::domainmap_t* newMap, const vector<string>
   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;
   }
 }
 
@@ -229,26 +231,28 @@ void convertServersForAD(const std::string& input, SyncRes::AuthDomain& ad, cons
 
 void* pleaseWipeNegCache()
 {
-  t_sstorage->negcache.clear();   
+  SyncRes::clearNegCache();
   return 0;
 }
 
-void* pleaseUseNewSDomainsMap(SyncRes::domainmap_t* newmap)
+void* pleaseUseNewSDomainsMap(std::shared_ptr<SyncRes::domainmap_t> newmap)
 {
-  t_sstorage->domainmap = newmap;
+  SyncRes::setDomainMap(newmap);
   return 0;
 }
 
 string reloadAuthAndForwards()
 {
-  SyncRes::domainmap_t* original=t_sstorage->domainmap;  
+  std::shared_ptr<SyncRes::domainmap_t> original=SyncRes::getDomainMap();
   
   try {
     L<<Logger::Warning<<"Reloading zones, purging data from cache"<<endl;
-  
-    for(SyncRes::domainmap_t::const_iterator i = t_sstorage->domainmap->begin(); i != t_sstorage->domainmap->end(); ++i) {
-      for(SyncRes::AuthDomain::records_t::const_iterator j = i->second.d_records.begin(); j != i->second.d_records.end(); ++j) 
-        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, j->d_name, false));
+
+    if (original) {
+      for(const auto& i : *original) {
+        for(const auto& j : i.second.d_records)
+          broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, j.d_name, false));
+      }
     }
 
     string configname=::arg()["config-dir"]+"/recursor.conf";
@@ -285,17 +289,16 @@ string reloadAuthAndForwards()
     ::arg().preParse(g_argc, g_argv, "export-etc-hosts");
     ::arg().preParse(g_argc, g_argv, "serve-rfc1918");
 
-    SyncRes::domainmap_t* newDomainMap = parseAuthAndForwards();
+    std::shared_ptr<SyncRes::domainmap_t> newDomainMap = parseAuthAndForwards();
     
     // purge again - new zones need to blank out the cache
-    for(SyncRes::domainmap_t::const_iterator i = newDomainMap->begin(); i != newDomainMap->end(); ++i) {
-        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, i->first, true));
-        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, i->first, true));
-        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, i->first, true));
+    for(const auto& i : *newDomainMap) {
+        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, i.first, true));
+        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, i.first, true));
+        broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, i.first, true));
     }
 
-    broadcastFunction(boost::bind(pleaseUseNewSDomainsMap, newDomainMap)); 
-    delete original;
+    broadcastFunction(boost::bind(pleaseUseNewSDomainsMap, newDomainMap));
     return "ok\n";
   }
   catch(std::exception& e) {
@@ -397,12 +400,12 @@ void RPZIXFRTracker(const ComboAddress& master, const DNSName& zoneName, boost::
   }
 }
 
-SyncRes::domainmap_t* parseAuthAndForwards()
+std::shared_ptr<SyncRes::domainmap_t> parseAuthAndForwards()
 {
   TXTRecordContent::report();
   OPTRecordContent::report();
 
-  SyncRes::domainmap_t* newMap = new SyncRes::domainmap_t();
+  auto newMap = std::make_shared<SyncRes::domainmap_t>();
 
   typedef vector<string> parts_t;
   parts_t parts;  
@@ -430,11 +433,9 @@ SyncRes::domainmap_t* parseAuthAndForwards()
            dr.d_place=DNSResourceRecord::ANSWER;
           }
           catch(std::exception &e) {
-            delete newMap;
             throw PDNSException("Error parsing record '"+rr.qname.toString()+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"': "+e.what());
           }
           catch(...) {
-            delete newMap;
             throw PDNSException("Error parsing record '"+rr.qname.toString()+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"'");
           }
 
@@ -455,8 +456,9 @@ SyncRes::domainmap_t* parseAuthAndForwards()
           ad.d_rdForward = true;
         }
       }
-      
-      (*newMap)[DNSName(headers.first)]=ad; 
+
+      ad.d_name = DNSName(headers.first);
+      (*newMap)[ad.d_name]=ad;
     }
   }
   
@@ -466,7 +468,6 @@ SyncRes::domainmap_t* parseAuthAndForwards()
     FILE *rfp=fopen(::arg()["forward-zones-file"].c_str(), "r");
 
     if(!rfp) {
-      delete newMap;
       throw PDNSException("Error opening forward-zones-file '"+::arg()["forward-zones-file"]+"': "+stringerror());
     }
 
@@ -494,7 +495,6 @@ SyncRes::domainmap_t* parseAuthAndForwards()
       else
         ad.d_rdForward = false;
       if(domain.empty()) {
-        delete newMap;
         throw PDNSException("Error parsing line "+std::to_string(linenum)+" of " +::arg()["forward-zones-file"]);
       }
 
@@ -502,11 +502,11 @@ SyncRes::domainmap_t* parseAuthAndForwards()
         convertServersForAD(instructions, ad, ",; ", false);
       }
       catch(...) {
-        delete newMap;
         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 1fa95b0bad8721f36d787e69dab3bcae27d39277..4652fb1754f885f3032f6abd9e4728e85c42ebf2 100644 (file)
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include <boost/algorithm/string.hpp>
 
-#include "lua-recursor4.hh"
-#include "utility.hh"
-#include "syncres.hh"
-#include <iostream>
-#include <map>
-#include "dnsrecords.hh"
-#include <algorithm>
-#include <set>
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <utility>
-#include <deque>
-#include "logger.hh"
-#include "validate.hh"
-#include "misc.hh"
 #include "arguments.hh"
-#include "lwres.hh"
-#include "recursor_cache.hh"
-#include "dnsparser.hh"
+#include "cachecleaner.hh"
 #include "dns_random.hh"
-#include "lock.hh"
+#include "dnsparser.hh"
+#include "dnsrecords.hh"
 #include "ednssubnet.hh"
-#include "cachecleaner.hh"
+#include "logger.hh"
+#include "lua-recursor4.hh"
 #include "rec-lua-conf.hh"
-__thread SyncRes::StaticStorage* t_sstorage;
+#include "syncres.hh"
+
+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;
@@ -69,20 +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;
+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) {
@@ -114,12 +103,9 @@ 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_now(now),
-                                              d_cacheonly(false), d_nocache(false), d_doDNSSEC(false), d_doEDNS0(false), d_lm(s_lm)
+                                              d_cacheonly(false), d_doDNSSEC(false), d_doEDNS0(false), d_lm(s_lm)
                                                  
 { 
-  if(!t_sstorage) {
-    t_sstorage = new StaticStorage();
-  }
 }
 
 /** everything begins here - this is the entry point just after receiving a packet */
@@ -158,7 +144,7 @@ int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qcl
  * - version.pdns. CH TXT
  * - id.server. CH TXT
  */
-bool SyncRes::doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t &qclass, vector<DNSRecord> &ret)
+bool SyncRes::doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t qclass, vector<DNSRecord> &ret)
 {
   static const DNSName arpa("1.0.0.127.in-addr.arpa."), ip6_arpa("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa."),
     localhost("localhost."), versionbind("version.bind."), idserver("id.server."), versionpdns("version.pdns.");
@@ -214,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");
@@ -328,13 +334,36 @@ void SyncRes::doEDNSDumpAndClose(int fd)
     return;
   }
   fprintf(fp,"IP Address\tMode\tMode last updated at\n");
-  for(const auto& eds : t_sstorage->ednsstatus) {
+  for(const auto& eds : t_sstorage.ednsstatus) {
     fprintf(fp, "%s\t%d\t%s", eds.first.toString().c_str(), (int)eds.second.mode, ctime(&eds.second.modeSetAt));
   }
 
   fclose(fp);
 }
 
+uint64_t SyncRes::doDumpNSSpeeds(int fd)
+{
+  FILE* fp=fdopen(dup(fd), "w");
+  if(!fp)
+    return 0;
+  fprintf(fp, "; nsspeed dump from thread follows\n;\n");
+  uint64_t count=0;
+
+  for(const auto& i : t_sstorage.nsSpeeds)
+  {
+    count++;
+    fprintf(fp, "%s -> ", i.first.toString().c_str());
+    for(const auto& j : i.second.d_collection)
+    {
+      // typedef vector<pair<ComboAddress, DecayingEwma> > collection_t;
+      fprintf(fp, "%s/%f ", j.first.toString().c_str(), j.second.peek());
+    }
+    fprintf(fp, "\n");
+  }
+  fclose(fp);
+  return count;
+}
+
 /* so here is the story. First we complete the full resolution process for a domain name. And only THEN do we decide
    to also do DNSSEC validation, which leads to new queries. To make this simple, we *always* ask for DNSSEC records
    so that if there are RRSIGs for a name, we'll have them.
@@ -379,7 +408,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
   */
 
   SyncRes::EDNSStatus* ednsstatus;
-  ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port? YES
+  ednsstatus = &t_sstorage.ednsstatus[ip]; // does this include port? YES
 
   if(ednsstatus->modeSetAt && ednsstatus->modeSetAt + 3600 < d_now.tv_sec) {
     *ednsstatus=SyncRes::EDNSStatus();
@@ -470,20 +499,20 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
   int res=0;
 
   // This is a difficult way of expressing "this is a normal query", i.e. not getRootNS.
-  if(!(d_nocache && qtype.getCode()==QType::NS && qname.isRoot())) {
+  if(!(d_updatingRootNS && qtype.getCode()==QType::NS && qname.isRoot())) {
     if(d_cacheonly) { // very limited OOB support
       LWResult lwr;
       LOG(prefix<<qname<<": Recursion not requested for '"<<qname<<"|"<<qtype.getName()<<"', peeking at auth/forward zones"<<endl);
       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 != t_sstorage.domainmap->end()) {
+        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);
 
@@ -608,9 +637,9 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
     random_shuffle(ret.begin(), ret.end(), dns_random);
 
     // move 'best' address for this nameserver name up front
-    nsspeeds_t::iterator best = t_sstorage->nsSpeeds.find(qname);
+    nsspeeds_t::iterator best = t_sstorage.nsSpeeds.find(qname);
 
-    if(best != t_sstorage->nsSpeeds.end())
+    if(best != t_sstorage.nsSpeeds.end())
       for(ret_t::iterator i=ret.begin(); i != ret.end(); ++i) {
         if(*i==best->second.d_best) {  // got the fastest one
           if(i!=ret.begin()) {
@@ -696,7 +725,10 @@ 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(d_now, d_asyncResolve);
+      /* let's prevent an infinite loop */
+      if (!d_updatingRootNS) {
+        getRootNS(d_now, d_asyncResolve);
+      }
     }
   }while(subdomain.chopOff());
 }
@@ -705,8 +737,8 @@ SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname) co
 {
   SyncRes::domainmap_t::const_iterator ret;
   do {
-    ret=t_sstorage->domainmap->find(*qname);
-    if(ret!=t_sstorage->domainmap->end())
+    ret=t_sstorage.domainmap->find(*qname);
+    if(ret!=t_sstorage.domainmap->end())
       break;
   }while(qname->chopOff());
   return ret;
@@ -719,15 +751,16 @@ DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtyp
   DNSName authdomain(qname);
 
   domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
-  if(iter!=t_sstorage->domainmap->end()) {
-    if( iter->second.d_servers.empty() )
+  if(iter!=t_sstorage.domainmap->end()) {
+    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}});
     else {
       // Again, picked up in doResolveAt. An empty DNSName, combined with a
       // non-empty vector of ComboAddresses means 'this is a forwarded domain'
-      nsset.insert({DNSName(), {iter->second.d_servers, iter->second.d_rdForward}});
+      // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
+      nsset.insert({DNSName(), {iter->second.d_servers, iter->second.shouldRecurse() }});
     }
     return authdomain;
   }
@@ -831,7 +864,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector<DNSR
   bool wasForwardedOrAuth = false;
   bool wasAuth = false;
   domainmap_t::const_iterator iter=getBestAuthZone(&authname);
-  if(iter != t_sstorage->domainmap->end()) {
+  if(iter != t_sstorage.domainmap->end()) {
     wasForwardedOrAuth = true;
     const vector<ComboAddress>& servers = iter->second.d_servers;
     if(servers.empty()) {
@@ -841,7 +874,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector<DNSR
   NegCache::NegCacheEntry ne;
 
   if(s_rootNXTrust &&
-     t_sstorage->negcache.getRootNXTrust(qname, d_now, ne) &&
+     t_sstorage.negcache.getRootNXTrust(qname, d_now, ne) &&
       ne.d_auth.isRoot() &&
       !(wasForwardedOrAuth && !authname.isRoot())) { // when forwarding, the root may only neg-cache if it was forwarded to.
     sttl = ne.d_ttd - d_now.tv_sec;
@@ -849,7 +882,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector<DNSR
     res = RCode::NXDomain;
     giveNegative = true;
   }
-  else if (t_sstorage->negcache.get(qname, qtype, d_now, ne) &&
+  else if (t_sstorage.negcache.get(qname, qtype, d_now, ne) &&
            !(wasForwardedOrAuth && ne.d_auth != authname)) { // Only the authname nameserver can neg cache entries
     res = 0;
     sttl = ne.d_ttd - d_now.tv_sec;
@@ -948,7 +981,7 @@ inline vector<DNSName> SyncRes::shuffleInSpeedOrder(NsSet &tnameservers, const s
 
   for(const auto& val: rnameservers) {
     double speed;
-    speed=t_sstorage->nsSpeeds[val].get(&d_now);
+    speed=t_sstorage.nsSpeeds[val].get(&d_now);
     speeds[val]=speed;
   }
   random_shuffle(rnameservers.begin(),rnameservers.end(), dns_random);
@@ -1088,19 +1121,17 @@ vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix,
 
 bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery)
 {
-  extern NetmaskGroup* g_dontQuery;
-
-  if(t_sstorage->throttle.shouldThrottle(d_now.tv_sec, boost::make_tuple(remoteIP, "", 0))) {
+  if(t_sstorage.throttle.shouldThrottle(d_now.tv_sec, boost::make_tuple(remoteIP, "", 0))) {
     LOG(prefix<<qname<<": server throttled "<<endl);
     s_throttledqueries++; d_throttledqueries++;
     return true;
   }
-  else if(t_sstorage->throttle.shouldThrottle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()))) {
-    LOG(prefix<<qname<<": query throttled "<<endl);
+  else if(t_sstorage.throttle.shouldThrottle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()))) {
+    LOG(prefix<<qname<<": query throttled "<<remoteIP.toString()<<", "<<qname<<"; "<<qtype.getName()<<endl);
     s_throttledqueries++; d_throttledqueries++;
     return true;
   }
-  else if(!pierceDontQuery && g_dontQuery && g_dontQuery->match(&remoteIP)) {
+  else if(!pierceDontQuery && s_dontQuery && s_dontQuery->match(&remoteIP)) {
     LOG(prefix<<qname<<": not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
     s_dontqueries++;
     return true;
@@ -1108,7 +1139,7 @@ bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress&
   return false;
 }
 
-RCode::rcodes_ SyncRes::updateCacheFromRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const DNSName& auth, NsSet& nameservers, const DNSName& tns, const boost::optional<Netmask> ednsmask)
+RCode::rcodes_ SyncRes::updateCacheFromRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask> ednsmask)
 {
   struct CachePair
   {
@@ -1153,25 +1184,25 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(const std::string& prefix, LWResu
       if(rec.d_type == QType::RRSIG) {
         LOG("RRSIG - separate"<<endl);
       }
-      else if(lwr.d_aabit && lwr.d_rcode==RCode::NoError && rec.d_place==DNSResourceRecord::ANSWER && (rec.d_type != QType::DNSKEY || rec.d_name != auth) && g_delegationOnly.count(auth)) {
+      else if(lwr.d_aabit && lwr.d_rcode==RCode::NoError && rec.d_place==DNSResourceRecord::ANSWER && (rec.d_type != QType::DNSKEY || rec.d_name != auth) && s_delegationOnly.count(auth)) {
         LOG("NO! Is from delegation-only zone"<<endl);
         s_nodelegated++;
         return RCode::NXDomain;
       }
       else {
         bool haveLogged = false;
-        if (!t_sstorage->domainmap->empty()) {
+        if (!t_sstorage.domainmap->empty()) {
           // Check if we are authoritative for a zone in this answer
           DNSName tmp_qname(rec.d_name);
           auto auth_domain_iter=getBestAuthZone(&tmp_qname);
-          if(auth_domain_iter!=t_sstorage->domainmap->end() &&
+          if(auth_domain_iter!=t_sstorage.domainmap->end() &&
              auth.countLabels() <= auth_domain_iter->first.countLabels()) {
             if (auth_domain_iter->first != auth) {
               LOG("NO! - we are authoritative for the zone "<<auth_domain_iter->first<<endl);
               continue;
             } else {
               LOG("YES! - This answer was ");
-              if (nameservers[tns].first.empty()) {
+              if (!wasForwarded) {
                 LOG("retrieved from the local auth store.");
               } else {
                 LOG("received from a server we forward to.");
@@ -1213,7 +1244,9 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(const std::string& prefix, LWResu
 //             cout<<'|'<<DNSRecordContent::NumberToType(i->first.type)<<endl;
     if(i->second.records.empty()) // this happens when we did store signatures, but passed on the records themselves
       continue;
+
     t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, lwr.d_aabit, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::optional<Netmask>());
+
     if(i->first.place == DNSResourceRecord::ANSWER && ednsmask)
       d_wasVariable=true;
   }
@@ -1221,7 +1254,7 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(const std::string& prefix, LWResu
   return RCode::NoError;
 }
 
-bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, bool& sawDS)
+bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic)
 {
   bool done = false;
 
@@ -1244,10 +1277,10 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
         ne.d_qtype = QType(0); // this encodes 'whole record'
         ne.d_auth = rec.d_name;
         harvestNXRecords(lwr.d_records, ne);
-        t_sstorage->negcache.add(ne);
+        t_sstorage.negcache.add(ne);
         if(s_rootNXTrust && ne.d_auth.isRoot() && auth.isRoot()) {
           ne.d_name = ne.d_name.getLastLabel();
-          t_sstorage->negcache.add(ne);
+          t_sstorage.negcache.add(ne);
         }
       }
 
@@ -1290,7 +1323,6 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
     }
     else if(rec.d_place==DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name) && rec.d_type==QType::DS) {
       LOG(prefix<<qname<<": got DS record '"<<rec.d_name<<"' -> '"<<rec.d_content->getZoneRepresentation()<<"'"<<endl);
-      sawDS=true;
     }
     else if(!done && rec.d_place==DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name) && rec.d_type==QType::SOA &&
             lwr.d_rcode==RCode::NoError) {
@@ -1310,7 +1342,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
           ne.d_qtype = qtype;
           harvestNXRecords(lwr.d_records, ne);
           if(qtype.getCode()) {  // prevents us from blacking out a whole domain
-            t_sstorage->negcache.add(ne);
+            t_sstorage.negcache.add(ne);
           }
         }
         negindic=true;
@@ -1321,6 +1353,223 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
   return done;
 }
 
+bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated)
+{
+  int resolveret;
+  s_outqueries++;
+  d_outqueries++;
+
+  if(d_outqueries + d_throttledqueries > s_maxqperq) {
+    throw ImmediateServFailException("more than "+std::to_string(s_maxqperq)+" (max-qperq) queries sent while resolving "+qname.toLogString());
+  }
+
+  if(s_maxtotusec && d_totUsec > s_maxtotusec) {
+    throw ImmediateServFailException("Too much time waiting for "+qname.toLogString()+"|"+qtype.getName()+", timeouts: "+std::to_string(d_timeouts) +", throttles: "+std::to_string(d_throttledqueries) + ", queries: "+std::to_string(d_outqueries)+", "+std::to_string(d_totUsec/1000)+"msec");
+  }
+
+  if(doTCP) {
+    LOG(prefix<<qname<<": using TCP with "<< remoteIP.toStringWithPort() <<endl);
+    s_tcpoutqueries++;
+    d_tcpoutqueries++;
+  }
+
+  if(d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, resolveret)) {
+    LOG(prefix<<qname<<": query handled by Lua"<<endl);
+  }
+  else {
+    ednsmask=getEDNSSubnetMask(d_requestor, qname, remoteIP);
+    if(ednsmask) {
+      LOG(prefix<<qname<<": Adding EDNS Client Subnet Mask "<<ednsmask->toString()<<" to query"<<endl);
+    }
+    resolveret = asyncresolveWrapper(remoteIP, d_doDNSSEC, qname,  qtype.getCode(),
+                                     doTCP, sendRDQuery, &d_now, ednsmask, &lwr);    // <- we go out on the wire!
+    if(ednsmask) {
+      LOG(prefix<<qname<<": Received EDNS Client Subnet Mask "<<ednsmask->toString()<<" on response"<<endl);
+    }
+  }
+
+  /* preoutquery killed the query by setting dq.rcode to -3 */
+  if(resolveret==-3) {
+    throw ImmediateServFailException("Query killed by policy");
+  }
+
+  d_totUsec += lwr.d_usec;
+  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
+
+  if(resolveret != 1) {
+    /* Error while resolving */
+    if(resolveret == 0) {
+      /* Time out */
+
+      LOG(prefix<<qname<<": timeout resolving after "<<lwr.d_usec/1000.0<<"msec "<< (doTCP ? "over TCP" : "")<<endl);
+      d_timeouts++;
+      s_outgoingtimeouts++;
+
+      if(remoteIP.sin4.sin_family == AF_INET)
+        s_outgoing4timeouts++;
+      else
+        s_outgoing6timeouts++;
+    }
+    else if(resolveret == -2) {
+      /* OS resource limit reached */
+      LOG(prefix<<qname<<": hit a local resource limit resolving"<< (doTCP ? " over TCP" : "")<<", probable error: "<<stringerror()<<endl);
+      g_stats.resourceLimits++;
+    }
+    else {
+      /* -1 means server unreachable */
+      s_unreachables++;
+      d_unreachables++;
+      LOG(prefix<<qname<<": error resolving from "<<remoteIP.toString()<< (doTCP ? " over TCP" : "") <<", possible error: "<<strerror(errno)<< endl);
+    }
+
+    if(resolveret != -2) { // don't account for resource limits, they are our own fault
+      t_sstorage.nsSpeeds[nsName].submit(remoteIP, 1000000, &d_now); // 1 sec
+
+      // code below makes sure we don't filter COM or the root
+      if (s_serverdownmaxfails > 0 && (auth != g_rootdnsname) && t_sstorage.fails.incr(remoteIP) >= s_serverdownmaxfails) {
+        LOG(prefix<<qname<<": Max fails reached resolving on "<< remoteIP.toString() <<". Going full throttle for "<< s_serverdownthrottletime <<" seconds" <<endl);
+        // mark server as down
+        t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, "", 0), s_serverdownthrottletime, 10000);
+      }
+      else if (resolveret == -1) {
+        // unreachable, 1 minute or 100 queries
+        t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 60, 100);
+      }
+      else {
+        // timeout
+        t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 10, 5);
+      }
+    }
+
+    return false;
+  }
+
+  /* we got an answer */
+  if(lwr.d_rcode==RCode::ServFail || lwr.d_rcode==RCode::Refused) {
+    LOG(prefix<<qname<<": "<<nsName<<" ("<<remoteIP.toString()<<") returned a "<< (lwr.d_rcode==RCode::ServFail ? "ServFail" : "Refused") << ", trying sibling IP or NS"<<endl);
+    t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 60, 3);
+    return false;
+  }
+
+  /* this server sent a valid answer, mark it backup up if it was down */
+  if(s_serverdownmaxfails > 0) {
+    t_sstorage.fails.clear(remoteIP);
+  }
+
+  if(lwr.d_tcbit) {
+    *truncated = true;
+
+    if (doTCP) {
+      LOG(prefix<<qname<<": truncated bit set, over TCP?"<<endl);
+      /* let's treat that as a ServFail answer from this server */
+      t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 60, 3);
+      return false;
+    }
+
+    return true;
+  }
+
+  return true;
+}
+
+bool SyncRes::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)
+{
+  string prefix;
+  if(doLog()) {
+    prefix=d_prefix;
+    prefix.append(depth, ' ');
+  }
+
+  if(s_minimumTTL) {
+    for(auto& rec : lwr.d_records) {
+      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
+    }
+  }
+
+  *rcode = updateCacheFromRecords(prefix, lwr, qname, auth, wasForwarded, ednsmask);
+  if (*rcode != RCode::NoError) {
+    return true;
+  }
+
+  LOG(prefix<<qname<<": determining status after receiving this packet"<<endl);
+
+  set<DNSName> nsset;
+  bool realreferral=false, negindic=false;
+  DNSName newauth;
+  DNSName newtarget;
+
+  bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic);
+
+  if(done){
+    LOG(prefix<<qname<<": status=got results, this level of recursion done"<<endl);
+    *rcode = RCode::NoError;
+    return true;
+  }
+
+  if(!newtarget.empty()) {
+    if(newtarget == qname) {
+      LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
+      *rcode = RCode::ServFail;
+      return true;
+    }
+
+    if(depth > 10) {
+      LOG(prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl);
+      *rcode = RCode::ServFail;
+      return true;
+    }
+
+    LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
+
+    set<GetBestNSAnswer> beenthere2;
+    *rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere2);
+    return true;
+  }
+
+  if(lwr.d_rcode == RCode::NXDomain) {
+    LOG(prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl);
+
+    if(d_doDNSSEC)
+      addNXNSECS(ret, lwr.d_records);
+
+    *rcode = RCode::NXDomain;
+    return true;
+  }
+
+  if(nsset.empty() && !lwr.d_rcode && (negindic || lwr.d_aabit || sendRDQuery)) {
+    LOG(prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA) " : "")<<(lwr.d_aabit ? "(have aa bit) " : "")<<endl);
+
+    if(d_doDNSSEC)
+      addNXNSECS(ret, lwr.d_records);
+
+    *rcode = RCode::NoError;
+    return true;
+  }
+
+  if(realreferral) {
+    LOG(prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, ");
+    auth=newauth;
+
+    nameservers.clear();
+    for (auto const &nameserver : nsset) {
+      if (d_wantsRPZ) {
+        d_appliedPolicy = dfe.getProcessingPolicy(nameserver, d_discardedPolicies);
+        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+          LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
+          *rcode = -2;
+          return true;
+        }
+      }
+      nameservers.insert({nameserver, {{}, false}});
+    }
+    LOG("looping to them"<<endl);
+    *gotNewServers = true;
+    return false;
+  }
+
+  return false;
+}
+
 /** returns:
  *  -1 in case of no results
  *  -2 when a FilterEngine Policy was hit
@@ -1359,6 +1608,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
         }
         return -1;
       }
+
       // this line needs to identify the 'self-resolving' behaviour, but we get it wrong now
       if(qname == *tns && qtype.getCode()==QType::A && rnameservers.size() > (size_t)(1+1*s_doIPv6)) {
         LOG(prefix<<qname<<": Not using NS to resolve itself! ("<<(1+tns-rnameservers.cbegin())<<"/"<<rnameservers.size()<<")"<<endl);
@@ -1368,18 +1618,31 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
       typedef vector<ComboAddress> remoteIPs_t;
       remoteIPs_t remoteIPs;
       remoteIPs_t::const_iterator remoteIP;
-      bool doTCP=false;
       bool pierceDontQuery=false;
       bool sendRDQuery=false;
       boost::optional<Netmask> ednsmask;
       LWResult lwr;
-      if(tns->empty() && nameservers[*tns].first.empty() ) {
+      const bool wasForwarded = tns->empty() && (!nameservers[*tns].first.empty());
+      int rcode = RCode::NoError;
+      bool gotNewServers = false;
+
+      if(tns->empty() && !wasForwarded) {
         LOG(prefix<<qname<<": Domain is out-of-band"<<endl);
         d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, lwr.d_rcode);
         lwr.d_tcbit=false;
         lwr.d_aabit=true;
+
+        /* we have received an answer, are we done ? */
+        bool done = processAnswer(depth, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode);
+        if (done) {
+          return rcode;
+        }
+        if (gotNewServers) {
+          break;
+        }
       }
       else {
+        /* if tns is empty, retrieveAddressesForNS() knows we have hardcoded servers (i.e. "forwards") */
         remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet);
 
         if(remoteIPs.empty()) {
@@ -1406,197 +1669,53 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
 
         for(remoteIP = remoteIPs.cbegin(); remoteIP != remoteIPs.cend(); ++remoteIP) {
           LOG(prefix<<qname<<": Trying IP "<< remoteIP->toStringWithPort() <<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl);
+
           if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
             continue;
           }
-          else {
-            int resolveret;
-            s_outqueries++; d_outqueries++;
-            if(d_outqueries + d_throttledqueries > s_maxqperq) throw ImmediateServFailException("more than "+std::to_string(s_maxqperq)+" (max-qperq) queries sent while resolving "+qname.toLogString());
-          TryTCP:
-            if(doTCP) {
-              LOG(prefix<<qname<<": using TCP with "<< remoteIP->toStringWithPort() <<endl);
-              s_tcpoutqueries++; d_tcpoutqueries++;
-            }
-
-           if(s_maxtotusec && d_totUsec > s_maxtotusec)
-             throw ImmediateServFailException("Too much time waiting for "+qname.toLogString()+"|"+qtype.getName()+", timeouts: "+std::to_string(d_timeouts) +", throttles: "+std::to_string(d_throttledqueries) + ", queries: "+std::to_string(d_outqueries)+", "+std::to_string(d_totUsec/1000)+"msec");
-
-           if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, resolveret)) {
-             LOG(prefix<<qname<<": query handled by Lua"<<endl);
-           }
-           else {
-             ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP);
-              if(ednsmask) {
-                LOG(prefix<<qname<<": Adding EDNS Client Subnet Mask "<<ednsmask->toString()<<" to query"<<endl);
-              }
-             resolveret=asyncresolveWrapper(*remoteIP, d_doDNSSEC, qname,  qtype.getCode(),
-                                            doTCP, sendRDQuery, &d_now, ednsmask, &lwr);    // <- we go out on the wire!
-              if(ednsmask) {
-                LOG(prefix<<qname<<": Received EDNS Client Subnet Mask "<<ednsmask->toString()<<" on response"<<endl);
-              }
 
+          bool truncated = false;
+          bool gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery,
+                                             *tns, *remoteIP, false, &truncated);
+          if (gotAnswer && truncated ) {
+            /* retry, over TCP this time */
+            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery,
+                                          *tns, *remoteIP, true, &truncated);
+          }
 
-           }
-            if(resolveret==-3)
-             throw ImmediateServFailException("Query killed by policy");
-
-           d_totUsec += lwr.d_usec;
-           accountAuthLatency(lwr.d_usec, remoteIP->sin4.sin_family);
-           if(resolveret != 1) {
-              if(resolveret==0) {
-                LOG(prefix<<qname<<": timeout resolving after "<<lwr.d_usec/1000.0<<"msec "<< (doTCP ? "over TCP" : "")<<endl);
-                d_timeouts++;
-                s_outgoingtimeouts++;
-               if(remoteIP->sin4.sin_family == AF_INET)
-                 s_outgoing4timeouts++;
-               else
-                 s_outgoing6timeouts++;
-              }
-              else if(resolveret==-2) {
-                LOG(prefix<<qname<<": hit a local resource limit resolving"<< (doTCP ? " over TCP" : "")<<", probable error: "<<stringerror()<<endl);
-                g_stats.resourceLimits++;
-              }
-              else {
-                s_unreachables++; d_unreachables++;
-                LOG(prefix<<qname<<": error resolving from "<<remoteIP->toString()<< (doTCP ? " over TCP" : "") <<", possible error: "<<strerror(errno)<< endl);
-              }
-
-              if(resolveret!=-2) { // don't account for resource limits, they are our own fault
-               t_sstorage->nsSpeeds[*tns].submit(*remoteIP, 1000000, &d_now); // 1 sec
-
-               // code below makes sure we don't filter COM or the root
-                if (s_serverdownmaxfails > 0 && (auth != g_rootdnsname) && t_sstorage->fails.incr(*remoteIP) >= s_serverdownmaxfails) {
-                  LOG(prefix<<qname<<": Max fails reached resolving on "<< remoteIP->toString() <<". Going full throttle for "<< s_serverdownthrottletime <<" seconds" <<endl);
-                  t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, "", 0), s_serverdownthrottletime, 10000); // mark server as down
-                } else if(resolveret==-1)
-                  t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // unreachable, 1 minute or 100 queries
-                else
-                  t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 10, 5);  // timeout
-              }
-              continue;
-            }
+          if (!gotAnswer) {
+            continue;
+          }
 
-//         if(d_timeouts + 0.5*d_throttledqueries > 6.0 && d_timeouts > 2) throw ImmediateServFailException("Too much work resolving "+qname+"|"+qtype.getName()+", timeouts: "+std::to_string(d_timeouts) +", throttles: "+std::to_string(d_throttledqueries));
+          LOG(prefix<<qname<<": Got "<<(unsigned int)lwr.d_records.size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl);
 
-            if(lwr.d_rcode==RCode::ServFail || lwr.d_rcode==RCode::Refused) {
-              LOG(prefix<<qname<<": "<<*tns<<" ("<<remoteIP->toString()<<") returned a "<< (lwr.d_rcode==RCode::ServFail ? "ServFail" : "Refused") << ", trying sibling IP or NS"<<endl);
-              t_sstorage->throttle.throttle(d_now.tv_sec,boost::make_tuple(*remoteIP, qname, qtype.getCode()),60,3); // servfail or refused
-              continue;
-            }
+          /*  // for you IPv6 fanatics :-)
+              if(remoteIP->sin4.sin_family==AF_INET6)
+              lwr.d_usec/=3;
+          */
+          //        cout<<"msec: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
 
-            if(s_serverdownmaxfails > 0)
-              t_sstorage->fails.clear(*remoteIP);
+          t_sstorage.nsSpeeds[*tns].submit(*remoteIP, lwr.d_usec, &d_now);
 
-            break;  // this IP address worked!
-          wasLame:; // well, it didn't
-            LOG(prefix<<qname<<": status=NS "<<*tns<<" ("<< remoteIP->toString() <<") is lame for '"<<auth<<"', trying sibling IP or NS"<<endl);
-            t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // lame
+          /* we have received an answer, are we done ? */
+          bool done = processAnswer(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode);
+          if (done) {
+            return rcode;
           }
-        }
-
-        if(remoteIP == remoteIPs.cend())  // we tried all IP addresses, none worked
-          continue;
-
-        if(lwr.d_tcbit) {
-          if(!doTCP) {
-            doTCP=true;
-            LOG(prefix<<qname<<": truncated bit set, retrying via TCP"<<endl);
-            goto TryTCP;
+          if (gotNewServers) {
+            break;
           }
-          LOG(prefix<<qname<<": truncated bit set, over TCP?"<<endl);
-          return RCode::ServFail;
+          /* was lame */
+          t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100);
         }
-        LOG(prefix<<qname<<": Got "<<(unsigned int)lwr.d_records.size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl);
-
-        /*  // for you IPv6 fanatics :-)
-        if(remoteIP->sin4.sin_family==AF_INET6)
-          lwr.d_usec/=3;
-        */
-        //        cout<<"msec: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
-
-        t_sstorage->nsSpeeds[*tns].submit(*remoteIP, lwr.d_usec, &d_now);
-      }
-
-      if(s_minimumTTL) {
-       for(auto& rec : lwr.d_records) {
-         rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
-       }
-      }
-
-      RCode::rcodes_ rcode = updateCacheFromRecords(prefix, lwr, qname, auth, nameservers, *tns, ednsmask);
-      if (rcode != RCode::NoError) {
-        return rcode;
-      }
 
-      LOG(prefix<<qname<<": determining status after receiving this packet"<<endl);
-
-      set<DNSName> nsset;
-      bool realreferral=false, negindic=false, sawDS=false;
-      DNSName newauth;
-      DNSName newtarget;
-
-      bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, sawDS);
-
-      if(done){
-        LOG(prefix<<qname<<": status=got results, this level of recursion done"<<endl);
-        return 0;
-      }
-      if(!newtarget.empty()) {
-        if(newtarget == qname) {
-          LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
-          return RCode::ServFail;
-        }
-        if(depth > 10) {
-          LOG(prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl);
-          return RCode::ServFail;
+        if (gotNewServers) {
+          break;
         }
-        LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
-
-        set<GetBestNSAnswer> beenthere2;
-        return doResolve(newtarget, qtype, ret, depth + 1, beenthere2);
-      }
-      if(lwr.d_rcode==RCode::NXDomain) {
-        LOG(prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl);
 
-        if(d_doDNSSEC)
-          addNXNSECS(ret, lwr.d_records);
+        if(remoteIP == remoteIPs.cend())  // we tried all IP addresses, none worked
+          continue;
 
-        return RCode::NXDomain;
-      }
-      if(nsset.empty() && !lwr.d_rcode && (negindic || lwr.d_aabit || sendRDQuery)) {
-        LOG(prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA) " : "")<<(lwr.d_aabit ? "(have aa bit) " : "")<<endl);
-        
-        if(d_doDNSSEC)
-          addNXNSECS(ret, lwr.d_records);
-        return 0;
-      }
-      else if(realreferral) {
-        LOG(prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, ");
-       if(sawDS) {
-         t_sstorage->dnssecmap[newauth]=true;
-         /*      for(const auto& e : t_sstorage->dnssecmap)
-           cout<<e.first<<' ';
-           cout<<endl;*/
-       }
-        auth=newauth;
-
-        nameservers.clear();
-        for (auto const &nameserver : nsset) {
-          if (d_wantsRPZ) {
-            d_appliedPolicy = luaconfsLocal->dfe.getProcessingPolicy(nameserver, d_discardedPolicies);
-            if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
-              LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
-              return -2;
-            }
-          }
-          nameservers.insert({nameserver, {{}, false}});
-        }
-        LOG("looping to them"<<endl);
-        break;
-      }
-      else if(!tns->empty()) { // means: not OOB, OOB == empty
-        goto wasLame;
       }
     }
   }
@@ -1625,7 +1744,7 @@ boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const ComboAddress& local, c
     return result;
   }
 
-  if(g_ednsdomains.check(dn) || g_ednssubnets.match(rem)) {
+  if(s_ednsdomains.check(dn) || s_ednssubnets.match(rem)) {
     bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
     trunc.truncate(bits);
     return boost::optional<Netmask>(Netmask(trunc, bits));
@@ -1634,6 +1753,20 @@ boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const ComboAddress& local, c
   return result;
 }
 
+void SyncRes::parseEDNSSubnetWhitelist(const std::string& wlist)
+{
+  vector<string> parts;
+  stringtok(parts, wlist, ",; ");
+  for(const auto& a : parts) {
+    try {
+      s_ednssubnets.addMask(Netmask(a));
+    }
+    catch(...) {
+      s_ednsdomains.add(DNSName(a));
+    }
+  }
+}
+
 // used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_ercursor.cc
 int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<DNSRecord>& ret)
 {
@@ -1651,7 +1784,7 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<D
 int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback) {
   SyncRes sr(now);
   sr.setDoEDNS0(true);
-  sr.setNoCache();
+  sr.setUpdatingRootNS();
   sr.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
   sr.setAsyncCallback(asyncCallback);
 
index e0d110178dd14f99aad83587ef86a24c0c8f6549..e069eafc13e032785a22d92407c849dd49cded89 100644 (file)
@@ -59,8 +59,6 @@
 #include <boost/uuid/uuid_generators.hpp>
 #endif
 
-void primeHints(void);
-
 class RecursorLua4;
 
 typedef map<
@@ -158,20 +156,9 @@ public:
   {
   }
 
-  struct timeval getOrMakeTime(struct timeval* tv)
+  void submit(int val, const struct timeval* tv)
   {
-    if(tv)
-      return *tv;
-    else {
-      struct timeval ret;
-      Utility::gettimeofday(&ret, 0);
-      return ret;
-    }
-  }
-
-  void submit(int val, struct timeval* tv)
-  {
-    struct timeval now=getOrMakeTime(tv);
+    struct timeval now=*tv;
 
     if(d_needinit) {
       d_last=now;
@@ -188,9 +175,9 @@ public:
     }
   }
 
-  double get(struct timeval* tv)
+  double get(const struct timeval* tv)
   {
-    struct timeval now=getOrMakeTime(tv);
+    struct timeval now=*tv;
     float diff=makeFloat(d_lastget-now);
     d_lastget=now;
     float factor=exp(diff/60.0f); // is 1.0 or less
@@ -279,10 +266,277 @@ class SyncRes : public boost::noncopyable
 {
 public:
   enum LogMode { LogNone, Log, Store};
+  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;
 
-  explicit SyncRes(const struct timeval& now);
+  struct EDNSStatus
+  {
+    EDNSStatus() : mode(UNKNOWN), modeSetAt(0) {}
+    enum EDNSMode { UNKNOWN=0, EDNSOK=1, EDNSIGNORANT=2, NOEDNS=3 } mode;
+    time_t modeSetAt;
+  };
 
-  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;
+    //! This represents a number of decaying Ewmas, used to store performance per nameserver-name.
+  /** Modelled to work mostly like the underlying DecayingEwma. After you've called get,
+      d_best is filled out with the best address for this collection */
+  struct DecayingEwmaCollection
+  {
+    void submit(const ComboAddress& remote, int usecs, const struct timeval* now)
+    {
+      collection_t::iterator pos;
+      for(pos=d_collection.begin(); pos != d_collection.end(); ++pos)
+        if(pos->first==remote)
+          break;
+      if(pos!=d_collection.end()) {
+        pos->second.submit(usecs, now);
+      }
+      else {
+        DecayingEwma de;
+        de.submit(usecs, now);
+        d_collection.push_back(make_pair(remote, de));
+      }
+    }
+
+    double get(const struct timeval* now)
+    {
+      if(d_collection.empty())
+        return 0;
+      double ret=std::numeric_limits<double>::max();
+      double tmp;
+      for(collection_t::iterator pos=d_collection.begin(); pos != d_collection.end(); ++pos) {
+        if((tmp=pos->second.get(now)) < ret) {
+          ret=tmp;
+          d_best=pos->first;
+        }
+      }
+
+      return ret;
+    }
+
+    bool stale(time_t limit) const
+    {
+      for(collection_t::const_iterator pos=d_collection.begin(); pos != d_collection.end(); ++pos)
+        if(!pos->second.stale(limit))
+          return false;
+      return true;
+    }
+
+    typedef vector<pair<ComboAddress, DecayingEwma> > collection_t;
+    collection_t d_collection;
+    ComboAddress d_best;
+  };
+
+  typedef map<DNSName, DecayingEwmaCollection> nsspeeds_t;
+  typedef map<ComboAddress, EDNSStatus> ednsstatus_t;
+
+  class AuthDomain
+  {
+  public:
+    typedef multi_index_container <
+      DNSRecord,
+      indexed_by <
+        ordered_non_unique<
+          composite_key< DNSRecord,
+                         member<DNSRecord, DNSName, &DNSRecord::d_name>,
+                         member<DNSRecord, uint16_t, &DNSRecord::d_type>
+                       >,
+          composite_key_compare<std::less<DNSName>, std::less<uint16_t> >
+        >
+      >
+    > 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;
+  typedef Throttle<boost::tuple<ComboAddress,DNSName,uint16_t> > throttle_t;
+  typedef Counters<ComboAddress> fails_t;
+
+  struct ThreadLocalStorage {
+    NegCache negcache;
+    nsspeeds_t nsSpeeds;
+    throttle_t throttle;
+    ednsstatus_t ednsstatus;
+    fails_t fails;
+    std::shared_ptr<domainmap_t> domainmap;
+  };
+
+  static void setDefaultLogMode(LogMode lm)
+  {
+    s_lm = lm;
+  }
+  static void doEDNSDumpAndClose(int fd);
+  static uint64_t doDumpNSSpeeds(int fd);
+  static int getRootNS(struct timeval now, asyncresolve_t asyncCallback);
+  static void clearDelegationOnly()
+  {
+    s_delegationOnly.clear();
+  }
+  static void addDelegationOnly(const DNSName& name)
+  {
+    s_delegationOnly.insert(name);
+  }
+  static void addDontQuery(const std::string& mask)
+  {
+    if (!s_dontQuery)
+      s_dontQuery = std::unique_ptr<NetmaskGroup>(new NetmaskGroup());
+
+    s_dontQuery->addMask(mask);
+  }
+  static void addDontQuery(const Netmask& mask)
+  {
+    if (!s_dontQuery)
+      s_dontQuery = std::unique_ptr<NetmaskGroup>(new NetmaskGroup());
+
+    s_dontQuery->addMask(mask);
+  }
+  static void clearDontQuery()
+  {
+    s_dontQuery = nullptr;
+  }
+  static void parseEDNSSubnetWhitelist(const std::string& wlist);
+  static void addEDNSSubnet(const Netmask& subnet)
+  {
+    s_ednssubnets.addMask(subnet);
+  }
+  static void addEDNSDomain(const DNSName& domain)
+  {
+    s_ednsdomains.add(domain);
+  }
+  static void clearEDNSSubnets()
+  {
+    s_ednssubnets.clear();
+  }
+  static void clearEDNSDomains()
+  {
+    s_ednsdomains = SuffixMatchNode();
+  }
+  static void pruneNSSpeeds(time_t limit)
+  {
+    for(auto i = t_sstorage.nsSpeeds.begin(), end = t_sstorage.nsSpeeds.end(); i != end; ) {
+      if(i->second.stale(limit)) {
+        i = t_sstorage.nsSpeeds.erase(i);
+      }
+      else {
+        ++i;
+      }
+    }
+  }
+  static uint64_t getNSSpeedsSize()
+  {
+    return t_sstorage.nsSpeeds.size();
+  }
+  static void submitNSSpeed(const DNSName& server, const ComboAddress& ca, uint32_t usec, const struct timeval* now)
+  {
+    t_sstorage.nsSpeeds[server].submit(ca, usec, now);
+  }
+  static void clearNSSpeeds()
+  {
+    t_sstorage.nsSpeeds.clear();
+  }
+  static EDNSStatus::EDNSMode getEDNSStatus(const ComboAddress& server)
+  {
+    const auto& it = t_sstorage.ednsstatus.find(server);
+    if (it == t_sstorage.ednsstatus.end())
+      return EDNSStatus::UNKNOWN;
+
+    return it->second.mode;
+  }
+  static uint64_t getEDNSStatusesSize()
+  {
+    return t_sstorage.ednsstatus.size();
+  }
+  static void clearEDNSStatuses()
+  {
+    t_sstorage.ednsstatus.clear();
+  }
+  static uint64_t getThrottledServersSize()
+  {
+    return t_sstorage.throttle.size();
+  }
+  static void clearThrottle()
+  {
+    t_sstorage.throttle.clear();
+  }
+  static bool isThrottled(time_t now, const ComboAddress& server, const DNSName& target, uint16_t qtype)
+  {
+    return t_sstorage.throttle.shouldThrottle(now, boost::make_tuple(server, target, qtype));
+  }
+  static bool isThrottled(time_t now, const ComboAddress& server)
+  {
+    return t_sstorage.throttle.shouldThrottle(now, boost::make_tuple(server, "", 0));
+  }
+  static void doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries)
+  {
+    t_sstorage.throttle.throttle(now, boost::make_tuple(server, "", 0), duration, tries);
+  }
+  static uint64_t getFailedServersSize()
+  {
+    return t_sstorage.fails.size();
+  }
+  static void clearFailedServers()
+  {
+    t_sstorage.fails.clear();
+  }
+  static unsigned long getServerFailsCount(const ComboAddress& server)
+  {
+    return t_sstorage.fails.value(server);
+  }
+
+  static void clearNegCache()
+  {
+    t_sstorage.negcache.clear();
+  }
+
+  static uint64_t getNegCacheSize()
+  {
+    return t_sstorage.negcache.size();
+  }
+
+  static void pruneNegCache(unsigned int maxEntries)
+  {
+    t_sstorage.negcache.prune(maxEntries);
+  }
+
+  static uint64_t wipeNegCache(const DNSName& name, bool subtree = false)
+  {
+    return t_sstorage.negcache.wipe(name, subtree);
+  }
+
+  static void setDomainMap(std::shared_ptr<domainmap_t> newMap)
+  {
+    t_sstorage.domainmap = newMap;
+  }
+
+  static const std::shared_ptr<domainmap_t> getDomainMap()
+  {
+    return t_sstorage.domainmap;
+  }
+
+  explicit SyncRes(const struct timeval& now);
 
   int beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret);
   void setId(int id)
@@ -290,10 +544,6 @@ public:
     if(doLog())
       d_prefix="["+itoa(id)+"] ";
   }
-  static void setDefaultLogMode(LogMode lm)
-  {
-    s_lm = lm;
-  }
 
   void setLogMode(LogMode lm)
   {
@@ -309,10 +559,6 @@ public:
   {
     d_cacheonly=state;
   }
-  void setNoCache(bool state=true)
-  {
-    d_nocache=state;
-  }
 
   void setDoEDNS0(bool state=true)
   {
@@ -386,8 +632,7 @@ public:
     d_asyncResolve = func;
   }
 
-  static void doEDNSDumpAndClose(int fd);
-  static int getRootNS(struct timeval now, asyncresolve_t asyncCallback);
+  static thread_local ThreadLocalStorage t_sstorage;
 
   static std::atomic<uint64_t> s_queries;
   static std::atomic<uint64_t> s_outgoingtimeouts;
@@ -399,11 +644,26 @@ public:
   static std::atomic<uint64_t> s_tcpoutqueries;
   static std::atomic<uint64_t> s_nodelegated;
   static std::atomic<uint64_t> s_unreachables;
+
+  static string s_serverID;
   static unsigned int s_minimumTTL;
-  static bool s_doIPv6;
   static unsigned int s_maxqperq;
   static unsigned int s_maxtotusec;
   static unsigned int s_maxdepth;
+  static unsigned int s_maxnegttl;
+  static unsigned int s_maxcachettl;
+  static unsigned int s_packetcachettl;
+  static unsigned int s_packetcacheservfailttl;
+  static unsigned int s_serverdownmaxfails;
+  static unsigned int s_serverdownthrottletime;
+  static uint8_t s_ecsipv4limit;
+  static uint8_t s_ecsipv6limit;
+  static bool s_doIPv6;
+  static bool s_noEDNSPing;
+  static bool s_noEDNS;
+  static bool s_rootNXTrust;
+  static bool s_nopacketcache;
+
   std::unordered_map<std::string,bool> d_discardedPolicies;
   DNSFilterEngine::Policy d_appliedPolicy;
   unsigned int d_outqueries;
@@ -414,123 +674,33 @@ public:
   unsigned int d_totUsec;
   ComboAddress d_requestor;
 
-  //! This represents a number of decaying Ewmas, used to store performance per nameserver-name.
-  /** Modelled to work mostly like the underlying DecayingEwma. After you've called get,
-      d_best is filled out with the best address for this collection */
-  struct DecayingEwmaCollection
-  {
-    void submit(const ComboAddress& remote, int usecs, struct timeval* now)
-    {
-      collection_t::iterator pos;
-      for(pos=d_collection.begin(); pos != d_collection.end(); ++pos)
-        if(pos->first==remote)
-          break;
-      if(pos!=d_collection.end()) {
-        pos->second.submit(usecs, now);
-      }
-      else {
-        DecayingEwma de;
-        de.submit(usecs, now);
-        d_collection.push_back(make_pair(remote, de));
-      }
-    }
-
-    double get(struct timeval* now)
-    {
-      if(d_collection.empty())
-        return 0;
-      double ret=std::numeric_limits<double>::max();
-      double tmp;
-      for(collection_t::iterator pos=d_collection.begin(); pos != d_collection.end(); ++pos) {
-        if((tmp=pos->second.get(now)) < ret) {
-          ret=tmp;
-          d_best=pos->first;
-        }
-      }
+private:
 
-      return ret;
-    }
+  static std::unordered_set<DNSName> s_delegationOnly;
+  static NetmaskGroup s_ednssubnets;
+  static SuffixMatchNode s_ednsdomains;
+  static LogMode s_lm;
+  static std::unique_ptr<NetmaskGroup> s_dontQuery;
 
-    bool stale(time_t limit) const
+  struct GetBestNSAnswer
+  {
+    DNSName qname;
+    set<pair<DNSName,DNSName> > bestns;
+    uint8_t qtype; // only A and AAAA anyhow
+    bool operator<(const GetBestNSAnswer &b) const
     {
-      for(collection_t::const_iterator pos=d_collection.begin(); pos != d_collection.end(); ++pos)
-        if(!pos->second.stale(limit))
-          return false;
-      return true;
+      return boost::tie(qname, qtype, bestns) <
+       boost::tie(b.qname, b.qtype, b.bestns);
     }
-
-    typedef vector<pair<ComboAddress, DecayingEwma> > collection_t;
-    collection_t d_collection;
-    ComboAddress d_best;
-  };
-
-  typedef map<DNSName, DecayingEwmaCollection> nsspeeds_t;  
-
-  struct EDNSStatus
-  {
-    EDNSStatus() : mode(UNKNOWN), modeSetAt(0) {}
-    enum EDNSMode { UNKNOWN=0, EDNSOK=1, EDNSIGNORANT=2, NOEDNS=3 } mode;
-    time_t modeSetAt;
-  };
-
-  typedef map<ComboAddress, EDNSStatus> ednsstatus_t;
-
-  static bool s_noEDNSPing;
-  static bool s_noEDNS;
-  static bool s_rootNXTrust;
-  struct AuthDomain
-  {
-    vector<ComboAddress> d_servers;
-    bool d_rdForward;
-    typedef multi_index_container <
-      DNSRecord,
-      indexed_by <
-        ordered_non_unique<
-          composite_key< DNSRecord,
-                        member<DNSRecord, DNSName, &DNSRecord::d_name>,
-                        member<DNSRecord, uint16_t, &DNSRecord::d_type>
-                       >,
-          composite_key_compare<std::less<DNSName>, std::less<uint16_t> >
-        >
-      >
-    > records_t;
-    records_t d_records;
   };
 
-
-  typedef map<DNSName, AuthDomain> domainmap_t;
-
-
-  typedef Throttle<boost::tuple<ComboAddress,DNSName,uint16_t> > throttle_t;
-
-  typedef Counters<ComboAddress> fails_t;
-
-  static unsigned int s_maxnegttl;
-  static unsigned int s_maxcachettl;
-  static unsigned int s_packetcachettl;
-  static unsigned int s_packetcacheservfailttl;
-  static unsigned int s_serverdownmaxfails;
-  static unsigned int s_serverdownthrottletime;
-  static uint8_t s_ecsipv4limit;
-  static uint8_t s_ecsipv6limit;
-  static bool s_nopacketcache;
-  static string s_serverID;
-
-  struct StaticStorage {
-    nsspeeds_t nsSpeeds;
-    ednsstatus_t ednsstatus;
-    throttle_t throttle;
-    fails_t fails;
-    domainmap_t* domainmap;
-    map<DNSName, bool> dnssecmap;
-    NegCache negcache;
-  };
-
-private:
-  struct GetBestNSAnswer;
   int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret,
                   unsigned int depth, set<GetBestNSAnswer>&beenthere);
+  bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated);
+  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);
@@ -547,15 +717,21 @@ private:
   bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
 
   vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet);
-  RCode::rcodes_ updateCacheFromRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const DNSName& auth, NsSet& nameservers, const DNSName& tns, const boost::optional<Netmask>);
-  bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, bool& sawDS);
+  RCode::rcodes_ updateCacheFromRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>);
+  bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic);
 
-  bool doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t &qclass, vector<DNSRecord> &ret);
+  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;
 
   boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem);
 
+  void setUpdatingRootNS()
+  {
+    d_updatingRootNS = true;
+  }
+
+
   ostringstream d_trace;
   shared_ptr<RecursorLua4> d_pdl;
   boost::optional<const EDNSSubnetOpts&> d_incomingECS;
@@ -570,35 +746,17 @@ private:
    * This is set when the RD bit is unset in the incoming query
    */
   bool d_cacheonly;
-  /* d_nocache is *only* set in getRootNS() (in pdns_recursor.cc).
-   * It forces us to not look in the cache or local auth.
-   */
-  bool d_nocache;
   bool d_doDNSSEC;
   bool d_doEDNS0{true};
   bool d_incomingECSFound{false};
   bool d_skipCNAMECheck{false};
+  bool d_updatingRootNS{false};
   bool d_wantsRPZ{true};
   bool d_wasOutOfBand{false};
   bool d_wasVariable{false};
 
-  static LogMode s_lm;
   LogMode d_lm;
-
-  struct GetBestNSAnswer
-  {
-    DNSName qname;
-    set<pair<DNSName,DNSName> > bestns; 
-    uint8_t qtype; // only A and AAAA anyhow
-    bool operator<(const GetBestNSAnswer &b) const
-    {
-      return boost::tie(qname, qtype, bestns) <
-       boost::tie(b.qname, b.qtype, b.bestns);
-    }
-  };
-
 };
-extern __thread SyncRes::StaticStorage* t_sstorage;
 
 class Socket;
 /* external functions, opaque to us */
@@ -658,10 +816,10 @@ struct PacketIDBirthdayCompare: public std::binary_function<PacketID, PacketID,
     return a.domain < b.domain;
   }
 };
-extern __thread MemRecursorCache* t_RC;
-extern __thread RecursorPacketCache* t_packetCache;
+extern thread_local std::unique_ptr<MemRecursorCache> t_RC;
+extern thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
 typedef MTasker<PacketID,string> MT_t;
-extern __thread MT_t* MT;
+extern thread_local std::unique_ptr<MT_t> MT;
 
 struct RecursorStats
 {
@@ -741,16 +899,15 @@ typedef boost::circular_buffer<SComboAddress> addrringbuf_t;
 #else
 typedef boost::circular_buffer<ComboAddress> addrringbuf_t;
 #endif
-extern __thread addrringbuf_t* t_servfailremotes, *t_largeanswerremotes, *t_remotes;
+extern thread_local std::unique_ptr<addrringbuf_t> t_servfailremotes, t_largeanswerremotes, t_remotes;
 
-extern __thread boost::circular_buffer<pair<DNSName,uint16_t> >* t_queryring, *t_servfailqueryring;
-extern __thread NetmaskGroup* t_allowFrom;
+extern thread_local std::unique_ptr<boost::circular_buffer<pair<DNSName,uint16_t> > > t_queryring, t_servfailqueryring;
+extern thread_local std::shared_ptr<NetmaskGroup> t_allowFrom;
 string doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end);
 string doTraceRegex(vector<string>::const_iterator begin, vector<string>::const_iterator end);
 void parseACLs();
 extern RecursorStats g_stats;
 extern unsigned int g_numThreads;
-extern std::unordered_set<DNSName> g_delegationOnly;
 extern uint16_t g_outgoingEDNSBufsize;
 
 
@@ -765,7 +922,7 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<D
 
 template<class T> T broadcastAccFunction(const boost::function<T*()>& func, bool skipSelf=false);
 
-SyncRes::domainmap_t* parseAuthAndForwards();
+std::shared_ptr<SyncRes::domainmap_t> parseAuthAndForwards();
 uint64_t* pleaseGetNsSpeedsSize();
 uint64_t* pleaseGetCacheSize();
 uint64_t* pleaseGetNegCacheSize();
@@ -779,13 +936,10 @@ uint64_t* pleaseWipeCache(const DNSName& canon, bool subtree=false);
 uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree);
 uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree=false);
 void doCarbonDump(void*);
-void  parseEDNSSubnetWhitelist(const std::string& wlist);
+void primeHints(void);
 
 extern __thread struct timeval g_now;
 
-extern NetmaskGroup g_ednssubnets;
-extern SuffixMatchNode g_ednsdomains;
-
 #ifdef HAVE_PROTOBUF
-extern __thread boost::uuids::random_generator* t_uuidGenerator;
+extern thread_local std::unique_ptr<boost::uuids::random_generator> t_uuidGenerator;
 #endif
index 7b284fd89c8b53d10ce718d4543ff7d502c0ee16..97734a703abb662e664930ecedfc9d52a8dd6a3b 100644 (file)
@@ -40,7 +40,7 @@
 #include "logger.hh"
 #include "ext/incbin/incbin.h"
 
-extern __thread FDMultiplexer* t_fdm;
+extern thread_local FDMultiplexer* t_fdm;
 
 using json11::Json;
 
@@ -116,8 +116,8 @@ static void apiServerConfigAllowFrom(HttpRequest* req, HttpResponse* resp)
 
 static void fillZone(const DNSName& zonename, HttpResponse* resp)
 {
-  auto iter = t_sstorage->domainmap->find(zonename);
-  if (iter == t_sstorage->domainmap->end())
+  auto iter = SyncRes::t_sstorage.domainmap->find(zonename);
+  if (iter == SyncRes::t_sstorage.domainmap->end())
     throw ApiException("Could not find domain '"+zonename.toString()+"'");
 
   const SyncRes::AuthDomain& zone = iter->second;
@@ -251,8 +251,8 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp)
 
     DNSName zonename = apiNameToDNSName(stringFromJson(document, "name"));
 
-    auto iter = t_sstorage->domainmap->find(zonename);
-    if (iter != t_sstorage->domainmap->end())
+    auto iter = SyncRes::t_sstorage.domainmap->find(zonename);
+    if (iter != SyncRes::t_sstorage.domainmap->end())
       throw ApiException("Zone already exists");
 
     doCreateZone(document);
@@ -266,7 +266,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp)
     throw HttpMethodNotAllowedException();
 
   Json::array doc;
-  for(const SyncRes::domainmap_t::value_type& val :  *t_sstorage->domainmap) {
+  for(const SyncRes::domainmap_t::value_type& val :  *SyncRes::t_sstorage.domainmap) {
     const SyncRes::AuthDomain& zone = val.second;
     Json::array servers;
     for(const ComboAddress& server : zone.d_servers) {
@@ -290,8 +290,8 @@ static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp)
 {
   DNSName zonename = apiZoneIdToName(req->parameters["id"]);
 
-  SyncRes::domainmap_t::const_iterator iter = t_sstorage->domainmap->find(zonename);
-  if (iter == t_sstorage->domainmap->end())
+  SyncRes::domainmap_t::const_iterator iter = SyncRes::t_sstorage.domainmap->find(zonename);
+  if (iter == SyncRes::t_sstorage.domainmap->end())
     throw ApiException("Could not find domain '"+zonename.toString()+"'");
 
   if(req->method == "PUT" && !::arg().mustDo("api-readonly")) {
@@ -328,7 +328,7 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) {
     throw ApiException("Query q can't be blank");
 
   Json::array doc;
-  for(const SyncRes::domainmap_t::value_type& val : *t_sstorage->domainmap) {
+  for(const SyncRes::domainmap_t::value_type& val : *SyncRes::t_sstorage.domainmap) {
     string zoneId = apiZoneNameToId(val.first);
     string zoneName = val.first.toString();
     if (pdns_ci_find(zoneName, q) != string::npos) {