]> granicus.if.org Git - pdns/commitdiff
fix . zone refreshing bug (we didn't actually import what the root-servers were telli...
authorBert Hubert <bert.hubert@netherlabs.nl>
Thu, 27 Apr 2006 17:11:44 +0000 (17:11 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Thu, 27 Apr 2006 17:11:44 +0000 (17:11 +0000)
fix case of domain with nameservers with multiple IP addresses of which only one is lame
add: --auth-zones, --forward-zones and --export-etc-hosts

git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@780 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/backends/bind/zoneparser2.cc
pdns/pdns_recursor.cc
pdns/syncres.cc
pdns/syncres.hh

index 24c569f8489053541bf9ee01a5892b1cff3d4b71..81a9235aec710d2583233ea0f404f1b9d3323644 100644 (file)
@@ -275,7 +275,6 @@ bool ZoneParser::isType(const string &s)
   if(isClass(s))
     return false;
 
-
   return true;
 }
 
index 20bca2c58fa5fdee12ad59d3d7b7a4c2b1c66d68..ca068d487f92587455bd5e25982e03521d6fa512 100644 (file)
@@ -42,6 +42,7 @@
 #include <boost/shared_array.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/function.hpp>
+#include <boost/algorithm/string.hpp>
 #include "dnsparser.hh"
 #include "dnswriter.hh"
 #include "dnsrecords.hh"
@@ -1146,6 +1147,143 @@ FDMultiplexer* getMultiplexer()
   exit(1);
 }
 
+static void makeNameToIPZone(const string& hostname, const string& ip)
+{
+  SyncRes::AuthDomain ad;
+  DNSResourceRecord rr;
+  rr.qname=toCanonic("", hostname);
+  rr.d_place=DNSResourceRecord::ANSWER;
+  rr.ttl=86400;
+  rr.qtype=QType::SOA;
+  rr.content="localhost. root 1 604800 864002419200 604800";
+  
+  ad.d_records.insert(rr);
+
+  rr.qtype=QType::NS;
+  rr.content="localhost.";
+
+  ad.d_records.insert(rr);
+  
+  rr.qtype=QType::A;
+  rr.content=ip;
+  ad.d_records.insert(rr);
+  
+  if(SyncRes::s_domainmap.count(rr.qname)) {
+    L<<Logger::Warning<<"Hosts file will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
+  }
+  else {
+    L<<Logger::Warning<<"Inserting forward zone '"<<rr.qname<<"' based on hosts file"<<endl;
+    SyncRes::s_domainmap[rr.qname]=ad;
+  }
+}
+
+static void makeIPToNamesZone(const vector<string>& parts) 
+{
+  string address=parts[0];
+  vector<string> ipparts;
+  stringtok(ipparts, address,".");
+  if(ipparts.size()!=4)
+    return;
+  
+
+  SyncRes::AuthDomain ad;
+  DNSResourceRecord rr;
+  for(int n=3; n>=0 ; --n) {
+    rr.qname.append(ipparts[n]);
+    rr.qname.append(1,'.');
+  }
+  rr.qname.append("in-addr.arpa.");
+
+  rr.d_place=DNSResourceRecord::ANSWER;
+  rr.ttl=86400;
+  rr.qtype=QType::SOA;
+  rr.content="localhost. root 1 604800 864002419200 604800";
+  
+  ad.d_records.insert(rr);
+
+  rr.qtype=QType::NS;
+  rr.content="localhost.";
+
+  ad.d_records.insert(rr);
+  rr.qtype=QType::PTR;
+
+  for(unsigned int n=1; n < parts.size(); ++n) {
+    rr.content=toCanonic("", parts[n]);
+    ad.d_records.insert(rr);
+  }
+
+  if(SyncRes::s_domainmap.count(rr.qname)) {
+    L<<Logger::Warning<<"Hosts file will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
+  }
+  else {
+    L<<Logger::Warning<<"Inserting reverse zone '"<<rr.qname<<"' based on hosts file"<<endl;
+    SyncRes::s_domainmap[rr.qname]=ad;
+  }
+}
+
+void parseAuthAndForwards()
+{
+  SyncRes::s_domainmap.clear(); // this makes us idempotent
+
+  typedef vector<string> parts_t;
+  parts_t parts;  
+  for(int n=0; n < 2 ; ++n ) {
+    parts.clear();
+    stringtok(parts, ::arg()[n ? "forward-zones" : "auth-zones"], ",\t\n\r");
+    for(parts_t::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) {
+      SyncRes::AuthDomain ad;
+      pair<string,string> headers=splitField(*iter, '=');
+      trim(headers.first);
+      trim(headers.second);
+      headers.first=toCanonic("", headers.first);
+      if(n==0) {
+       L<<Logger::Error<<"Parsing authoritative data for zone '"<<headers.first<<"' from file '"<<headers.second<<"'"<<endl;
+       ZoneParserTNG zpt(headers.second, headers.first);
+       DNSResourceRecord rr;
+       while(zpt.get(rr)) {
+         ad.d_records.insert(rr);
+       }
+      }
+      else {
+       L<<Logger::Error<<"Redirecting queries for zone '"<<headers.first<<"' to IP '"<<headers.second<<"'"<<endl;
+       ad.d_server=headers.second;
+      }
+      
+      SyncRes::s_domainmap[headers.first]=ad;
+    }
+  }
+  
+  if(!::arg().mustDo("export-etc-hosts"))
+    return;
+
+  string line;
+  string fname;
+
+  ifstream ifs("/etc/hosts");
+  if(!ifs) {
+    L<<Logger::Warning<<"Could not open /etc/hosts for reading"<<endl;
+    return;
+  }
+    
+  string::size_type pos;
+  while(getline(ifs,line)) {
+    pos=line.find('#');
+    if(pos!=string::npos)
+     line.resize(pos);
+    trim(line);
+    if(line.empty())
+      continue;
+    parts.clear();
+    stringtok(parts, line, "\t\r\n ");
+    if(parts[0].find(':')!=string::npos)
+      continue;
+
+    for(unsigned int n=1; n < parts.size(); ++n)
+      makeNameToIPZone(parts[n], parts[0]);
+    makeIPToNamesZone(parts);
+  }
+}
+
 int main(int argc, char **argv) 
 {
   reportBasicTypes();
@@ -1189,6 +1327,9 @@ int main(int argc, char **argv)
     ::arg().set("fork", "If set, fork the daemon for possible double performance")="no";
     ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses")="20";
     ::arg().set("single-socket", "If set, only use a single socket for outgoing queries")="off";
+    ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ")="";
+    ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs")="";
+    ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts")="off";
 
     ::arg().setCmd("help","Provide a helpful message");
     ::arg().setCmd("config","Output blank configuration");
@@ -1262,6 +1403,9 @@ int main(int argc, char **argv)
       gethostname(tmp, sizeof(tmp)-1);
       SyncRes::s_serverID=tmp;
     }
