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

Revision 1168, 32.3 KB (checked in by ahu, 5 years ago)

implement multiple forwarders per domain, both from file and from
configuration file, based on patch by Aaron Thompson, with work from Augie
Schwer. Closes ticket 81.

Plus, add support for forwarding to ports != 53, closing ticket 122.

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