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

Revision 859, 30.7 KB (checked in by ahu, 7 years ago)

add support for class=ANY queries.

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