root/trunk/pdns/pdns/syncres.cc @ 802

Revision 802, 28.3 KB (checked in by ahu, 7 years ago)

Big bag of VC++ fixes, mostly by Michel 'Wimpie' Stol

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2003 - 2006  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as published
7    by the Free Software Foundation
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17*/
18
19#include "utility.hh"
20#include "syncres.hh"
21#include <iostream>
22#include <map>
23#include <algorithm>
24#include <set>
25#include <cerrno>
26#include <cstdio>
27#include <cstdlib>
28#include <utility>
29#include <deque>
30#include "logger.hh"
31#include "misc.hh"
32#include "arguments.hh"
33#include "lwres.hh"
34#include "recursor_cache.hh"
35#include "dnsparser.hh"
36
37extern MemRecursorCache RC;
38
39SyncRes::negcache_t SyncRes::s_negcache;   
40SyncRes::nsspeeds_t SyncRes::s_nsSpeeds;   
41unsigned int SyncRes::s_maxnegttl;
42unsigned int SyncRes::s_queries;
43unsigned int SyncRes::s_outgoingtimeouts;
44unsigned int SyncRes::s_outqueries;
45unsigned int SyncRes::s_tcpoutqueries;
46unsigned int SyncRes::s_throttledqueries;
47unsigned int SyncRes::s_nodelegated;
48unsigned int SyncRes::s_unreachables;
49bool SyncRes::s_doIPv6;
50
51SyncRes::domainmap_t SyncRes::s_domainmap;
52string SyncRes::s_serverID;
53bool SyncRes::s_log;
54
55#define LOG if(s_log) L<<Logger::Warning
56
57SyncRes::throttle_t SyncRes::s_throttle;
58
59/** everything begins here - this is the entry point just after receiving a packet */
60int SyncRes::beginResolve(const string &qname, const QType &qtype, uint16_t qclass, vector<DNSResourceRecord>&ret)
61{
62  s_queries++;
63  if( (qtype.getCode()==QType::PTR && !Utility::strcasecmp(qname.c_str(), "1.0.0.127.in-addr.arpa.")) ||
64      (qtype.getCode()==QType::A && qname.length()==10 && !Utility::strcasecmp(qname.c_str(), "localhost."))) {
65    ret.clear();
66    DNSResourceRecord rr;
67    rr.qname=qname;
68    rr.qtype=qtype;
69    rr.qclass=1;
70    rr.ttl=86400;
71    if(qtype.getCode()==QType::PTR)
72      rr.content="localhost.";
73    else
74      rr.content="127.0.0.1";
75    ret.push_back(rr);
76    return 0;
77  }
78
79  if(qclass==3 && qtype.getCode()==QType::TXT && 
80        (!Utility::strcasecmp(qname.c_str(), "version.bind.") || !Utility::strcasecmp(qname.c_str(), "id.server.") || !Utility::strcasecmp(qname.c_str(), "version.pdns.") ) 
81     ) {
82    ret.clear();
83    DNSResourceRecord rr;
84    rr.qname=qname;
85    rr.qtype=qtype;
86    rr.qclass=qclass;
87    rr.ttl=86400;
88    if(!Utility::strcasecmp(qname.c_str(),"version.bind.")  || !Utility::strcasecmp(qname.c_str(),"version.pdns."))
89      rr.content="\""+::arg()["version-string"]+"\"";
90    else
91      rr.content="\""+s_serverID+"\"";
92    ret.push_back(rr);
93    return 0;
94  }
95 
96  if(qclass!=1)
97    return -1;
98 
99  set<GetBestNSAnswer> beenthere;
100  int res=doResolve(qname, qtype, ret, 0, beenthere);
101  if(!res)
102    addCruft(qname, ret);
103  return res;
104}
105
106bool SyncRes::doOOBResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int& res)
107{
108  string prefix;
109  if(s_log) {
110    prefix=d_prefix;
111    prefix.append(depth, ' ');
112  }
113
114  LOG<<prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
115  string authdomain(qname);
116
117  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
118  if(iter==s_domainmap.end()) {
119    LOG<<prefix<<qname<<": auth storage has no zone for this query!"<<endl;
120    return false;
121  }
122  LOG<<prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl;
123  pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range;
124
125  range=iter->second.d_records.equal_range(tie(qname)); // partial lookup
126 
127  ret.clear();
128  AuthDomain::records_t::const_iterator ziter;
129  for(ziter=range.first; ziter!=range.second; ++ziter) {
130    if(qtype.getCode()==QType::ANY || ziter->qtype==qtype || ziter->qtype.getCode()==QType::CNAME)  // let rest of nameserver do the legwork on this one
131      ret.push_back(*ziter);
132  }
133  if(!ret.empty()) {
134    LOG<<prefix<<qname<<": exact match in zone '"<<authdomain<<"'"<<endl;
135    res=0;
136    return true;
137  }
138
139  string nsdomain(qname);
140
141  while(chopOffDotted(nsdomain) && nsdomain!=iter->first) {
142    range=iter->second.d_records.equal_range(make_tuple(nsdomain,QType(QType::NS))); 
143    if(range.first==range.second)
144      continue;
145
146    for(ziter=range.first; ziter!=range.second; ++ziter) {
147      DNSResourceRecord rr=*ziter;
148      rr.d_place=DNSResourceRecord::AUTHORITY;
149      ret.push_back(rr);
150    }
151  }
152  if(ret.empty()) {
153    LOG<<prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl;
154    ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA)));
155    if(ziter!=iter->second.d_records.end()) {
156      DNSResourceRecord rr=*ziter;
157      rr.d_place=DNSResourceRecord::AUTHORITY;
158      ret.push_back(rr);
159    }
160    else
161      LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl;
162    res=RCode::NXDomain;
163  }
164  else 
165    res=0;
166
167  return true;;
168}
169
170int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
171{
172  string prefix;
173  if(s_log) {
174    prefix=d_prefix;
175    prefix.append(depth, ' ');
176  }
177 
178  int res=0;
179  if(!(d_nocache && qtype.getCode()==QType::NS && qname==".")) {
180    if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
181      return res;
182   
183    if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
184      return res;
185  }
186
187  if(d_cacheonly)
188    return 0;
189
190  LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
191
192  string subdomain(qname);
193
194  set<string, CIStringCompare> nsset;
195  for(int tries=0;tries<2 && nsset.empty();++tries) {
196    subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); //  pass beenthere to both occasions
197
198    if(nsset.empty()) { // must've lost root records
199      LOG<<prefix<<qname<<": our root expired, repriming from hints and retrying"<<endl;
200      primeHints();
201    }
202  }
203
204  if(!(res=doResolveAt(nsset, subdomain, qname, qtype, ret, depth, beenthere)))
205    return 0;
206 
207  LOG<<prefix<<qname<<": failed (res="<<res<<")"<<endl;
208  return res<0 ? RCode::ServFail : res;
209}
210
211/** This function explicitly goes out for A addresses, but if configured to use IPv6 as well, will also return any IPv6 addresses in the cache
212    Additionally, it will return the 'best' address up front, and the rest shufled
213*/
214vector<ComboAddress> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
215{
216  typedef vector<DNSResourceRecord> res_t;
217  res_t res;
218
219  typedef vector<ComboAddress> ret_t;
220  ret_t ret;
221
222  if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) {
223    for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) {
224      if(i->qtype.getCode()==QType::A) {
225        ret.push_back(ComboAddress(i->content, 53));
226      }
227    }
228  }
229
230  if(s_doIPv6) {
231    typedef set<DNSResourceRecord> ipv6_t;
232    ipv6_t ipv6;
233    if(RC.get(d_now.tv_sec, qname, QType(QType::AAAA), &ipv6) > 0) {
234      for(ipv6_t::const_iterator i=ipv6.begin(); i != ipv6.end(); ++i) 
235        ret.push_back(ComboAddress(i->content, 53));
236    }
237  }
238 
239  if(ret.size() > 1) {
240    random_shuffle(ret.begin(), ret.end());
241
242    // move 'best' address up front
243    nsspeeds_t::iterator best=s_nsSpeeds.find(qname);
244
245    if(best != s_nsSpeeds.end())
246      for(ret_t::iterator i=ret.begin(); i != ret.end(); ++i) {
247        //      cerr<<"Is "<<i->toString()<<" equal to "<<best->second.d_best.toString()<<"?\n";
248        if(*i==best->second.d_best) {
249          if(i!=ret.begin()) {
250            //      cerr<<"Moving "<<best->second.d_best.toString()<<" up front!\n";
251            *i=*ret.begin();
252            *ret.begin()=best->second.d_best;
253          }
254          break;
255        }
256      }
257  }
258   
259  return ret;
260}
261
262void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere)
263{
264  string prefix, subdomain(qname);
265  if(s_log) {
266    prefix=d_prefix;
267    prefix.append(depth, ' ');
268  }
269  bestns.clear();
270
271  do {
272    LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
273    set<DNSResourceRecord> ns;
274    if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) {
275      for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
276        if(k->ttl > (unsigned int)d_now.tv_sec ) { 
277          set<DNSResourceRecord>aset;
278
279          DNSResourceRecord rr=*k;
280          rr.content=k->content;
281          if(!dottedEndsOn(rr.content, subdomain) || RC.get(d_now.tv_sec, rr.content, QType(QType::A), s_log ? &aset : 0) > 5) {
282            bestns.insert(rr);
283           
284            LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<rr.content<<"'"<<endl;
285            LOG<<prefix<<qname<<": within bailiwick: "<<dottedEndsOn(rr.content, subdomain);
286            if(!aset.empty()) {
287              LOG<<", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->ttl- d_now.tv_sec ))<<endl;
288            }
289            else {
290              LOG<<", not in cache"<<endl;
291            }
292          }
293          else
294            LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
295        }
296      }
297      if(!bestns.empty()) {
298        GetBestNSAnswer answer;
299        answer.qname=qname; answer.bestns=bestns;
300        if(beenthere.count(answer)) {
301          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
302          for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
303            LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<(unsigned int)j->bestns.size()<<")"<<endl;
304          bestns.clear();
305        }
306        else {
307          beenthere.insert(answer);
308          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
309          return;
310        }
311      }
312    }
313    LOG<<prefix<<qname<<": no valid/useful NS in cache for '"<<subdomain<<"'"<<endl;
314  }while(chopOffDotted(subdomain));
315}
316
317SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(string* qname)
318{
319  SyncRes::domainmap_t::const_iterator ret;
320  do {
321    ret=s_domainmap.find(*qname);
322    if(ret!=s_domainmap.end()) 
323      break;
324  }while(chopOffDotted(*qname));
325  return ret;
326}
327
328/** doesn't actually do the work, leaves that to getBestNSFromCache */
329string SyncRes::getBestNSNamesFromCache(const string &qname,set<string, CIStringCompare>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
330{
331  string subdomain(qname);
332
333  string authdomain(qname);
334 
335  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
336  if(iter!=s_domainmap.end()) {
337    nsset.insert(iter->second.d_server);
338    return authdomain;
339  }
340
341  set<DNSResourceRecord> bestns;
342  getBestNSFromCache(subdomain, bestns, depth, beenthere);
343
344  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
345    nsset.insert(k->content);
346    if(k==bestns.begin())
347      subdomain=k->qname;
348  }
349  return subdomain;
350}
351
352bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
353{
354  string prefix;
355  if(s_log) {
356    prefix=d_prefix; 
357    prefix.append(depth, ' ');
358  }
359
360  if(depth>10) {
361    LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl;
362    res=RCode::ServFail;
363    return true;
364  }
365 
366  LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<(qname+"|CNAME")<<"'"<<endl;
367  set<DNSResourceRecord> cset;
368  if(RC.get(d_now.tv_sec, qname,QType(QType::CNAME),&cset) > 0) {
369
370    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
371      if(j->ttl>(unsigned int) d_now.tv_sec) {
372        LOG<<prefix<<qname<<": Found cache CNAME hit for '"<< (qname+"|CNAME") <<"' to '"<<j->content<<"'"<<endl;   
373        DNSResourceRecord rr=*j;
374        rr.ttl-=d_now.tv_sec;
375        ret.push_back(rr);
376        if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME!
377          set<GetBestNSAnswer>beenthere;
378          res=doResolve(j->content, qtype, ret, depth+1, beenthere);
379        }
380        else
381          res=0;
382        return true;
383      }
384    }
385  }
386  LOG<<prefix<<qname<<": No CNAME cache hit of '"<< (qname+"|CNAME") <<"' found"<<endl;
387  return false;
388}
389
390bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
391{
392  bool giveNegative=false;
393 
394  string prefix;
395  if(s_log) {
396    prefix=d_prefix;
397    prefix.append(depth, ' ');
398  }
399
400  string sqname(qname);
401  QType sqt(qtype);
402  uint32_t sttl=0;
403  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"'\n";
404
405  pair<negcache_t::const_iterator, negcache_t::const_iterator> range=s_negcache.equal_range(tie(qname));
406  negcache_t::iterator ni;
407  for(ni=range.first; ni != range.second; ni++) {
408    // we have something
409    if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) {
410      res=0;
411      if((uint32_t)d_now.tv_sec < ni->d_ttd) {
412        sttl=ni->d_ttd - d_now.tv_sec;
413        if(ni->d_qtype.getCode()) {
414          LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
415          res = RCode::NoError;
416        }
417        else {
418          LOG<<prefix<<qname<<": Entire record '"<<qname<<"', is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
419          res= RCode::NXDomain; 
420        }
421        giveNegative=true;
422        sqname=ni->d_qname;
423        sqt=QType::SOA;
424        break;
425      }
426      else {
427        LOG<<prefix<<qname<<": Entire record '"<<qname<<"' was negatively cached, but entry expired"<<endl;
428      }
429    }
430  }
431
432  set<DNSResourceRecord> cset;
433  bool found=false, expired=false;
434
435  if(RC.get(d_now.tv_sec, sqname, sqt, &cset) > 0) {
436    LOG<<prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ";
437    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
438      LOG<<j->content;
439      if(j->ttl>(unsigned int) d_now.tv_sec) {
440        DNSResourceRecord rr=*j;
441        rr.ttl-=d_now.tv_sec;
442        if(giveNegative) {
443          rr.d_place=DNSResourceRecord::AUTHORITY;
444          rr.ttl=sttl;
445        }
446        ret.push_back(rr);
447        LOG<<"[ttl="<<rr.ttl<<"] ";
448        found=true;
449      }
450      else {
451        LOG<<"[expired] ";
452        expired=true;
453      }
454    }
455 
456    LOG<<endl;
457    if(found && !expired) {
458      if(!giveNegative)
459        res=0;
460      return true;
461    }
462    else
463      LOG<<prefix<<qname<<": cache had only stale entries"<<endl;
464  }
465
466  return false;
467}
468
469bool SyncRes::moreSpecificThan(const string& a, const string &b)
470{
471  static string dot(".");
472  int counta=(a!=dot), countb=(b!=dot);
473 
474  for(string::size_type n=0;n<a.size();++n)
475    if(a[n]=='.')
476      counta++;
477  for(string::size_type n=0;n<b.size();++n)
478    if(b[n]=='.')
479      countb++;
480  return counta>countb;
481}
482
483struct speedOrder
484{
485  speedOrder(map<string,double> &speeds) : d_speeds(speeds) {}
486  bool operator()(const string &a, const string &b) const
487  {
488    return d_speeds[a] < d_speeds[b];
489  }
490  map<string,double>& d_speeds;
491};
492
493inline vector<string> SyncRes::shuffleInSpeedOrder(set<string, CIStringCompare> &nameservers, const string &prefix)
494{
495  vector<string> rnameservers;
496  rnameservers.reserve(nameservers.size());
497  map<string,double> speeds;
498
499  for(set<string, CIStringCompare>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) {
500    rnameservers.push_back(*i);
501    speeds[*i]=s_nsSpeeds[*i].get(&d_now);
502  }
503  random_shuffle(rnameservers.begin(),rnameservers.end());
504  speedOrder so(speeds);
505  stable_sort(rnameservers.begin(),rnameservers.end(), so);
506 
507  if(s_log) {
508    L<<Logger::Warning<<prefix<<"Nameservers: ";
509    for(vector<string>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) {
510      if(i!=rnameservers.begin()) {
511        L<<", ";
512        if(!((i-rnameservers.begin())%3))
513          L<<endl<<Logger::Warning<<prefix<<"             ";
514      }
515      L<<*i<<"(" << (int)(speeds[*i]/1000.0) <<"ms)";
516    }
517    L<<endl;
518  }
519  return rnameservers;
520}
521
522struct TCacheComp
523{
524  bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const
525  {
526    int cmp=Utility::strcasecmp(a.first.c_str(), b.first.c_str());
527    if(cmp < 0)
528      return true;
529    if(cmp > 0)
530      return false;
531
532    return a.second < b.second;
533  }
534};
535
536
537
538/** returns -1 in case of no results, rcode otherwise */
539int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, const string &qname, const QType &qtype, 
540                         vector<DNSResourceRecord>&ret, 
541                         int depth, set<GetBestNSAnswer>&beenthere)
542{
543  string prefix;
544  if(s_log) {
545    prefix=d_prefix;
546    prefix.append(depth, ' ');
547  }
548 
549  LWRes::res_t result;
550
551  LOG<<prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact"<<endl;
552
553  for(;;) { // we may get more specific nameservers
554    result.clear();
555
556    vector<string> rnameservers=shuffleInSpeedOrder(nameservers, s_log ? (prefix+qname+": ") : string() );
557
558    for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) { 
559      if(tns==rnameservers.end()) {
560        LOG<<prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS"<<endl;
561        return -1;
562      }
563      if(qname==*tns && qtype.getCode()==QType::A) {
564        LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
565        continue;
566      }
567
568      typedef vector<ComboAddress> remoteIPs_t;
569      remoteIPs_t remoteIPs;
570      remoteIPs_t::const_iterator remoteIP;
571      bool doTCP=false;
572      int resolveret;
573
574      if(tns->empty()) {
575        LOG<<prefix<<qname<<": Domain is out-of-band"<<endl;
576        doOOBResolve(qname, qtype, result, depth, d_lwr.d_rcode);
577        d_lwr.d_tcbit=false;
578      }
579      else {
580        LOG<<prefix<<qname<<": Trying to resolve NS '"<<*tns<<"' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl;
581        if(!isCanonical(*tns)) {
582          LOG<<prefix<<qname<<": Domain has hardcoded nameserver"<<endl;
583          remoteIPs.push_back(ComboAddress(*tns, 53));
584        }
585        else
586          remoteIPs=getAs(*tns, depth+1, beenthere);
587
588        if(remoteIPs.empty()) {
589          LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
590          continue;
591        }
592        else {
593          LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to: ";
594          for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
595            if(remoteIP != remoteIPs.begin())
596              LOG<<", ";
597            LOG<<remoteIP->toString();
598          }
599          LOG<<endl;
600
601        }
602        for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
603          LOG<<prefix<<qname<<": Trying IP "<< remoteIP->toString() <<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
604         
605          if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) {
606            LOG<<prefix<<qname<<": query throttled "<<endl;
607            s_throttledqueries++; d_throttledqueries++;
608            continue;
609          }
610          else {
611            s_outqueries++; d_outqueries++;
612          TryTCP:
613            if(doTCP) {
614              s_tcpoutqueries++; d_tcpoutqueries++;
615            }
616           
617            resolveret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now);    // <- we go out on the wire!
618            if(resolveret != 1) {
619              if(resolveret==0) {
620                LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl;
621                d_timeouts++;
622                s_outgoingtimeouts++;
623              }
624              else if(resolveret==-2) {
625                LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl;
626                g_stats.resourceLimits++;
627              }
628              else {
629                s_unreachables++; d_unreachables++;
630                LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl;
631              }
632             
633              if(resolveret!=-2) { // don't account for resource limits, they are our own fault
634                s_nsSpeeds[*tns].submit(*remoteIP, 1000000, &d_now); // 1 sec
635                if(resolveret==-1)
636                  s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // unreachable
637                else
638                  s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 20, 5);  // timeout
639              }
640              continue;
641            }
642           
643            break;  // this IP address worked!
644          wasLame:; // well, it didn't
645            LOG<<prefix<<qname<<": status=NS "<<*tns<<" ("<< remoteIP->toString() <<") is lame for '"<<auth<<"', trying sibling IP or NS"<<endl;
646            s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100);
647          }
648        }
649       
650        if(remoteIP == remoteIPs.end())  // we tried all IP addresses, none worked
651          continue; 
652       
653        result=d_lwr.result();
654     
655        if(d_lwr.d_tcbit) {
656          if(!doTCP) {
657            doTCP=true;
658            LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
659            goto TryTCP;
660          }
661          LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
662          return RCode::ServFail;
663        }
664       
665        if(d_lwr.d_rcode==RCode::ServFail) {
666          LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling IP or NS"<<endl;
667          s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3);
668          continue;
669        }
670        LOG<<prefix<<qname<<": Got "<<(unsigned int)result.size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl;
671
672        /*  // for you IPv6 fanatics :-)
673        if(remoteIP->sin4.sin_family==AF_INET6)
674          d_lwr.d_usec/=3;
675        */
676
677        s_nsSpeeds[*tns].submit(*remoteIP, d_lwr.d_usec, &d_now);
678      }
679
680      typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
681      tcache_t tcache;
682
683      // reap all answers from this packet that are acceptable
684      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
685        LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
686        if(i->qtype.getCode()==QType::ANY) {
687          LOG<<"NO! - we don't accept 'ANY' data"<<endl;
688          continue;
689        }
690         
691        if(dottedEndsOn(i->qname, auth)) {
692          if(d_lwr.d_aabit && d_lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && ::arg().contains("delegation-only",auth)) {
693            LOG<<"NO! Is from delegation-only zone"<<endl;
694            s_nodelegated++;
695            return RCode::NXDomain;
696          }
697          else {
698            LOG<<"YES!"<<endl;
699           
700            DNSResourceRecord rr=*i;
701            rr.d_place=DNSResourceRecord::ANSWER;
702            //      if(rr.ttl < 5)
703            //  rr.ttl=60;
704            rr.ttl+=d_now.tv_sec;
705            if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
706              rr.content=toLower(rr.content); // this must stay!
707            tcache[make_pair(i->qname,i->qtype)].insert(rr);
708          }
709        }         
710        else
711          LOG<<"NO!"<<endl;
712      }
713   
714      // supplant
715      for(tcache_t::const_iterator i=tcache.begin();i!=tcache.end();++i) {
716        RC.replace(i->first.first, i->first.second, i->second);
717      }
718      set<string, CIStringCompare> nsset; 
719      LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
720
721      bool done=false, realreferral=false, negindic=false;
722      string newauth, soaname, newtarget;
723
724      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
725        if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
726           d_lwr.d_rcode==RCode::NXDomain) {
727          LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl;
728          ret.push_back(*i);
729
730          NegCacheEntry ne;
731
732          ne.d_qname=i->qname;
733          ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial
734          ne.d_name=qname;
735          ne.d_qtype=QType(0);
736         
737          replacing_insert(s_negcache, ne);
738          negindic=true;
739        }
740        else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
741          ret.push_back(*i);
742          newtarget=i->content;
743        }
744        // for ANY answers we *must* have an authoritive answer
745        else if(i->d_place==DNSResourceRecord::ANSWER && !Utility::strcasecmp(i->qname.c_str(),qname.c_str()) && 
746                ( (i->qtype==qtype) ||
747                                      ( qtype==QType(QType::ANY) && d_lwr.d_aabit)))  {
748         
749          LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
750
751
752          done=true;
753          ret.push_back(*i);
754        }
755        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
756          if(moreSpecificThan(i->qname,auth)) {
757            newauth=i->qname;
758            LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
759            realreferral=true;
760          }
761          else 
762            LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
763          nsset.insert(i->content);
764        }
765        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
766           d_lwr.d_rcode==RCode::NoError) {
767          LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl;
768          ret.push_back(*i);
769         
770          NegCacheEntry ne;
771          ne.d_qname=i->qname;
772          ne.d_ttd=d_now.tv_sec + min(s_maxnegttl, i->ttl);
773          ne.d_name=qname;
774          ne.d_qtype=qtype;
775          if(qtype.getCode()) {  // prevents us from blacking out a whole domain
776            replacing_insert(s_negcache, ne);
777          }
778          negindic=true;
779        }
780      }
781
782      if(done){ 
783        LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
784        return 0;
785      }
786      if(d_lwr.d_rcode==RCode::NXDomain) {
787        LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
788        return RCode::NXDomain;
789      }
790      if(!newtarget.empty()) {
791        LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl;
792        set<GetBestNSAnswer>beenthere2;
793        return doResolve(newtarget, qtype, ret,0,beenthere2);
794      }
795      if(nsset.empty() && !d_lwr.d_rcode) {
796        LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
797        return 0;
798      }
799      else if(realreferral) {
800        LOG<<prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl;
801        auth=newauth;
802        nameservers=nsset;
803        break; 
804      }
805      else if(isCanonical(*tns)) {
806        goto wasLame;;
807      }
808    }
809  }
810  return -1;
811}
812
813static bool uniqueComp(const DNSResourceRecord& a, const DNSResourceRecord& b)
814{
815  return(a.qtype==b.qtype && a.qname==b.qname && a.content==b.content);
816}
817
818void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret)
819{
820  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)  // don't add stuff to an NXDOMAIN!
821    if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
822      return;
823
824  //  LOG<<qname<<": Adding best authority records from cache"<<endl;
825  // addAuthorityRecords(qname,ret,0);
826  // LOG<<qname<<": Done adding best authority records."<<endl;
827
828  LOG<<d_prefix<<qname<<": Starting additional processing"<<endl;
829  vector<DNSResourceRecord> addit;
830  static optional<bool> l_doIPv6AP;
831  if(!l_doIPv6AP)
832    l_doIPv6AP=::arg().mustDo("aaaa-additional-processing");
833
834  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) 
835    if( (k->d_place==DNSResourceRecord::ANSWER && (k->qtype==QType(QType::MX) || k->qtype==QType(QType::SRV)))  || 
836       ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
837      LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl;
838      set<GetBestNSAnswer> beenthere;
839      vector<pair<string::size_type, string::size_type> > fields;
840      vstringtok(fields, k->content, " ");
841      string host;
842      if(k->qtype==QType(QType::MX) && fields.size()==2)
843        host=string(k->content.c_str() + fields[1].first, fields[1].second - fields[1].first);
844      else if(k->qtype==QType(QType::NS))
845        host=k->content;
846      else if(k->qtype==QType(QType::SRV) && fields.size()==4)
847        host=string(k->content.c_str() + fields[3].first, fields[3].second - fields[3].first);
848      else 
849        continue;
850      doResolve(host, QType(QType::A), addit, 1, beenthere);
851      if(*l_doIPv6AP)
852        doResolve(host, QType(QType::AAAA), addit, 1, beenthere);
853    }
854 
855  sort(addit.begin(), addit.end());
856  addit.erase(unique(addit.begin(), addit.end(), uniqueComp), addit.end());
857  for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
858    if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) {
859      k->d_place=DNSResourceRecord::ADDITIONAL;
860      ret.push_back(*k);
861    }
862  }
863  LOG<<d_prefix<<qname<<": Done with additional processing"<<endl;
864}
865
866void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
867{
868  set<DNSResourceRecord> bestns;
869  set<GetBestNSAnswer>beenthere;
870  getBestNSFromCache(qname, bestns, depth,beenthere);
871
872  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
873    DNSResourceRecord ns=*k;
874    ns.d_place=DNSResourceRecord::AUTHORITY;
875    ns.ttl-=d_now.tv_sec;
876    ret.push_back(ns);
877  }
878}
Note: See TracBrowser for help on using the browser.