]>
Commit | Line | Data |
---|---|---|
86c152f2 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
1d5b3ce6 | 3 | Copyright (C) 2003 - 2006 PowerDNS.COM BV |
86c152f2 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
36c5ee42 BH |
6 | it under the terms of the GNU General Public License version 2 as published |
7 | by the Free Software Foundation | |
86c152f2 BH |
8 | |
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; if not, write to the Free Software | |
06bd9ccf | 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
86c152f2 | 17 | */ |
caa6eefa | 18 | |
6d78ece6 | 19 | |
caa6eefa | 20 | #include "utility.hh" |
288f4aa9 | 21 | #include "syncres.hh" |
86c152f2 BH |
22 | #include <iostream> |
23 | #include <map> | |
24 | #include <algorithm> | |
afbe2787 | 25 | #include <set> |
86c152f2 BH |
26 | #include <cerrno> |
27 | #include <cstdio> | |
28 | #include <cstdlib> | |
86c152f2 | 29 | #include <utility> |
3de83124 | 30 | #include <deque> |
c836dc19 | 31 | #include "logger.hh" |
20177d1d | 32 | #include "misc.hh" |
86c152f2 BH |
33 | #include "arguments.hh" |
34 | #include "lwres.hh" | |
eefd15f9 | 35 | #include "recursor_cache.hh" |
ea634573 | 36 | #include "dnsparser.hh" |
eefd15f9 BH |
37 | |
38 | extern MemRecursorCache RC; | |
86c152f2 | 39 | |
9fdf67d5 BH |
40 | SyncRes::negcache_t SyncRes::s_negcache; |
41 | SyncRes::nsspeeds_t SyncRes::s_nsSpeeds; | |
a9af3782 | 42 | unsigned int SyncRes::s_maxnegttl; |
7b35aa49 | 43 | unsigned int SyncRes::s_queries; |
7becf07f | 44 | unsigned int SyncRes::s_outgoingtimeouts; |
7b35aa49 | 45 | unsigned int SyncRes::s_outqueries; |
5c633640 | 46 | unsigned int SyncRes::s_tcpoutqueries; |
3de83124 | 47 | unsigned int SyncRes::s_throttledqueries; |
525b8a7c | 48 | unsigned int SyncRes::s_nodelegated; |
a9af3782 | 49 | string SyncRes::s_serverID; |
7b35aa49 | 50 | bool SyncRes::s_log; |
c836dc19 | 51 | |
ea634573 | 52 | #define LOG if(s_log) L<<Logger::Warning |
728485ca | 53 | |
42724edf | 54 | Throttle<tuple<uint32_t,string,uint16_t> > SyncRes::s_throttle; |
3de83124 | 55 | |
728485ca | 56 | /** everything begins here - this is the entry point just after receiving a packet */ |
a9af3782 | 57 | int SyncRes::beginResolve(const string &qname, const QType &qtype, uint16_t qclass, vector<DNSResourceRecord>&ret) |
728485ca | 58 | { |
c836dc19 | 59 | s_queries++; |
31ad43ab BH |
60 | if( (qtype.getCode()==QType::PTR && !strcasecmp(qname.c_str(), "1.0.0.127.in-addr.arpa.")) || |
61 | (qtype.getCode()==QType::A && qname.length()==10 && !strcasecmp(qname.c_str(), "localhost."))) { | |
62 | ret.clear(); | |
63 | DNSResourceRecord rr; | |
64 | rr.qname=qname; | |
65 | rr.qtype=qtype; | |
a9af3782 | 66 | rr.qclass=1; |
31ad43ab BH |
67 | rr.ttl=86400; |
68 | if(qtype.getCode()==QType::PTR) | |
69 | rr.content="localhost."; | |
70 | else | |
71 | rr.content="127.0.0.1"; | |
72 | ret.push_back(rr); | |
73 | return 0; | |
74 | } | |
75 | ||
a9af3782 BH |
76 | if(qclass==3 && qtype.getCode()==QType::TXT && |
77 | (!strcasecmp(qname.c_str(), "version.bind.") || !strcasecmp(qname.c_str(), "id.server.") || !strcasecmp(qname.c_str(), "version.pdns.") ) | |
78 | ) { | |
79 | ret.clear(); | |
80 | DNSResourceRecord rr; | |
81 | rr.qname=qname; | |
82 | rr.qtype=qtype; | |
83 | rr.qclass=qclass; | |
84 | rr.ttl=86400; | |
85 | if(!strcasecmp(qname.c_str(),"version.bind.") || !strcasecmp(qname.c_str(),"version.pdns.")) | |
86 | rr.content="\""+::arg()["version-string"]+"\""; | |
87 | else | |
88 | rr.content="\""+s_serverID+"\""; | |
89 | ret.push_back(rr); | |
90 | return 0; | |
91 | } | |
9a97cc5c BH |
92 | |
93 | if(qclass!=1) | |
94 | return -1; | |
95 | ||
31ad43ab | 96 | set<GetBestNSAnswer> beenthere; |
809fe23f | 97 | int res=doResolve(qname, qtype, ret, 0, beenthere); |
728485ca BH |
98 | if(!res) |
99 | addCruft(qname, ret); | |
728485ca BH |
100 | return res; |
101 | } | |
afbe2787 | 102 | |
7b35aa49 | 103 | int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere) |
afbe2787 | 104 | { |
ded77b10 BH |
105 | string prefix; |
106 | if(s_log) { | |
107 | prefix=d_prefix; | |
108 | prefix.append(depth, ' '); | |
109 | } | |
728485ca | 110 | |
f4df5e89 | 111 | int res=0; |
c836dc19 BH |
112 | if(!(d_nocache && qtype.getCode()==QType::NS && qname.empty())) { |
113 | if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed | |
114 | return res; | |
115 | ||
116 | if(doCacheCheck(qname,qtype,ret,depth,res)) // we done | |
117 | return res; | |
118 | } | |
afbe2787 | 119 | |
c836dc19 BH |
120 | if(d_cacheonly) |
121 | return 0; | |
afbe2787 | 122 | |
cd553f6c BH |
123 | // place for lookaside cache |
124 | ||
c836dc19 | 125 | LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl; |
728485ca BH |
126 | |
127 | string subdomain(qname); | |
128 | ||
7738a23f | 129 | set<string, CIStringCompare> nsset; |
bdf40704 BH |
130 | for(int tries=0;tries<2 && nsset.empty();++tries) { |
131 | subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); // pass beenthere to both occasions | |
132 | ||
133 | if(nsset.empty()) { // must've lost root records | |
134 | LOG<<prefix<<qname<<": our root expired, repriming from hints and retrying"<<endl; | |
135 | primeHints(); | |
136 | } | |
137 | } | |
138 | ||
7bf26383 | 139 | if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere))) |
728485ca BH |
140 | return 0; |
141 | ||
c836dc19 | 142 | LOG<<prefix<<qname<<": failed"<<endl; |
20177d1d | 143 | return res<0 ? RCode::ServFail : res; |
afbe2787 BH |
144 | } |
145 | ||
42724edf | 146 | vector<uint32_t> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere) |
75b49099 | 147 | { |
bfea0d0b BH |
148 | typedef vector<DNSResourceRecord> res_t; |
149 | res_t res; | |
75b49099 | 150 | |
42724edf | 151 | vector<uint32_t> ret; |
75b49099 | 152 | |
bfea0d0b | 153 | if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) { |
f4df5e89 | 154 | for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) { |
42724edf BH |
155 | if(i->qtype.getCode()==QType::A) { |
156 | uint32_t ip; | |
157 | if(IpToU32(i->content, &ip)) | |
158 | ret.push_back(ntohl(ip)); | |
159 | } | |
f4df5e89 | 160 | } |
bfea0d0b BH |
161 | } |
162 | if(ret.size() > 1) | |
163 | random_shuffle(ret.begin(), ret.end()); | |
728485ca | 164 | return ret; |
75b49099 BH |
165 | } |
166 | ||
7b35aa49 | 167 | void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere) |
86c152f2 | 168 | { |
ded77b10 BH |
169 | string prefix, subdomain(qname); |
170 | if(s_log) { | |
171 | prefix=d_prefix; | |
172 | prefix.append(depth, ' '); | |
173 | } | |
75b49099 BH |
174 | bestns.clear(); |
175 | ||
75b49099 | 176 | do { |
c836dc19 | 177 | LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl; |
7738a23f BH |
178 | set<DNSResourceRecord> ns; |
179 | if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) { | |
7bf26383 | 180 | for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) { |
d6d5dea7 | 181 | if(k->ttl > (unsigned int)d_now.tv_sec ) { |
7bf26383 | 182 | set<DNSResourceRecord>aset; |
b0d4fb45 | 183 | |
eab7dbda | 184 | DNSResourceRecord rr=*k; |
7738a23f BH |
185 | rr.content=k->content; |
186 | if(!dottedEndsOn(rr.content,subdomain) || RC.get(d_now.tv_sec, rr.content ,QType(QType::A),&aset) > 5) { | |
b0d4fb45 | 187 | bestns.insert(rr); |
9fdf67d5 | 188 | |
b0d4fb45 | 189 | LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<rr.content<<"'"<<endl; |
7738a23f | 190 | LOG<<prefix<<qname<<": within bailiwick: "<<dottedEndsOn(rr.content, subdomain); |
9fdf67d5 BH |
191 | if(!aset.empty()) { |
192 | LOG<<", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->ttl- d_now.tv_sec ))<<endl; | |
193 | } | |
194 | else { | |
195 | LOG<<", not in cache"<<endl; | |
196 | } | |
0d8612f2 | 197 | } |
20177d1d | 198 | else |
7738a23f | 199 | LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl; |
ac539791 | 200 | } |
afbe2787 | 201 | } |
75b49099 | 202 | if(!bestns.empty()) { |
7bf26383 | 203 | GetBestNSAnswer answer; |
7738a23f | 204 | answer.qname=qname; answer.bestns=bestns; |
7bf26383 | 205 | if(beenthere.count(answer)) { |
c836dc19 | 206 | LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl; |
7b35aa49 | 207 | for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j) |
c836dc19 | 208 | LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<j->bestns.size()<<")"<<endl; |
7bf26383 BH |
209 | bestns.clear(); |
210 | } | |
211 | else { | |
212 | beenthere.insert(answer); | |
c836dc19 | 213 | LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl; |
7bf26383 BH |
214 | return; |
215 | } | |
75b49099 | 216 | } |
afbe2787 | 217 | } |
7738a23f | 218 | }while(chopOffDotted(subdomain)); |
75b49099 BH |
219 | } |
220 | ||
288f4aa9 | 221 | |
7bf26383 | 222 | /** doesn't actually do the work, leaves that to getBestNSFromCache */ |
7738a23f | 223 | string SyncRes::getBestNSNamesFromCache(const string &qname,set<string, CIStringCompare>& nsset, int depth, set<GetBestNSAnswer>&beenthere) |
75b49099 BH |
224 | { |
225 | string subdomain(qname); | |
226 | ||
227 | set<DNSResourceRecord> bestns; | |
7bf26383 | 228 | getBestNSFromCache(subdomain, bestns, depth, beenthere); |
75b49099 BH |
229 | |
230 | for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) { | |
231 | nsset.insert(k->content); | |
232 | subdomain=k->qname; | |
86c152f2 | 233 | } |
75b49099 | 234 | return subdomain; |
afbe2787 BH |
235 | } |
236 | ||
7b35aa49 | 237 | bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res) |
afbe2787 | 238 | { |
ded77b10 BH |
239 | string prefix; |
240 | if(s_log) { | |
241 | prefix=d_prefix; | |
242 | prefix.append(depth, ' '); | |
243 | } | |
36f5e3db | 244 | |
c6644fc5 | 245 | if(depth>10) { |
c836dc19 | 246 | LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl; |
c6644fc5 BH |
247 | res=RCode::ServFail; |
248 | return true; | |
249 | } | |
36f5e3db | 250 | |
7738a23f | 251 | LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<(qname+"|CNAME")<<"'"<<endl; |
7bf26383 | 252 | set<DNSResourceRecord> cset; |
d6d5dea7 | 253 | if(RC.get(d_now.tv_sec, qname,QType(QType::CNAME),&cset) > 0) { |
36c5ee42 | 254 | |
7bf26383 | 255 | for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) { |
d6d5dea7 | 256 | if(j->ttl>(unsigned int) d_now.tv_sec) { |
7738a23f | 257 | LOG<<prefix<<qname<<": Found cache CNAME hit for '"<< (qname+"|CNAME") <<"' to '"<<j->content<<"'"<<endl; |
ac539791 | 258 | DNSResourceRecord rr=*j; |
d6d5dea7 | 259 | rr.ttl-=d_now.tv_sec; |
ac539791 | 260 | ret.push_back(rr); |
b0d4fb45 | 261 | if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME! |
7bf26383 | 262 | set<GetBestNSAnswer>beenthere; |
7738a23f | 263 | res=doResolve(j->content, qtype, ret, depth+1, beenthere); |
7bf26383 | 264 | } |
b0d4fb45 BH |
265 | else |
266 | res=0; | |
75b49099 | 267 | return true; |
ac539791 BH |
268 | } |
269 | } | |
afbe2787 | 270 | } |
7738a23f | 271 | LOG<<prefix<<qname<<": No CNAME cache hit of '"<< (qname+"|CNAME") <<"' found"<<endl; |
75b49099 BH |
272 | return false; |
273 | } | |
274 | ||
7b35aa49 | 275 | bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res) |
75b49099 | 276 | { |
fd8bc993 | 277 | bool giveNegative=false; |
ded77b10 | 278 | |
be718669 | 279 | string prefix; |
ded77b10 BH |
280 | if(s_log) { |
281 | prefix=d_prefix; | |
282 | prefix.append(depth, ' '); | |
283 | } | |
afbe2787 | 284 | |
288f4aa9 BH |
285 | string sqname(qname); |
286 | QType sqt(qtype); | |
092f210a | 287 | uint32_t sttl=0; |
f4df5e89 BH |
288 | // cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"'\n"; |
289 | ||
7738a23f | 290 | pair<negcache_t::const_iterator, negcache_t::const_iterator> range=s_negcache.equal_range(tie(qname)); |
f4df5e89 BH |
291 | negcache_t::iterator ni; |
292 | for(ni=range.first; ni != range.second; ni++) { | |
293 | // we have something | |
294 | if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) { | |
295 | res=0; | |
33988bfb BH |
296 | if((uint32_t)d_now.tv_sec < ni->d_ttd) { |
297 | sttl=ni->d_ttd - d_now.tv_sec; | |
f4df5e89 | 298 | if(ni->d_qtype.getCode()) { |
f15a5b2a | 299 | LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl; |
f4df5e89 BH |
300 | res = RCode::NoError; |
301 | } | |
302 | else { | |
f15a5b2a | 303 | LOG<<prefix<<qname<<": Entire record '"<<qname<<"', is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl; |
f4df5e89 BH |
304 | res= RCode::NXDomain; |
305 | } | |
38e22b5a | 306 | giveNegative=true; |
33988bfb | 307 | sqname=ni->d_qname; |
f4df5e89 BH |
308 | sqt=QType::SOA; |
309 | break; | |
38e22b5a BH |
310 | } |
311 | else { | |
7738a23f | 312 | LOG<<prefix<<qname<<": Entire record '"<<qname<<"' was negatively cached, but entry expired"<<endl; |
38e22b5a | 313 | } |
fd8bc993 BH |
314 | } |
315 | } | |
20177d1d | 316 | |
7bf26383 | 317 | set<DNSResourceRecord> cset; |
75b49099 | 318 | bool found=false, expired=false; |
be718669 BH |
319 | |
320 | if(RC.get(d_now.tv_sec, sqname,sqt, &cset) > 0) { | |
f15a5b2a | 321 | LOG<<prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": "; |
7bf26383 | 322 | for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) { |
c836dc19 | 323 | LOG<<j->content; |
d6d5dea7 | 324 | if(j->ttl>(unsigned int) d_now.tv_sec) { |
ac539791 | 325 | DNSResourceRecord rr=*j; |
d6d5dea7 | 326 | rr.ttl-=d_now.tv_sec; |
38e22b5a | 327 | if(giveNegative) { |
20177d1d | 328 | rr.d_place=DNSResourceRecord::AUTHORITY; |
38e22b5a BH |
329 | rr.ttl=sttl; |
330 | } | |
ac539791 | 331 | ret.push_back(rr); |
c836dc19 | 332 | LOG<<"[ttl="<<rr.ttl<<"] "; |
75b49099 | 333 | found=true; |
ac539791 | 334 | } |
75b49099 | 335 | else { |
c836dc19 | 336 | LOG<<"[expired] "; |
75b49099 BH |
337 | expired=true; |
338 | } | |
afbe2787 | 339 | } |
ac539791 | 340 | |
c836dc19 | 341 | LOG<<endl; |
f4df5e89 | 342 | if(found && !expired) { |
f15a5b2a BH |
343 | if(!giveNegative) |
344 | res=0; | |
75b49099 | 345 | return true; |
f4df5e89 | 346 | } |
75b49099 | 347 | else |
c836dc19 | 348 | LOG<<prefix<<qname<<": cache had only stale entries"<<endl; |
afbe2787 | 349 | } |
f4df5e89 | 350 | |
75b49099 BH |
351 | return false; |
352 | } | |
afbe2787 | 353 | |
7b35aa49 | 354 | bool SyncRes::moreSpecificThan(const string& a, const string &b) |
75b49099 | 355 | { |
7738a23f BH |
356 | static string dot("."); |
357 | int counta=(a!=dot), countb=(b!=dot); | |
afbe2787 | 358 | |
728485ca BH |
359 | for(string::size_type n=0;n<a.size();++n) |
360 | if(a[n]=='.') | |
361 | counta++; | |
362 | for(string::size_type n=0;n<b.size();++n) | |
363 | if(b[n]=='.') | |
364 | countb++; | |
365 | return counta>countb; | |
afbe2787 BH |
366 | } |
367 | ||
d8d0bb8f | 368 | struct speedOrder |
eefd15f9 | 369 | { |
d8d0bb8f | 370 | speedOrder(map<string,double> &speeds) : d_speeds(speeds) {} |
c3d9d009 BH |
371 | bool operator()(const string &a, const string &b) const |
372 | { | |
373 | return d_speeds[a] < d_speeds[b]; | |
c3d9d009 BH |
374 | } |
375 | map<string,double>& d_speeds; | |
376 | }; | |
377 | ||
7738a23f | 378 | inline vector<string> SyncRes::shuffle(set<string, CIStringCompare> &nameservers, const string &prefix) |
afbe2787 | 379 | { |
728485ca | 380 | vector<string> rnameservers; |
c3d9d009 BH |
381 | rnameservers.reserve(nameservers.size()); |
382 | map<string,double> speeds; | |
eefd15f9 | 383 | |
7738a23f | 384 | for(set<string, CIStringCompare>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) { |
728485ca | 385 | rnameservers.push_back(*i); |
7738a23f | 386 | DecayingEwma& temp=s_nsSpeeds[*i]; |
d6d5dea7 | 387 | speeds[*i]=temp.get(&d_now); |
eefd15f9 | 388 | } |
728485ca | 389 | random_shuffle(rnameservers.begin(),rnameservers.end()); |
ded77b10 | 390 | stable_sort(rnameservers.begin(),rnameservers.end(), speedOrder(speeds)); |
eefd15f9 | 391 | |
d8d0bb8f BH |
392 | if(s_log) { |
393 | L<<Logger::Warning<<prefix<<"Nameservers: "; | |
394 | for(vector<string>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) { | |
395 | if(i!=rnameservers.begin()) { | |
396 | L<<", "; | |
397 | if(!((i-rnameservers.begin())%4)) | |
398 | L<<endl<<Logger::Warning<<prefix<<" "; | |
399 | } | |
49f076e8 | 400 | L<<*i<<"(" << (int)(speeds[*i]/1000.0) <<"ms)"; |
d8d0bb8f BH |
401 | } |
402 | L<<endl; | |
403 | } | |
728485ca | 404 | return rnameservers; |
afbe2787 BH |
405 | } |
406 | ||
7738a23f BH |
407 | struct TCacheComp |
408 | { | |
409 | bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const | |
410 | { | |
411 | int cmp=strcasecmp(a.first.c_str(), b.first.c_str()); | |
412 | if(cmp < 0) | |
413 | return true; | |
414 | if(cmp > 0) | |
415 | return false; | |
416 | ||
417 | return a.second < b.second; | |
418 | } | |
419 | }; | |
420 | ||
421 | ||
422 | ||
ac539791 | 423 | /** returns -1 in case of no results, rcode otherwise */ |
7738a23f | 424 | int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, |
7bf26383 | 425 | int depth, set<GetBestNSAnswer>&beenthere) |
86c152f2 | 426 | { |
ded77b10 BH |
427 | string prefix; |
428 | if(s_log) { | |
429 | prefix=d_prefix; | |
430 | prefix.append(depth, ' '); | |
431 | } | |
86c152f2 | 432 | |
86c152f2 | 433 | LWRes::res_t result; |
86c152f2 | 434 | |
c836dc19 | 435 | LOG<<prefix<<qname<<": Cache consultations done, have "<<nameservers.size()<<" NS to contact"<<endl; |
afbe2787 BH |
436 | |
437 | for(;;) { // we may get more specific nameservers | |
86c152f2 BH |
438 | result.clear(); |
439 | ||
ded77b10 | 440 | vector<string> rnameservers=shuffle(nameservers, s_log ? (prefix+qname+": ") : string() ); |
20177d1d | 441 | |
728485ca BH |
442 | for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) { |
443 | if(tns==rnameservers.end()) { | |
c836dc19 | 444 | LOG<<prefix<<qname<<": Failed to resolve via any of the "<<rnameservers.size()<<" offered NS"<<endl; |
ac539791 | 445 | return -1; |
afbe2787 | 446 | } |
288f4aa9 | 447 | if(qname==*tns && qtype.getCode()==QType::A) { |
c836dc19 | 448 | LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl; |
20177d1d BH |
449 | continue; |
450 | } | |
c836dc19 | 451 | LOG<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl; |
42724edf | 452 | typedef vector<uint32_t> remoteIPs_t; |
bfea0d0b BH |
453 | remoteIPs_t remoteIPs=getAs(*tns, depth+1, beenthere); |
454 | if(remoteIPs.empty()) { | |
c836dc19 | 455 | LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl; |
afbe2787 BH |
456 | continue; |
457 | } | |
bfea0d0b | 458 | remoteIPs_t::const_iterator remoteIP; |
5c633640 | 459 | bool doTCP=false; |
bfea0d0b | 460 | for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { |
42724edf | 461 | LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to "<<U32ToIP(*remoteIP)<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl; |
bfea0d0b | 462 | |
cd7bf56b | 463 | if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) { |
bfea0d0b BH |
464 | LOG<<prefix<<qname<<": query throttled "<<endl; |
465 | s_throttledqueries++; | |
466 | d_throttledqueries++; | |
467 | continue; | |
5c633640 | 468 | } |
bfea0d0b BH |
469 | else { |
470 | s_outqueries++; | |
471 | d_outqueries++; | |
472 | TryTCP: | |
473 | if(doTCP) { | |
474 | s_tcpoutqueries++; | |
475 | d_tcpoutqueries++; | |
7becf07f | 476 | } |
7becf07f | 477 | |
83206e14 | 478 | int ret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now); // <- we go out on the wire! |
bfea0d0b BH |
479 | if(ret != 1) { |
480 | if(ret==0) { | |
50c81227 | 481 | LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl; |
bfea0d0b BH |
482 | d_timeouts++; |
483 | s_outgoingtimeouts++; | |
484 | } | |
998a4334 BH |
485 | else if(ret==-2) { |
486 | LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl; | |
487 | g_stats.resourceLimits++; | |
488 | } | |
bfea0d0b | 489 | else |
50c81227 | 490 | LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl; |
bfea0d0b | 491 | |
998a4334 BH |
492 | if(ret!=-2) { // don't account for resource limits, they are our own fault |
493 | s_nsSpeeds[*tns].submit(1000000, &d_now); // 1 sec | |
494 | s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),20,5); | |
495 | } | |
bfea0d0b BH |
496 | continue; |
497 | } | |
5e3da48d | 498 | |
bfea0d0b | 499 | break; // it did work! |
3de83124 BH |
500 | } |
501 | } | |
20177d1d | 502 | |
bfea0d0b BH |
503 | if(remoteIP == remoteIPs.end()) // we tried all IP addresses, none worked |
504 | continue; | |
505 | ||
eefd15f9 | 506 | result=d_lwr.result(); |
66ab6a63 | 507 | |
d0166f4a BH |
508 | if(d_lwr.d_tcbit) { |
509 | if(!doTCP) { | |
510 | doTCP=true; | |
511 | LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl; | |
512 | goto TryTCP; | |
513 | } | |
514 | LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl; | |
515 | return RCode::ServFail; | |
516 | } | |
517 | ||
288f4aa9 | 518 | if(d_lwr.d_rcode==RCode::ServFail) { |
c836dc19 | 519 | LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling NS"<<endl; |
cd7bf56b | 520 | s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3); |
20177d1d BH |
521 | continue; |
522 | } | |
42724edf | 523 | LOG<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<U32ToIP(*remoteIP)<<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl; |
7738a23f | 524 | s_nsSpeeds[*tns].submit(d_lwr.d_usec, &d_now); |
20177d1d | 525 | |
7738a23f | 526 | typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t; |
1ac4e536 BH |
527 | tcache_t tcache; |
528 | ||
728485ca BH |
529 | // reap all answers from this packet that are acceptable |
530 | for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) { | |
5456e605 | 531 | LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? "; |
6cf876a2 | 532 | |
7738a23f | 533 | if(dottedEndsOn(i->qname, auth)) { |
84de7c8a | 534 | if(d_lwr.d_aabit && d_lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && ::arg().contains("delegation-only",auth)) { |
562588a3 | 535 | LOG<<"NO! Is from delegation-only zone"<<endl; |
525b8a7c | 536 | s_nodelegated++; |
562588a3 BH |
537 | return RCode::NXDomain; |
538 | } | |
539 | else { | |
540 | LOG<<"YES!"<<endl; | |
541 | ||
542 | DNSResourceRecord rr=*i; | |
543 | rr.d_place=DNSResourceRecord::ANSWER; | |
d6d5dea7 | 544 | rr.ttl+=d_now.tv_sec; |
5456e605 | 545 | if(rr.qtype.getCode() == QType::NS) // people fiddle with the case |
7738a23f BH |
546 | rr.content=toLower(rr.content); // this must stay! |
547 | tcache[make_pair(i->qname,i->qtype)].insert(rr); | |
562588a3 BH |
548 | } |
549 | } | |
728485ca | 550 | else |
c836dc19 | 551 | LOG<<"NO!"<<endl; |
86c152f2 | 552 | } |
728485ca BH |
553 | |
554 | // supplant | |
1ac4e536 | 555 | for(tcache_t::const_iterator i=tcache.begin();i!=tcache.end();++i) { |
5456e605 | 556 | RC.replace(i->first.first, i->first.second, i->second); |
288f4aa9 | 557 | } |
7738a23f | 558 | set<string, CIStringCompare> nsset; |
c836dc19 | 559 | LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl; |
728485ca | 560 | |
20177d1d | 561 | bool done=false, realreferral=false, negindic=false; |
c6644fc5 | 562 | string newauth, soaname, newtarget; |
728485ca BH |
563 | |
564 | for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) { | |
7738a23f | 565 | if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && |
fd8bc993 | 566 | d_lwr.d_rcode==RCode::NXDomain) { |
7738a23f | 567 | LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl; |
728485ca | 568 | ret.push_back(*i); |
fd8bc993 | 569 | |
38e22b5a | 570 | NegCacheEntry ne; |
33988bfb BH |
571 | |
572 | ne.d_qname=i->qname; | |
a9af3782 | 573 | ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial |
7738a23f | 574 | ne.d_name=qname; |
f4df5e89 | 575 | ne.d_qtype=QType(0); |
33988bfb | 576 | s_negcache.insert(ne); |
20177d1d | 577 | negindic=true; |
728485ca BH |
578 | } |
579 | else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) { | |
728485ca | 580 | ret.push_back(*i); |
7738a23f | 581 | newtarget=i->content; |
728485ca BH |
582 | } |
583 | // for ANY answers we *must* have an authoritive answer | |
e5bad90b | 584 | else if(i->d_place==DNSResourceRecord::ANSWER && !strcasecmp(i->qname.c_str(),qname.c_str()) && |
5456e605 BH |
585 | ( (i->qtype==qtype) || |
586 | ( qtype==QType(QType::ANY) && d_lwr.d_aabit))) { | |
587 | ||
588 | LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl; | |
589 | ||
6cf876a2 | 590 | |
728485ca BH |
591 | done=true; |
592 | ret.push_back(*i); | |
593 | } | |
7738a23f | 594 | else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { |
728485ca BH |
595 | if(moreSpecificThan(i->qname,auth)) { |
596 | newauth=i->qname; | |
c836dc19 | 597 | LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl; |
728485ca BH |
598 | realreferral=true; |
599 | } | |
7bf26383 | 600 | else |
c836dc19 | 601 | LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl; |
7738a23f | 602 | nsset.insert(i->content); |
728485ca | 603 | } |
7738a23f | 604 | else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && |
fd8bc993 | 605 | d_lwr.d_rcode==RCode::NoError) { |
7738a23f | 606 | LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl; |
fd8bc993 | 607 | ret.push_back(*i); |
38e22b5a BH |
608 | |
609 | NegCacheEntry ne; | |
33988bfb BH |
610 | ne.d_qname=i->qname; |
611 | ne.d_ttd=d_now.tv_sec + min(3600U,i->ttl); | |
7738a23f | 612 | ne.d_name=qname; |
f4df5e89 | 613 | ne.d_qtype=qtype; |
be718669 BH |
614 | if(qtype.getCode()) // prevents us from blacking out a whole domain |
615 | s_negcache.insert(ne); | |
fd8bc993 BH |
616 | negindic=true; |
617 | } | |
86c152f2 | 618 | } |
86c152f2 | 619 | |
728485ca | 620 | if(done){ |
c836dc19 | 621 | LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl; |
728485ca | 622 | return 0; |
ac539791 | 623 | } |
288f4aa9 | 624 | if(d_lwr.d_rcode==RCode::NXDomain) { |
c836dc19 | 625 | LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl; |
728485ca | 626 | return RCode::NXDomain; |
86c152f2 | 627 | } |
c6644fc5 | 628 | if(!newtarget.empty()) { |
c836dc19 | 629 | LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl; |
c6644fc5 BH |
630 | set<GetBestNSAnswer>beenthere2; |
631 | return doResolve(newtarget, qtype, ret,0,beenthere2); | |
632 | } | |
caa6eefa BH |
633 | if(nsset.empty() && !d_lwr.d_rcode) { |
634 | LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl; | |
635 | return 0; | |
636 | } | |
728485ca | 637 | else if(realreferral) { |
c836dc19 | 638 | LOG<<prefix<<qname<<": status=did not resolve, got "<<nsset.size()<<" NS, looping to them"<<endl; |
728485ca BH |
639 | auth=newauth; |
640 | nameservers=nsset; | |
641 | break; | |
642 | } | |
643 | else { | |
c836dc19 | 644 | LOG<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl; |
cd7bf56b | 645 | s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),60,0); |
86c152f2 BH |
646 | } |
647 | } | |
86c152f2 | 648 | } |
ac539791 | 649 | return -1; |
86c152f2 BH |
650 | } |
651 | ||
7b35aa49 | 652 | void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret) |
728485ca BH |
653 | { |
654 | for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) // don't add stuff to an NXDOMAIN! | |
655 | if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA)) | |
656 | return; | |
657 | ||
c836dc19 | 658 | // LOG<<qname<<": Adding best authority records from cache"<<endl; |
288f4aa9 | 659 | // addAuthorityRecords(qname,ret,0); |
c836dc19 | 660 | // LOG<<qname<<": Done adding best authority records."<<endl; |
728485ca | 661 | |
c836dc19 | 662 | LOG<<d_prefix<<qname<<": Starting additional processing"<<endl; |
728485ca | 663 | vector<DNSResourceRecord> addit; |
1ac4e536 BH |
664 | static optional<bool> l_doIPv6AP; |
665 | if(!l_doIPv6AP) | |
666 | l_doIPv6AP=::arg().mustDo("aaaa-additional-processing"); | |
667 | ||
728485ca BH |
668 | for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) |
669 | if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) || | |
288f4aa9 | 670 | ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) { |
d8d0bb8f | 671 | LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl; |
7bf26383 | 672 | set<GetBestNSAnswer>beenthere; |
ea634573 | 673 | if(k->qtype==QType(QType::MX)) { |
50c79a76 | 674 | pair<string,string> prioServer=splitField(k->content,' '); |
01608dca BH |
675 | doResolve(prioServer.second, QType(QType::A), addit, 1, beenthere); |
676 | if(*l_doIPv6AP) | |
677 | doResolve(prioServer.second, QType(QType::AAAA), addit, 1, beenthere); | |
ea634573 | 678 | } |
e29f6ed0 | 679 | else { |
ea634573 | 680 | doResolve(k->content,QType(QType::A),addit,1,beenthere); |
1ac4e536 | 681 | if(*l_doIPv6AP) |
e29f6ed0 BH |
682 | doResolve(k->content,QType(QType::AAAA),addit,1,beenthere); |
683 | } | |
728485ca BH |
684 | } |
685 | ||
686 | for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) { | |
288f4aa9 BH |
687 | if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) { |
688 | k->d_place=DNSResourceRecord::ADDITIONAL; | |
689 | ret.push_back(*k); | |
690 | } | |
728485ca | 691 | } |
c836dc19 | 692 | LOG<<d_prefix<<qname<<": Done with additional processing"<<endl; |
728485ca BH |
693 | } |
694 | ||
7b35aa49 | 695 | void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth) |
86c152f2 | 696 | { |
288f4aa9 BH |
697 | set<DNSResourceRecord> bestns; |
698 | set<GetBestNSAnswer>beenthere; | |
699 | getBestNSFromCache(qname, bestns, depth,beenthere); | |
36c5ee42 | 700 | |
288f4aa9 BH |
701 | for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) { |
702 | DNSResourceRecord ns=*k; | |
703 | ns.d_place=DNSResourceRecord::AUTHORITY; | |
d6d5dea7 | 704 | ns.ttl-=d_now.tv_sec; |
288f4aa9 | 705 | ret.push_back(ns); |
86c152f2 BH |
706 | } |
707 | } |