]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/syncres.cc
compile
[thirdparty/pdns.git] / pdns / syncres.cc
CommitLineData
86c152f2
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
20177d1d 3 Copyright (C) 2003 PowerDNS.COM BV
86c152f2
BH
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 <iostream>
20#include <map>
21#include <algorithm>
afbe2787 22#include <set>
86c152f2
BH
23#include <cerrno>
24#include <cstdio>
25#include <cstdlib>
86c152f2 26#include <utility>
20177d1d 27#include "misc.hh"
86c152f2
BH
28#include "arguments.hh"
29#include "lwres.hh"
30
ac539791 31typedef map<string,set<DNSResourceRecord> > cache_t;
86c152f2 32cache_t cache;
20177d1d 33map<string,string> negcache;
86c152f2 34
7bf26383
BH
35struct GetBestNSAnswer
36{
37 string qname;
38 set<DNSResourceRecord> bestns;
39 bool operator<(const GetBestNSAnswer &b) const
40 {
41 if(qname<b.qname)
42 return true;
43 if(qname==b.qname)
44 return bestns<b.bestns;
45 return false;
46 }
47};
48
728485ca 49/** dramatis personae */
7bf26383
BH
50int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,
51 int depth, set<GetBestNSAnswer>&beenthere);
52int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere);
728485ca
BH
53bool doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
54bool doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
7bf26383 55void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere);
728485ca 56void addCruft(const string &qname, vector<DNSResourceRecord>& ret);
7bf26383 57string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth, set<GetBestNSAnswer>&beenthere);
728485ca
BH
58void addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth);
59
60/** everything begins here - this is the entry point just after receiving a packet */
61int beginResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret)
62{
7bf26383
BH
63 set<GetBestNSAnswer> beenthere;
64 int res=doResolve(qname, qtype, ret,0,beenthere);
728485ca
BH
65 if(!res)
66 addCruft(qname, ret);
67 cout<<endl;
68 return res;
69}
afbe2787 70
7bf26383 71int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
afbe2787 72{
728485ca
BH
73 string prefix;
74 prefix.assign(3*depth, ' ');
75
76 int res;
77 if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
78 return res;
afbe2787 79
728485ca
BH
80 if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
81 return res;
afbe2787 82
728485ca
BH
83 cout<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
84
85 string subdomain(qname);
86
87 set<string> nsset;
7bf26383
BH
88 subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); // pass beenthere to both occasions/
89 if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere)))
728485ca
BH
90 return 0;
91
92 cout<<prefix<<qname<<": failed"<<endl;
20177d1d 93 return res<0 ? RCode::ServFail : res;
afbe2787
BH
94}
95
7bf26383 96string getA(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
75b49099 97{
728485ca
BH
98 vector<DNSResourceRecord> res;
99 string ret;
75b49099 100
7bf26383 101 if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty())
728485ca 102 ret=res[res.size()-1].content; // last entry, in case of CNAME in between
75b49099 103
728485ca 104 return ret;
75b49099
BH
105}
106
7bf26383
BH
107int getCache(const string &qname, const QType& qt, set<DNSResourceRecord>* res=0)
108{
109 cache_t::const_iterator j=cache.find(toLower(qname)+"|"+qt.getName());
110 if(j!=cache.end() && j->first==toLower(qname)+"|"+qt.getName() && j->second.begin()->ttl>(unsigned int)time(0)) {
111 if(res)
112 *res=j->second;
113 return (unsigned int)j->second.begin()->ttl-time(0);
114 }
115 return -1;
116}
117
118void replaceCache(const string &tuple, const set<DNSResourceRecord>& content)
119{
120 cache[tuple]=content;
121}
122
123
124void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere)
86c152f2 125{
20177d1d 126 string prefix, subdomain(qname);
afbe2787 127 prefix.assign(3*depth, ' ');
75b49099
BH
128 bestns.clear();
129
75b49099
BH
130 do {
131 cout<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
7bf26383
BH
132 set<DNSResourceRecord>ns;
133 if(getCache(subdomain,QType(QType::NS),&ns)>0) {
134 for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
135 if(k->ttl>(unsigned int)time(0)) {
136 set<DNSResourceRecord>aset;
137 if(!endsOn(k->content,subdomain) || getCache(k->content,QType(QType::A),&aset) > 5) {
20177d1d 138 bestns.insert(*k);
0d8612f2
BH
139 cout<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<k->content<<"'"<<endl;
140 cout<<prefix<<qname<<": endson: "<<endsOn(k->content,subdomain);
7bf26383
BH
141 if(!aset.empty())
142 cout<<", in cache, ttl="<<((time_t)aset.begin()->ttl-time(0))<<endl;
0d8612f2
BH
143 else
144 cout<<", not in cache"<<endl;
145 }
20177d1d 146 else
7bf26383 147 cout<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
ac539791 148 }
afbe2787 149 }
75b49099 150 if(!bestns.empty()) {
7bf26383
BH
151 GetBestNSAnswer answer;
152 answer.qname=toLower(qname); answer.bestns=bestns;
153 if(beenthere.count(answer)) {
154 cout<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
155 for(set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
156 cout<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<j->bestns.size()<<")"<<endl;
157 bestns.clear();
158 }
159 else {
160 beenthere.insert(answer);
161 cout<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
162 return;
163 }
75b49099 164 }
afbe2787 165 }
75b49099
BH
166 }while(chopOff(subdomain));
167}
168
169void addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
170{
171 set<DNSResourceRecord> bestns;
7bf26383
BH
172 set<GetBestNSAnswer>beenthere;
173 getBestNSFromCache(qname, bestns, depth,beenthere);
75b49099
BH
174 for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
175 DNSResourceRecord ns=*k;
176 ns.d_place=DNSResourceRecord::AUTHORITY;
177 ns.ttl-=time(0);
178 ret.push_back(ns);
179 }
180}
7bf26383
BH
181/** doesn't actually do the work, leaves that to getBestNSFromCache */
182string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
75b49099
BH
183{
184 string subdomain(qname);
185
186 set<DNSResourceRecord> bestns;
7bf26383 187 getBestNSFromCache(subdomain, bestns, depth, beenthere);
75b49099
BH
188
189 for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
190 nsset.insert(k->content);
191 subdomain=k->qname;
86c152f2 192 }
75b49099 193 return subdomain;
afbe2787
BH
194}
195
728485ca 196bool doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
afbe2787 197{
75b49099 198 string prefix, tuple=toLower(qname)+"|CNAME";
afbe2787 199 prefix.assign(3*depth, ' ');
afbe2787 200
75b49099 201 cout<<prefix<<qname<<": Looking for CNAME cache hit of '"<<tuple<<"'"<<endl;
7bf26383
BH
202 set<DNSResourceRecord> cset;
203 if(getCache(qname,QType(QType::CNAME),&cset) > 0) {
204 for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
75b49099 205 if(j->ttl>(unsigned int)time(0)) {
7bf26383 206 cout<<prefix<<qname<<": Found cache CNAME hit for '"<<tuple<<"' to '"<<j->content<<"'"<<endl;
ac539791
BH
207 DNSResourceRecord rr=*j;
208 rr.ttl-=time(0);
209 ret.push_back(rr);
7bf26383
BH
210 if(!(qtype==QType(QType::CNAME))) {// perhaps they really wanted a CNAME!
211 set<GetBestNSAnswer>beenthere;
212 res=doResolve(j->content, qtype, ret, depth, beenthere);
213 }
75b49099 214 return true;
ac539791
BH
215 }
216 }
afbe2787 217 }
75b49099
BH
218 cout<<prefix<<qname<<": No CNAME cache hit of '"<<tuple<<"' found"<<endl;
219 return false;
220}
221
222bool doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
223{
224 string prefix, tuple;
225 prefix.assign(3*depth, ' ');
afbe2787
BH
226
227 tuple=toLower(qname)+"|"+qtype.getName();
75b49099 228 cout<<prefix<<qname<<": Looking for direct cache hit of '"<<tuple<<"'"<<endl;
afbe2787 229
20177d1d
BH
230 res=0;
231 map<string,string>::const_iterator ni=negcache.find(tuple);
232 if(ni!=negcache.end()) {
233 cout<<prefix<<qname<<": is negatively cached, will return immediately if we still have SOA to prove it"<<endl;
234 res=RCode::NXDomain;
235 tuple=ni->second+"|SOA";
236 }
237
7bf26383 238 set<DNSResourceRecord> cset;
75b49099 239 bool found=false, expired=false;
7bf26383 240 if(getCache(qname,qtype,&cset)>0) {
6e059b95 241 cout<<prefix<<qname<<": Found cache hit for "<<qtype.getName()<<": ";
7bf26383 242 for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
ac539791 243 cout<<j->content;
75b49099 244 if(j->ttl>(unsigned int)time(0)) {
ac539791
BH
245 DNSResourceRecord rr=*j;
246 rr.ttl-=time(0);
20177d1d
BH
247 if(res==RCode::NXDomain)
248 rr.d_place=DNSResourceRecord::AUTHORITY;
ac539791
BH
249 ret.push_back(rr);
250 cout<<"[fresh] ";
75b49099 251 found=true;
ac539791 252 }
75b49099 253 else {
ac539791 254 cout<<"[expired] ";
75b49099
BH
255 expired=true;
256 }
afbe2787 257 }
ac539791 258
afbe2787 259 cout<<endl;
20177d1d 260 if(found && !expired)
75b49099 261 return true;
75b49099 262 else
20177d1d 263 cout<<prefix<<qname<<": cache had only stale entries"<<endl;
afbe2787 264 }
75b49099
BH
265 return false;
266}
afbe2787 267
728485ca 268inline bool moreSpecificThan(const string& a, const string &b)
75b49099 269{
728485ca 270 int counta=!a.empty(), countb=!b.empty();
afbe2787 271
728485ca
BH
272 for(string::size_type n=0;n<a.size();++n)
273 if(a[n]=='.')
274 counta++;
275 for(string::size_type n=0;n<b.size();++n)
276 if(b[n]=='.')
277 countb++;
278 return counta>countb;
afbe2787
BH
279}
280
728485ca 281inline vector<string>shuffle(set<string> &nameservers)
afbe2787 282{
728485ca
BH
283 vector<string> rnameservers;
284 for(set<string>::const_iterator i=nameservers.begin();i!=nameservers.end();++i)
285 rnameservers.push_back(*i);
286
287 random_shuffle(rnameservers.begin(),rnameservers.end());
288 return rnameservers;
afbe2787
BH
289}
290
ac539791 291/** returns -1 in case of no results, rcode otherwise */
7bf26383
BH
292int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,
293 int depth, set<GetBestNSAnswer>&beenthere)
86c152f2
BH
294{
295 string prefix;
296 prefix.assign(3*depth, ' ');
297
298 LWRes r;
299 LWRes::res_t result;
86c152f2 300
7bf26383 301 cout<<prefix<<qname<<": Cache consultations done, have "<<nameservers.size()<<" NS to contact"<<endl;
afbe2787
BH
302
303 for(;;) { // we may get more specific nameservers
75b49099 304 bool aabit=false;
86c152f2
BH
305 result.clear();
306
728485ca 307 vector<string>rnameservers=shuffle(nameservers);
86c152f2 308
20177d1d
BH
309 // what if we don't have an A for an NS anymore, but do have an NS for that NS?
310
728485ca
BH
311 for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) {
312 if(tns==rnameservers.end()) {
75b49099 313 cout<<prefix<<qname<<": Failed to resolve via any of the "<<rnameservers.size()<<" offered NS"<<endl;
ac539791 314 return -1;
afbe2787 315 }
20177d1d
BH
316 if(qname==*tns) {
317 cout<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
318 continue;
319 }
7bf26383
BH
320 cout<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
321 string remoteIP=getA(*tns, depth+1,beenthere);
afbe2787 322 if(remoteIP.empty()) {
728485ca 323 cout<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
afbe2787
BH
324 continue;
325 }
728485ca 326 cout<<prefix<<qname<<": Resolved NS "<<*tns<<" to "<<remoteIP<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
86c152f2 327
ac539791 328 if(r.asyncresolve(remoteIP,qname.c_str(),qtype.getCode())!=1) { // <- we go out on the wire!
728485ca
BH
329 cout<<prefix<<qname<<": error resolving (perhaps timeout?)"<<endl;
330 continue;
afbe2787 331 }
20177d1d
BH
332
333 if(r.d_rcode==RCode::ServFail) {
334 cout<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling NS"<<endl;
335 continue;
336 }
728485ca 337 result=r.result(aabit);
20177d1d
BH
338 cout<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<remoteIP<<"), rcode="<<r.d_rcode<<endl;
339
728485ca
BH
340 cache_t tcache;
341 // reap all answers from this packet that are acceptable
342 for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
343 cout<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
344 cout.flush();
345 if(endsOn(i->qname, auth)) {
346 cout<<"YES!"<<endl;
347
348 DNSResourceRecord rr=*i;
349 rr.d_place=DNSResourceRecord::ANSWER;
350 rr.ttl+=time(0);
7bf26383 351 // rr.ttl=time(0)+10+10*rr.qtype.getCode();
728485ca
BH
352 tcache[toLower(i->qname)+"|"+i->qtype.getName()].insert(rr);
353 }
354 else
355 cout<<"NO!"<<endl;
86c152f2 356 }
728485ca
BH
357
358 // supplant
359 for(cache_t::const_iterator i=tcache.begin();i!=tcache.end();++i)
7bf26383 360 replaceCache(i->first,i->second);
728485ca
BH
361
362 set<string> nsset;
363 cout<<prefix<<qname<<": determining status after receiving this packet"<<endl;
364
20177d1d 365 bool done=false, realreferral=false, negindic=false;
728485ca
BH
366 string newauth;
367
368 for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
369 if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA) {
370 cout<<prefix<<qname<<": got negative caching indication"<<endl;
371 ret.push_back(*i);
20177d1d
BH
372 negcache[toLower(qname)+"|"+qtype.getName()]=i->qname;
373 negindic=true;
728485ca
BH
374 }
375 else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
376 cout<<prefix<<qname<<": got a CNAME referral, starting over with "<<i->content<<endl<<endl;
377 ret.push_back(*i);
7bf26383
BH
378 set<GetBestNSAnswer>beenthere2;
379 return doResolve(i->content, qtype, ret,0,beenthere2);
728485ca
BH
380 }
381 // for ANY answers we *must* have an authoritive answer
382 else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && (i->qtype==qtype || ( qtype==QType(QType::ANY) && aabit))) {
383 cout<<prefix<<qname<<": answer is in: resolved to '"<<i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
384 done=true;
385 ret.push_back(*i);
386 }
387 else if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) {
388 if(moreSpecificThan(i->qname,auth)) {
389 newauth=i->qname;
390 cout<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
391 realreferral=true;
392 }
7bf26383
BH
393 else
394 cout<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
728485ca
BH
395 nsset.insert(toLower(i->content));
396 }
86c152f2 397 }
86c152f2 398
728485ca
BH
399 if(done){
400 cout<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
401 return 0;
ac539791 402 }
728485ca 403 if(r.d_rcode==RCode::NXDomain) {
20177d1d 404 cout<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
728485ca 405 return RCode::NXDomain;
86c152f2 406 }
728485ca 407 if(nsset.empty() && !r.d_rcode) {
20177d1d 408 cout<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
728485ca 409 return 0;
86c152f2 410 }
728485ca
BH
411 else if(realreferral) {
412 cout<<prefix<<qname<<": status=did not resolve, got "<<nsset.size()<<" NS, looping to them"<<endl;
413 auth=newauth;
414 nameservers=nsset;
415 break;
416 }
417 else {
7bf26383 418 cout<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl;
86c152f2
BH
419 }
420 }
86c152f2 421 }
ac539791 422 return -1;
86c152f2
BH
423}
424
728485ca
BH
425void addCruft(const string &qname, vector<DNSResourceRecord>& ret)
426{
427 for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) // don't add stuff to an NXDOMAIN!
428 if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
429 return;
430
431 cout<<qname<<": Adding best authority records from cache"<<endl;
432 addAuthorityRecords(qname,ret,0);
433 cout<<qname<<": Done adding best authority records."<<endl;
434
435 cout<<qname<<": Starting additional processing"<<endl;
436 vector<DNSResourceRecord> addit;
437 for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)
438 if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) ||
439 (k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::NS))) {
440 cout<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs an IP address"<<endl;
7bf26383
BH
441 set<GetBestNSAnswer>beenthere;
442 doResolve(k->content,QType(QType::A),addit,1,beenthere);
728485ca
BH
443 }
444
445 for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
446 k->d_place=DNSResourceRecord::ADDITIONAL;
447 ret.push_back(*k);
448 }
449 cout<<qname<<": Done with additional processing"<<endl;
450}
451
86c152f2
BH
452void init(void)
453{
454 // prime root cache
728485ca
BH
455 static char*ips[]={"198.41.0.4", "128.9.0.107", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", "192.112.36.4", "128.63.2.53",
456 "192.36.148.17","198.41.0.10", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
ac539791
BH
457 DNSResourceRecord arr, nsrr;
458 arr.qtype=QType::A;
459 arr.ttl=time(0)+86400;
ac539791
BH
460 nsrr.qtype=QType::NS;
461 nsrr.ttl=time(0)+86400;
ac539791 462
86c152f2
BH
463 for(char c='a';c<='m';++c) {
464 static char templ[40];
465 strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
466 *templ=c;
728485ca 467 arr.qname=nsrr.content=templ;
ac539791
BH
468 arr.content=ips[c-'a'];
469 cache[string(templ)+"|A"].insert(arr);
ac539791 470 cache["|NS"].insert(nsrr);
86c152f2
BH
471 }
472}