+    
+
+    parseAuthAndForwards();
 
     g_stats.remotes.resize(::arg().asNum("remotes-ringbuffer-entries"));
     memset(&*g_stats.remotes.begin(), 0, g_stats.remotes.size() * sizeof(RecursorStats::remotes_t::value_type));
index 36ac11d034252cb5bdd7fdfcff102bb6cf4f2787..d8607df2427f31964ec10275b798f5e3cb56c5b1 100644 (file)
@@ -46,6 +46,7 @@ unsigned int SyncRes::s_outqueries;
 unsigned int SyncRes::s_tcpoutqueries;
 unsigned int SyncRes::s_throttledqueries;
 unsigned int SyncRes::s_nodelegated;
+SyncRes::domainmap_t SyncRes::s_domainmap;
 string SyncRes::s_serverID;
 bool SyncRes::s_log;
 
@@ -102,7 +103,65 @@ int SyncRes::beginResolve(const string &qname, const QType &qtype, uint16_t qcla
 
 bool SyncRes::doOOBResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int& res)
 {
-  return false;
+  string prefix;
+  if(s_log) {
+    prefix=d_prefix;
+    prefix.append(depth, ' ');
+  }
+
+  LOG<<prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
+  string authdomain(qname);
+
+  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
+  if(iter==s_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;
+  for(ziter=range.first; ziter!=range.second; ++ziter) {
+    if(ziter->qtype==qtype || ziter->qtype.getCode()==QType::CNAME)  // let rest of nameserver do the legwork on this one
+      ret.push_back(*ziter);
+  }
+  if(ret.empty()) {
+    LOG<<prefix<<qname<<": no exact match in zone '"<<authdomain<<"'"<<endl;
+    return false;
+  }
+
+  string nsdomain(qname);
+
+  while(chopOffDotted(nsdomain) && nsdomain!=iter->first) {
+    range=iter->second.d_records.equal_range(make_tuple(nsdomain,QType(QType::NS))); 
+    if(range.first==range.second)
+      continue;
+
+    for(ziter=range.first; ziter!=range.second; ++ziter) {
+      DNSResourceRecord rr=*ziter;
+      rr.d_place=DNSResourceRecord::AUTHORITY;
+      ret.push_back(rr);
+    }
+  }
+  if(ret.empty()) {
+    LOG<<prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl;
+    ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA)));
+    if(ziter!=iter->second.d_records.end()) {
+      DNSResourceRecord rr=*ziter;
+      rr.d_place=DNSResourceRecord::AUTHORITY;
+      ret.push_back(rr);
+    }
+    else
+      LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl;
+    res=RCode::NXDomain;
+  }
+  else 
+    res=0;
+
+  return true;;
 }
 
 int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
