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