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

Revision 848, 30.6 KB (checked in by ahu, 7 years ago)

oops, internal authoritative server did not differentiate between NXDOMAIN and 'NXRRSET'.
Thanks to Bryan Seitz <seitz@…> for noticing

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