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