]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/syncres.cc
silence silly warning
[thirdparty/pdns.git] / pdns / syncres.cc
CommitLineData
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
39extern MemRecursorCache RC;
86c152f2 40
9fdf67d5
BH
41SyncRes::negcache_t SyncRes::s_negcache;
42SyncRes::nsspeeds_t SyncRes::s_nsSpeeds;
a9af3782 43unsigned int SyncRes::s_maxnegttl;
7b35aa49 44unsigned int SyncRes::s_queries;
7becf07f 45unsigned int SyncRes::s_outgoingtimeouts;
7b35aa49 46unsigned int SyncRes::s_outqueries;
5c633640 47unsigned int SyncRes::s_tcpoutqueries;
3de83124 48unsigned int SyncRes::s_throttledqueries;
525b8a7c 49unsigned int SyncRes::s_nodelegated;
c571588b 50unsigned int SyncRes::s_unreachables;
996c89cc 51bool SyncRes::s_doIPv6;
c571588b 52
5605c067 53SyncRes::domainmap_t SyncRes::s_domainmap;
a9af3782 54string SyncRes::s_serverID;
7b35aa49 55bool SyncRes::s_log;
c836dc19 56
ea634573 57#define LOG if(s_log) L<<Logger::Warning
728485ca 58
996c89cc 59SyncRes::throttle_t SyncRes::s_throttle;
3de83124 60
728485ca 61/** everything begins here - this is the entry point just after receiving a packet */
a9af3782 62int 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
111bool 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 190int 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*/
263vector<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 300void 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
360SyncRes::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 372string 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 400bool 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 438bool 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 517bool 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 531struct 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 541inline 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
570struct 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
584static 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 592int 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
906static 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 911void 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 957void 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}