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