@@ -114,7 +173,7 @@ int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResour
   }
   
   int res=0;
-  if(!(d_nocache && qtype.getCode()==QType::NS && qname.empty())) {
+  if(!(d_nocache && qtype.getCode()==QType::NS && qname==".")) {
     if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
       return res;
     
@@ -125,11 +184,6 @@ int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResour
   if(d_cacheonly)
     return 0;
 
-  // place for lookaside cache
-
-  if(doOOBResolve(qname, qtype, ret, depth, res))
-    return res;
-
   LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
 
   string subdomain(qname);
@@ -144,10 +198,10 @@ int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResour
     }
   }
 
-  if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere)))
+  if(!(res=doResolveAt(nsset, subdomain, qname, qtype, ret, depth, beenthere)))
     return 0;
   
-  LOG<<prefix<<qname<<": failed"<<endl;
+  LOG<<prefix<<qname<<": failed (res="<<res<<")"<<endl;
   return res<0 ? RCode::ServFail : res;
 }
 
@@ -227,12 +281,30 @@ void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bes
   }while(chopOffDotted(subdomain));
 }
 
+SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(string* qname)
+{
+  SyncRes::domainmap_t::const_iterator ret;
+  do {
+    ret=s_domainmap.find(*qname);
+    if(ret!=s_domainmap.end()) 
+      break;
+  }while(chopOffDotted(*qname));
+  return ret;
+}
 
 /** doesn't actually do the work, leaves that to getBestNSFromCache */
 string SyncRes::getBestNSNamesFromCache(const string &qname,set<string, CIStringCompare>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
 {
   string subdomain(qname);
 
+  string authdomain(qname);
+  
+  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
+  if(iter!=s_domainmap.end()) {
+    nsset.insert(iter->second.d_server);
+    return authdomain;
+  }
+
   set<DNSResourceRecord> bestns;
   getBestNSFromCache(subdomain, bestns, depth, beenthere);
 
@@ -431,8 +503,9 @@ struct TCacheComp
 
 
 /** returns -1 in case of no results, rcode otherwise */
-int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, 
-               int depth, set<GetBestNSAnswer>&beenthere)
+int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, const string &qname, const QType &qtype, 
+                        vector<DNSResourceRecord>&ret, 
+                        int depth, set<GetBestNSAnswer>&beenthere)
 {
   string prefix;
   if(s_log) {
@@ -458,80 +531,100 @@ int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth,
        LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
        continue;
       }
-      LOG<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
+
       typedef vector<uint32_t> remoteIPs_t;
-      remoteIPs_t remoteIPs=getAs(*tns, depth+1, beenthere);
-      if(remoteIPs.empty()) {
-       LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
-       continue;
-      }
+      remoteIPs_t remoteIPs;
       remoteIPs_t::const_iterator remoteIP;
       bool doTCP=false;
-      for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
-       LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to "<<U32ToIP(*remoteIP)<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
-       
-       if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) {
-         LOG<<prefix<<qname<<": query throttled "<<endl;
-         s_throttledqueries++;
-         d_throttledqueries++;
+      int resolveret;
+
+      if(tns->empty()) {
+       LOG<<prefix<<qname<<": Domain is out-of-band"<<endl;
+       doOOBResolve(qname, qtype, result, depth, d_lwr.d_rcode);
+       d_lwr.d_tcbit=false;
+      }
+      else {
+       LOG<<prefix<<qname<<": Trying to resolve NS '"<<*tns<<"' ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
+       if(!isCanonical(*tns)) {
+         LOG<<prefix<<qname<<": Domain has hardcoded nameserver"<<endl;
+         uint32_t tmp=0;
+         IpToU32(*tns, &tmp);
+         remoteIPs.push_back(htonl(tmp));
+       }
+       else
+         remoteIPs=getAs(*tns, depth+1, beenthere);
+
+       if(remoteIPs.empty()) {
+         LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
          continue;
        }
-       else {
-         s_outqueries++;
-         d_outqueries++;
-       TryTCP:
-         if(doTCP) {
-           s_tcpoutqueries++;
-           d_tcpoutqueries++;
-         }
+
+       for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
+         LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to "<<U32ToIP(*remoteIP)<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
          
-         int ret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now);    // <- we go out on the wire!
-         if(ret != 1) {
-           if(ret==0) {
-             LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl;
-             d_timeouts++;
-             s_outgoingtimeouts++;
-           }
-           else if(ret==-2) {
-             LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl;
-             g_stats.resourceLimits++;
+         if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) {
+           LOG<<prefix<<qname<<": query throttled "<<endl;
+           s_throttledqueries++; d_throttledqueries++;
+           continue;
+         }
+         else {
+           s_outqueries++; d_outqueries++;
+         TryTCP:
+           if(doTCP) {
+             s_tcpoutqueries++; d_tcpoutqueries++;
            }
-           else
-             LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl;
            
-           if(ret!=-2) { // don't account for resource limits, they are our own fault
-             s_nsSpeeds[*tns].submit(1000000, &d_now); // 1 sec
-             s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),20,5);
+           resolveret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now);    // <- we go out on the wire!
+           if(resolveret != 1) {
+             if(resolveret==0) {
+               LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl;
+               d_timeouts++;
+               s_outgoingtimeouts++;
+             }
+             else if(resolveret==-2) {
+               LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl;
+               g_stats.resourceLimits++;
+             }
+             else
+               LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl;
+             
+             if(resolveret!=-2) { // don't account for resource limits, they are our own fault
+               s_nsSpeeds[*tns].submit(1000000, &d_now); // 1 sec
+               s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),20,5);
+             }
+             continue;
            }
