]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ueberbackend.cc
rec: Don't account chained queries more than once
[thirdparty/pdns.git] / pdns / ueberbackend.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <boost/archive/binary_iarchive.hpp>
26 #include <boost/archive/binary_oarchive.hpp>
27
28 #include "auth-querycache.hh"
29 #include "utility.hh"
30
31
32 #include <dlfcn.h>
33 #include <string>
34 #include <map>
35 #include <sys/types.h>
36 #include <sstream>
37 #include <errno.h>
38 #include <iostream>
39 #include <sstream>
40 #include <functional>
41
42 #include "dns.hh"
43 #include "arguments.hh"
44 #include "dnsbackend.hh"
45 #include "ueberbackend.hh"
46 #include "dnspacket.hh"
47 #include "logger.hh"
48 #include "statbag.hh"
49
50 extern StatBag S;
51
52 vector<UeberBackend *>UeberBackend::instances;
53 pthread_mutex_t UeberBackend::instances_lock=PTHREAD_MUTEX_INITIALIZER;
54
55 // initially we are blocked
56 bool UeberBackend::d_go=false;
57 pthread_mutex_t UeberBackend::d_mut = PTHREAD_MUTEX_INITIALIZER;
58 pthread_cond_t UeberBackend::d_cond = PTHREAD_COND_INITIALIZER;
59
60 //! Loads a module and reports it to all UeberBackend threads
61 bool UeberBackend::loadmodule(const string &name)
62 {
63 L<<Logger::Warning <<"Loading '"<<name<<"'" << endl;
64
65 void *dlib=dlopen(name.c_str(), RTLD_NOW);
66
67 if(dlib == NULL) {
68 L<<Logger::Error <<"Unable to load module '"<<name<<"': "<<dlerror() << endl;
69 return false;
70 }
71
72 return true;
73 }
74
75 void UeberBackend::go(void)
76 {
77 pthread_mutex_lock(&d_mut);
78 d_go=true;
79 pthread_cond_broadcast(&d_cond);
80 pthread_mutex_unlock(&d_mut);
81 }
82
83 bool UeberBackend::getDomainInfo(const DNSName &domain, DomainInfo &di)
84 {
85 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
86 if((*i)->getDomainInfo(domain, di))
87 return true;
88 return false;
89 }
90
91 bool UeberBackend::createDomain(const DNSName &domain)
92 {
93 for(DNSBackend* mydb : backends) {
94 if(mydb->createDomain(domain)) {
95 return true;
96 }
97 }
98 return false;
99 }
100
101 bool UeberBackend::doesDNSSEC()
102 {
103 for(auto* db : backends) {
104 if(db->doesDNSSEC())
105 return true;
106 }
107 return false;
108 }
109
110 bool UeberBackend::addDomainKey(const DNSName& name, const DNSBackend::KeyData& key, int64_t& id)
111 {
112 id = -1;
113 for(DNSBackend* db : backends) {
114 if(db->addDomainKey(name, key, id))
115 return true;
116 }
117 return false;
118 }
119 bool UeberBackend::getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys)
120 {
121 for(DNSBackend* db : backends) {
122 if(db->getDomainKeys(name, keys))
123 return true;
124 }
125 return false;
126 }
127
128 bool UeberBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta)
129 {
130 for(DNSBackend* db : backends) {
131 if(db->getAllDomainMetadata(name, meta))
132 return true;
133 }
134 return false;
135 }
136
137 bool UeberBackend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
138 {
139 for(DNSBackend* db : backends) {
140 if(db->getDomainMetadata(name, kind, meta))
141 return true;
142 }
143 return false;
144 }
145
146 bool UeberBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
147 {
148 for(DNSBackend* db : backends) {
149 if(db->setDomainMetadata(name, kind, meta))
150 return true;
151 }
152 return false;
153 }
154
155 bool UeberBackend::activateDomainKey(const DNSName& name, unsigned int id)
156 {
157 for(DNSBackend* db : backends) {
158 if(db->activateDomainKey(name, id))
159 return true;
160 }
161 return false;
162 }
163
164 bool UeberBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
165 {
166 for(DNSBackend* db : backends) {
167 if(db->deactivateDomainKey(name, id))
168 return true;
169 }
170 return false;
171 }
172
173 bool UeberBackend::removeDomainKey(const DNSName& name, unsigned int id)
174 {
175 for(DNSBackend* db : backends) {
176 if(db->removeDomainKey(name, id))
177 return true;
178 }
179 return false;
180 }
181
182
183 bool UeberBackend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
184 {
185 for(DNSBackend* db : backends) {
186 if(db->getTSIGKey(name, algorithm, content))
187 return true;
188 }
189 return false;
190 }
191
192
193 bool UeberBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
194 {
195 for(DNSBackend* db : backends) {
196 if(db->setTSIGKey(name, algorithm, content))
197 return true;
198 }
199 return false;
200 }
201
202 bool UeberBackend::deleteTSIGKey(const DNSName& name)
203 {
204 for(DNSBackend* db : backends) {
205 if(db->deleteTSIGKey(name))
206 return true;
207 }
208 return false;
209 }
210
211 bool UeberBackend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
212 {
213 for(DNSBackend* db : backends) {
214 db->getTSIGKeys(keys);
215 }
216 return true;
217 }
218
219 void UeberBackend::reload()
220 {
221 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
222 {
223 ( *i )->reload();
224 }
225 }
226
227 void UeberBackend::rediscover(string *status)
228 {
229
230 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
231 {
232 string tmpstr;
233 ( *i )->rediscover(&tmpstr);
234 if(status)
235 *status+=tmpstr + (i!=backends.begin() ? "\n" : "");
236 }
237 }
238
239
240 void UeberBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
241 {
242 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
243 {
244 ( *i )->getUnfreshSlaveInfos( domains );
245 }
246 }
247
248
249
250 void UeberBackend::getUpdatedMasters(vector<DomainInfo>* domains)
251 {
252 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
253 {
254 ( *i )->getUpdatedMasters( domains );
255 }
256 }
257
258 bool UeberBackend::getAuth(const DNSName &target, const QType& qtype, SOAData* sd, bool cachedOk)
259 {
260 // A backend can respond to our authority request with the 'best' match it
261 // has. For example, when asked for a.b.c.example.com. it might respond with
262 // com. We then store that and keep querying the other backends in case one
263 // of them has a more specific zone but don't bother asking this specific
264 // backend again for b.c.example.com., c.example.com. and example.com.
265 // If a backend has no match it may respond with an enmpty qname.
266
267 bool found = false;
268 int cstat;
269 DNSName shorter(target);
270 vector<pair<size_t, SOAData> > bestmatch (backends.size(), make_pair(target.wirelength()+1, SOAData()));
271 do {
272
273 // Check cache
274 if(cachedOk && (d_cache_ttl || d_negcache_ttl)) {
275 d_question.qtype = QType::SOA;
276 d_question.qname = shorter;
277 d_question.zoneId = -1;
278
279 cstat = cacheHas(d_question,d_answers);
280
281 if(cstat == 1 && !d_answers.empty() && d_cache_ttl) {
282 DLOG(L<<Logger::Error<<"has pos cache entry: "<<shorter<<endl);
283 fillSOAData(d_answers[0], *sd);
284
285 sd->db = 0;
286 sd->qname = shorter;
287 goto found;
288 } else if(cstat == 0 && d_negcache_ttl) {
289 DLOG(L<<Logger::Error<<"has neg cache entry: "<<shorter<<endl);
290 continue;
291 }
292 }
293
294 // Check backends
295 {
296 vector<DNSBackend *>::const_iterator i = backends.begin();
297 vector<pair<size_t, SOAData> >::iterator j = bestmatch.begin();
298 for(; i != backends.end() && j != bestmatch.end(); ++i, ++j) {
299
300 DLOG(L<<Logger::Error<<"backend: "<<i-backends.begin()<<", qname: "<<shorter<<endl);
301
302 if(j->first < shorter.wirelength()) {
303 DLOG(L<<Logger::Error<<"skipped, we already found a shorter best match in this backend: "<<j->second.qname<<endl);
304 continue;
305 } else if(j->first == shorter.wirelength()) {
306 DLOG(L<<Logger::Error<<"use shorter best match: "<<j->second.qname<<endl);
307 *sd = j->second;
308 break;
309 } else {
310 DLOG(L<<Logger::Error<<"lookup: "<<shorter<<endl);
311 if((*i)->getAuth(shorter, sd)) {
312 DLOG(L<<Logger::Error<<"got: "<<sd->qname<<endl);
313 j->first = sd->qname.wirelength();
314 j->second = *sd;
315 if(sd->qname == shorter) {
316 break;
317 }
318 } else {
319 DLOG(L<<Logger::Error<<"no match for: "<<shorter<<endl);
320 }
321 }
322 }
323
324 // Add to cache
325 if(i == backends.end()) {
326 if(d_negcache_ttl) {
327 DLOG(L<<Logger::Error<<"add neg cache entry:"<<shorter<<endl);
328 d_question.qname=shorter;
329 addNegCache(d_question);
330 }
331 continue;
332 } else if(d_cache_ttl) {
333 DLOG(L<<Logger::Error<<"add pos cache entry: "<<sd->qname<<endl);
334 d_question.qtype = QType::SOA;
335 d_question.qname = sd->qname;
336 d_question.zoneId = -1;
337
338 DNSZoneRecord rr;
339 rr.dr.d_name = sd->qname;
340 rr.dr.d_type = QType::SOA;
341 rr.dr.d_content = makeSOAContent(*sd);
342 rr.dr.d_ttl = sd->ttl;
343 rr.domain_id = sd->domain_id;
344
345 addCache(d_question, {rr});
346 }
347 }
348
349 found:
350 if(found == (qtype == QType::DS) || target != shorter) {
351 DLOG(L<<Logger::Error<<"found: "<<sd->qname<<endl);
352 return true;
353 } else {
354 DLOG(L<<Logger::Error<<"chasing next: "<<sd->qname<<endl);
355 found = true;
356 }
357
358 } while(shorter.chopOff());
359 return found;
360 }
361
362 bool UeberBackend::getSOA(const DNSName &domain, SOAData &sd)
363 {
364 d_question.qtype=QType::SOA;
365 d_question.qname=domain;
366 d_question.zoneId=-1;
367
368 int cstat=cacheHas(d_question,d_answers);
369 if(cstat==0) { // negative
370 return false;
371 }
372 else if(cstat==1 && !d_answers.empty()) {
373 fillSOAData(d_answers[0],sd);
374 sd.domain_id=d_answers[0].domain_id;
375 sd.ttl=d_answers[0].dr.d_ttl;
376 sd.db=0;
377 return true;
378 }
379
380 // not found in neg. or pos. cache, look it up
381 return getSOAUncached(domain, sd);
382 }
383
384 bool UeberBackend::getSOAUncached(const DNSName &domain, SOAData &sd)
385 {
386 d_question.qtype=QType::SOA;
387 d_question.qname=domain;
388 d_question.zoneId=-1;
389
390 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
391 if((*i)->getSOA(domain, sd)) {
392 if( d_cache_ttl ) {
393 DNSZoneRecord rr;
394 rr.dr.d_name = sd.qname;
395 rr.dr.d_type = QType::SOA;
396
397 rr.dr.d_content = makeSOAContent(sd);
398 rr.dr.d_ttl = sd.ttl;
399 rr.domain_id = sd.domain_id;
400
401 addCache(d_question, {rr});
402
403 }
404 return true;
405 }
406
407 if(d_negcache_ttl)
408 addNegCache(d_question);
409 return false;
410 }
411
412 bool UeberBackend::superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db)
413 {
414 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
415 if((*i)->superMasterBackend(ip, domain, nsset, nameserver, account, db))
416 return true;
417 return false;
418 }
419
420 UeberBackend::UeberBackend(const string &pname)
421 {
422 pthread_mutex_lock(&instances_lock);
423 instances.push_back(this); // report to the static list of ourself
424 pthread_mutex_unlock(&instances_lock);
425
426 d_negcached=0;
427 d_ancount=0;
428 d_domain_id=-1;
429 d_cached=0;
430 d_cache_ttl = ::arg().asNum("query-cache-ttl");
431 d_negcache_ttl = ::arg().asNum("negquery-cache-ttl");
432
433 d_tid=pthread_self();
434 d_stale=false;
435
436 backends=BackendMakers().all(pname=="key-only");
437 }
438
439 void del(DNSBackend* d)
440 {
441 delete d;
442 }
443
444 void UeberBackend::cleanup()
445 {
446 pthread_mutex_lock(&instances_lock);
447
448 remove(instances.begin(),instances.end(),this);
449 instances.resize(instances.size()-1);
450
451 pthread_mutex_unlock(&instances_lock);
452
453 for_each(backends.begin(),backends.end(),del);
454 }
455
456 // returns -1 for miss, 0 for negative match, 1 for hit
457 int UeberBackend::cacheHas(const Question &q, vector<DNSZoneRecord> &rrs)
458 {
459 extern AuthQueryCache QC;
460
461 if(!d_cache_ttl && ! d_negcache_ttl) {
462 return -1;
463 }
464
465 rrs.clear();
466 // L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
467
468 bool ret=QC.getEntry(q.qname, q.qtype, rrs, q.zoneId); // think about lowercasing here
469 if(!ret) {
470 return -1;
471 }
472 if(rrs.empty()) // negatively cached
473 return 0;
474
475 return 1;
476 }
477
478 void UeberBackend::addNegCache(const Question &q)
479 {
480 extern AuthQueryCache QC;
481 if(!d_negcache_ttl)
482 return;
483 // we should also not be storing negative answers if a pipebackend does scopeMask, but we can't pass a negative scopeMask in an empty set!
484 QC.insert(q.qname, q.qtype, vector<DNSZoneRecord>(), d_negcache_ttl, q.zoneId);
485 }
486
487 void UeberBackend::addCache(const Question &q, const vector<DNSZoneRecord> &rrs)
488 {
489 extern AuthQueryCache QC;
490
491 if(!d_cache_ttl)
492 return;
493
494 unsigned int store_ttl = d_cache_ttl;
495 for(const auto& rr : rrs) {
496 if (rr.dr.d_ttl < d_cache_ttl)
497 store_ttl = rr.dr.d_ttl;
498 if (rr.scopeMask)
499 return;
500 }
501
502 QC.insert(q.qname, q.qtype, rrs, store_ttl, q.zoneId);
503 }
504
505 void UeberBackend::alsoNotifies(const DNSName &domain, set<string> *ips)
506 {
507 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
508 (*i)->alsoNotifies(domain,ips);
509 }
510
511 UeberBackend::~UeberBackend()
512 {
513 DLOG(L<<Logger::Error<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl);
514 cleanup();
515 }
516
517 // this handle is more magic than most
518 void UeberBackend::lookup(const QType &qtype,const DNSName &qname, DNSPacket *pkt_p, int zoneId)
519 {
520 if(d_stale) {
521 L<<Logger::Error<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl;
522 throw PDNSException("We are stale, please recycle");
523 }
524
525 DLOG(L<<"UeberBackend received question for "<<qtype.getName()<<" of "<<qname<<endl);
526 if(!d_go) {
527 pthread_mutex_lock(&d_mut);
528 while (d_go==false) {
529 L<<Logger::Error<<"UeberBackend is blocked, waiting for 'go'"<<endl;
530 pthread_cond_wait(&d_cond, &d_mut);
531 L<<Logger::Error<<"Broadcast received, unblocked"<<endl;
532 }
533 pthread_mutex_unlock(&d_mut);
534 }
535
536 d_domain_id=zoneId;
537
538 d_handle.i=0;
539 d_handle.qtype=qtype;
540 d_handle.qname=qname;
541 d_handle.pkt_p=pkt_p;
542 d_ancount=0;
543
544 if(!backends.size()) {
545 L<<Logger::Error<<"No database backends available - unable to answer questions."<<endl;
546 d_stale=true; // please recycle us!
547 throw PDNSException("We are stale, please recycle");
548 }
549 else {
550 d_question.qtype=qtype;
551 d_question.qname=qname;
552 d_question.zoneId=zoneId;
553 int cstat=cacheHas(d_question, d_answers);
554 if(cstat<0) { // nothing
555 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): uncached"<<endl;
556 d_negcached=d_cached=false;
557 d_answers.clear();
558 (d_handle.d_hinterBackend=backends[d_handle.i++])->lookup(qtype, qname,pkt_p,zoneId);
559 }
560 else if(cstat==0) {
561 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
562 d_negcached=true;
563 d_cached=false;
564 d_answers.clear();
565 }
566 else {
567 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
568 d_negcached=false;
569 d_cached=true;
570 d_cachehandleiter = d_answers.begin();
571 }
572 }
573
574 d_handle.parent=this;
575 }
576
577 void UeberBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled) {
578 for (vector<DNSBackend*>::iterator i = backends.begin(); i != backends.end(); ++i )
579 {
580 (*i)->getAllDomains(domains, include_disabled);
581 }
582 }
583
584 bool UeberBackend::get(DNSZoneRecord &rr)
585 {
586 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
587 if(d_negcached) {
588 return false;
589 }
590
591 if(d_cached) {
592 if(d_cachehandleiter != d_answers.end()) {
593 rr=*d_cachehandleiter++;;
594 return true;
595 }
596 return false;
597 }
598 if(!d_handle.get(rr)) {
599 // cout<<"end of ueberbackend get, seeing if we should cache"<<endl;
600 if(!d_ancount && d_handle.qname.countLabels()) {// don't cache axfr
601 // cout<<"adding negcache"<<endl;
602 addNegCache(d_question);
603 }
604 else {
605 // cout<<"adding query cache"<<endl;
606 addCache(d_question, d_answers);
607 }
608 d_answers.clear();
609 return false;
610 }
611 d_ancount++;
612 d_answers.push_back(rr);
613 return true;
614 }
615
616 bool UeberBackend::searchRecords(const string& pattern, int maxResults, vector<DNSResourceRecord>& result)
617 {
618 bool rc = false;
619 for ( vector< DNSBackend * >::iterator i = backends.begin(); result.size() < static_cast<vector<DNSResourceRecord>::size_type>(maxResults) && i != backends.end(); ++i )
620 if ((*i)->searchRecords(pattern, maxResults - result.size(), result)) rc = true;
621 return rc;
622 }
623
624 bool UeberBackend::searchComments(const string& pattern, int maxResults, vector<Comment>& result)
625 {
626 bool rc = false;
627 for ( vector< DNSBackend * >::iterator i = backends.begin(); result.size() < static_cast<vector<Comment>::size_type>(maxResults) && i != backends.end(); ++i )
628 if ((*i)->searchComments(pattern, maxResults - result.size(), result)) rc = true;
629 return rc;
630 }
631
632 AtomicCounter UeberBackend::handle::instances(0);
633
634 UeberBackend::handle::handle()
635 {
636 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
637 ++instances;
638 parent=NULL;
639 d_hinterBackend=NULL;
640 pkt_p=NULL;
641 i=0;
642 }
643
644 UeberBackend::handle::~handle()
645 {
646 --instances;
647 }
648
649 bool UeberBackend::handle::get(DNSZoneRecord &r)
650 {
651 DLOG(L << "Ueber get() was called for a "<<qtype.getName()<<" record" << endl);
652 bool isMore=false;
653 while(d_hinterBackend && !(isMore=d_hinterBackend->get(r))) { // this backend out of answers
654 if(i<parent->backends.size()) {
655 DLOG(L<<"Backend #"<<i<<" of "<<parent->backends.size()
656 <<" out of answers, taking next"<<endl);
657
658 d_hinterBackend=parent->backends[i++];
659 d_hinterBackend->lookup(qtype,qname,pkt_p,parent->d_domain_id);
660 }
661 else
662 break;
663
664 DLOG(L<<"Now asking backend #"<<i<<endl);
665 }
666
667 if(!isMore && i==parent->backends.size()) {
668 DLOG(L<<"UeberBackend reached end of backends"<<endl);
669 return false;
670 }
671
672 DLOG(L<<"Found an answering backend - will not try another one"<<endl);
673 i=parent->backends.size(); // don't go on to the next backend
674 return true;
675 }