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