-           continue;
+           
+           break;  // this IP address worked!
+         wasLame:; // well, it didn't
+           LOG<<prefix<<qname<<": status=NS "<<*tns<<" ("<<U32ToIP(*remoteIP)<<") is lame for '"<<auth<<"', trying sibling IP or NS"<<endl;
+           s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),60,0);
          }
-         
-         break; // it did work!
        }
-      }
-
-      if(remoteIP == remoteIPs.end())  // we tried all IP addresses, none worked
-       continue; 
-
-      result=d_lwr.result();
+       
+       if(remoteIP == remoteIPs.end())  // we tried all IP addresses, none worked
+         continue; 
+       
+       result=d_lwr.result();
       
-      if(d_lwr.d_tcbit) {
-       if(!doTCP) {
-         doTCP=true;
-         LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
-         goto TryTCP;
+       if(d_lwr.d_tcbit) {
+         if(!doTCP) {
+           doTCP=true;
+           LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
+           goto TryTCP;
+         }
+         LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
+         return RCode::ServFail;
        }
-       LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
-       return RCode::ServFail;
-      }
-
-      if(d_lwr.d_rcode==RCode::ServFail) {
-       LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling NS"<<endl;
-       s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3);
-       continue;
+       
+       if(d_lwr.d_rcode==RCode::ServFail) {
+         LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling IP or NS"<<endl;
+         s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3);
+         continue;
+       }
+       LOG<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<U32ToIP(*remoteIP)<<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl;
+       s_nsSpeeds[*tns].submit(d_lwr.d_usec, &d_now);
       }
-      LOG<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<U32ToIP(*remoteIP)<<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl;
-      s_nsSpeeds[*tns].submit(d_lwr.d_usec, &d_now);
 
       typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
       tcache_t tcache;
@@ -650,9 +743,8 @@ int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth,
        nameservers=nsset;
        break; 
       }
-      else {
-       LOG<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl;
-       s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),60,0);
+      else if(isCanonical(*tns)) {
+       goto wasLame;;
       }
     }
   }
index 91f76e8aa0f6b78934a3ef21c1d99ed1849a883e..926b94b4fc8514223556f38f1f6179c6aaa8daaf 100644 (file)
@@ -264,6 +264,28 @@ public:
   typedef map<string,DecayingEwma, CIStringCompare> nsspeeds_t;
   static nsspeeds_t s_nsSpeeds;
 
+  struct AuthDomain
+  {
+    string d_server;
+    typedef multi_index_container <
+      DNSResourceRecord,
+      indexed_by < 
+        ordered_non_unique< 
+          composite_key< DNSResourceRecord,
+                        member<DNSResourceRecord, string, &DNSResourceRecord::qname>,
+                        member<DNSResourceRecord, QType, &DNSResourceRecord::qtype>
+                       >,
+          composite_key_compare<CIStringCompare, std::less<QType> >
+        >
+      >
+    > records_t;
+    records_t d_records;       
+  };
+  
+
+  typedef map<string, AuthDomain, CIStringCompare> domainmap_t;
+  static domainmap_t s_domainmap;
+
   typedef Throttle<tuple<uint32_t,string,uint16_t> > throttle_t;
   static throttle_t s_throttle;
   struct timeval d_now;
@@ -275,6 +297,7 @@ private:
                  int depth, set<GetBestNSAnswer>&beenthere);
   int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere);
   bool doOOBResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
+  domainmap_t::const_iterator getBestAuthZone(string* qname);
   bool doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
   bool doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
   void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere);