]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/syncres.cc
small stuff
[thirdparty/pdns.git] / pdns / syncres.cc
CommitLineData
86c152f2
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 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 <iostream>
20#include <map>
21#include <algorithm>
afbe2787 22#include <set>
86c152f2
BH
23
24#include <cerrno>
25#include <cstdio>
26#include <cstdlib>
27
28#include <utility>
29#include "statbag.hh"
30#include "arguments.hh"
31#include "lwres.hh"
32
ac539791
BH
33/** Issues:
34 - we need a cache pruning thing
35 - case issues
36 - transparency
37 everything should be record=unknown except for A, MX, NS, AAAA
38 - negative caching
39 - compressions sucks [ortho]
40*/
afbe2787 41
ac539791 42typedef map<string,set<DNSResourceRecord> > cache_t;
86c152f2
BH
43cache_t cache;
44
ac539791
BH
45bool operator<(const DNSResourceRecord &a, const DNSResourceRecord &b)
46{
47 if(a.qname<b.qname)
48 return true;
49 if(a.qname==b.qname)
50 return(a.content<b.content);
51 return false;
52}
86c152f2 53
ac539791
BH
54int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,int depth=0);
55int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth=0);
afbe2787
BH
56
57string getA(const string &qname, int depth=0)
58{
59 vector<DNSResourceRecord> res;
60 string ret;
61
ac539791 62 if(!doResolve(qname,QType(QType::A), res,depth+1))
afbe2787
BH
63 ret=res[0].content;
64
65 return ret;
66}
67
75b49099
BH
68bool chopOff(string &domain)
69{
70 if(domain.empty())
71 return false;
72
73 string::size_type fdot=domain.find('.');
74
75 if(fdot==string::npos)
76 domain="";
77 else
78 domain=domain.substr(fdot+1);
79 return true;
80}
81
82
83void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth)
86c152f2 84{
afbe2787
BH
85 string prefix;
86 prefix.assign(3*depth, ' ');
86c152f2 87
75b49099
BH
88 bestns.clear();
89
90 string subdomain(qname);
91
92 do {
93 cout<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
94
ac539791
BH
95 cache_t::const_iterator j=cache.find(toLower(subdomain)+"|NS");
96 if(j!=cache.end() && j->first==toLower(subdomain)+"|NS") {
75b49099 97
ac539791 98 for(set<DNSResourceRecord>::const_iterator k=j->second.begin();k!=j->second.end();++k) {
75b49099
BH
99 if(k->ttl>(unsigned int)time(0)) {
100 bestns.insert(*k);
ac539791 101 }
afbe2787 102 }
75b49099
BH
103 if(!bestns.empty()) {
104 cout<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
105 return;
106 }
afbe2787 107 }
75b49099
BH
108 }while(chopOff(subdomain));
109}
110
111void addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
112{
113 set<DNSResourceRecord> bestns;
114 getBestNSFromCache(qname, bestns, depth);
115 for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
116 DNSResourceRecord ns=*k;
117 ns.d_place=DNSResourceRecord::AUTHORITY;
118 ns.ttl-=time(0);
119 ret.push_back(ns);
120 }
121}
122
123string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth)
124{
125 string subdomain(qname);
126
127 set<DNSResourceRecord> bestns;
128 getBestNSFromCache(subdomain, bestns, depth);
129
130 for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
131 nsset.insert(k->content);
132 subdomain=k->qname;
86c152f2 133 }
75b49099 134 return subdomain;
afbe2787
BH
135}
136
137
afbe2787
BH
138void addCruft(const string &qname, vector<DNSResourceRecord>& ret)
139{
ac539791
BH
140 for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) // don't add stuff to an NXDOMAIN!
141 if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
142 return;
143
75b49099
BH
144 cout<<qname<<": Adding best authority records from cache"<<endl;
145 addAuthorityRecords(qname,ret,0);
146 cout<<qname<<": Done adding best authority records."<<endl;
147
148 cout<<qname<<": Starting additional processing"<<endl;
afbe2787
BH
149 vector<DNSResourceRecord> addit;
150 for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)
151 if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) ||
152 (k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::NS))) {
ac539791 153 cout<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs an IP address"<<endl;
afbe2787
BH
154 doResolve(k->content,QType(QType::A),addit,1);
155 }
156
86c152f2 157
afbe2787
BH
158 for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
159 k->d_place=DNSResourceRecord::ADDITIONAL;
160 ret.push_back(*k);
161 }
75b49099 162 cout<<qname<<": Done with additional processing"<<endl;
afbe2787
BH
163}
164
ac539791 165int beginResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret)
afbe2787 166{
ac539791
BH
167 int res=doResolve(qname, qtype, ret,0);
168 if(!res)
afbe2787
BH
169 addCruft(qname, ret);
170 return res;
86c152f2
BH
171}
172
75b49099 173bool doCNAMECheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
afbe2787 174{
75b49099 175 string prefix, tuple=toLower(qname)+"|CNAME";
afbe2787 176 prefix.assign(3*depth, ' ');
afbe2787 177
75b49099 178 cout<<prefix<<qname<<": Looking for CNAME cache hit of '"<<tuple<<"'"<<endl;
afbe2787
BH
179 cache_t::const_iterator i=cache.find(tuple);
180 if(i!=cache.end() && i->first==tuple) { // found it
ac539791 181 for(set<DNSResourceRecord>::const_iterator j=i->second.begin();j!=i->second.end();++j) {
75b49099
BH
182 if(j->ttl>(unsigned int)time(0)) {
183 cout<<prefix<<qname<<": Found cache CNAME hit for '"<<tuple<<"' to '"<<i->second.begin()->content<<"'"<<endl;
ac539791
BH
184 DNSResourceRecord rr=*j;
185 rr.ttl-=time(0);
186 ret.push_back(rr);
75b49099
BH
187 res=doResolve(i->second.begin()->content, qtype, ret, depth);
188 return true;
ac539791
BH
189 }
190 }
afbe2787 191 }
75b49099
BH
192 cout<<prefix<<qname<<": No CNAME cache hit of '"<<tuple<<"' found"<<endl;
193 return false;
194}
195
196bool doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
197{
198 string prefix, tuple;
199 prefix.assign(3*depth, ' ');
afbe2787
BH
200
201 tuple=toLower(qname)+"|"+qtype.getName();
75b49099 202 cout<<prefix<<qname<<": Looking for direct cache hit of '"<<tuple<<"'"<<endl;
afbe2787 203
75b49099
BH
204 cache_t::const_iterator i=cache.find(tuple);
205 bool found=false, expired=false;
afbe2787 206 if(i!=cache.end() && i->first==tuple) { // found it
6e059b95 207 cout<<prefix<<qname<<": Found cache hit for "<<qtype.getName()<<": ";
ac539791
BH
208 for(set<DNSResourceRecord>::const_iterator j=i->second.begin();j!=i->second.end();++j) {
209 cout<<j->content;
75b49099 210 if(j->ttl>(unsigned int)time(0)) {
ac539791
BH
211 DNSResourceRecord rr=*j;
212 rr.ttl-=time(0);
213 ret.push_back(rr);
214 cout<<"[fresh] ";
75b49099 215 found=true;
ac539791 216 }
75b49099 217 else {
ac539791 218 cout<<"[expired] ";
75b49099
BH
219 expired=true;
220 }
afbe2787 221 }
ac539791 222
afbe2787 223 cout<<endl;
75b49099
BH
224 if(found && !expired) {
225 res=0;
226 return true;
227 }
228 else
229 cout<<prefix<<qname<<": cache had no or only stale entries"<<endl;
afbe2787 230 }
75b49099
BH
231 return false;
232}
afbe2787 233
afbe2787 234
75b49099
BH
235int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth)
236{
237 string prefix;
238 prefix.assign(3*depth, ' ');
afbe2787 239
75b49099
BH
240 int res;
241 if(doCNAMECheck(qname,qtype,ret,depth,res))
242 return res;
243
244 if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
245 return res;
246
247 cout<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
248
249 string subdomain(qname);
250
251 do {
252 set<string> nsset;
253 subdomain=getBestNSNamesFromCache(subdomain,nsset,depth);
254 if(!doResolveAt(nsset,subdomain,qname,qtype,ret,depth))
255 return 0;
256 // failed, restart at a lower level of NS
257 }while(chopOff(subdomain));
258
ac539791 259 cout<<prefix<<qname<<": FOUND NO NS FOR '"<<subdomain<<"'"<<endl;
75b49099 260 return RCode::ServFail;
afbe2787
BH
261}
262
263bool endsOn(const string &domain, const string &suffix)
264{
265 if(domain==suffix || suffix.empty())
266 return true;
ac539791 267 if(domain.size()<=suffix.size())
afbe2787
BH
268 return false;
269 return (domain.substr(domain.size()-suffix.size()-1,suffix.size()+1)=="."+suffix);
270}
271
ac539791
BH
272/** returns -1 in case of no results, rcode otherwise */
273int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth)
86c152f2
BH
274{
275 string prefix;
276 prefix.assign(3*depth, ' ');
277
278 LWRes r;
279 LWRes::res_t result;
280 vector<DNSResourceRecord>usefulrrs;
afbe2787 281 set<string> nsset;
75b49099 282 cout<<prefix<<qname<<": Cache consultations done, going on the wire!"<<endl;
86c152f2 283
afbe2787
BH
284
285 for(;;) { // we may get more specific nameservers
75b49099 286 bool aabit=false;
86c152f2
BH
287 result.clear();
288
afbe2787
BH
289 vector<string>rnameservers;
290 for(set<string>::const_iterator i=nameservers.begin();i!=nameservers.end();++i)
291 rnameservers.push_back(*i);
86c152f2 292
afbe2787
BH
293 random_shuffle(rnameservers.begin(),rnameservers.end());
294 for(vector<string>::const_iterator i=rnameservers.begin();;++i){
295 if(i==rnameservers.end()) {
75b49099 296 cout<<prefix<<qname<<": Failed to resolve via any of the "<<rnameservers.size()<<" offered NS"<<endl;
ac539791 297 return -1;
afbe2787 298 }
75b49099 299 cout<<prefix<<qname<<": Trying to resolve NS "<<*i<<endl;
afbe2787
BH
300 string remoteIP=getA(*i, depth+1);
301 if(remoteIP.empty()) {
75b49099 302 cout<<prefix<<qname<<": Failed to resolve NS "<<*i<<", trying next if available"<<endl;
afbe2787
BH
303 continue;
304 }
75b49099 305 cout<<prefix<<qname<<": Resolved NS "<<*i<<" to "<<remoteIP<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
86c152f2 306
ac539791 307 if(r.asyncresolve(remoteIP,qname.c_str(),qtype.getCode())!=1) { // <- we go out on the wire!
afbe2787
BH
308 cout<<prefix<<qname<<": error resolving"<<endl;
309 }
310 else {
75b49099 311 result=r.result(aabit);
afbe2787 312
75b49099 313 cout<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*i<<" ("<<remoteIP<<")"<<endl;
86c152f2
BH
314 break;
315 }
316 }
86c152f2 317
afbe2787
BH
318
319 cache_t tcache;
320 // reap all answers from this packet that are acceptable
321 for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
322 cout<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
ac539791 323 cout.flush();
afbe2787
BH
324 if(endsOn(i->qname, auth)) {
325 cout<<"YES!"<<endl;
326
afbe2787
BH
327 DNSResourceRecord rr=*i;
328 rr.d_place=DNSResourceRecord::ANSWER;
ac539791
BH
329 rr.ttl+=time(0);
330 tcache[toLower(i->qname)+"|"+i->qtype.getName()].insert(rr);
86c152f2 331 }
afbe2787
BH
332 else
333 cout<<"NO!"<<endl;
86c152f2 334
afbe2787 335 }
ac539791
BH
336
337 // supplant
afbe2787
BH
338 for(cache_t::const_iterator i=tcache.begin();i!=tcache.end();++i)
339 cache[i->first]=i->second;
340
86c152f2 341 nsset.clear();
ac539791
BH
342 cout<<prefix<<qname<<": determining status after receiving this packet"<<endl;
343 bool done=false;
86c152f2 344 for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
ac539791
BH
345 if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA) {
346 cout<<prefix<<qname<<": got a NXDOMAIN"<<endl;
347 ret.push_back(*i);
348 return RCode::NXDomain; // NXDOMAIN
349 }
86c152f2 350 if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME) {
afbe2787
BH
351 cout<<prefix<<qname<<": got a CNAME referral, starting over with "<<i->content<<endl<<endl;
352 ret.push_back(*i);
353 return doResolve(i->content, qtype, ret,0);
86c152f2 354 }
75b49099
BH
355 if(aabit && i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && (i->qtype==qtype || qtype==QType(QType::ANY)) ) {
356 cout<<prefix<<qname<<": answer is in: resolved to '"<<i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
ac539791 357 done=true;
afbe2787 358 ret.push_back(*i);
86c152f2 359 }
afbe2787
BH
360 if(i->d_place==DNSResourceRecord::AUTHORITY && i->qtype.getCode()==QType::NS) { // XXX FIXME check if suffix!
361 auth=i->qname;
362 cout<<prefix<<qname<<": got NS record "<<i->content<<endl;
363 nsset.insert(toLower(i->content));
86c152f2
BH
364 }
365 }
ac539791 366 if(done){
75b49099 367 cout<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
ac539791 368 return 0;
afbe2787 369 }
86c152f2 370 if(nsset.empty()) {
75b49099 371 cout<<prefix<<qname<<": status=not resolved, did not get referral"<<endl;
ac539791 372 return -1;
86c152f2 373 }
afbe2787 374
75b49099 375 cout<<prefix<<qname<<": status=did not resolve, got "<<nsset.size()<<" NS, looping to them"<<endl;
86c152f2
BH
376 nameservers=nsset;
377 }
ac539791 378 return -1;
86c152f2
BH
379}
380
381void init(void)
382{
383 // prime root cache
384 static char*ips[]={"198.41.0.4", "128.9.0.107", "192.33.4.12", "128.8.10.90", "192.203.230.10",
385 "192.5.5.241", "192.112.36.4", "128.63.2.53", "192.36.148.17",
386 "198.41.0.10", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
ac539791
BH
387 DNSResourceRecord arr, nsrr;
388 arr.qtype=QType::A;
389 arr.ttl=time(0)+86400;
390
391 nsrr.qtype=QType::NS;
392 nsrr.ttl=time(0)+86400;
393 nsrr.qname="";
394
86c152f2
BH
395 for(char c='a';c<='m';++c) {
396 static char templ[40];
397 strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
398 *templ=c;
ac539791
BH
399 arr.qname=templ;
400 arr.content=ips[c-'a'];
401 cache[string(templ)+"|A"].insert(arr);
402
403 nsrr.content=templ;
404 cache["|NS"].insert(nsrr);
86c152f2
BH
405 }
406}