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