]>
Commit | Line | Data |
---|---|---|
86c152f2 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
263f6a5a | 3 | Copyright (C) 2003 - 2007 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 | |
c9f52071 | 19 | #include <boost/algorithm/string.hpp> |
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; |
c571588b | 49 | unsigned int SyncRes::s_unreachables; |
996c89cc | 50 | bool SyncRes::s_doIPv6; |
c571588b | 51 | |
5605c067 | 52 | SyncRes::domainmap_t SyncRes::s_domainmap; |
a9af3782 | 53 | string SyncRes::s_serverID; |
7b35aa49 | 54 | bool SyncRes::s_log; |
c836dc19 | 55 | |
ea634573 | 56 | #define LOG if(s_log) L<<Logger::Warning |
728485ca | 57 | |
996c89cc | 58 | SyncRes::throttle_t SyncRes::s_throttle; |
3de83124 | 59 | |
728485ca | 60 | /** everything begins here - this is the entry point just after receiving a packet */ |
a9af3782 | 61 | int SyncRes::beginResolve(const string &qname, const QType &qtype, uint16_t qclass, vector<DNSResourceRecord>&ret) |
728485ca | 62 | { |
c836dc19 | 63 | s_queries++; |
705f31ae BH |
64 | if( (qtype.getCode()==QType::PTR && !Utility::strcasecmp(qname.c_str(), "1.0.0.127.in-addr.arpa.")) || |
65 | (qtype.getCode()==QType::A && qname.length()==10 && !Utility::strcasecmp(qname.c_str(), "localhost."))) { | |
31ad43ab BH |
66 | ret.clear(); |
67 | DNSResourceRecord rr; | |
68 | rr.qname=qname; | |
69 | rr.qtype=qtype; | |
a9af3782 | 70 | rr.qclass=1; |
31ad43ab BH |
71 | rr.ttl=86400; |
72 | if(qtype.getCode()==QType::PTR) | |
73 | rr.content="localhost."; | |
74 | else | |
75 | rr.content="127.0.0.1"; | |
76 | ret.push_back(rr); | |
77 | return 0; | |
78 | } | |
79 | ||
a9af3782 | 80 | if(qclass==3 && qtype.getCode()==QType::TXT && |
705f31ae | 81 | (!Utility::strcasecmp(qname.c_str(), "version.bind.") || !Utility::strcasecmp(qname.c_str(), "id.server.") || !Utility::strcasecmp(qname.c_str(), "version.pdns.") ) |
a9af3782 BH |
82 | ) { |
83 | ret.clear(); | |
84 | DNSResourceRecord rr; | |
85 | rr.qname=qname; | |
86 | rr.qtype=qtype; | |
87 | rr.qclass=qclass; | |
88 | rr.ttl=86400; | |
705f31ae | 89 | if(!Utility::strcasecmp(qname.c_str(),"version.bind.") || !Utility::strcasecmp(qname.c_str(),"version.pdns.")) |
a9af3782 BH |
90 | rr.content="\""+::arg()["version-string"]+"\""; |
91 | else | |
92 | rr.content="\""+s_serverID+"\""; | |
93 | ret.push_back(rr); | |
94 | return 0; | |
95 | } | |
9a97cc5c | 96 | |
a114f49e BH |
97 | if(qclass==0xff) |
98 | qclass=1; | |
99 | else if(qclass!=1) | |
9a97cc5c BH |
100 | return -1; |
101 | ||
31ad43ab | 102 | set<GetBestNSAnswer> beenthere; |
809fe23f | 103 | int res=doResolve(qname, qtype, ret, 0, beenthere); |
728485ca BH |
104 | if(!res) |
105 | addCruft(qname, ret); | |
728485ca BH |
106 | return res; |
107 | } | |
afbe2787 | 108 | |
ab5c053d | 109 | //! This is the 'out of band resolver', in other words, the authoritative server |
e93c956b BH |
110 | bool SyncRes::doOOBResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int& res) |
111 | { | |
5605c067 BH |
112 | string prefix; |
113 | if(s_log) { | |
114 | prefix=d_prefix; | |
115 | prefix.append(depth, ' '); | |
116 | } | |
117 | ||
118 | LOG<<prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl; | |
119 | string authdomain(qname); | |
120 | ||
121 | domainmap_t::const_iterator iter=getBestAuthZone(&authdomain); | |
122 | if(iter==s_domainmap.end()) { | |
123 | LOG<<prefix<<qname<<": auth storage has no zone for this query!"<<endl; | |
124 | return false; | |
125 | } | |
126 | LOG<<prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl; | |
127 | pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range; | |
128 | ||
129 | range=iter->second.d_records.equal_range(tie(qname)); // partial lookup | |
130 | ||
131 | ret.clear(); | |
132 | AuthDomain::records_t::const_iterator ziter; | |
9e9844e2 | 133 | bool somedata=false; |
5605c067 | 134 | for(ziter=range.first; ziter!=range.second; ++ziter) { |
9e9844e2 | 135 | somedata=true; |
9bc8c14c | 136 | if(qtype.getCode()==QType::ANY || ziter->qtype==qtype || ziter->qtype.getCode()==QType::CNAME) // let rest of nameserver do the legwork on this one |
5605c067 BH |
137 | ret.push_back(*ziter); |
138 | } | |
9bc8c14c BH |
139 | if(!ret.empty()) { |
140 | LOG<<prefix<<qname<<": exact match in zone '"<<authdomain<<"'"<<endl; | |
141 | res=0; | |
142 | return true; | |
5605c067 | 143 | } |
9e9844e2 BH |
144 | if(somedata) { |
145 | LOG<<prefix<<qname<<": found record in '"<<authdomain<<"', but nothing of the right type, sending SOA"<<endl; | |
146 | ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA))); | |
147 | if(ziter!=iter->second.d_records.end()) { | |
148 | DNSResourceRecord rr=*ziter; | |
149 | rr.d_place=DNSResourceRecord::AUTHORITY; | |
150 | ret.push_back(rr); | |
151 | } | |
152 | else | |
153 | LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl; | |
154 | res=RCode::NoError; | |
155 | return true; | |
156 | } | |
5605c067 BH |
157 | |
158 | string nsdomain(qname); | |
159 | ||
160 | while(chopOffDotted(nsdomain) && nsdomain!=iter->first) { | |
161 | range=iter->second.d_records.equal_range(make_tuple(nsdomain,QType(QType::NS))); | |
162 | if(range.first==range.second) | |
163 | continue; | |
164 | ||
165 | for(ziter=range.first; ziter!=range.second; ++ziter) { | |
166 | DNSResourceRecord rr=*ziter; | |
167 | rr.d_place=DNSResourceRecord::AUTHORITY; | |
168 | ret.push_back(rr); | |
169 | } | |
170 | } | |
171 | if(ret.empty()) { | |
172 | LOG<<prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl; | |
173 | ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA))); | |
174 | if(ziter!=iter->second.d_records.end()) { | |
175 | DNSResourceRecord rr=*ziter; | |
176 | rr.d_place=DNSResourceRecord::AUTHORITY; | |
177 | ret.push_back(rr); | |
178 | } | |
179 | else | |
180 | LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl; | |
181 | res=RCode::NXDomain; | |
182 | } | |
183 | else | |
184 | res=0; | |
185 | ||
9e9844e2 | 186 | return true; |
e93c956b BH |
187 | } |
188 | ||
7b35aa49 | 189 | int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere) |
afbe2787 | 190 | { |
ded77b10 BH |
191 | string prefix; |
192 | if(s_log) { | |
193 | prefix=d_prefix; | |
194 | prefix.append(depth, ' '); | |
195 | } | |
728485ca | 196 | |
f4df5e89 | 197 | int res=0; |
5605c067 | 198 | if(!(d_nocache && qtype.getCode()==QType::NS && qname==".")) { |
115d07ad | 199 | if(d_cacheonly) { // very limited OOB support |
263f6a5a | 200 | LWResult lwr; |
115d07ad BH |
201 | LOG<<prefix<<qname<<": Recursion not requested for '"<<qname<<"|"<<qtype.getName()<<"', peeking at auth/forward zones"<<endl; |
202 | string authname(qname); | |
203 | domainmap_t::const_iterator iter=getBestAuthZone(&authname); | |
204 | if(iter != s_domainmap.end()) { | |
205 | string server=iter->second.d_server; | |
206 | if(server.empty()) { | |
115d07ad BH |
207 | ret.clear(); |
208 | doOOBResolve(qname, qtype, ret, depth, res); | |
209 | return res; | |
210 | } | |
211 | else { | |
212 | LOG<<prefix<<qname<<": forwarding query to hardcoded nameserver '"<<server<<"' for zone '"<<authname<<"'"<<endl; | |
213 | ComboAddress remoteIP(server, 53); | |
115d07ad | 214 | |
2188dcc3 | 215 | res=asyncresolve(remoteIP, qname, qtype.getCode(), false, false, &d_now, &lwr); |
263f6a5a BH |
216 | // filter out the good stuff from lwr.result() |
217 | ||
218 | for(LWResult::res_t::const_iterator i=lwr.d_result.begin();i!=lwr.d_result.end();++i) { | |
115d07ad BH |
219 | if(i->d_place == DNSResourceRecord::ANSWER) |
220 | ret.push_back(*i); | |
221 | } | |
222 | return res; | |
223 | } | |
224 | } | |
225 | } | |
226 | ||
c836dc19 BH |
227 | if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed |
228 | return res; | |
229 | ||
230 | if(doCacheCheck(qname,qtype,ret,depth,res)) // we done | |
231 | return res; | |
232 | } | |
afbe2787 | 233 | |
115d07ad | 234 | if(d_cacheonly) |
c836dc19 | 235 | return 0; |
115d07ad | 236 | |
c836dc19 | 237 | LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl; |
728485ca BH |
238 | |
239 | string subdomain(qname); | |
240 | ||
7738a23f | 241 | set<string, CIStringCompare> nsset; |
7305df82 | 242 | bool flawedNSSet=false; |
bdf40704 | 243 | for(int tries=0;tries<2 && nsset.empty();++tries) { |
7305df82 | 244 | subdomain=getBestNSNamesFromCache(subdomain, nsset, &flawedNSSet, depth, beenthere); // pass beenthere to both occasions |
bdf40704 BH |
245 | |
246 | if(nsset.empty()) { // must've lost root records | |
247 | LOG<<prefix<<qname<<": our root expired, repriming from hints and retrying"<<endl; | |
248 | primeHints(); | |
249 | } | |
250 | } | |
251 | ||
7305df82 | 252 | if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere))) |
728485ca BH |
253 | return 0; |
254 | ||
5605c067 | 255 | LOG<<prefix<<qname<<": failed (res="<<res<<")"<<endl; |
20177d1d | 256 | return res<0 ? RCode::ServFail : res; |
afbe2787 BH |
257 | } |
258 | ||
996c89cc BH |
259 | /** This function explicitly goes out for A addresses, but if configured to use IPv6 as well, will also return any IPv6 addresses in the cache |
260 | Additionally, it will return the 'best' address up front, and the rest shufled | |
261 | */ | |
262 | vector<ComboAddress> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere) | |
75b49099 | 263 | { |
bfea0d0b BH |
264 | typedef vector<DNSResourceRecord> res_t; |
265 | res_t res; | |
75b49099 | 266 | |
996c89cc BH |
267 | typedef vector<ComboAddress> ret_t; |
268 | ret_t ret; | |
75b49099 | 269 | |
bfea0d0b | 270 | if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) { |
f4df5e89 | 271 | for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) { |
42724edf | 272 | if(i->qtype.getCode()==QType::A) { |
996c89cc | 273 | ret.push_back(ComboAddress(i->content, 53)); |
42724edf | 274 | } |
f4df5e89 | 275 | } |
bfea0d0b | 276 | } |
996c89cc BH |
277 | |
278 | if(s_doIPv6) { | |
279 | typedef set<DNSResourceRecord> ipv6_t; | |
280 | ipv6_t ipv6; | |
281 | if(RC.get(d_now.tv_sec, qname, QType(QType::AAAA), &ipv6) > 0) { | |
282 | for(ipv6_t::const_iterator i=ipv6.begin(); i != ipv6.end(); ++i) | |
283 | ret.push_back(ComboAddress(i->content, 53)); | |
284 | } | |
285 | } | |
286 | ||
287 | if(ret.size() > 1) { | |
bfea0d0b | 288 | random_shuffle(ret.begin(), ret.end()); |
996c89cc BH |
289 | |
290 | // move 'best' address up front | |
291 | nsspeeds_t::iterator best=s_nsSpeeds.find(qname); | |
292 | ||
293 | if(best != s_nsSpeeds.end()) | |
294 | for(ret_t::iterator i=ret.begin(); i != ret.end(); ++i) { | |
295 | // cerr<<"Is "<<i->toString()<<" equal to "<<best->second.d_best.toString()<<"?\n"; | |
296 | if(*i==best->second.d_best) { | |
297 | if(i!=ret.begin()) { | |
298 | // cerr<<"Moving "<<best->second.d_best.toString()<<" up front!\n"; | |
299 | *i=*ret.begin(); | |
300 | *ret.begin()=best->second.d_best; | |
301 | } | |
302 | break; | |
303 | } | |
304 | } | |
305 | } | |
306 | ||
728485ca | 307 | return ret; |
75b49099 BH |
308 | } |
309 | ||
7305df82 | 310 | void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, bool* flawedNSSet, int depth, set<GetBestNSAnswer>& beenthere) |
86c152f2 | 311 | { |
ded77b10 BH |
312 | string prefix, subdomain(qname); |
313 | if(s_log) { | |
314 | prefix=d_prefix; | |
315 | prefix.append(depth, ' '); | |
316 | } | |
75b49099 BH |
317 | bestns.clear(); |
318 | ||
75b49099 | 319 | do { |
c836dc19 | 320 | LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl; |
7738a23f | 321 | set<DNSResourceRecord> ns; |
7305df82 | 322 | *flawedNSSet = false; |
7738a23f | 323 | if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) { |
7bf26383 | 324 | for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) { |
d6d5dea7 | 325 | if(k->ttl > (unsigned int)d_now.tv_sec ) { |
7bf26383 | 326 | set<DNSResourceRecord>aset; |
b0d4fb45 | 327 | |
eab7dbda | 328 | DNSResourceRecord rr=*k; |
7738a23f | 329 | rr.content=k->content; |
e93c956b | 330 | if(!dottedEndsOn(rr.content, subdomain) || RC.get(d_now.tv_sec, rr.content, QType(QType::A), s_log ? &aset : 0) > 5) { |
b0d4fb45 | 331 | bestns.insert(rr); |
9fdf67d5 | 332 | |
b0d4fb45 | 333 | LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<rr.content<<"'"<<endl; |
7738a23f | 334 | LOG<<prefix<<qname<<": within bailiwick: "<<dottedEndsOn(rr.content, subdomain); |
9fdf67d5 BH |
335 | if(!aset.empty()) { |
336 | LOG<<", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->ttl- d_now.tv_sec ))<<endl; | |
337 | } | |
338 | else { | |
61973281 | 339 | LOG<<", not in cache / did not look at cache"<<endl; |
9fdf67d5 | 340 | } |
0d8612f2 | 341 | } |
7305df82 BH |
342 | else { |
343 | *flawedNSSet=true; | |
7738a23f | 344 | LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl; |
7305df82 | 345 | } |
ac539791 | 346 | } |
afbe2787 | 347 | } |
75b49099 | 348 | if(!bestns.empty()) { |
7bf26383 | 349 | GetBestNSAnswer answer; |
7738a23f | 350 | answer.qname=qname; answer.bestns=bestns; |
7bf26383 | 351 | if(beenthere.count(answer)) { |
c836dc19 | 352 | LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl; |
7305df82 BH |
353 | if(s_log) |
354 | for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j) | |
355 | LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<(unsigned int)j->bestns.size()<<")"<<endl; | |
7bf26383 BH |
356 | bestns.clear(); |
357 | } | |
358 | else { | |
359 | beenthere.insert(answer); | |
7305df82 | 360 | LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' (flawedNSSet="<<*flawedNSSet<<")"<<endl; |
7bf26383 BH |
361 | return; |
362 | } | |
75b49099 | 363 | } |
afbe2787 | 364 | } |
e93c956b | 365 | LOG<<prefix<<qname<<": no valid/useful NS in cache for '"<<subdomain<<"'"<<endl; |
7738a23f | 366 | }while(chopOffDotted(subdomain)); |
75b49099 BH |
367 | } |
368 | ||
5605c067 BH |
369 | SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(string* qname) |
370 | { | |
371 | SyncRes::domainmap_t::const_iterator ret; | |
372 | do { | |
373 | ret=s_domainmap.find(*qname); | |
374 | if(ret!=s_domainmap.end()) | |
375 | break; | |
376 | }while(chopOffDotted(*qname)); | |
377 | return ret; | |
378 | } | |
288f4aa9 | 379 | |
7bf26383 | 380 | /** doesn't actually do the work, leaves that to getBestNSFromCache */ |
7305df82 | 381 | string SyncRes::getBestNSNamesFromCache(const string &qname, set<string, CIStringCompare>& nsset, bool* flawedNSSet, int depth, set<GetBestNSAnswer>&beenthere) |
75b49099 BH |
382 | { |
383 | string subdomain(qname); | |
384 | ||
5605c067 BH |
385 | string authdomain(qname); |
386 | ||
387 | domainmap_t::const_iterator iter=getBestAuthZone(&authdomain); | |
388 | if(iter!=s_domainmap.end()) { | |
67828389 | 389 | nsset.insert(iter->second.d_server); // this gets picked up in doResolveAt, if empty it means "we are auth", otherwise it denotes a forward |
5605c067 BH |
390 | return authdomain; |
391 | } | |
392 | ||
75b49099 | 393 | set<DNSResourceRecord> bestns; |
7305df82 | 394 | getBestNSFromCache(subdomain, bestns, flawedNSSet, depth, beenthere); |
75b49099 BH |
395 | |
396 | for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) { | |
397 | nsset.insert(k->content); | |
e93c956b BH |
398 | if(k==bestns.begin()) |
399 | subdomain=k->qname; | |
86c152f2 | 400 | } |
75b49099 | 401 | return subdomain; |
afbe2787 BH |
402 | } |
403 | ||
7b35aa49 | 404 | bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res) |
afbe2787 | 405 | { |
ded77b10 BH |
406 | string prefix; |
407 | if(s_log) { | |
408 | prefix=d_prefix; | |
409 | prefix.append(depth, ' '); | |
410 | } | |
36f5e3db | 411 | |
c6644fc5 | 412 | if(depth>10) { |
c836dc19 | 413 | LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl; |
c6644fc5 BH |
414 | res=RCode::ServFail; |
415 | return true; | |
416 | } | |
36f5e3db | 417 | |
7738a23f | 418 | LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<(qname+"|CNAME")<<"'"<<endl; |
7bf26383 | 419 | set<DNSResourceRecord> cset; |
d6d5dea7 | 420 | if(RC.get(d_now.tv_sec, qname,QType(QType::CNAME),&cset) > 0) { |
36c5ee42 | 421 | |
7bf26383 | 422 | for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) { |
d6d5dea7 | 423 | if(j->ttl>(unsigned int) d_now.tv_sec) { |
7738a23f | 424 | LOG<<prefix<<qname<<": Found cache CNAME hit for '"<< (qname+"|CNAME") <<"' to '"<<j->content<<"'"<<endl; |
ac539791 | 425 | DNSResourceRecord rr=*j; |
d6d5dea7 | 426 | rr.ttl-=d_now.tv_sec; |
ac539791 | 427 | ret.push_back(rr); |
b0d4fb45 | 428 | if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME! |
7bf26383 | 429 | set<GetBestNSAnswer>beenthere; |
7738a23f | 430 | res=doResolve(j->content, qtype, ret, depth+1, beenthere); |
7bf26383 | 431 | } |
b0d4fb45 BH |
432 | else |
433 | res=0; | |
75b49099 | 434 | return true; |
ac539791 BH |
435 | } |
436 | } | |
afbe2787 | 437 | } |
7738a23f | 438 | LOG<<prefix<<qname<<": No CNAME cache hit of '"<< (qname+"|CNAME") <<"' found"<<endl; |
75b49099 BH |
439 | return false; |
440 | } | |
441 | ||
7b35aa49 | 442 | bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res) |
75b49099 | 443 | { |
fd8bc993 | 444 | bool giveNegative=false; |
ded77b10 | 445 | |
be718669 | 446 | string prefix; |
ded77b10 BH |
447 | if(s_log) { |
448 | prefix=d_prefix; | |
449 | prefix.append(depth, ' '); | |
450 | } | |
afbe2787 | 451 | |
288f4aa9 BH |
452 | string sqname(qname); |
453 | QType sqt(qtype); | |
092f210a | 454 | uint32_t sttl=0; |
f4df5e89 BH |
455 | // cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"'\n"; |
456 | ||
7738a23f | 457 | pair<negcache_t::const_iterator, negcache_t::const_iterator> range=s_negcache.equal_range(tie(qname)); |
f4df5e89 BH |
458 | negcache_t::iterator ni; |
459 | for(ni=range.first; ni != range.second; ni++) { | |
460 | // we have something | |
461 | if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) { | |
462 | res=0; | |
33988bfb BH |
463 | if((uint32_t)d_now.tv_sec < ni->d_ttd) { |
464 | sttl=ni->d_ttd - d_now.tv_sec; | |
f4df5e89 | 465 | if(ni->d_qtype.getCode()) { |
f15a5b2a | 466 | LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl; |
f4df5e89 BH |
467 | res = RCode::NoError; |
468 | } | |
469 | else { | |
f15a5b2a | 470 | LOG<<prefix<<qname<<": Entire record '"<<qname<<"', is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl; |
f4df5e89 BH |
471 | res= RCode::NXDomain; |
472 | } | |
38e22b5a | 473 | giveNegative=true; |
33988bfb | 474 | sqname=ni->d_qname; |
f4df5e89 BH |
475 | sqt=QType::SOA; |
476 | break; | |
38e22b5a BH |
477 | } |
478 | else { | |
7738a23f | 479 | LOG<<prefix<<qname<<": Entire record '"<<qname<<"' was negatively cached, but entry expired"<<endl; |
38e22b5a | 480 | } |
fd8bc993 BH |
481 | } |
482 | } | |
20177d1d | 483 | |
7bf26383 | 484 | set<DNSResourceRecord> cset; |
75b49099 | 485 | bool found=false, expired=false; |
be718669 | 486 | |
9bc8c14c | 487 | if(RC.get(d_now.tv_sec, sqname, sqt, &cset) > 0) { |
f15a5b2a | 488 | LOG<<prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": "; |
7bf26383 | 489 | for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) { |
c836dc19 | 490 | LOG<<j->content; |
d6d5dea7 | 491 | if(j->ttl>(unsigned int) d_now.tv_sec) { |
ac539791 | 492 | DNSResourceRecord rr=*j; |
d6d5dea7 | 493 | rr.ttl-=d_now.tv_sec; |
38e22b5a | 494 | if(giveNegative) { |
20177d1d | 495 | rr.d_place=DNSResourceRecord::AUTHORITY; |
38e22b5a BH |
496 | rr.ttl=sttl; |
497 | } | |
ac539791 | 498 | ret.push_back(rr); |
c836dc19 | 499 | LOG<<"[ttl="<<rr.ttl<<"] "; |
75b49099 | 500 | found=true; |
ac539791 | 501 | } |
75b49099 | 502 | else { |
c836dc19 | 503 | LOG<<"[expired] "; |
75b49099 BH |
504 | expired=true; |
505 | } | |
afbe2787 | 506 | } |
ac539791 | 507 | |
c836dc19 | 508 | LOG<<endl; |
f4df5e89 | 509 | if(found && !expired) { |
f15a5b2a BH |
510 | if(!giveNegative) |
511 | res=0; | |
75b49099 | 512 | return true; |
f4df5e89 | 513 | } |
75b49099 | 514 | else |
c836dc19 | 515 | LOG<<prefix<<qname<<": cache had only stale entries"<<endl; |
afbe2787 | 516 | } |
f4df5e89 | 517 | |
75b49099 BH |
518 | return false; |
519 | } | |
afbe2787 | 520 | |
7b35aa49 | 521 | bool SyncRes::moreSpecificThan(const string& a, const string &b) |
75b49099 | 522 | { |
7738a23f BH |
523 | static string dot("."); |
524 | int counta=(a!=dot), countb=(b!=dot); | |
afbe2787 | 525 | |
728485ca BH |
526 | for(string::size_type n=0;n<a.size();++n) |
527 | if(a[n]=='.') | |
528 | counta++; | |
529 | for(string::size_type n=0;n<b.size();++n) | |
530 | if(b[n]=='.') | |
531 | countb++; | |
532 | return counta>countb; | |
afbe2787 BH |
533 | } |
534 | ||
d8d0bb8f | 535 | struct speedOrder |
eefd15f9 | 536 | { |
d8d0bb8f | 537 | speedOrder(map<string,double> &speeds) : d_speeds(speeds) {} |
c3d9d009 BH |
538 | bool operator()(const string &a, const string &b) const |
539 | { | |
540 | return d_speeds[a] < d_speeds[b]; | |
c3d9d009 BH |
541 | } |
542 | map<string,double>& d_speeds; | |
543 | }; | |
544 | ||
996c89cc | 545 | inline vector<string> SyncRes::shuffleInSpeedOrder(set<string, CIStringCompare> &nameservers, const string &prefix) |
afbe2787 | 546 | { |
728485ca | 547 | vector<string> rnameservers; |
c3d9d009 BH |
548 | rnameservers.reserve(nameservers.size()); |
549 | map<string,double> speeds; | |
eefd15f9 | 550 | |
7738a23f | 551 | for(set<string, CIStringCompare>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) { |
728485ca | 552 | rnameservers.push_back(*i); |
996c89cc | 553 | speeds[*i]=s_nsSpeeds[*i].get(&d_now); |
eefd15f9 | 554 | } |
728485ca | 555 | random_shuffle(rnameservers.begin(),rnameservers.end()); |
996c89cc BH |
556 | speedOrder so(speeds); |
557 | stable_sort(rnameservers.begin(),rnameservers.end(), so); | |
eefd15f9 | 558 | |
d8d0bb8f BH |
559 | if(s_log) { |
560 | L<<Logger::Warning<<prefix<<"Nameservers: "; | |
561 | for(vector<string>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) { | |
562 | if(i!=rnameservers.begin()) { | |
563 | L<<", "; | |
996c89cc | 564 | if(!((i-rnameservers.begin())%3)) |
d8d0bb8f BH |
565 | L<<endl<<Logger::Warning<<prefix<<" "; |
566 | } | |
49f076e8 | 567 | L<<*i<<"(" << (int)(speeds[*i]/1000.0) <<"ms)"; |
d8d0bb8f BH |
568 | } |
569 | L<<endl; | |
570 | } | |
728485ca | 571 | return rnameservers; |
afbe2787 BH |
572 | } |
573 | ||
7738a23f BH |
574 | struct TCacheComp |
575 | { | |
576 | bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const | |
577 | { | |
705f31ae | 578 | int cmp=Utility::strcasecmp(a.first.c_str(), b.first.c_str()); |
7738a23f BH |
579 | if(cmp < 0) |
580 | return true; | |
581 | if(cmp > 0) | |
582 | return false; | |
583 | ||
584 | return a.second < b.second; | |
585 | } | |
586 | }; | |
587 | ||
588 | ||
589 | ||
ac539791 | 590 | /** returns -1 in case of no results, rcode otherwise */ |
7305df82 | 591 | int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, bool flawedNSSet, const string &qname, const QType &qtype, |
5605c067 BH |
592 | vector<DNSResourceRecord>&ret, |
593 | int depth, set<GetBestNSAnswer>&beenthere) | |
86c152f2 | 594 | { |
ded77b10 BH |
595 | string prefix; |
596 | if(s_log) { | |
597 | prefix=d_prefix; | |
598 | prefix.append(depth, ' '); | |
599 | } | |
86c152f2 | 600 | |
263f6a5a | 601 | LWResult::res_t result; |
86c152f2 | 602 | |
705f31ae | 603 | LOG<<prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact"<<endl; |
afbe2787 BH |
604 | |
605 | for(;;) { // we may get more specific nameservers | |
86c152f2 BH |
606 | result.clear(); |
607 | ||
996c89cc | 608 | vector<string> rnameservers=shuffleInSpeedOrder(nameservers, s_log ? (prefix+qname+": ") : string() ); |
20177d1d | 609 | |
728485ca BH |
610 | for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) { |
611 | if(tns==rnameservers.end()) { | |
1ef00ba1 | 612 | LOG<<prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl; |
7305df82 | 613 | if(auth!="." && flawedNSSet) { |
1ef00ba1 BH |
614 | g_stats.nsSetInvalidations++; |
615 | LOG<<prefix<<qname<<": Invalidating nameservers for level '"<<auth<<"', next query might succeed"<<endl; | |
616 | RC.doWipeCache(auth, QType::NS); | |
617 | } | |
ac539791 | 618 | return -1; |
afbe2787 | 619 | } |
288f4aa9 | 620 | if(qname==*tns && qtype.getCode()==QType::A) { |
c836dc19 | 621 | LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl; |
20177d1d BH |
622 | continue; |
623 | } | |
5605c067 | 624 | |
996c89cc | 625 | typedef vector<ComboAddress> remoteIPs_t; |
5605c067 | 626 | remoteIPs_t remoteIPs; |
bfea0d0b | 627 | remoteIPs_t::const_iterator remoteIP; |
5c633640 | 628 | bool doTCP=false; |
5605c067 BH |
629 | int resolveret; |
630 | ||
263f6a5a | 631 | LWResult lwr; |
5605c067 BH |
632 | if(tns->empty()) { |
633 | LOG<<prefix<<qname<<": Domain is out-of-band"<<endl; | |
263f6a5a BH |
634 | doOOBResolve(qname, qtype, result, depth, lwr.d_rcode); |
635 | lwr.d_tcbit=false; | |
636 | lwr.d_aabit=true; | |
5605c067 BH |
637 | } |
638 | else { | |
705f31ae | 639 | LOG<<prefix<<qname<<": Trying to resolve NS '"<<*tns<<"' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl; |
5605c067 BH |
640 | if(!isCanonical(*tns)) { |
641 | LOG<<prefix<<qname<<": Domain has hardcoded nameserver"<<endl; | |
996c89cc | 642 | remoteIPs.push_back(ComboAddress(*tns, 53)); |
5605c067 BH |
643 | } |
644 | else | |
645 | remoteIPs=getAs(*tns, depth+1, beenthere); | |
646 | ||
647 | if(remoteIPs.empty()) { | |
648 | LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl; | |
7305df82 | 649 | flawedNSSet=true; |
bfea0d0b | 650 | continue; |
5c633640 | 651 | } |
996c89cc BH |
652 | else { |
653 | LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to: "; | |
654 | for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { | |
655 | if(remoteIP != remoteIPs.begin()) | |
656 | LOG<<", "; | |
657 | LOG<<remoteIP->toString(); | |
658 | } | |
659 | LOG<<endl; | |
5605c067 | 660 | |
996c89cc | 661 | } |
263f6a5a | 662 | |
5605c067 | 663 | for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { |
996c89cc | 664 | LOG<<prefix<<qname<<": Trying IP "<< remoteIP->toString() <<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl; |
eb5bae86 | 665 | extern NetmaskGroup* g_dontQuery; |
7becf07f | 666 | |
5605c067 BH |
667 | if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) { |
668 | LOG<<prefix<<qname<<": query throttled "<<endl; | |
669 | s_throttledqueries++; d_throttledqueries++; | |
670 | continue; | |
eb5bae86 BH |
671 | } |
672 | else if(g_dontQuery && g_dontQuery->match(&*remoteIP)) { | |
673 | LOG<<prefix<<qname<<": not sending query to " << remoteIP->toString() << ", blocked by 'dont-query' setting" << endl; | |
674 | continue; | |
5605c067 BH |
675 | } |
676 | else { | |
677 | s_outqueries++; d_outqueries++; | |
678 | TryTCP: | |
679 | if(doTCP) { | |
680 | s_tcpoutqueries++; d_tcpoutqueries++; | |
998a4334 | 681 | } |
bfea0d0b | 682 | |
2188dcc3 | 683 | resolveret=asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, d_doEDNS0, &d_now, &lwr); // <- we go out on the wire! |
5605c067 BH |
684 | if(resolveret != 1) { |
685 | if(resolveret==0) { | |
686 | LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl; | |
687 | d_timeouts++; | |
688 | s_outgoingtimeouts++; | |
689 | } | |
690 | else if(resolveret==-2) { | |
23bfe484 | 691 | LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<", probable error: "<<stringerror()<<endl; |
5605c067 BH |
692 | g_stats.resourceLimits++; |
693 | } | |
c571588b BH |
694 | else { |
695 | s_unreachables++; d_unreachables++; | |
263f6a5a | 696 | LOG<<prefix<<qname<<": error resolving"<< (doTCP ? " over TCP" : "") <<", possible error: "<<strerror(errno)<< endl; |
c571588b | 697 | } |
5605c067 BH |
698 | |
699 | if(resolveret!=-2) { // don't account for resource limits, they are our own fault | |
996c89cc | 700 | s_nsSpeeds[*tns].submit(*remoteIP, 1000000, &d_now); // 1 sec |
c571588b BH |
701 | if(resolveret==-1) |
702 | s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // unreachable | |
703 | else | |
704 | s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 20, 5); // timeout | |
5605c067 BH |
705 | } |
706 | continue; | |
998a4334 | 707 | } |
5605c067 BH |
708 | |
709 | break; // this IP address worked! | |
710 | wasLame:; // well, it didn't | |
996c89cc | 711 | LOG<<prefix<<qname<<": status=NS "<<*tns<<" ("<< remoteIP->toString() <<") is lame for '"<<auth<<"', trying sibling IP or NS"<<endl; |
c571588b | 712 | s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); |
bfea0d0b | 713 | } |
3de83124 | 714 | } |
5605c067 BH |
715 | |
716 | if(remoteIP == remoteIPs.end()) // we tried all IP addresses, none worked | |
717 | continue; | |
718 | ||
263f6a5a | 719 | if(lwr.d_tcbit) { |
5605c067 BH |
720 | if(!doTCP) { |
721 | doTCP=true; | |
722 | LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl; | |
723 | goto TryTCP; | |
724 | } | |
725 | LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl; | |
726 | return RCode::ServFail; | |
d0166f4a | 727 | } |
5605c067 | 728 | |
263f6a5a | 729 | if(lwr.d_rcode==RCode::ServFail) { |
5605c067 BH |
730 | LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling IP or NS"<<endl; |
731 | s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3); | |
732 | continue; | |
733 | } | |
263f6a5a | 734 | LOG<<prefix<<qname<<": Got "<<(unsigned int)lwr.d_result.size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<", in "<<lwr.d_usec/1000<<"ms"<<endl; |
996c89cc BH |
735 | |
736 | /* // for you IPv6 fanatics :-) | |
737 | if(remoteIP->sin4.sin_family==AF_INET6) | |
263f6a5a | 738 | lwr.d_usec/=3; |
996c89cc BH |
739 | */ |
740 | ||
263f6a5a | 741 | s_nsSpeeds[*tns].submit(*remoteIP, lwr.d_usec, &d_now); |
20177d1d | 742 | } |
20177d1d | 743 | |
7738a23f | 744 | typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t; |
1ac4e536 BH |
745 | tcache_t tcache; |
746 | ||
728485ca | 747 | // reap all answers from this packet that are acceptable |
263f6a5a | 748 | for(LWResult::res_t::const_iterator i=lwr.d_result.begin();i != lwr.d_result.end();++i) { |
2188dcc3 BH |
749 | if(i->qtype.getCode() == QType::OPT) { |
750 | LOG<<prefix<<qname<<": skipping OPT answer '"<<i->qname<<"' from '"<<auth<<"' nameservers" <<endl; | |
751 | continue; | |
752 | } | |
5456e605 | 753 | LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? "; |
9bc8c14c BH |
754 | if(i->qtype.getCode()==QType::ANY) { |
755 | LOG<<"NO! - we don't accept 'ANY' data"<<endl; | |
756 | continue; | |
757 | } | |
758 | ||
7738a23f | 759 | if(dottedEndsOn(i->qname, auth)) { |
263f6a5a | 760 | if(lwr.d_aabit && lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && ::arg().contains("delegation-only",auth)) { |
562588a3 | 761 | LOG<<"NO! Is from delegation-only zone"<<endl; |
525b8a7c | 762 | s_nodelegated++; |
562588a3 BH |
763 | return RCode::NXDomain; |
764 | } | |
765 | else { | |
766 | LOG<<"YES!"<<endl; | |
767 | ||
768 | DNSResourceRecord rr=*i; | |
769 | rr.d_place=DNSResourceRecord::ANSWER; | |
61973281 | 770 | |
ab5c053d | 771 | rr.ttl=min(86400*14U, rr.ttl); // limit TTL to two weeks |
40170d23 | 772 | rr.ttl += d_now.tv_sec; |
61973281 | 773 | |
5456e605 | 774 | if(rr.qtype.getCode() == QType::NS) // people fiddle with the case |
61973281 | 775 | rr.content=toLower(rr.content); // this must stay! (the cache can't be case-insensitive on the RHS of records) |
7738a23f | 776 | tcache[make_pair(i->qname,i->qtype)].insert(rr); |
562588a3 BH |
777 | } |
778 | } | |
728485ca | 779 | else |
c836dc19 | 780 | LOG<<"NO!"<<endl; |
86c152f2 | 781 | } |
728485ca BH |
782 | |
783 | // supplant | |
60b859b9 | 784 | for(tcache_t::iterator i=tcache.begin();i!=tcache.end();++i) { |
ab5c053d | 785 | if(i->second.size() > 1) { // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2) |
40170d23 BH |
786 | uint32_t lowestTTL=numeric_limits<uint32_t>::max(); |
787 | for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j) | |
788 | lowestTTL=min(lowestTTL, j->ttl); | |
789 | ||
790 | for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j) | |
791 | ((tcache_t::value_type::second_type::value_type*)&(*j))->ttl=lowestTTL; | |
792 | } | |
793 | ||
263f6a5a | 794 | RC.replace(d_now.tv_sec, i->first.first, i->first.second, i->second, lwr.d_aabit); |
288f4aa9 | 795 | } |
7738a23f | 796 | set<string, CIStringCompare> nsset; |
c836dc19 | 797 | LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl; |
728485ca | 798 | |
20177d1d | 799 | bool done=false, realreferral=false, negindic=false; |
c6644fc5 | 800 | string newauth, soaname, newtarget; |
728485ca | 801 | |
263f6a5a | 802 | for(LWResult::res_t::const_iterator i=lwr.d_result.begin();i!=lwr.d_result.end();++i) { |
7738a23f | 803 | if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && |
263f6a5a | 804 | lwr.d_rcode==RCode::NXDomain) { |
7738a23f | 805 | LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl; |
728485ca | 806 | ret.push_back(*i); |
fd8bc993 | 807 | |
38e22b5a | 808 | NegCacheEntry ne; |
33988bfb BH |
809 | |
810 | ne.d_qname=i->qname; | |
a9af3782 | 811 | ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial |
7738a23f | 812 | ne.d_name=qname; |
ab5c053d | 813 | ne.d_qtype=QType(0); // this encodes 'whole record' |
f40c8545 | 814 | |
739f6278 | 815 | replacing_insert(s_negcache, ne); |
20177d1d | 816 | negindic=true; |
728485ca BH |
817 | } |
818 | else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) { | |
728485ca | 819 | ret.push_back(*i); |
7738a23f | 820 | newtarget=i->content; |
728485ca BH |
821 | } |
822 | // for ANY answers we *must* have an authoritive answer | |
705f31ae | 823 | else if(i->d_place==DNSResourceRecord::ANSWER && !Utility::strcasecmp(i->qname.c_str(),qname.c_str()) && |
5456e605 | 824 | ( (i->qtype==qtype) || |
263f6a5a | 825 | ( qtype==QType(QType::ANY) && lwr.d_aabit))) { |
5456e605 BH |
826 | |
827 | LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl; | |
828 | ||
728485ca BH |
829 | done=true; |
830 | ret.push_back(*i); | |
831 | } | |
7738a23f | 832 | else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { |
728485ca BH |
833 | if(moreSpecificThan(i->qname,auth)) { |
834 | newauth=i->qname; | |
c836dc19 | 835 | LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl; |
728485ca BH |
836 | realreferral=true; |
837 | } | |
7bf26383 | 838 | else |
c836dc19 | 839 | LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl; |
7738a23f | 840 | nsset.insert(i->content); |
728485ca | 841 | } |
d3178e4b | 842 | else if(!done && i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && |
263f6a5a | 843 | lwr.d_rcode==RCode::NoError) { |
7738a23f | 844 | LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl; |
fd8bc993 | 845 | ret.push_back(*i); |
38e22b5a BH |
846 | |
847 | NegCacheEntry ne; | |
33988bfb | 848 | ne.d_qname=i->qname; |
f40c8545 | 849 | ne.d_ttd=d_now.tv_sec + min(s_maxnegttl, i->ttl); |
7738a23f | 850 | ne.d_name=qname; |
f4df5e89 | 851 | ne.d_qtype=qtype; |
f40c8545 | 852 | if(qtype.getCode()) { // prevents us from blacking out a whole domain |
739f6278 | 853 | replacing_insert(s_negcache, ne); |
f40c8545 | 854 | } |
fd8bc993 BH |
855 | negindic=true; |
856 | } | |
86c152f2 | 857 | } |
86c152f2 | 858 | |
728485ca | 859 | if(done){ |
c836dc19 | 860 | LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl; |
728485ca | 861 | return 0; |
ac539791 | 862 | } |
263f6a5a | 863 | if(lwr.d_rcode==RCode::NXDomain) { |
c836dc19 | 864 | LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl; |
728485ca | 865 | return RCode::NXDomain; |
86c152f2 | 866 | } |
c6644fc5 | 867 | if(!newtarget.empty()) { |
c9f52071 BH |
868 | if(iequals(newtarget,qname)) { |
869 | LOG<<prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl; | |
870 | return RCode::ServFail; | |
871 | } | |
872 | if(depth > 10) { | |
873 | LOG<<prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl; | |
874 | return RCode::ServFail; | |
875 | } | |
c836dc19 | 876 | LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl; |
c9f52071 | 877 | |
c6644fc5 | 878 | set<GetBestNSAnswer>beenthere2; |
c9f52071 | 879 | return doResolve(newtarget, qtype, ret, depth + 1, beenthere2); |
c6644fc5 | 880 | } |
263f6a5a | 881 | if(nsset.empty() && !lwr.d_rcode) { |
caa6eefa BH |
882 | LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl; |
883 | return 0; | |
884 | } | |
728485ca | 885 | else if(realreferral) { |
705f31ae | 886 | LOG<<prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl; |
728485ca BH |
887 | auth=newauth; |
888 | nameservers=nsset; | |
889 | break; | |
890 | } | |
5605c067 | 891 | else if(isCanonical(*tns)) { |
263f6a5a | 892 | goto wasLame; |
86c152f2 BH |
893 | } |
894 | } | |
86c152f2 | 895 | } |
ac539791 | 896 | return -1; |
86c152f2 BH |
897 | } |
898 | ||
9bc8c14c BH |
899 | static bool uniqueComp(const DNSResourceRecord& a, const DNSResourceRecord& b) |
900 | { | |
901 | return(a.qtype==b.qtype && a.qname==b.qname && a.content==b.content); | |
902 | } | |
903 | ||
7b35aa49 | 904 | void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret) |
728485ca BH |
905 | { |
906 | for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) // don't add stuff to an NXDOMAIN! | |
907 | if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA)) | |
908 | return; | |
909 | ||
c836dc19 | 910 | // LOG<<qname<<": Adding best authority records from cache"<<endl; |
288f4aa9 | 911 | // addAuthorityRecords(qname,ret,0); |
c836dc19 | 912 | // LOG<<qname<<": Done adding best authority records."<<endl; |
728485ca | 913 | |
c836dc19 | 914 | LOG<<d_prefix<<qname<<": Starting additional processing"<<endl; |
728485ca | 915 | vector<DNSResourceRecord> addit; |
1ac4e536 BH |
916 | static optional<bool> l_doIPv6AP; |
917 | if(!l_doIPv6AP) | |
918 | l_doIPv6AP=::arg().mustDo("aaaa-additional-processing"); | |
919 | ||
728485ca | 920 | for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) |
e4d89ee9 | 921 | if( (k->d_place==DNSResourceRecord::ANSWER && (k->qtype==QType(QType::MX) || k->qtype==QType(QType::SRV))) || |
288f4aa9 | 922 | ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) { |
d8d0bb8f | 923 | LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl; |
e4d89ee9 BH |
924 | set<GetBestNSAnswer> beenthere; |
925 | vector<pair<string::size_type, string::size_type> > fields; | |
926 | vstringtok(fields, k->content, " "); | |
927 | string host; | |
928 | if(k->qtype==QType(QType::MX) && fields.size()==2) | |
929 | host=string(k->content.c_str() + fields[1].first, fields[1].second - fields[1].first); | |
930 | else if(k->qtype==QType(QType::NS)) | |
931 | host=k->content; | |
932 | else if(k->qtype==QType(QType::SRV) && fields.size()==4) | |
933 | host=string(k->content.c_str() + fields[3].first, fields[3].second - fields[3].first); | |
934 | else | |
935 | continue; | |
936 | doResolve(host, QType(QType::A), addit, 1, beenthere); | |
937 | if(*l_doIPv6AP) | |
938 | doResolve(host, QType(QType::AAAA), addit, 1, beenthere); | |
728485ca BH |
939 | } |
940 | ||
9bc8c14c BH |
941 | sort(addit.begin(), addit.end()); |
942 | addit.erase(unique(addit.begin(), addit.end(), uniqueComp), addit.end()); | |
728485ca | 943 | for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) { |
288f4aa9 BH |
944 | if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) { |
945 | k->d_place=DNSResourceRecord::ADDITIONAL; | |
946 | ret.push_back(*k); | |
947 | } | |
728485ca | 948 | } |
c836dc19 | 949 | LOG<<d_prefix<<qname<<": Done with additional processing"<<endl; |
728485ca BH |
950 | } |
951 | ||
7b35aa49 | 952 | void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth) |
86c152f2 | 953 | { |
288f4aa9 | 954 | set<DNSResourceRecord> bestns; |
7305df82 BH |
955 | set<GetBestNSAnswer> beenthere; |
956 | bool dontcare; | |
957 | getBestNSFromCache(qname, bestns, &dontcare, depth, beenthere); | |
36c5ee42 | 958 | |
288f4aa9 BH |
959 | for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) { |
960 | DNSResourceRecord ns=*k; | |
961 | ns.d_place=DNSResourceRecord::AUTHORITY; | |
d6d5dea7 | 962 | ns.ttl-=d_now.tv_sec; |
288f4aa9 | 963 | ret.push_back(ns); |
86c152f2 BH |
964 | } |
965 | } |