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

Revision 1142, 31.8 KB (checked in by ahu, 5 years ago)

fix auth-server support again - it only worked for non-rd queries! Reported by Stefan Schmidt

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