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

Revision 901, 31.2 KB (checked in by ahu, 7 years ago)

narrow down NSSET invalidation to "flawed sets", remove some needless LOG processing

  • 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==0xff)
97    qclass=1;
98  else if(qclass!=1)
99    return -1;
100 
101  set<GetBestNSAnswer> beenthere;
102  int res=doResolve(qname, qtype, ret, 0, beenthere);
103  if(!res)
104    addCruft(qname, ret);
105  return res;
106}
107
108//! This is the 'out of band resolver', in other words, the authoritative server
109bool SyncRes::doOOBResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int& res)
110{
111  string prefix;
112  if(s_log) {
113    prefix=d_prefix;
114    prefix.append(depth, ' ');
115  }
116
117  LOG<<prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
118  string authdomain(qname);
119
120  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
121  if(iter==s_domainmap.end()) {
122    LOG<<prefix<<qname<<": auth storage has no zone for this query!"<<endl;
123    return false;
124  }
125  LOG<<prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl;
126  pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range;
127
128  range=iter->second.d_records.equal_range(tie(qname)); // partial lookup
129 
130  ret.clear();
131  AuthDomain::records_t::const_iterator ziter;
132  bool somedata=false;
133  for(ziter=range.first; ziter!=range.second; ++ziter) {
134    somedata=true;
135    if(qtype.getCode()==QType::ANY || ziter->qtype==qtype || ziter->qtype.getCode()==QType::CNAME)  // let rest of nameserver do the legwork on this one
136      ret.push_back(*ziter);
137  }
138  if(!ret.empty()) {
139    LOG<<prefix<<qname<<": exact match in zone '"<<authdomain<<"'"<<endl;
140    res=0;
141    return true;
142  }
143  if(somedata) {
144    LOG<<prefix<<qname<<": found record in '"<<authdomain<<"', but nothing of the right type, sending SOA"<<endl;
145    ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA)));
146    if(ziter!=iter->second.d_records.end()) {
147      DNSResourceRecord rr=*ziter;
148      rr.d_place=DNSResourceRecord::AUTHORITY;
149      ret.push_back(rr);
150    }
151    else
152      LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl;
153    res=RCode::NoError;
154    return true;
155  }
156
157  string nsdomain(qname);
158
159  while(chopOffDotted(nsdomain) && nsdomain!=iter->first) {
160    range=iter->second.d_records.equal_range(make_tuple(nsdomain,QType(QType::NS))); 
161    if(range.first==range.second)
162      continue;
163
164    for(ziter=range.first; ziter!=range.second; ++ziter) {
165      DNSResourceRecord rr=*ziter;
166      rr.d_place=DNSResourceRecord::AUTHORITY;
167      ret.push_back(rr);
168    }
169  }
170  if(ret.empty()) {
171    LOG<<prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl;
172    ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA)));
173    if(ziter!=iter->second.d_records.end()) {
174      DNSResourceRecord rr=*ziter;
175      rr.d_place=DNSResourceRecord::AUTHORITY;
176      ret.push_back(rr);
177    }
178    else
179      LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl;
180    res=RCode::NXDomain;
181  }
182  else 
183    res=0;
184
185  return true;
186}
187
188int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
189{
190  string prefix;
191  if(s_log) {
192    prefix=d_prefix;
193    prefix.append(depth, ' ');
194  }
195 
196  int res=0;
197  if(!(d_nocache && qtype.getCode()==QType::NS && qname==".")) {
198    if(d_cacheonly) { // very limited OOB support
199      LOG<<prefix<<qname<<": Recursion not requested for '"<<qname<<"|"<<qtype.getName()<<"', peeking at auth/forward zones"<<endl;
200      string authname(qname);
201      domainmap_t::const_iterator iter=getBestAuthZone(&authname);
202      if(iter != s_domainmap.end()) {
203        string server=iter->second.d_server;
204        if(server.empty()) {
205          LWRes::res_t result;
206          ret.clear();
207          doOOBResolve(qname, qtype, ret, depth, res);
208          return res;
209        }
210        else {
211          LOG<<prefix<<qname<<": forwarding query to hardcoded nameserver '"<<server<<"' for zone '"<<authname<<"'"<<endl;
212          ComboAddress remoteIP(server, 53);
213          res=d_lwr.asyncresolve(remoteIP, qname, qtype.getCode(), false, &d_now);   
214          // filter out the good stuff from d_lwr.result()
215          LWRes::res_t result=d_lwr.result();
216
217          for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
218            if(i->d_place == DNSResourceRecord::ANSWER)
219              ret.push_back(*i);
220          }
221          return res;
222        }
223      }
224    }
225
226    if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
227      return res;
228   
229    if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
230      return res;
231  }
232
233  if(d_cacheonly)
234    return 0;
235   
236  LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
237
238  string subdomain(qname);
239
240  set<string, CIStringCompare> nsset;
241  bool flawedNSSet=false;
242  for(int tries=0;tries<2 && nsset.empty();++tries) {
243    subdomain=getBestNSNamesFromCache(subdomain, nsset, &flawedNSSet, depth, beenthere); //  pass beenthere to both occasions
244
245    if(nsset.empty()) { // must've lost root records
246      LOG<<prefix<<qname<<": our root expired, repriming from hints and retrying"<<endl;
247      primeHints();
248    }
249  }
250
251  if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere)))
252    return 0;
253 
254  LOG<<prefix<<qname<<": failed (res="<<res<<")"<<endl;
255  return res<0 ? RCode::ServFail : res;
256}
257
258/** 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
259    Additionally, it will return the 'best' address up front, and the rest shufled
260*/
261vector<ComboAddress> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
262{
263  typedef vector<DNSResourceRecord> res_t;
264  res_t res;
265
266  typedef vector<ComboAddress> ret_t;
267  ret_t ret;
268
269  if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) {
270    for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) {
271      if(i->qtype.getCode()==QType::A) {
272        ret.push_back(ComboAddress(i->content, 53));
273      }
274    }
275  }
276
277  if(s_doIPv6) {
278    typedef set<DNSResourceRecord> ipv6_t;
279    ipv6_t ipv6;
280    if(RC.get(d_now.tv_sec, qname, QType(QType::AAAA), &ipv6) > 0) {
281      for(ipv6_t::const_iterator i=ipv6.begin(); i != ipv6.end(); ++i) 
282        ret.push_back(ComboAddress(i->content, 53));
283    }
284  }
285 
286  if(ret.size() > 1) {
287    random_shuffle(ret.begin(), ret.end());
288
289    // move 'best' address up front
290    nsspeeds_t::iterator best=s_nsSpeeds.find(qname);
291
292    if(best != s_nsSpeeds.end())
293      for(ret_t::iterator i=ret.begin(); i != ret.end(); ++i) {
294        //      cerr<<"Is "<<i->toString()<<" equal to "<<best->second.d_best.toString()<<"?\n";
295        if(*i==best->second.d_best) {
296          if(i!=ret.begin()) {
297            //      cerr<<"Moving "<<best->second.d_best.toString()<<" up front!\n";
298            *i=*ret.begin();
299            *ret.begin()=best->second.d_best;
300          }
301          break;
302        }
303      }
304  }
305   
306  return ret;
307}
308
309void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, bool* flawedNSSet, int depth, set<GetBestNSAnswer>& beenthere)
310{
311  string prefix, subdomain(qname);
312  if(s_log) {
313    prefix=d_prefix;
314    prefix.append(depth, ' ');
315  }
316  bestns.clear();
317
318  do {
319    LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
320    set<DNSResourceRecord> ns;
321    *flawedNSSet = false;
322    if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) {
323      for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
324        if(k->ttl > (unsigned int)d_now.tv_sec ) { 
325          set<DNSResourceRecord>aset;
326
327          DNSResourceRecord rr=*k;
328          rr.content=k->content;
329          if(!dottedEndsOn(rr.content, subdomain) || RC.get(d_now.tv_sec, rr.content, QType(QType::A), s_log ? &aset : 0) > 5) {
330            bestns.insert(rr);
331           
332            LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<rr.content<<"'"<<endl;
333            LOG<<prefix<<qname<<": within bailiwick: "<<dottedEndsOn(rr.content, subdomain);
334            if(!aset.empty()) {
335              LOG<<", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->ttl- d_now.tv_sec ))<<endl;
336            }
337            else {
338              LOG<<", not in cache / did not look at cache"<<endl;
339            }
340          }
341          else {
342            *flawedNSSet=true;
343            LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
344          }
345        }
346      }
347      if(!bestns.empty()) {
348        GetBestNSAnswer answer;
349        answer.qname=qname; answer.bestns=bestns;
350        if(beenthere.count(answer)) {
351          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
352          if(s_log)
353            for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
354              LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<(unsigned int)j->bestns.size()<<")"<<endl;
355          bestns.clear();
356        }
357        else {
358          beenthere.insert(answer);
359          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' (flawedNSSet="<<*flawedNSSet<<")"<<endl;
360          return;
361        }
362      }
363    }
364    LOG<<prefix<<qname<<": no valid/useful NS in cache for '"<<subdomain<<"'"<<endl;
365  }while(chopOffDotted(subdomain));
366}
367
368SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(string* qname)
369{
370  SyncRes::domainmap_t::const_iterator ret;
371  do {
372    ret=s_domainmap.find(*qname);
373    if(ret!=s_domainmap.end()) 
374      break;
375  }while(chopOffDotted(*qname));
376  return ret;
377}
378
379/** doesn't actually do the work, leaves that to getBestNSFromCache */
380string SyncRes::getBestNSNamesFromCache(const string &qname, set<string, CIStringCompare>& nsset, bool* flawedNSSet, int depth, set<GetBestNSAnswer>&beenthere)
381{
382  string subdomain(qname);
383
384  string authdomain(qname);
385 
386  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
387  if(iter!=s_domainmap.end()) {
388    nsset.insert(iter->second.d_server); // this gets picked up in doResolveAt, if empty it means "we are auth", otherwise it denotes a forward
389    return authdomain;
390  }
391
392  set<DNSResourceRecord> bestns;
393  getBestNSFromCache(subdomain, bestns, flawedNSSet, depth, beenthere);
394
395  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
396    nsset.insert(k->content);
397    if(k==bestns.begin())
398      subdomain=k->qname;
399  }
400  return subdomain;
401}
402
403bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
404{
405  string prefix;
406  if(s_log) {
407    prefix=d_prefix; 
408    prefix.append(depth, ' ');
409  }
410
411  if(depth>10) {
412    LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl;
413    res=RCode::ServFail;
414    return true;
415  }
416 
417  LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<(qname+"|CNAME")<<"'"<<endl;
418  set<DNSResourceRecord> cset;
419  if(RC.get(d_now.tv_sec, qname,QType(QType::CNAME),&cset) > 0) {
420
421    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
422      if(j->ttl>(unsigned int) d_now.tv_sec) {
423        LOG<<prefix<<qname<<": Found cache CNAME hit for '"<< (qname+"|CNAME") <<"' to '"<<j->content<<"'"<<endl;   
424        DNSResourceRecord rr=*j;
425        rr.ttl-=d_now.tv_sec;
426        ret.push_back(rr);
427        if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME!
428          set<GetBestNSAnswer>beenthere;
429          res=doResolve(j->content, qtype, ret, depth+1, beenthere);
430        }
431        else
432          res=0;
433        return true;
434      }
435    }
436  }
437  LOG<<prefix<<qname<<": No CNAME cache hit of '"<< (qname+"|CNAME") <<"' found"<<endl;
438  return false;
439}
440
441bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
442{
443  bool giveNegative=false;
444 
445  string prefix;
446  if(s_log) {
447    prefix=d_prefix;
448    prefix.append(depth, ' ');
449  }
450
451  string sqname(qname);
452  QType sqt(qtype);
453  uint32_t sttl=0;
454  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"'\n";
455
456  pair<negcache_t::const_iterator, negcache_t::const_iterator> range=s_negcache.equal_range(tie(qname));
457  negcache_t::iterator ni;
458  for(ni=range.first; ni != range.second; ni++) {
459    // we have something
460    if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) {
461      res=0;
462      if((uint32_t)d_now.tv_sec < ni->d_ttd) {
463        sttl=ni->d_ttd - d_now.tv_sec;
464        if(ni->d_qtype.getCode()) {
465          LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
466          res = RCode::NoError;
467        }
468        else {
469          LOG<<prefix<<qname<<": Entire record '"<<qname<<"', is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
470          res= RCode::NXDomain; 
471        }
472        giveNegative=true;
473        sqname=ni->d_qname;
474        sqt=QType::SOA;
475        break;
476      }
477      else {
478        LOG<<prefix<<qname<<": Entire record '"<<qname<<"' was negatively cached, but entry expired"<<endl;
479      }
480    }
481  }
482
483  set<DNSResourceRecord> cset;
484  bool found=false, expired=false;
485
486  if(RC.get(d_now.tv_sec, sqname, sqt, &cset) > 0) {
487    LOG<<prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ";
488    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
489      LOG<<j->content;
490      if(j->ttl>(unsigned int) d_now.tv_sec) {
491        DNSResourceRecord rr=*j;
492        rr.ttl-=d_now.tv_sec;
493        if(giveNegative) {
494          rr.d_place=DNSResourceRecord::AUTHORITY;
495          rr.ttl=sttl;
496        }
497        ret.push_back(rr);
498        LOG<<"[ttl="<<rr.ttl<<"] ";
499        found=true;
500      }
501      else {
502        LOG<<"[expired] ";
503        expired=true;
504      }
505    }
506 
507    LOG<<endl;
508    if(found && !expired) {
509      if(!giveNegative)
510        res=0;
511      return true;
512    }
513    else
514      LOG<<prefix<<qname<<": cache had only stale entries"<<endl;
515  }
516
517  return false;
518}
519
520bool SyncRes::moreSpecificThan(const string& a, const string &b)
521{
522  static string dot(".");
523  int counta=(a!=dot), countb=(b!=dot);
524 
525  for(string::size_type n=0;n<a.size();++n)
526    if(a[n]=='.')
527      counta++;
528  for(string::size_type n=0;n<b.size();++n)
529    if(b[n]=='.')
530      countb++;
531  return counta>countb;
532}
533
534struct speedOrder
535{
536  speedOrder(map<string,double> &speeds) : d_speeds(speeds) {}
537  bool operator()(const string &a, const string &b) const
538  {
539    return d_speeds[a] < d_speeds[b];
540  }
541  map<string,double>& d_speeds;
542};
543
544inline vector<string> SyncRes::shuffleInSpeedOrder(set<string, CIStringCompare> &nameservers, const string &prefix)
545{
546  vector<string> rnameservers;
547  rnameservers.reserve(nameservers.size());
548  map<string,double> speeds;
549
550  for(set<string, CIStringCompare>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) {
551    rnameservers.push_back(*i);
552    speeds[*i]=s_nsSpeeds[*i].get(&d_now);
553  }
554  random_shuffle(rnameservers.begin(),rnameservers.end());
555  speedOrder so(speeds);
556  stable_sort(rnameservers.begin(),rnameservers.end(), so);
557 
558  if(s_log) {
559    L<<Logger::Warning<<prefix<<"Nameservers: ";
560    for(vector<string>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) {
561      if(i!=rnameservers.begin()) {
562        L<<", ";
563        if(!((i-rnameservers.begin())%3))
564          L<<endl<<Logger::Warning<<prefix<<"             ";
565      }
566      L<<*i<<"(" << (int)(speeds[*i]/1000.0) <<"ms)";
567    }
568    L<<endl;
569  }
570  return rnameservers;
571}
572
573struct TCacheComp
574{
575  bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const
576  {
577    int cmp=Utility::strcasecmp(a.first.c_str(), b.first.c_str());
578    if(cmp < 0)
579      return true;
580    if(cmp > 0)
581      return false;
582
583    return a.second < b.second;
584  }
585};
586
587
588
589/** returns -1 in case of no results, rcode otherwise */
590int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, bool flawedNSSet, const string &qname, const QType &qtype, 
591                         vector<DNSResourceRecord>&ret, 
592                         int depth, set<GetBestNSAnswer>&beenthere)
593{
594  string prefix;
595  if(s_log) {
596    prefix=d_prefix;
597    prefix.append(depth, ' ');
598  }
599 
600  LWRes::res_t result;
601
602  LOG<<prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact"<<endl;
603
604  for(;;) { // we may get more specific nameservers
605    result.clear();
606
607    vector<string> rnameservers=shuffleInSpeedOrder(nameservers, s_log ? (prefix+qname+": ") : string() );
608
609    for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) { 
610      if(tns==rnameservers.end()) {
611        LOG<<prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl;
612        if(auth!="." && flawedNSSet) {
613          g_stats.nsSetInvalidations++;
614          LOG<<prefix<<qname<<": Invalidating nameservers for level '"<<auth<<"', next query might succeed"<<endl;
615          RC.doWipeCache(auth, QType::NS);
616        }
617        return -1;
618      }
619      if(qname==*tns && qtype.getCode()==QType::A) {
620        LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
621        continue;
622      }
623
624      typedef vector<ComboAddress> remoteIPs_t;
625      remoteIPs_t remoteIPs;
626      remoteIPs_t::const_iterator remoteIP;
627      bool doTCP=false;
628      int resolveret;
629
630      if(tns->empty()) {
631        LOG<<prefix<<qname<<": Domain is out-of-band"<<endl;
632        doOOBResolve(qname, qtype, result, depth, d_lwr.d_rcode);
633        d_lwr.d_tcbit=false;
634        d_lwr.d_aabit=true;
635      }
636      else {
637        LOG<<prefix<<qname<<": Trying to resolve NS '"<<*tns<<"' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl;
638        if(!isCanonical(*tns)) {
639          LOG<<prefix<<qname<<": Domain has hardcoded nameserver"<<endl;
640          remoteIPs.push_back(ComboAddress(*tns, 53));
641        }
642        else
643          remoteIPs=getAs(*tns, depth+1, beenthere);
644
645        if(remoteIPs.empty()) {
646          LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
647          flawedNSSet=true;
648          continue;
649        }
650        else {
651          LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to: ";
652          for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
653            if(remoteIP != remoteIPs.begin())
654              LOG<<", ";
655            LOG<<remoteIP->toString();
656          }
657          LOG<<endl;
658
659        }
660        for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
661          LOG<<prefix<<qname<<": Trying IP "<< remoteIP->toString() <<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
662         
663          if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) {
664            LOG<<prefix<<qname<<": query throttled "<<endl;
665            s_throttledqueries++; d_throttledqueries++;
666            continue;
667          }
668          else {
669            s_outqueries++; d_outqueries++;
670          TryTCP:
671            if(doTCP) {
672              s_tcpoutqueries++; d_tcpoutqueries++;
673            }
674           
675            resolveret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now);    // <- we go out on the wire!
676            if(resolveret != 1) {
677              if(resolveret==0) {
678                LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl;
679                d_timeouts++;
680                s_outgoingtimeouts++;
681              }
682              else if(resolveret==-2) {
683                LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl;
684                g_stats.resourceLimits++;
685              }
686              else {
687                s_unreachables++; d_unreachables++;
688                LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl;
689              }
690             
691              if(resolveret!=-2) { // don't account for resource limits, they are our own fault
692                s_nsSpeeds[*tns].submit(*remoteIP, 1000000, &d_now); // 1 sec
693                if(resolveret==-1)
694                  s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // unreachable
695                else
696                  s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 20, 5);  // timeout
697              }
698              continue;
699            }
700           
701            break;  // this IP address worked!
702          wasLame:; // well, it didn't
703            LOG<<prefix<<qname<<": status=NS "<<*tns<<" ("<< remoteIP->toString() <<") is lame for '"<<auth<<"', trying sibling IP or NS"<<endl;
704            s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100);
705          }
706        }
707       
708        if(remoteIP == remoteIPs.end())  // we tried all IP addresses, none worked
709          continue; 
710       
711        result=d_lwr.result();
712     
713        if(d_lwr.d_tcbit) {
714          if(!doTCP) {
715            doTCP=true;
716            LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
717            goto TryTCP;
718          }
719          LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
720          return RCode::ServFail;
721        }
722       
723        if(d_lwr.d_rcode==RCode::ServFail) {
724          LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling IP or NS"<<endl;
725          s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3);
726          continue;
727        }
728        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;
729
730        /*  // for you IPv6 fanatics :-)
731        if(remoteIP->sin4.sin_family==AF_INET6)
732          d_lwr.d_usec/=3;
733        */
734
735        s_nsSpeeds[*tns].submit(*remoteIP, d_lwr.d_usec, &d_now);
736      }
737
738      typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
739      tcache_t tcache;
740
741      // reap all answers from this packet that are acceptable
742      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
743        LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
744        if(i->qtype.getCode()==QType::ANY) {
745          LOG<<"NO! - we don't accept 'ANY' data"<<endl;
746          continue;
747        }
748         
749        if(dottedEndsOn(i->qname, auth)) {
750          if(d_lwr.d_aabit && d_lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && ::arg().contains("delegation-only",auth)) {
751            LOG<<"NO! Is from delegation-only zone"<<endl;
752            s_nodelegated++;
753            return RCode::NXDomain;
754          }
755          else {
756            LOG<<"YES!"<<endl;
757           
758            DNSResourceRecord rr=*i;
759            rr.d_place=DNSResourceRecord::ANSWER;
760
761            rr.ttl=min(86400*14U, rr.ttl); // limit TTL to two weeks
762            rr.ttl += d_now.tv_sec;
763
764            if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
765              rr.content=toLower(rr.content); // this must stay! (the cache can't be case-insensitive on the RHS of records)
766            tcache[make_pair(i->qname,i->qtype)].insert(rr);
767          }
768        }         
769        else
770          LOG<<"NO!"<<endl;
771      }
772   
773      // supplant
774      for(tcache_t::iterator i=tcache.begin();i!=tcache.end();++i) {
775        if(i->second.size() > 1) {  // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2)
776          uint32_t lowestTTL=numeric_limits<uint32_t>::max();
777          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
778            lowestTTL=min(lowestTTL, j->ttl);
779         
780          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
781            ((tcache_t::value_type::second_type::value_type*)&(*j))->ttl=lowestTTL;
782        }
783
784        RC.replace(d_now.tv_sec, i->first.first, i->first.second, i->second, d_lwr.d_aabit);
785      }
786      set<string, CIStringCompare> nsset; 
787      LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
788
789      bool done=false, realreferral=false, negindic=false;
790      string newauth, soaname, newtarget;
791
792      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
793        if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
794           d_lwr.d_rcode==RCode::NXDomain) {
795          LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl;
796          ret.push_back(*i);
797
798          NegCacheEntry ne;
799
800          ne.d_qname=i->qname;
801          ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial
802          ne.d_name=qname;
803          ne.d_qtype=QType(0); // this encodes 'whole record'
804         
805          replacing_insert(s_negcache, ne);
806          negindic=true;
807        }
808        else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
809          ret.push_back(*i);
810          newtarget=i->content;
811        }
812        // for ANY answers we *must* have an authoritive answer
813        else if(i->d_place==DNSResourceRecord::ANSWER && !Utility::strcasecmp(i->qname.c_str(),qname.c_str()) && 
814                ( (i->qtype==qtype) ||
815                                      ( qtype==QType(QType::ANY) && d_lwr.d_aabit)))  {
816         
817          LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
818
819          done=true;
820          ret.push_back(*i);
821        }
822        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
823          if(moreSpecificThan(i->qname,auth)) {
824            newauth=i->qname;
825            LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
826            realreferral=true;
827          }
828          else 
829            LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
830          nsset.insert(i->content);
831        }
832        else if(!done && i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
833           d_lwr.d_rcode==RCode::NoError) {
834          LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl;
835          ret.push_back(*i);
836         
837          NegCacheEntry ne;
838          ne.d_qname=i->qname;
839          ne.d_ttd=d_now.tv_sec + min(s_maxnegttl, i->ttl);
840          ne.d_name=qname;
841          ne.d_qtype=qtype;
842          if(qtype.getCode()) {  // prevents us from blacking out a whole domain
843            replacing_insert(s_negcache, ne);
844          }
845          negindic=true;
846        }
847      }
848
849      if(done){ 
850        LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
851        return 0;
852      }
853      if(d_lwr.d_rcode==RCode::NXDomain) {
854        LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
855        return RCode::NXDomain;
856      }
857      if(!newtarget.empty()) {
858        LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl;
859        set<GetBestNSAnswer>beenthere2;
860        return doResolve(newtarget, qtype, ret,0,beenthere2);
861      }
862      if(nsset.empty() && !d_lwr.d_rcode) {
863        LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
864        return 0;
865      }
866      else if(realreferral) {
867        LOG<<prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl;
868        auth=newauth;
869        nameservers=nsset;
870        break; 
871      }
872      else if(isCanonical(*tns)) {
873        goto wasLame;;
874      }
875    }
876  }
877  return -1;
878}
879
880static bool uniqueComp(const DNSResourceRecord& a, const DNSResourceRecord& b)
881{
882  return(a.qtype==b.qtype && a.qname==b.qname && a.content==b.content);
883}
884
885void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret)
886{
887  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)  // don't add stuff to an NXDOMAIN!
888    if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
889      return;
890
891  //  LOG<<qname<<": Adding best authority records from cache"<<endl;
892  // addAuthorityRecords(qname,ret,0);
893  // LOG<<qname<<": Done adding best authority records."<<endl;
894
895  LOG<<d_prefix<<qname<<": Starting additional processing"<<endl;
896  vector<DNSResourceRecord> addit;
897  static optional<bool> l_doIPv6AP;
898  if(!l_doIPv6AP)
899    l_doIPv6AP=::arg().mustDo("aaaa-additional-processing");
900
901  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) 
902    if( (k->d_place==DNSResourceRecord::ANSWER && (k->qtype==QType(QType::MX) || k->qtype==QType(QType::SRV)))  || 
903       ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
904      LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl;
905      set<GetBestNSAnswer> beenthere;
906      vector<pair<string::size_type, string::size_type> > fields;
907      vstringtok(fields, k->content, " ");
908      string host;
909      if(k->qtype==QType(QType::MX) && fields.size()==2)
910        host=string(k->content.c_str() + fields[1].first, fields[1].second - fields[1].first);
911      else if(k->qtype==QType(QType::NS))
912        host=k->content;
913      else if(k->qtype==QType(QType::SRV) && fields.size()==4)
914        host=string(k->content.c_str() + fields[3].first, fields[3].second - fields[3].first);
915      else 
916        continue;
917      doResolve(host, QType(QType::A), addit, 1, beenthere);
918      if(*l_doIPv6AP)
919        doResolve(host, QType(QType::AAAA), addit, 1, beenthere);
920    }
921 
922  sort(addit.begin(), addit.end());
923  addit.erase(unique(addit.begin(), addit.end(), uniqueComp), addit.end());
924  for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
925    if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) {
926      k->d_place=DNSResourceRecord::ADDITIONAL;
927      ret.push_back(*k);
928    }
929  }
930  LOG<<d_prefix<<qname<<": Done with additional processing"<<endl;
931}
932
933void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
934{
935  set<DNSResourceRecord> bestns;
936  set<GetBestNSAnswer> beenthere;
937  bool dontcare;
938  getBestNSFromCache(qname, bestns, &dontcare, depth, beenthere);
939
940  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
941    DNSResourceRecord ns=*k;
942    ns.d_place=DNSResourceRecord::AUTHORITY;
943    ns.ttl-=d_now.tv_sec;
944    ret.push_back(ns);
945  }
946}
Note: See TracBrowser for help on using the browser.