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

Revision 137, 15.4 KB (checked in by ahu, 10 years ago)

recursor work, alignment issues

  • 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  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 as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*/
19#include "syncres.hh"
20#include <iostream>
21#include <map>
22#include <algorithm>
23#include <set>
24#include <cerrno>
25#include <cstdio>
26#include <cstdlib>
27#include <utility>
28#include "logger.hh"
29#include "misc.hh"
30#include "arguments.hh"
31#include "lwres.hh"
32
33map<string,string> SyncRes::s_negcache;
34unsigned int SyncRes::s_queries;
35unsigned int SyncRes::s_outqueries;
36bool SyncRes::s_log;
37
38#define LOG if(s_log)L<<Logger::Warning
39
40/** everything begins here - this is the entry point just after receiving a packet */
41int SyncRes::beginResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret)
42{
43  set<GetBestNSAnswer> beenthere;
44  s_queries++;
45  int res=doResolve(qname, qtype, ret,0,beenthere);
46  if(!res)
47    addCruft(qname, ret);
48  return res;
49}
50
51int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
52{
53  string prefix(d_prefix);
54  prefix.append(depth, ' ');
55 
56  int res;
57  if(!(d_nocache && qtype.getCode()==QType::NS && qname.empty())) {
58    if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
59      return res;
60   
61    if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
62      return res;
63  }
64
65  if(d_cacheonly)
66    return 0;
67
68  LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
69
70  string subdomain(qname);
71
72  set<string> nsset;
73  subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); //  pass beenthere to both occasions/
74  if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere)))
75    return 0;
76 
77  LOG<<prefix<<qname<<": failed"<<endl;
78  return res<0 ? RCode::ServFail : res;
79}
80
81string SyncRes::getA(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
82{
83  vector<DNSResourceRecord> res;
84  string ret;
85
86  if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) 
87    ret=res[res.size()-1].content; // last entry, in case of CNAME in between
88
89  return ret;
90}
91
92void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere)
93{
94  string prefix(d_prefix), subdomain(qname);
95  prefix.append(depth, ' ');
96  bestns.clear();
97
98  do {
99    LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
100    set<DNSResourceRecord>ns;
101    if(getCache(subdomain,QType(QType::NS),&ns)>0) {
102      for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
103        if(k->ttl>(unsigned int)time(0)) { 
104          set<DNSResourceRecord>aset;
105          if(!endsOn(k->content,subdomain) || getCache(k->content,QType(QType::A),&aset) > 5) {
106            bestns.insert(*k);
107            LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<k->content<<"'"<<endl;
108            LOG<<prefix<<qname<<": endson: "<<endsOn(k->content,subdomain);
109            if(!aset.empty())
110              LOG<<", in cache, ttl="<<((time_t)aset.begin()->ttl-time(0))<<endl;
111            else
112              LOG<<", not in cache"<<endl;
113          }
114          else
115            LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
116        }
117      }
118      if(!bestns.empty()) {
119        GetBestNSAnswer answer;
120        answer.qname=toLower(qname); answer.bestns=bestns;
121        if(beenthere.count(answer)) {
122          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
123          for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
124            LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<j->bestns.size()<<")"<<endl;
125          bestns.clear();
126        }
127        else {
128          beenthere.insert(answer);
129          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
130          return;
131        }
132      }
133    }
134  }while(chopOff(subdomain));
135}
136
137
138/** doesn't actually do the work, leaves that to getBestNSFromCache */
139string SyncRes::getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
140{
141  string subdomain(qname);
142
143  set<DNSResourceRecord> bestns;
144  getBestNSFromCache(subdomain, bestns, depth, beenthere);
145
146  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
147    nsset.insert(k->content);
148    subdomain=k->qname;
149  }
150  return subdomain;
151}
152
153bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
154{
155  string prefix(d_prefix), tuple=toLower(qname)+"|CNAME";
156  prefix.append(depth, ' ');
157
158  if(depth>10) {
159    LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl;
160    res=RCode::ServFail;
161    return true;
162  }
163 
164  LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<tuple<<"'"<<endl;
165  set<DNSResourceRecord> cset;
166  if(getCache(qname,QType(QType::CNAME),&cset) > 0) {
167    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
168      if(j->ttl>(unsigned int)time(0)) {
169        LOG<<prefix<<qname<<": Found cache CNAME hit for '"<<tuple<<"' to '"<<j->content<<"'"<<endl;   
170        DNSResourceRecord rr=*j;
171        rr.ttl-=time(0);
172        ret.push_back(rr);
173        if(!(qtype==QType(QType::CNAME))) {// perhaps they really wanted a CNAME!
174          set<GetBestNSAnswer>beenthere;
175          res=doResolve(j->content, qtype, ret, depth, beenthere);
176        }
177        return true;
178      }
179    }
180  }
181  LOG<<prefix<<qname<<": No CNAME cache hit of '"<<tuple<<"' found"<<endl;
182  return false;
183}
184
185bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
186{
187  string prefix(d_prefix), tuple;
188  prefix.append(depth, ' ');
189
190  tuple=toLower(qname)+"|"+qtype.getName();
191  LOG<<prefix<<qname<<": Looking for direct cache hit of '"<<tuple<<"', "<<s_negcache.count(tuple)<<endl;
192
193  string sqname(qname);
194  QType sqt(qtype);
195
196  res=0;
197  map<string,string>::const_iterator ni=s_negcache.find(tuple);
198  if(ni!=s_negcache.end()) {
199    LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached, will return immediately if we still have SOA ("<<ni->second<<") to prove it"<<endl;
200    res=RCode::NXDomain;
201    sqname=ni->second;
202    sqt="SOA";
203  }
204
205  set<DNSResourceRecord> cset;
206  bool found=false, expired=false;
207  if(getCache(sqname,sqt,&cset)>0) {
208    LOG<<prefix<<qname<<": Found cache hit for "<<sqt.getName()<<": ";
209    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
210      LOG<<j->content;
211      if(j->ttl>(unsigned int)time(0)) {
212        DNSResourceRecord rr=*j;
213        rr.ttl-=time(0);
214        if(res==RCode::NXDomain)
215          rr.d_place=DNSResourceRecord::AUTHORITY;
216        ret.push_back(rr);
217        LOG<<"[ttl="<<rr.ttl<<"] ";
218        found=true;
219      }
220      else {
221        LOG<<"[expired] ";
222        expired=true;
223      }
224    }
225 
226    LOG<<endl;
227    if(found && !expired) 
228      return true;
229    else
230      LOG<<prefix<<qname<<": cache had only stale entries"<<endl;
231  }
232  return false;
233}
234
235bool SyncRes::moreSpecificThan(const string& a, const string &b)
236{
237  int counta=!a.empty(), countb=!b.empty();
238 
239  for(string::size_type n=0;n<a.size();++n)
240    if(a[n]=='.')
241      counta++;
242  for(string::size_type n=0;n<b.size();++n)
243    if(b[n]=='.')
244      countb++;
245  return counta>countb;
246}
247
248vector<string> SyncRes::shuffle(set<string> &nameservers)
249{
250  vector<string> rnameservers;
251  for(set<string>::const_iterator i=nameservers.begin();i!=nameservers.end();++i)
252    rnameservers.push_back(*i);
253 
254  random_shuffle(rnameservers.begin(),rnameservers.end());
255  return rnameservers;
256}
257
258/** returns -1 in case of no results, rcode otherwise */
259int SyncRes::doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, 
260                int depth, set<GetBestNSAnswer>&beenthere)
261{
262  string prefix(d_prefix);
263  prefix.append(depth, ' ');
264 
265  LWRes::res_t result;
266
267  LOG<<prefix<<qname<<": Cache consultations done, have "<<nameservers.size()<<" NS to contact"<<endl;
268
269  for(;;) { // we may get more specific nameservers
270    bool aabit=false;
271    result.clear();
272
273    vector<string>rnameservers=shuffle(nameservers);
274
275    // what if we don't have an A for an NS anymore, but do have an NS for that NS?
276
277    for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) { 
278      if(tns==rnameservers.end()) {
279        LOG<<prefix<<qname<<": Failed to resolve via any of the "<<rnameservers.size()<<" offered NS"<<endl;
280        return -1;
281      }
282      if(qname==*tns && qtype.getCode()==QType::A) {
283        LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
284        continue;
285      }
286      LOG<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
287      string remoteIP=getA(*tns, depth+1,beenthere);
288      if(remoteIP.empty()) {
289        LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
290        continue;
291      }
292      LOG<<prefix<<qname<<": Resolved NS "<<*tns<<" to "<<remoteIP<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
293
294      s_outqueries++;
295      d_outqueries++;
296      if(d_lwr.asyncresolve(remoteIP,qname.c_str(),qtype.getCode())!=1) { // <- we go out on the wire!
297        LOG<<prefix<<qname<<": error resolving (perhaps timeout?)"<<endl;
298        continue;
299      }
300
301      result=d_lwr.result(aabit);
302      if(d_lwr.d_rcode==RCode::ServFail) {
303        LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling NS"<<endl;
304        continue;
305      }
306      LOG<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<remoteIP<<"), rcode="<<d_lwr.d_rcode<<endl;
307
308      map<string,set<DNSResourceRecord> > tcache;
309      // reap all answers from this packet that are acceptable
310      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
311        LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
312
313        if(endsOn(i->qname, auth)) {
314          LOG<<"YES!"<<endl;
315
316          DNSResourceRecord rr=*i;
317          rr.d_place=DNSResourceRecord::ANSWER;
318          rr.ttl+=time(0);
319          //      rr.ttl=time(0)+10+10*rr.qtype.getCode();
320          tcache[toLower(i->qname)+"|"+i->qtype.getName()].insert(rr);
321        }
322        else
323          LOG<<"NO!"<<endl;
324      }
325   
326      // supplant
327      for(map<string,set<DNSResourceRecord> >::const_iterator i=tcache.begin();i!=tcache.end();++i) {
328        vector<string>parts;
329        stringtok(parts,i->first,"|");
330        QType qt;
331        if(parts.size()==2) {
332          qt=parts[1];
333          replaceCache(parts[0],qt,i->second);
334        }
335        else {
336          qt=parts[0];
337          replaceCache("",qt,i->second);
338        }
339      }
340      set<string> nsset; 
341      LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
342
343      bool done=false, realreferral=false, negindic=false;
344      string newauth, soaname, newtarget;
345
346      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
347        if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA) {
348          LOG<<prefix<<qname<<": got negative caching indication for '"<<toLower(qname)+"|"+qtype.getName()<<"'"<<endl;
349          ret.push_back(*i);
350          s_negcache[toLower(qname)+"|"+qtype.getName()]=i->qname;
351          negindic=true;
352        }
353        else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
354          ret.push_back(*i);
355          newtarget=i->content;
356        }
357        // for ANY answers we *must* have an authoritive answer
358        else if(i->d_place==DNSResourceRecord::ANSWER && toLower(i->qname)==toLower(qname) && (i->qtype==qtype || ( qtype==QType(QType::ANY) && aabit)))  {
359          LOG<<prefix<<qname<<": answer is in: resolved to '"<<i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
360          done=true;
361          ret.push_back(*i);
362        }
363        else if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
364          if(moreSpecificThan(i->qname,auth)) {
365            newauth=i->qname;
366            LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
367            realreferral=true;
368          }
369          else 
370            LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
371          nsset.insert(toLower(i->content));
372        }
373      }
374
375      if(done){ 
376        LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
377        return 0;
378      }
379      if(d_lwr.d_rcode==RCode::NXDomain) {
380        LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
381        return RCode::NXDomain;
382      }
383      if(nsset.empty() && !d_lwr.d_rcode) {
384        LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
385        return 0;
386      }
387      if(!newtarget.empty()) {
388        LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl;
389        set<GetBestNSAnswer>beenthere2;
390        return doResolve(newtarget, qtype, ret,0,beenthere2);
391      }
392      else if(realreferral) {
393        LOG<<prefix<<qname<<": status=did not resolve, got "<<nsset.size()<<" NS, looping to them"<<endl;
394        auth=newauth;
395        nameservers=nsset;
396        break; 
397      }
398      else {
399        LOG<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl;
400      }
401    }
402  }
403  return -1;
404}
405
406void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret)
407{
408  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)  // don't add stuff to an NXDOMAIN!
409    if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
410      return;
411
412  //  LOG<<qname<<": Adding best authority records from cache"<<endl;
413  // addAuthorityRecords(qname,ret,0);
414  // LOG<<qname<<": Done adding best authority records."<<endl;
415
416  LOG<<d_prefix<<qname<<": Starting additional processing"<<endl;
417  vector<DNSResourceRecord> addit;
418  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) 
419    if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) || 
420       ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
421      LOG<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs an IP address"<<endl;
422      set<GetBestNSAnswer>beenthere;
423      doResolve(k->content,QType(QType::A),addit,1,beenthere);
424      doResolve(k->content,QType(QType::AAAA),addit,1,beenthere);
425    }
426 
427  for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
428    if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) {
429      k->d_place=DNSResourceRecord::ADDITIONAL;
430      ret.push_back(*k);
431    }
432  }
433  LOG<<d_prefix<<qname<<": Done with additional processing"<<endl;
434}
435
436void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
437{
438  set<DNSResourceRecord> bestns;
439  set<GetBestNSAnswer>beenthere;
440  getBestNSFromCache(qname, bestns, depth,beenthere);
441  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
442    DNSResourceRecord ns=*k;
443    ns.d_place=DNSResourceRecord::AUTHORITY;
444    ns.ttl-=time(0);
445    ret.push_back(ns);
446  }
447}
Note: See TracBrowser for help on using the browser.