]>
Commit | Line | Data |
---|---|---|
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 | 42 | typedef map<string,set<DNSResourceRecord> > cache_t; |
86c152f2 BH |
43 | cache_t cache; |
44 | ||
ac539791 BH |
45 | bool 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 |
54 | int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,int depth=0); |
55 | int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth=0); | |
afbe2787 BH |
56 | |
57 | string 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 |
68 | bool 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 | ||
83 | void 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 | ||
111 | void 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 | ||
123 | string 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 |
138 | void 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 | 165 | int 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 | 173 | bool 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 | ||
196 | bool 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 |
235 | int 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 | ||
263 | bool 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 */ |
273 | int 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 | ||
381 | void 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 | } |