]>
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 | ||
afbe2787 BH |
33 | typedef map<string, set<string> > nscache_t; |
34 | nscache_t nscache; | |
35 | ||
36 | typedef map<string,vector<DNSResourceRecord> > cache_t; | |
86c152f2 BH |
37 | cache_t cache; |
38 | ||
39 | vector<string>rootservers; | |
86c152f2 | 40 | |
86c152f2 | 41 | |
afbe2787 BH |
42 | bool doResolve(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,int depth=0); |
43 | bool doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth=0); | |
44 | ||
45 | string getA(const string &qname, int depth=0) | |
46 | { | |
47 | vector<DNSResourceRecord> res; | |
48 | string ret; | |
49 | ||
50 | if(doResolve(qname,QType(QType::A), res,depth+1)) | |
51 | ret=res[0].content; | |
52 | ||
53 | return ret; | |
54 | } | |
55 | ||
56 | void getBestNSFromCache(const string &qname, vector<DNSResourceRecord>&ret, int depth=0) | |
86c152f2 | 57 | { |
afbe2787 BH |
58 | string prefix; |
59 | prefix.assign(3*depth, ' '); | |
86c152f2 | 60 | |
afbe2787 BH |
61 | vector<string>parts; |
62 | stringtok(parts,qname,"."); // www.us.powerdns.com -> 'www' 'us' 'powerdns' 'com' | |
63 | ||
64 | unsigned int spos=0; | |
65 | string subdomain; | |
66 | ||
67 | while(spos<=parts.size()) { | |
68 | if(spos<parts.size()) { // www.us.powerdns.com -> us.powerdns.com -> powerdns.com -> com -> | |
69 | subdomain=parts[spos++]; | |
70 | for(unsigned int i=spos;i<parts.size();++i) { | |
71 | subdomain+="."; | |
72 | subdomain+=parts[i]; | |
73 | } | |
74 | } | |
75 | else { | |
76 | subdomain=""; // ROOT! | |
77 | spos++; | |
78 | } | |
79 | cout<<prefix<<qname<<": Checking if we have NS for '"<<subdomain<<"'"<<endl; | |
80 | nscache_t::const_iterator j=nscache.find(toLower(subdomain)); | |
81 | if(j!=nscache.end() && j->first==toLower(subdomain)) { | |
82 | cout<<prefix<<qname<<": Adding authority records for '"<<subdomain<<"'"<<endl; | |
83 | for(set<string>::const_iterator k=j->second.begin();k!=j->second.end();++k) { | |
84 | DNSResourceRecord rr; | |
85 | rr.qname=subdomain; | |
86 | rr.content=*k; | |
87 | rr.ttl=1234; | |
88 | rr.qtype=QType(QType::NS); | |
89 | rr.d_place=DNSResourceRecord::AUTHORITY; | |
90 | ret.push_back(rr); | |
91 | } | |
92 | return; | |
93 | } | |
86c152f2 | 94 | } |
afbe2787 BH |
95 | } |
96 | ||
97 | ||
98 | ||
99 | void addCruft(const string &qname, vector<DNSResourceRecord>& ret) | |
100 | { | |
101 | getBestNSFromCache(qname,ret); | |
102 | ||
103 | cout<<qname<<": Additional processing"<<endl; | |
104 | vector<DNSResourceRecord> addit; | |
105 | for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) | |
106 | if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) || | |
107 | (k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::NS))) { | |
108 | cout<<qname<<": record '"<<k->content<<"|"<<k->qtype.getCode()<<"' needs an IP address"<<endl; | |
109 | doResolve(k->content,QType(QType::A),addit,1); | |
110 | } | |
111 | ||
86c152f2 | 112 | |
afbe2787 BH |
113 | for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) { |
114 | k->d_place=DNSResourceRecord::ADDITIONAL; | |
115 | ret.push_back(*k); | |
116 | } | |
117 | } | |
118 | ||
119 | bool beginResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret) | |
120 | { | |
121 | bool res=doResolve(qname, qtype, ret,0); | |
122 | if(res) | |
123 | addCruft(qname, ret); | |
124 | return res; | |
86c152f2 BH |
125 | } |
126 | ||
afbe2787 BH |
127 | |
128 | bool doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth) | |
129 | { | |
130 | string prefix; | |
131 | prefix.assign(3*depth, ' '); | |
132 | ||
133 | // see if we have a CNAME hit | |
134 | string tuple=toLower(qname)+"|CNAME"; | |
135 | cout<<prefix<<"Looking for CNAME cache hit of '"<<tuple<<"'"<<endl; | |
136 | ||
137 | cache_t::const_iterator i=cache.find(tuple); | |
138 | if(i!=cache.end() && i->first==tuple) { // found it | |
139 | cout<<prefix<<"Found cache CNAME hit for '"<<tuple<<"' to '"<<i->second.begin()->content<<"'"<<endl; | |
140 | for(vector<DNSResourceRecord>::const_iterator j=i->second.begin();j!=i->second.end();++j) | |
141 | ret.push_back(*j); | |
142 | return doResolve(i->second.begin()->content, qtype, ret, depth); | |
143 | } | |
144 | ||
145 | tuple=toLower(qname)+"|"+qtype.getName(); | |
146 | cout<<prefix<<"Looking for direct cache hit of '"<<tuple<<"'"<<endl; | |
147 | ||
148 | i=cache.find(tuple); | |
149 | if(i!=cache.end() && i->first==tuple) { // found it | |
150 | cout<<prefix<<"Found cache hit for '"<<tuple<<"': "; | |
151 | for(vector<DNSResourceRecord>::const_iterator j=i->second.begin();j!=i->second.end();++j) { | |
152 | cout<<j->content<<" "; | |
153 | ret.push_back(*j); | |
154 | } | |
155 | cout<<endl; | |
156 | return true; | |
157 | } | |
158 | ||
159 | ||
160 | cout<<prefix<<"No cache hit for '"<<tuple<<"', trying to find an appropriate NS record"<<endl; | |
161 | // bummer, get the best NS record then | |
162 | ||
163 | vector<string>parts; | |
164 | stringtok(parts,qname,"."); // www.us.powerdns.com -> 'www' 'us' 'powerdns' 'com' | |
165 | ||
166 | unsigned int spos=0; | |
167 | string subdomain; | |
168 | ||
169 | while(spos<=parts.size()) { | |
170 | if(spos<parts.size()) { // www.us.powerdns.com -> us.powerdns.com -> powerdns.com -> com -> | |
171 | subdomain=parts[spos++]; | |
172 | for(unsigned int i=spos;i<parts.size();++i) { | |
173 | subdomain+="."; | |
174 | subdomain+=parts[i]; | |
175 | } | |
176 | } | |
177 | else { | |
178 | subdomain=""; // ROOT! | |
179 | spos++; | |
180 | } | |
181 | cout<<prefix<<qname<<": Checking if we have NS for '"<<subdomain<<"'"<<endl; | |
182 | nscache_t::const_iterator j=nscache.find(toLower(subdomain)); | |
183 | if(j!=nscache.end() && j->first==toLower(subdomain)) { | |
184 | cout<<prefix<<"Found NS for '"<<subdomain<<"', heading there for further questions"<<endl; | |
185 | bool hasResults=doResolve(j->second,subdomain,qname,qtype,ret,depth); | |
186 | if(!hasResults) | |
187 | continue; // perhaps less specific nameservers can help us | |
188 | ||
189 | return true; | |
190 | } | |
191 | } | |
192 | return false; | |
193 | } | |
194 | ||
195 | bool endsOn(const string &domain, const string &suffix) | |
196 | { | |
197 | if(domain==suffix || suffix.empty()) | |
198 | return true; | |
199 | if(domain.size()<suffix.size()) | |
200 | return false; | |
201 | return (domain.substr(domain.size()-suffix.size()-1,suffix.size()+1)=="."+suffix); | |
202 | } | |
203 | ||
204 | bool doResolve(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth) | |
86c152f2 BH |
205 | { |
206 | string prefix; | |
207 | prefix.assign(3*depth, ' '); | |
208 | ||
209 | LWRes r; | |
210 | LWRes::res_t result; | |
211 | vector<DNSResourceRecord>usefulrrs; | |
afbe2787 BH |
212 | set<string> nsset; |
213 | cout<<prefix<<qname<<": start of recursion!"<<endl; | |
86c152f2 | 214 | |
afbe2787 BH |
215 | |
216 | for(;;) { // we may get more specific nameservers | |
86c152f2 BH |
217 | result.clear(); |
218 | ||
afbe2787 BH |
219 | vector<string>rnameservers; |
220 | for(set<string>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) | |
221 | rnameservers.push_back(*i); | |
86c152f2 | 222 | |
afbe2787 BH |
223 | random_shuffle(rnameservers.begin(),rnameservers.end()); |
224 | for(vector<string>::const_iterator i=rnameservers.begin();;++i){ | |
225 | if(i==rnameservers.end()) { | |
226 | cout<<prefix<<qname<<": failed to resolve via any of the "<<rnameservers.size()<<" offered nameservers"<<endl; | |
227 | return false; | |
228 | } | |
229 | cout<<prefix<<qname<<": trying to resolve nameserver "<<*i<<endl; | |
230 | string remoteIP=getA(*i, depth+1); | |
231 | if(remoteIP.empty()) { | |
232 | cout<<prefix<<qname<<": failed to resolve nameserver "<<*i<<", trying next if available"<<endl; | |
233 | continue; | |
234 | } | |
235 | cout<<prefix<<qname<<": resolved nameserver "<<*i<<" to "<<remoteIP<<endl; | |
86c152f2 | 236 | |
afbe2787 BH |
237 | if(r.asyncresolve(remoteIP,qname.c_str(),qtype.getCode())!=1) { // <- shouldn't this be internal? |
238 | cout<<prefix<<qname<<": error resolving"<<endl; | |
239 | } | |
240 | else { | |
241 | result=r.result(); | |
242 | ||
243 | cout<<prefix<<qname<<": got "<<result.size()<<" answers from "<<*i<<" ("<<remoteIP<<")"<<endl; | |
86c152f2 BH |
244 | break; |
245 | } | |
246 | } | |
86c152f2 | 247 | |
afbe2787 BH |
248 | |
249 | cache_t tcache; | |
250 | // reap all answers from this packet that are acceptable | |
251 | for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) { | |
252 | cout<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? "; | |
253 | if(endsOn(i->qname, auth)) { | |
254 | cout<<"YES!"<<endl; | |
255 | ||
256 | if(i->qtype.getCode()==QType::NS) | |
257 | nscache[toLower(i->qname)].insert(toLower(i->content)); | |
258 | DNSResourceRecord rr=*i; | |
259 | rr.d_place=DNSResourceRecord::ANSWER; | |
260 | tcache[toLower(i->qname)+"|"+i->qtype.getName()].push_back(rr); | |
86c152f2 | 261 | } |
afbe2787 BH |
262 | else |
263 | cout<<"NO!"<<endl; | |
86c152f2 | 264 | |
afbe2787 BH |
265 | } |
266 | ||
267 | for(cache_t::const_iterator i=tcache.begin();i!=tcache.end();++i) | |
268 | cache[i->first]=i->second; | |
269 | ||
86c152f2 | 270 | nsset.clear(); |
afbe2787 | 271 | |
86c152f2 | 272 | for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) { |
afbe2787 | 273 | |
86c152f2 | 274 | if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME) { |
afbe2787 BH |
275 | cout<<prefix<<qname<<": got a CNAME referral, starting over with "<<i->content<<endl<<endl; |
276 | ret.push_back(*i); | |
277 | return doResolve(i->content, qtype, ret,0); | |
86c152f2 | 278 | } |
afbe2787 BH |
279 | if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype==qtype) { |
280 | cout<<prefix<<qname<<": resolved to "<<i->content<<endl; | |
281 | ret.push_back(*i); | |
86c152f2 | 282 | } |
afbe2787 BH |
283 | if(i->d_place==DNSResourceRecord::AUTHORITY && i->qtype.getCode()==QType::NS) { // XXX FIXME check if suffix! |
284 | auth=i->qname; | |
285 | cout<<prefix<<qname<<": got NS record "<<i->content<<endl; | |
286 | nsset.insert(toLower(i->content)); | |
86c152f2 BH |
287 | } |
288 | } | |
afbe2787 BH |
289 | if(!ret.empty()){ |
290 | cout<<prefix<<qname<<": got results, returning"<<endl; | |
291 | return true; | |
292 | } | |
86c152f2 | 293 | if(nsset.empty()) { |
afbe2787 BH |
294 | cout<<prefix<<qname<<": did not resolve "<<qname<<", did not get referral"<<endl; |
295 | return false; | |
86c152f2 | 296 | } |
afbe2787 BH |
297 | |
298 | cout<<prefix<<qname<<": did not resolve "<<qname<<", did get "<<nsset.size()<<" nameservers, looping to them"<<endl; | |
86c152f2 BH |
299 | nameservers=nsset; |
300 | } | |
afbe2787 | 301 | return false; |
86c152f2 BH |
302 | } |
303 | ||
304 | void init(void) | |
305 | { | |
306 | // prime root cache | |
307 | static char*ips[]={"198.41.0.4", "128.9.0.107", "192.33.4.12", "128.8.10.90", "192.203.230.10", | |
308 | "192.5.5.241", "192.112.36.4", "128.63.2.53", "192.36.148.17", | |
309 | "198.41.0.10", "193.0.14.129", "198.32.64.12", "202.12.27.33"}; | |
afbe2787 BH |
310 | DNSResourceRecord rr; |
311 | rr.qtype=QType::A; | |
312 | rr.ttl=86400; | |
86c152f2 BH |
313 | for(char c='a';c<='m';++c) { |
314 | static char templ[40]; | |
315 | strncpy(templ,"a.root-servers.net", sizeof(templ) - 1); | |
316 | *templ=c; | |
afbe2787 BH |
317 | nscache[""].insert(string(templ)); |
318 | rr.qname=templ; | |
319 | rr.content=ips[c-'a']; | |
320 | cache[string(templ)+"|A"].push_back(rr); | |
86c152f2 BH |
321 | } |
322 | } |