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