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

Revision 819, 28.7 KB (checked in by ahu, 7 years ago)

implement RFC 2181 paragraph 5.2:

Should an authoritative source send such a malformed RRSet, the
client should treat the RRs for all purposes as if all TTLs in the
RRSet had been set to the value of the lowest TTL in the RRSet.

Thanks to Stephen Harker for noticing in  http://mailman.powerdns.com/pipermail/pdns-users/2006-May/003341.html

  • 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
705            rr.ttl += d_now.tv_sec;
706            if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
707              rr.content=toLower(rr.content); // this must stay!
708            tcache[make_pair(i->qname,i->qtype)].insert(rr);
709          }
710        }         
711        else
712          LOG<<"NO!"<<endl;
713      }
714   
715      // supplant
716      for(tcache_t::const_iterator i=tcache.begin();i!=tcache.end();++i) {
717        if(i->second.size() > 1) {
718          uint32_t lowestTTL=numeric_limits<uint32_t>::max();
719          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
720            lowestTTL=min(lowestTTL, j->ttl);
721         
722          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
723            ((tcache_t::value_type::second_type::value_type*)&(*j))->ttl=lowestTTL;
724        }
725
726        RC.replace(i->first.first, i->first.second, i->second);
727      }
728      set<string, CIStringCompare> nsset; 
729      LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
730
731      bool done=false, realreferral=false, negindic=false;
732      string newauth, soaname, newtarget;
733
734      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
735        if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
736           d_lwr.d_rcode==RCode::NXDomain) {
737          LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl;
738          ret.push_back(*i);
739
740          NegCacheEntry ne;
741
742          ne.d_qname=i->qname;
743          ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial
744          ne.d_name=qname;
745          ne.d_qtype=QType(0);
746         
747          replacing_insert(s_negcache, ne);
748          negindic=true;
749        }
750        else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
751          ret.push_back(*i);
752          newtarget=i->content;
753        }
754        // for ANY answers we *must* have an authoritive answer
755        else if(i->d_place==DNSResourceRecord::ANSWER && !Utility::strcasecmp(i->qname.c_str(),qname.c_str()) && 
756                ( (i->qtype==qtype) ||
757                                      ( qtype==QType(QType::ANY) && d_lwr.d_aabit)))  {
758         
759          LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
760
761
762          done=true;
763          ret.push_back(*i);
764        }
765        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
766          if(moreSpecificThan(i->qname,auth)) {
767            newauth=i->qname;
768            LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
769            realreferral=true;
770          }
771          else 
772            LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
773          nsset.insert(i->content);
774        }
775        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
776           d_lwr.d_rcode==RCode::NoError) {
777          LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl;
778          ret.push_back(*i);
779         
780          NegCacheEntry ne;
781          ne.d_qname=i->qname;
782          ne.d_ttd=d_now.tv_sec + min(s_maxnegttl, i->ttl);
783          ne.d_name=qname;
784          ne.d_qtype=qtype;
785          if(qtype.getCode()) {  // prevents us from blacking out a whole domain
786            replacing_insert(s_negcache, ne);
787          }
788          negindic=true;
789        }
790      }
791
792      if(done){ 
793        LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
794        return 0;
795      }
796      if(d_lwr.d_rcode==RCode::NXDomain) {
797        LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
798        return RCode::NXDomain;
799      }
800      if(!newtarget.empty()) {
801        LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl;
802        set<GetBestNSAnswer>beenthere2;
803        return doResolve(newtarget, qtype, ret,0,beenthere2);
804      }
805      if(nsset.empty() && !d_lwr.d_rcode) {
806        LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
807        return 0;
808      }
809      else if(realreferral) {
810        LOG<<prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl;
811        auth=newauth;
812        nameservers=nsset;
813        break; 
814      }
815      else if(isCanonical(*tns)) {
816        goto wasLame;;
817      }
818    }
819  }
820  return -1;
821}
822
823static bool uniqueComp(const DNSResourceRecord& a, const DNSResourceRecord& b)
824{
825  return(a.qtype==b.qtype && a.qname==b.qname && a.content==b.content);
826}
827
828void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret)
829{
830  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)  // don't add stuff to an NXDOMAIN!
831    if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
832      return;
833
834  //  LOG<<qname<<": Adding best authority records from cache"<<endl;
835  // addAuthorityRecords(qname,ret,0);
836  // LOG<<qname<<": Done adding best authority records."<<endl;
837
838  LOG<<d_prefix<<qname<<": Starting additional processing"<<endl;
839  vector<DNSResourceRecord> addit;
840  static optional<bool> l_doIPv6AP;
841  if(!l_doIPv6AP)
842    l_doIPv6AP=::arg().mustDo("aaaa-additional-processing");
843
844  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) 
845    if( (k->d_place==DNSResourceRecord::ANSWER && (k->qtype==QType(QType::MX) || k->qtype==QType(QType::SRV)))  || 
846       ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
847      LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl;
848      set<GetBestNSAnswer> beenthere;
849      vector<pair<string::size_type, string::size_type> > fields;
850      vstringtok(fields, k->content, " ");
851      string host;
852      if(k->qtype==QType(QType::MX) && fields.size()==2)
853        host=string(k->content.c_str() + fields[1].first, fields[1].second - fields[1].first);
854      else if(k->qtype==QType(QType::NS))
855        host=k->content;
856      else if(k->qtype==QType(QType::SRV) && fields.size()==4)
857        host=string(k->content.c_str() + fields[3].first, fields[3].second - fields[3].first);
858      else 
859        continue;
860      doResolve(host, QType(QType::A), addit, 1, beenthere);
861      if(*l_doIPv6AP)
862        doResolve(host, QType(QType::AAAA), addit, 1, beenthere);
863    }
864 
865  sort(addit.begin(), addit.end());
866  addit.erase(unique(addit.begin(), addit.end(), uniqueComp), addit.end());
867  for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
868    if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) {
869      k->d_place=DNSResourceRecord::ADDITIONAL;
870      ret.push_back(*k);
871    }
872  }
873  LOG<<d_prefix<<qname<<": Done with additional processing"<<endl;
874}
875
876void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
877{
878  set<DNSResourceRecord> bestns;
879  set<GetBestNSAnswer>beenthere;
880  getBestNSFromCache(qname, bestns, depth,beenthere);
881
882  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
883    DNSResourceRecord ns=*k;
884    ns.d_place=DNSResourceRecord::AUTHORITY;
885    ns.ttl-=d_now.tv_sec;
886    ret.push_back(ns);
887  }
888}
Note: See TracBrowser for help on using the browser.