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