]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/syncres.cc
this fixes prio in MX and in SRV records from being zero on sun-endian platforms
[thirdparty/pdns.git] / pdns / syncres.cc
CommitLineData
86c152f2
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
1d5b3ce6 3 Copyright (C) 2003 - 2006 PowerDNS.COM BV
86c152f2
BH
4
5 This program is free software; you can redistribute it and/or modify
36c5ee42
BH
6 it under the terms of the GNU General Public License version 2 as published
7 by the Free Software Foundation
86c152f2
BH
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
06bd9ccf 16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
86c152f2 17*/
caa6eefa 18
6d78ece6 19
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
38extern MemRecursorCache RC;
86c152f2 39
9fdf67d5
BH
40SyncRes::negcache_t SyncRes::s_negcache;
41SyncRes::nsspeeds_t SyncRes::s_nsSpeeds;
a9af3782 42unsigned int SyncRes::s_maxnegttl;
7b35aa49 43unsigned int SyncRes::s_queries;
7becf07f 44unsigned int SyncRes::s_outgoingtimeouts;
7b35aa49 45unsigned int SyncRes::s_outqueries;
5c633640 46unsigned int SyncRes::s_tcpoutqueries;
3de83124 47unsigned int SyncRes::s_throttledqueries;
525b8a7c 48unsigned int SyncRes::s_nodelegated;
a9af3782 49string SyncRes::s_serverID;
7b35aa49 50bool SyncRes::s_log;
c836dc19 51
ea634573 52#define LOG if(s_log) L<<Logger::Warning
728485ca 53
42724edf 54Throttle<tuple<uint32_t,string,uint16_t> > SyncRes::s_throttle;
3de83124 55
728485ca 56/** everything begins here - this is the entry point just after receiving a packet */
a9af3782 57int SyncRes::beginResolve(const string &qname, const QType &qtype, uint16_t qclass, vector<DNSResourceRecord>&ret)
728485ca 58{
c836dc19 59 s_queries++;
31ad43ab
BH
60 if( (qtype.getCode()==QType::PTR && !strcasecmp(qname.c_str(), "1.0.0.127.in-addr.arpa.")) ||
61 (qtype.getCode()==QType::A && qname.length()==10 && !strcasecmp(qname.c_str(), "localhost."))) {
62 ret.clear();
63 DNSResourceRecord rr;
64 rr.qname=qname;
65 rr.qtype=qtype;
a9af3782 66 rr.qclass=1;
31ad43ab
BH
67 rr.ttl=86400;
68 if(qtype.getCode()==QType::PTR)
69 rr.content="localhost.";
70 else
71 rr.content="127.0.0.1";
72 ret.push_back(rr);
73 return 0;
74 }
75
a9af3782
BH
76 if(qclass==3 && qtype.getCode()==QType::TXT &&
77 (!strcasecmp(qname.c_str(), "version.bind.") || !strcasecmp(qname.c_str(), "id.server.") || !strcasecmp(qname.c_str(), "version.pdns.") )
78 ) {
79 ret.clear();
80 DNSResourceRecord rr;
81 rr.qname=qname;
82 rr.qtype=qtype;
83 rr.qclass=qclass;
84 rr.ttl=86400;
85 if(!strcasecmp(qname.c_str(),"version.bind.") || !strcasecmp(qname.c_str(),"version.pdns."))
86 rr.content="\""+::arg()["version-string"]+"\"";
87 else
88 rr.content="\""+s_serverID+"\"";
89 ret.push_back(rr);
90 return 0;
91 }
9a97cc5c
BH
92
93 if(qclass!=1)
94 return -1;
95
31ad43ab 96 set<GetBestNSAnswer> beenthere;
809fe23f 97 int res=doResolve(qname, qtype, ret, 0, beenthere);
728485ca
BH
98 if(!res)
99 addCruft(qname, ret);
728485ca
BH
100 return res;
101}
afbe2787 102
7b35aa49 103int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
afbe2787 104{
ded77b10
BH
105 string prefix;
106 if(s_log) {
107 prefix=d_prefix;
108 prefix.append(depth, ' ');
109 }
728485ca 110
f4df5e89 111 int res=0;
c836dc19
BH
112 if(!(d_nocache && qtype.getCode()==QType::NS && qname.empty())) {
113 if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
114 return res;
115
116 if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
117 return res;
118 }
afbe2787 119
c836dc19
BH
120 if(d_cacheonly)
121 return 0;
afbe2787 122
cd553f6c
BH
123 // place for lookaside cache
124
c836dc19 125 LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
728485ca
BH
126
127 string subdomain(qname);
128
7738a23f 129 set<string, CIStringCompare> nsset;
bdf40704
BH
130 for(int tries=0;tries<2 && nsset.empty();++tries) {
131 subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); // pass beenthere to both occasions
132
133 if(nsset.empty()) { // must've lost root records
134 LOG<<prefix<<qname<<": our root expired, repriming from hints and retrying"<<endl;
135 primeHints();
136 }
137 }
138
7bf26383 139 if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere)))
728485ca
BH
140 return 0;
141
c836dc19 142 LOG<<prefix<<qname<<": failed"<<endl;
20177d1d 143 return res<0 ? RCode::ServFail : res;
afbe2787
BH
144}
145
42724edf 146vector<uint32_t> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
75b49099 147{
bfea0d0b
BH
148 typedef vector<DNSResourceRecord> res_t;
149 res_t res;
75b49099 150
42724edf 151 vector<uint32_t> ret;
75b49099 152
bfea0d0b 153 if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) {
f4df5e89 154 for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) {
42724edf
BH
155 if(i->qtype.getCode()==QType::A) {
156 uint32_t ip;
157 if(IpToU32(i->content, &ip))
158 ret.push_back(ntohl(ip));
159 }
f4df5e89 160 }
bfea0d0b
BH
161 }
162 if(ret.size() > 1)
163 random_shuffle(ret.begin(), ret.end());
728485ca 164 return ret;
75b49099
BH
165}
166
7b35aa49 167void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere)
86c152f2 168{
ded77b10
BH
169 string prefix, subdomain(qname);
170 if(s_log) {
171 prefix=d_prefix;
172 prefix.append(depth, ' ');
173 }
75b49099
BH
174 bestns.clear();
175
75b49099 176 do {
c836dc19 177 LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
7738a23f
BH
178 set<DNSResourceRecord> ns;
179 if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) {
7bf26383 180 for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
d6d5dea7 181 if(k->ttl > (unsigned int)d_now.tv_sec ) {
7bf26383 182 set<DNSResourceRecord>aset;
b0d4fb45 183
eab7dbda 184 DNSResourceRecord rr=*k;
7738a23f
BH
185 rr.content=k->content;
186 if(!dottedEndsOn(rr.content,subdomain) || RC.get(d_now.tv_sec, rr.content ,QType(QType::A),&aset) > 5) {
b0d4fb45 187 bestns.insert(rr);
9fdf67d5 188
b0d4fb45 189 LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<rr.content<<"'"<<endl;
7738a23f 190 LOG<<prefix<<qname<<": within bailiwick: "<<dottedEndsOn(rr.content, subdomain);
9fdf67d5
BH
191 if(!aset.empty()) {
192 LOG<<", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->ttl- d_now.tv_sec ))<<endl;
193 }
194 else {
195 LOG<<", not in cache"<<endl;
196 }
0d8612f2 197 }
20177d1d 198 else
7738a23f 199 LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
ac539791 200 }
afbe2787 201 }
75b49099 202 if(!bestns.empty()) {
7bf26383 203 GetBestNSAnswer answer;
7738a23f 204 answer.qname=qname; answer.bestns=bestns;
7bf26383 205 if(beenthere.count(answer)) {
c836dc19 206 LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
7b35aa49 207 for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
c836dc19 208 LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<j->bestns.size()<<")"<<endl;
7bf26383
BH
209 bestns.clear();
210 }
211 else {
212 beenthere.insert(answer);
c836dc19 213 LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
7bf26383
BH
214 return;
215 }
75b49099 216 }
afbe2787 217 }
7738a23f 218 }while(chopOffDotted(subdomain));
75b49099
BH
219}
220
288f4aa9 221
7bf26383 222/** doesn't actually do the work, leaves that to getBestNSFromCache */
7738a23f 223string SyncRes::getBestNSNamesFromCache(const string &qname,set<string, CIStringCompare>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
75b49099
BH
224{
225 string subdomain(qname);
226
227 set<DNSResourceRecord> bestns;
7bf26383 228 getBestNSFromCache(subdomain, bestns, depth, beenthere);
75b49099
BH
229
230 for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
231 nsset.insert(k->content);
232 subdomain=k->qname;
86c152f2 233 }
75b49099 234 return subdomain;
afbe2787
BH
235}
236
7b35aa49 237bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
afbe2787 238{
ded77b10
BH
239 string prefix;
240 if(s_log) {
241 prefix=d_prefix;
242 prefix.append(depth, ' ');
243 }
36f5e3db 244
c6644fc5 245 if(depth>10) {
c836dc19 246 LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl;
c6644fc5
BH
247 res=RCode::ServFail;
248 return true;
249 }
36f5e3db 250
7738a23f 251 LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<(qname+"|CNAME")<<"'"<<endl;
7bf26383 252 set<DNSResourceRecord> cset;
d6d5dea7 253 if(RC.get(d_now.tv_sec, qname,QType(QType::CNAME),&cset) > 0) {
36c5ee42 254
7bf26383 255 for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
d6d5dea7 256 if(j->ttl>(unsigned int) d_now.tv_sec) {
7738a23f 257 LOG<<prefix<<qname<<": Found cache CNAME hit for '"<< (qname+"|CNAME") <<"' to '"<<j->content<<"'"<<endl;
ac539791 258 DNSResourceRecord rr=*j;
d6d5dea7 259 rr.ttl-=d_now.tv_sec;
ac539791 260 ret.push_back(rr);
b0d4fb45 261 if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME!
7bf26383 262 set<GetBestNSAnswer>beenthere;
7738a23f 263 res=doResolve(j->content, qtype, ret, depth+1, beenthere);
7bf26383 264 }
b0d4fb45
BH
265 else
266 res=0;
75b49099 267 return true;
ac539791
BH
268 }
269 }
afbe2787 270 }
7738a23f 271 LOG<<prefix<<qname<<": No CNAME cache hit of '"<< (qname+"|CNAME") <<"' found"<<endl;
75b49099
BH
272 return false;
273}
274
7b35aa49 275bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
75b49099 276{
fd8bc993 277 bool giveNegative=false;
ded77b10 278
be718669 279 string prefix;
ded77b10
BH
280 if(s_log) {
281 prefix=d_prefix;
282 prefix.append(depth, ' ');
283 }
afbe2787 284
288f4aa9
BH
285 string sqname(qname);
286 QType sqt(qtype);
092f210a 287 uint32_t sttl=0;
f4df5e89
BH
288 // cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"'\n";
289
7738a23f 290 pair<negcache_t::const_iterator, negcache_t::const_iterator> range=s_negcache.equal_range(tie(qname));
f4df5e89
BH
291 negcache_t::iterator ni;
292 for(ni=range.first; ni != range.second; ni++) {
293 // we have something
294 if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) {
295 res=0;
33988bfb
BH
296 if((uint32_t)d_now.tv_sec < ni->d_ttd) {
297 sttl=ni->d_ttd - d_now.tv_sec;
f4df5e89 298 if(ni->d_qtype.getCode()) {
f15a5b2a 299 LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
f4df5e89
BH
300 res = RCode::NoError;
301 }
302 else {
f15a5b2a 303 LOG<<prefix<<qname<<": Entire record '"<<qname<<"', is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
f4df5e89
BH
304 res= RCode::NXDomain;
305 }
38e22b5a 306 giveNegative=true;
33988bfb 307 sqname=ni->d_qname;
f4df5e89
BH
308 sqt=QType::SOA;
309 break;
38e22b5a
BH
310 }
311 else {
7738a23f 312 LOG<<prefix<<qname<<": Entire record '"<<qname<<"' was negatively cached, but entry expired"<<endl;
38e22b5a 313 }
fd8bc993
BH
314 }
315 }
20177d1d 316
7bf26383 317 set<DNSResourceRecord> cset;
75b49099 318 bool found=false, expired=false;
be718669
BH
319
320 if(RC.get(d_now.tv_sec, sqname,sqt, &cset) > 0) {
f15a5b2a 321 LOG<<prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ";
7bf26383 322 for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
c836dc19 323 LOG<<j->content;
d6d5dea7 324 if(j->ttl>(unsigned int) d_now.tv_sec) {
ac539791 325 DNSResourceRecord rr=*j;
d6d5dea7 326 rr.ttl-=d_now.tv_sec;
38e22b5a 327 if(giveNegative) {
20177d1d 328 rr.d_place=DNSResourceRecord::AUTHORITY;
38e22b5a
BH
329 rr.ttl=sttl;
330 }
ac539791 331 ret.push_back(rr);
c836dc19 332 LOG<<"[ttl="<<rr.ttl<<"] ";
75b49099 333 found=true;
ac539791 334 }
75b49099 335 else {
c836dc19 336 LOG<<"[expired] ";
75b49099
BH
337 expired=true;
338 }
afbe2787 339 }
ac539791 340
c836dc19 341 LOG<<endl;
f4df5e89 342 if(found && !expired) {
f15a5b2a
BH
343 if(!giveNegative)
344 res=0;
75b49099 345 return true;
f4df5e89 346 }
75b49099 347 else
c836dc19 348 LOG<<prefix<<qname<<": cache had only stale entries"<<endl;
afbe2787 349 }
f4df5e89 350
75b49099
BH
351 return false;
352}
afbe2787 353
7b35aa49 354bool SyncRes::moreSpecificThan(const string& a, const string &b)
75b49099 355{
7738a23f
BH
356 static string dot(".");
357 int counta=(a!=dot), countb=(b!=dot);
afbe2787 358
728485ca
BH
359 for(string::size_type n=0;n<a.size();++n)
360 if(a[n]=='.')
361 counta++;
362 for(string::size_type n=0;n<b.size();++n)
363 if(b[n]=='.')
364 countb++;
365 return counta>countb;
afbe2787
BH
366}
367
d8d0bb8f 368struct speedOrder
eefd15f9 369{
d8d0bb8f 370 speedOrder(map<string,double> &speeds) : d_speeds(speeds) {}
c3d9d009
BH
371 bool operator()(const string &a, const string &b) const
372 {
373 return d_speeds[a] < d_speeds[b];
c3d9d009
BH
374 }
375 map<string,double>& d_speeds;
376};
377
7738a23f 378inline vector<string> SyncRes::shuffle(set<string, CIStringCompare> &nameservers, const string &prefix)
afbe2787 379{
728485ca 380 vector<string> rnameservers;
c3d9d009
BH
381 rnameservers.reserve(nameservers.size());
382 map<string,double> speeds;
eefd15f9 383
7738a23f 384 for(set<string, CIStringCompare>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) {
728485ca 385 rnameservers.push_back(*i);
7738a23f 386 DecayingEwma& temp=s_nsSpeeds[*i];
d6d5dea7 387 speeds[*i]=temp.get(&d_now);
eefd15f9 388 }
728485ca 389 random_shuffle(rnameservers.begin(),rnameservers.end());
ded77b10 390 stable_sort(rnameservers.begin(),rnameservers.end(), speedOrder(speeds));
eefd15f9 391
d8d0bb8f
BH
392 if(s_log) {
393 L<<Logger::Warning<<prefix<<"Nameservers: ";
394 for(vector<string>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) {
395 if(i!=rnameservers.begin()) {
396 L<<", ";
397 if(!((i-rnameservers.begin())%4))
398 L<<endl<<Logger::Warning<<prefix<<" ";
399 }
49f076e8 400 L<<*i<<"(" << (int)(speeds[*i]/1000.0) <<"ms)";
d8d0bb8f
BH
401 }
402 L<<endl;
403 }
728485ca 404 return rnameservers;
afbe2787
BH
405}
406
7738a23f
BH
407struct TCacheComp
408{
409 bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const
410 {
411 int cmp=strcasecmp(a.first.c_str(), b.first.c_str());
412 if(cmp < 0)
413 return true;
414 if(cmp > 0)
415 return false;
416
417 return a.second < b.second;
418 }
419};
420
421
422
ac539791 423/** returns -1 in case of no results, rcode otherwise */
7738a23f 424int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,
7bf26383 425 int depth, set<GetBestNSAnswer>&beenthere)
86c152f2 426{
ded77b10
BH
427 string prefix;
428 if(s_log) {
429 prefix=d_prefix;
430 prefix.append(depth, ' ');
431 }
86c152f2 432
86c152f2 433 LWRes::res_t result;
86c152f2 434
c836dc19 435 LOG<<prefix<<qname<<": Cache consultations done, have "<<nameservers.size()<<" NS to contact"<<endl;
afbe2787
BH
436
437 for(;;) { // we may get more specific nameservers
86c152f2
BH
438 result.clear();
439
ded77b10 440 vector<string> rnameservers=shuffle(nameservers, s_log ? (prefix+qname+": ") : string() );
20177d1d 441
728485ca
BH
442 for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) {
443 if(tns==rnameservers.end()) {
c836dc19 444 LOG<<prefix<<qname<<": Failed to resolve via any of the "<<rnameservers.size()<<" offered NS"<<endl;
ac539791 445 return -1;
afbe2787 446 }
288f4aa9 447 if(qname==*tns && qtype.getCode()==QType::A) {
c836dc19 448 LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
20177d1d
BH
449 continue;
450 }
c836dc19 451 LOG<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
42724edf 452 typedef vector<uint32_t> remoteIPs_t;
bfea0d0b
BH
453 remoteIPs_t remoteIPs=getAs(*tns, depth+1, beenthere);
454 if(remoteIPs.empty()) {
c836dc19 455 LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
afbe2787
BH
456 continue;
457 }
bfea0d0b 458 remoteIPs_t::const_iterator remoteIP;
5c633640 459 bool doTCP=false;
bfea0d0b 460 for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
42724edf 461 LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to "<<U32ToIP(*remoteIP)<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
bfea0d0b 462
cd7bf56b 463 if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) {
bfea0d0b
BH
464 LOG<<prefix<<qname<<": query throttled "<<endl;
465 s_throttledqueries++;
466 d_throttledqueries++;
467 continue;
5c633640 468 }
bfea0d0b
BH
469 else {
470 s_outqueries++;
471 d_outqueries++;
472 TryTCP:
473 if(doTCP) {
474 s_tcpoutqueries++;
475 d_tcpoutqueries++;
7becf07f 476 }
7becf07f 477
83206e14 478 int ret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now); // <- we go out on the wire!
bfea0d0b
BH
479 if(ret != 1) {
480 if(ret==0) {
50c81227 481 LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl;
bfea0d0b
BH
482 d_timeouts++;
483 s_outgoingtimeouts++;
484 }
998a4334
BH
485 else if(ret==-2) {
486 LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl;
487 g_stats.resourceLimits++;
488 }
bfea0d0b 489 else
50c81227 490 LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl;
bfea0d0b 491
998a4334
BH
492 if(ret!=-2) { // don't account for resource limits, they are our own fault
493 s_nsSpeeds[*tns].submit(1000000, &d_now); // 1 sec
494 s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),20,5);
495 }
bfea0d0b
BH
496 continue;
497 }
5e3da48d 498
bfea0d0b 499 break; // it did work!
3de83124
BH
500 }
501 }
20177d1d 502
bfea0d0b
BH
503 if(remoteIP == remoteIPs.end()) // we tried all IP addresses, none worked
504 continue;
505
eefd15f9 506 result=d_lwr.result();
66ab6a63 507
d0166f4a
BH
508 if(d_lwr.d_tcbit) {
509 if(!doTCP) {
510 doTCP=true;
511 LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
512 goto TryTCP;
513 }
514 LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
515 return RCode::ServFail;
516 }
517
288f4aa9 518 if(d_lwr.d_rcode==RCode::ServFail) {
c836dc19 519 LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling NS"<<endl;
cd7bf56b 520 s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3);
20177d1d
BH
521 continue;
522 }
42724edf 523 LOG<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<U32ToIP(*remoteIP)<<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl;
7738a23f 524 s_nsSpeeds[*tns].submit(d_lwr.d_usec, &d_now);
20177d1d 525
7738a23f 526 typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
1ac4e536
BH
527 tcache_t tcache;
528
728485ca
BH
529 // reap all answers from this packet that are acceptable
530 for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
5456e605 531 LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
6cf876a2 532
7738a23f 533 if(dottedEndsOn(i->qname, auth)) {
84de7c8a 534 if(d_lwr.d_aabit && d_lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && ::arg().contains("delegation-only",auth)) {
562588a3 535 LOG<<"NO! Is from delegation-only zone"<<endl;
525b8a7c 536 s_nodelegated++;
562588a3
BH
537 return RCode::NXDomain;
538 }
539 else {
540 LOG<<"YES!"<<endl;
541
542 DNSResourceRecord rr=*i;
543 rr.d_place=DNSResourceRecord::ANSWER;
d6d5dea7 544 rr.ttl+=d_now.tv_sec;
5456e605 545 if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
7738a23f
BH
546 rr.content=toLower(rr.content); // this must stay!
547 tcache[make_pair(i->qname,i->qtype)].insert(rr);
562588a3
BH
548 }
549 }
728485ca 550 else
c836dc19 551 LOG<<"NO!"<<endl;
86c152f2 552 }
728485ca
BH
553
554 // supplant
1ac4e536 555 for(tcache_t::const_iterator i=tcache.begin();i!=tcache.end();++i) {
5456e605 556 RC.replace(i->first.first, i->first.second, i->second);
288f4aa9 557 }
7738a23f 558 set<string, CIStringCompare> nsset;
c836dc19 559 LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
728485ca 560
20177d1d 561 bool done=false, realreferral=false, negindic=false;
c6644fc5 562 string newauth, soaname, newtarget;
728485ca
BH
563
564 for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
7738a23f 565 if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA &&
fd8bc993 566 d_lwr.d_rcode==RCode::NXDomain) {
7738a23f 567 LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl;
728485ca 568 ret.push_back(*i);
fd8bc993 569
38e22b5a 570 NegCacheEntry ne;
33988bfb
BH
571
572 ne.d_qname=i->qname;
a9af3782 573 ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial
7738a23f 574 ne.d_name=qname;
f4df5e89 575 ne.d_qtype=QType(0);
33988bfb 576 s_negcache.insert(ne);
20177d1d 577 negindic=true;
728485ca
BH
578 }
579 else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
728485ca 580 ret.push_back(*i);
7738a23f 581 newtarget=i->content;
728485ca
BH
582 }
583 // for ANY answers we *must* have an authoritive answer
e5bad90b 584 else if(i->d_place==DNSResourceRecord::ANSWER && !strcasecmp(i->qname.c_str(),qname.c_str()) &&
5456e605
BH
585 ( (i->qtype==qtype) ||
586 ( qtype==QType(QType::ANY) && d_lwr.d_aabit))) {
587
588 LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
589
6cf876a2 590
728485ca
BH
591 done=true;
592 ret.push_back(*i);
593 }
7738a23f 594 else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) {
728485ca
BH
595 if(moreSpecificThan(i->qname,auth)) {
596 newauth=i->qname;
c836dc19 597 LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
728485ca
BH
598 realreferral=true;
599 }
7bf26383 600 else
c836dc19 601 LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
7738a23f 602 nsset.insert(i->content);
728485ca 603 }
7738a23f 604 else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA &&
fd8bc993 605 d_lwr.d_rcode==RCode::NoError) {
7738a23f 606 LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl;
fd8bc993 607 ret.push_back(*i);
38e22b5a
BH
608
609 NegCacheEntry ne;
33988bfb
BH
610 ne.d_qname=i->qname;
611 ne.d_ttd=d_now.tv_sec + min(3600U,i->ttl);
7738a23f 612 ne.d_name=qname;
f4df5e89 613 ne.d_qtype=qtype;
be718669
BH
614 if(qtype.getCode()) // prevents us from blacking out a whole domain
615 s_negcache.insert(ne);
fd8bc993
BH
616 negindic=true;
617 }
86c152f2 618 }
86c152f2 619
728485ca 620 if(done){
c836dc19 621 LOG<<prefix<<qname<<": status=got results, this level of recursion done"<<endl;
728485ca 622 return 0;
ac539791 623 }
288f4aa9 624 if(d_lwr.d_rcode==RCode::NXDomain) {
c836dc19 625 LOG<<prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
728485ca 626 return RCode::NXDomain;
86c152f2 627 }
c6644fc5 628 if(!newtarget.empty()) {
c836dc19 629 LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl;
c6644fc5
BH
630 set<GetBestNSAnswer>beenthere2;
631 return doResolve(newtarget, qtype, ret,0,beenthere2);
632 }
caa6eefa
BH
633 if(nsset.empty() && !d_lwr.d_rcode) {
634 LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
635 return 0;
636 }
728485ca 637 else if(realreferral) {
c836dc19 638 LOG<<prefix<<qname<<": status=did not resolve, got "<<nsset.size()<<" NS, looping to them"<<endl;
728485ca
BH
639 auth=newauth;
640 nameservers=nsset;
641 break;
642 }
643 else {
c836dc19 644 LOG<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl;
cd7bf56b 645 s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()),60,0);
86c152f2
BH
646 }
647 }
86c152f2 648 }
ac539791 649 return -1;
86c152f2
BH
650}
651
7b35aa49 652void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret)
728485ca
BH
653{
654 for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) // don't add stuff to an NXDOMAIN!
655 if(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::SOA))
656 return;
657
c836dc19 658 // LOG<<qname<<": Adding best authority records from cache"<<endl;
288f4aa9 659 // addAuthorityRecords(qname,ret,0);
c836dc19 660 // LOG<<qname<<": Done adding best authority records."<<endl;
728485ca 661
c836dc19 662 LOG<<d_prefix<<qname<<": Starting additional processing"<<endl;
728485ca 663 vector<DNSResourceRecord> addit;
1ac4e536
BH
664 static optional<bool> l_doIPv6AP;
665 if(!l_doIPv6AP)
666 l_doIPv6AP=::arg().mustDo("aaaa-additional-processing");
667
728485ca
BH
668 for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)
669 if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) ||
288f4aa9 670 ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
d8d0bb8f 671 LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl;
7bf26383 672 set<GetBestNSAnswer>beenthere;
ea634573 673 if(k->qtype==QType(QType::MX)) {
50c79a76 674 pair<string,string> prioServer=splitField(k->content,' ');
01608dca
BH
675 doResolve(prioServer.second, QType(QType::A), addit, 1, beenthere);
676 if(*l_doIPv6AP)
677 doResolve(prioServer.second, QType(QType::AAAA), addit, 1, beenthere);
ea634573 678 }
e29f6ed0 679 else {
ea634573 680 doResolve(k->content,QType(QType::A),addit,1,beenthere);
1ac4e536 681 if(*l_doIPv6AP)
e29f6ed0
BH
682 doResolve(k->content,QType(QType::AAAA),addit,1,beenthere);
683 }
728485ca
BH
684 }
685
686 for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
288f4aa9
BH
687 if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) {
688 k->d_place=DNSResourceRecord::ADDITIONAL;
689 ret.push_back(*k);
690 }
728485ca 691 }
c836dc19 692 LOG<<d_prefix<<qname<<": Done with additional processing"<<endl;
728485ca
BH
693}
694
7b35aa49 695void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
86c152f2 696{
288f4aa9
BH
697 set<DNSResourceRecord> bestns;
698 set<GetBestNSAnswer>beenthere;
699 getBestNSFromCache(qname, bestns, depth,beenthere);
36c5ee42 700
288f4aa9
BH
701 for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
702 DNSResourceRecord ns=*k;
703 ns.d_place=DNSResourceRecord::AUTHORITY;
d6d5dea7 704 ns.ttl-=d_now.tv_sec;
288f4aa9 705 ret.push_back(ns);
86c152f2
BH
706 }
707}