2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
25 #include <boost/archive/binary_iarchive.hpp>
26 #include <boost/archive/binary_oarchive.hpp>
28 #include "auth-querycache.hh"
35 #include <sys/types.h>
43 #include "arguments.hh"
44 #include "dnsbackend.hh"
45 #include "ueberbackend.hh"
46 #include "dnspacket.hh"
52 vector
<UeberBackend
*>UeberBackend::instances
;
53 pthread_mutex_t
UeberBackend::instances_lock
=PTHREAD_MUTEX_INITIALIZER
;
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
;
60 //! Loads a module and reports it to all UeberBackend threads
61 bool UeberBackend::loadmodule(const string
&name
)
63 g_log
<<Logger::Warning
<<"Loading '"<<name
<<"'" << endl
;
65 void *dlib
=dlopen(name
.c_str(), RTLD_NOW
);
68 g_log
<<Logger::Error
<<"Unable to load module '"<<name
<<"': "<<dlerror() << endl
;
75 bool UeberBackend::loadModules(const vector
<string
>& modules
, const string
& path
)
77 for (const auto& module
: modules
) {
79 if (module
.find(".")==string::npos
) {
80 res
= UeberBackend::loadmodule(path
+"/lib"+module
+"backend.so");
81 } else if (module
[0]=='/' || (module
[0]=='.' && module
[1]=='/') || (module
[0]=='.' && module
[1]=='.')) {
82 // absolute or current path
83 res
= UeberBackend::loadmodule(module
);
85 res
= UeberBackend::loadmodule(path
+"/"+module
);
95 void UeberBackend::go(void)
97 pthread_mutex_lock(&d_mut
);
99 pthread_cond_broadcast(&d_cond
);
100 pthread_mutex_unlock(&d_mut
);
103 bool UeberBackend::getDomainInfo(const DNSName
&domain
, DomainInfo
&di
, bool getSerial
)
105 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
106 if((*i
)->getDomainInfo(domain
, di
, getSerial
))
111 bool UeberBackend::createDomain(const DNSName
&domain
)
113 for(DNSBackend
* mydb
: backends
) {
114 if(mydb
->createDomain(domain
)) {
121 bool UeberBackend::doesDNSSEC()
123 for(auto* db
: backends
) {
130 bool UeberBackend::addDomainKey(const DNSName
& name
, const DNSBackend::KeyData
& key
, int64_t& id
)
133 for(DNSBackend
* db
: backends
) {
134 if(db
->addDomainKey(name
, key
, id
))
139 bool UeberBackend::getDomainKeys(const DNSName
& name
, std::vector
<DNSBackend::KeyData
>& keys
)
141 for(DNSBackend
* db
: backends
) {
142 if(db
->getDomainKeys(name
, keys
))
148 bool UeberBackend::getAllDomainMetadata(const DNSName
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
150 for(DNSBackend
* db
: backends
) {
151 if(db
->getAllDomainMetadata(name
, meta
))
157 bool UeberBackend::getDomainMetadata(const DNSName
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
159 for(DNSBackend
* db
: backends
) {
160 if(db
->getDomainMetadata(name
, kind
, meta
))
166 bool UeberBackend::setDomainMetadata(const DNSName
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
168 for(DNSBackend
* db
: backends
) {
169 if(db
->setDomainMetadata(name
, kind
, meta
))
175 bool UeberBackend::activateDomainKey(const DNSName
& name
, unsigned int id
)
177 for(DNSBackend
* db
: backends
) {
178 if(db
->activateDomainKey(name
, id
))
184 bool UeberBackend::deactivateDomainKey(const DNSName
& name
, unsigned int id
)
186 for(DNSBackend
* db
: backends
) {
187 if(db
->deactivateDomainKey(name
, id
))
193 bool UeberBackend::removeDomainKey(const DNSName
& name
, unsigned int id
)
195 for(DNSBackend
* db
: backends
) {
196 if(db
->removeDomainKey(name
, id
))
203 bool UeberBackend::getTSIGKey(const DNSName
& name
, DNSName
* algorithm
, string
* content
)
205 for(DNSBackend
* db
: backends
) {
206 if(db
->getTSIGKey(name
, algorithm
, content
))
213 bool UeberBackend::setTSIGKey(const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
215 for(DNSBackend
* db
: backends
) {
216 if(db
->setTSIGKey(name
, algorithm
, content
))
222 bool UeberBackend::deleteTSIGKey(const DNSName
& name
)
224 for(DNSBackend
* db
: backends
) {
225 if(db
->deleteTSIGKey(name
))
231 bool UeberBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
233 for(DNSBackend
* db
: backends
) {
234 db
->getTSIGKeys(keys
);
239 void UeberBackend::reload()
241 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
247 void UeberBackend::rediscover(string
*status
)
250 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
253 ( *i
)->rediscover(&tmpstr
);
255 *status
+=tmpstr
+ (i
!=backends
.begin() ? "\n" : "");
260 void UeberBackend::getUnfreshSlaveInfos(vector
<DomainInfo
>* domains
)
262 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
264 ( *i
)->getUnfreshSlaveInfos( domains
);
270 void UeberBackend::getUpdatedMasters(vector
<DomainInfo
>* domains
)
272 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
274 ( *i
)->getUpdatedMasters( domains
);
278 bool UeberBackend::getAuth(const DNSName
&target
, const QType
& qtype
, SOAData
* sd
, bool cachedOk
)
280 // A backend can respond to our authority request with the 'best' match it
281 // has. For example, when asked for a.b.c.example.com. it might respond with
282 // com. We then store that and keep querying the other backends in case one
283 // of them has a more specific zone but don't bother asking this specific
284 // backend again for b.c.example.com., c.example.com. and example.com.
285 // If a backend has no match it may respond with an empty qname.
289 DNSName
shorter(target
);
290 vector
<pair
<size_t, SOAData
> > bestmatch (backends
.size(), make_pair(target
.wirelength()+1, SOAData()));
294 if(cachedOk
&& (d_cache_ttl
|| d_negcache_ttl
)) {
295 d_question
.qtype
= QType::SOA
;
296 d_question
.qname
= shorter
;
297 d_question
.zoneId
= -1;
299 cstat
= cacheHas(d_question
,d_answers
);
301 if(cstat
== 1 && !d_answers
.empty() && d_cache_ttl
) {
302 DLOG(g_log
<<Logger::Error
<<"has pos cache entry: "<<shorter
<<endl
);
303 fillSOAData(d_answers
[0], *sd
);
308 } else if(cstat
== 0 && d_negcache_ttl
) {
309 DLOG(g_log
<<Logger::Error
<<"has neg cache entry: "<<shorter
<<endl
);
316 vector
<DNSBackend
*>::const_iterator i
= backends
.begin();
317 vector
<pair
<size_t, SOAData
> >::iterator j
= bestmatch
.begin();
318 for(; i
!= backends
.end() && j
!= bestmatch
.end(); ++i
, ++j
) {
320 DLOG(g_log
<<Logger::Error
<<"backend: "<<i
-backends
.begin()<<", qname: "<<shorter
<<endl
);
322 if(j
->first
< shorter
.wirelength()) {
323 DLOG(g_log
<<Logger::Error
<<"skipped, we already found a shorter best match in this backend: "<<j
->second
.qname
<<endl
);
325 } else if(j
->first
== shorter
.wirelength()) {
326 DLOG(g_log
<<Logger::Error
<<"use shorter best match: "<<j
->second
.qname
<<endl
);
330 DLOG(g_log
<<Logger::Error
<<"lookup: "<<shorter
<<endl
);
331 if((*i
)->getAuth(shorter
, sd
)) {
332 DLOG(g_log
<<Logger::Error
<<"got: "<<sd
->qname
<<endl
);
333 if(!sd
->qname
.empty() && !shorter
.isPartOf(sd
->qname
)) {
334 throw PDNSException("getAuth() returned an SOA for the wrong zone. Zone '"+sd
->qname
.toLogString()+"' is not part of '"+shorter
.toLogString()+"'");
336 j
->first
= sd
->qname
.wirelength();
338 if(sd
->qname
== shorter
) {
342 DLOG(g_log
<<Logger::Error
<<"no match for: "<<shorter
<<endl
);
348 if(i
== backends
.end()) {
350 DLOG(g_log
<<Logger::Error
<<"add neg cache entry:"<<shorter
<<endl
);
351 d_question
.qname
=shorter
;
352 addNegCache(d_question
);
355 } else if(d_cache_ttl
) {
356 DLOG(g_log
<<Logger::Error
<<"add pos cache entry: "<<sd
->qname
<<endl
);
357 d_question
.qtype
= QType::SOA
;
358 d_question
.qname
= sd
->qname
;
359 d_question
.zoneId
= -1;
362 rr
.dr
.d_name
= sd
->qname
;
363 rr
.dr
.d_type
= QType::SOA
;
364 rr
.dr
.d_content
= makeSOAContent(*sd
);
365 rr
.dr
.d_ttl
= sd
->ttl
;
366 rr
.domain_id
= sd
->domain_id
;
368 addCache(d_question
, {rr
});
373 if(found
== (qtype
== QType::DS
) || target
!= shorter
) {
374 DLOG(g_log
<<Logger::Error
<<"found: "<<sd
->qname
<<endl
);
377 DLOG(g_log
<<Logger::Error
<<"chasing next: "<<sd
->qname
<<endl
);
381 } while(shorter
.chopOff());
385 bool UeberBackend::getSOA(const DNSName
&domain
, SOAData
&sd
)
387 d_question
.qtype
=QType::SOA
;
388 d_question
.qname
=domain
;
389 d_question
.zoneId
=-1;
391 int cstat
=cacheHas(d_question
,d_answers
);
392 if(cstat
==0) { // negative
395 else if(cstat
==1 && !d_answers
.empty()) {
396 fillSOAData(d_answers
[0],sd
);
397 sd
.domain_id
=d_answers
[0].domain_id
;
398 sd
.ttl
=d_answers
[0].dr
.d_ttl
;
403 // not found in neg. or pos. cache, look it up
404 return getSOAUncached(domain
, sd
);
407 bool UeberBackend::getSOAUncached(const DNSName
&domain
, SOAData
&sd
)
409 d_question
.qtype
=QType::SOA
;
410 d_question
.qname
=domain
;
411 d_question
.zoneId
=-1;
413 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
414 if((*i
)->getSOA(domain
, sd
)) {
415 if(domain
!= sd
.qname
) {
416 throw PDNSException("getSOA() returned an SOA for the wrong zone. Question: '"+domain
.toLogString()+"', answer: '"+sd
.qname
.toLogString()+"'");
420 rr
.dr
.d_name
= sd
.qname
;
421 rr
.dr
.d_type
= QType::SOA
;
422 rr
.dr
.d_content
= makeSOAContent(sd
);
423 rr
.dr
.d_ttl
= sd
.ttl
;
424 rr
.domain_id
= sd
.domain_id
;
426 addCache(d_question
, {rr
});
433 addNegCache(d_question
);
437 bool UeberBackend::superMasterBackend(const string
&ip
, const DNSName
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**db
)
439 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
440 if((*i
)->superMasterBackend(ip
, domain
, nsset
, nameserver
, account
, db
))
445 UeberBackend::UeberBackend(const string
&pname
)
447 pthread_mutex_lock(&instances_lock
);
448 instances
.push_back(this); // report to the static list of ourself
449 pthread_mutex_unlock(&instances_lock
);
455 d_cache_ttl
= ::arg().asNum("query-cache-ttl");
456 d_negcache_ttl
= ::arg().asNum("negquery-cache-ttl");
458 d_tid
=pthread_self();
461 backends
=BackendMakers().all(pname
=="key-only");
464 void del(DNSBackend
* d
)
469 void UeberBackend::cleanup()
471 pthread_mutex_lock(&instances_lock
);
473 remove(instances
.begin(),instances
.end(),this);
474 instances
.resize(instances
.size()-1);
476 pthread_mutex_unlock(&instances_lock
);
478 for_each(backends
.begin(),backends
.end(),del
);
481 // returns -1 for miss, 0 for negative match, 1 for hit
482 int UeberBackend::cacheHas(const Question
&q
, vector
<DNSZoneRecord
> &rrs
)
484 extern AuthQueryCache QC
;
486 if(!d_cache_ttl
&& ! d_negcache_ttl
) {
491 // g_log<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
493 bool ret
=QC
.getEntry(q
.qname
, q
.qtype
, rrs
, q
.zoneId
); // think about lowercasing here
497 if(rrs
.empty()) // negatively cached
503 void UeberBackend::addNegCache(const Question
&q
)
505 extern AuthQueryCache QC
;
508 // 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!
509 QC
.insert(q
.qname
, q
.qtype
, vector
<DNSZoneRecord
>(), d_negcache_ttl
, q
.zoneId
);
512 void UeberBackend::addCache(const Question
&q
, const vector
<DNSZoneRecord
> &rrs
)
514 extern AuthQueryCache QC
;
519 unsigned int store_ttl
= d_cache_ttl
;
520 for(const auto& rr
: rrs
) {
521 if (rr
.dr
.d_ttl
< d_cache_ttl
)
522 store_ttl
= rr
.dr
.d_ttl
;
527 QC
.insert(q
.qname
, q
.qtype
, rrs
, store_ttl
, q
.zoneId
);
530 void UeberBackend::alsoNotifies(const DNSName
&domain
, set
<string
> *ips
)
532 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
533 (*i
)->alsoNotifies(domain
,ips
);
536 UeberBackend::~UeberBackend()
538 DLOG(g_log
<<Logger::Error
<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl
);
542 // this handle is more magic than most
543 void UeberBackend::lookup(const QType
&qtype
,const DNSName
&qname
, DNSPacket
*pkt_p
, int zoneId
)
546 g_log
<<Logger::Error
<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl
;
547 throw PDNSException("We are stale, please recycle");
550 DLOG(g_log
<<"UeberBackend received question for "<<qtype
.getName()<<" of "<<qname
<<endl
);
552 pthread_mutex_lock(&d_mut
);
553 while (d_go
==false) {
554 g_log
<<Logger::Error
<<"UeberBackend is blocked, waiting for 'go'"<<endl
;
555 pthread_cond_wait(&d_cond
, &d_mut
);
556 g_log
<<Logger::Error
<<"Broadcast received, unblocked"<<endl
;
558 pthread_mutex_unlock(&d_mut
);
564 d_handle
.qtype
=qtype
;
565 d_handle
.qname
=qname
;
566 d_handle
.pkt_p
=pkt_p
;
569 if(!backends
.size()) {
570 g_log
<<Logger::Error
<<"No database backends available - unable to answer questions."<<endl
;
571 d_stale
=true; // please recycle us!
572 throw PDNSException("We are stale, please recycle");
575 d_question
.qtype
=qtype
;
576 d_question
.qname
=qname
;
577 d_question
.zoneId
=zoneId
;
578 int cstat
=cacheHas(d_question
, d_answers
);
579 if(cstat
<0) { // nothing
580 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): uncached"<<endl;
581 d_negcached
=d_cached
=false;
583 (d_handle
.d_hinterBackend
=backends
[d_handle
.i
++])->lookup(qtype
, qname
,pkt_p
,zoneId
);
586 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
592 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
595 d_cachehandleiter
= d_answers
.begin();
599 d_handle
.parent
=this;
602 void UeberBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
) {
603 for (vector
<DNSBackend
*>::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
605 (*i
)->getAllDomains(domains
, include_disabled
);
609 bool UeberBackend::get(DNSZoneRecord
&rr
)
611 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
617 if(d_cachehandleiter
!= d_answers
.end()) {
618 rr
=*d_cachehandleiter
++;;
623 if(!d_handle
.get(rr
)) {
624 // cout<<"end of ueberbackend get, seeing if we should cache"<<endl;
625 if(!d_ancount
&& d_handle
.qname
.countLabels()) {// don't cache axfr
626 // cout<<"adding negcache"<<endl;
627 addNegCache(d_question
);
630 // cout<<"adding query cache"<<endl;
631 addCache(d_question
, d_answers
);
637 d_answers
.push_back(rr
);
641 bool UeberBackend::searchRecords(const string
& pattern
, int maxResults
, vector
<DNSResourceRecord
>& result
)
644 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); result
.size() < static_cast<vector
<DNSResourceRecord
>::size_type
>(maxResults
) && i
!= backends
.end(); ++i
)
645 if ((*i
)->searchRecords(pattern
, maxResults
- result
.size(), result
)) rc
= true;
649 bool UeberBackend::searchComments(const string
& pattern
, int maxResults
, vector
<Comment
>& result
)
652 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); result
.size() < static_cast<vector
<Comment
>::size_type
>(maxResults
) && i
!= backends
.end(); ++i
)
653 if ((*i
)->searchComments(pattern
, maxResults
- result
.size(), result
)) rc
= true;
657 AtomicCounter
UeberBackend::handle::instances(0);
659 UeberBackend::handle::handle()
661 // g_log<<Logger::Warning<<"Handle instances: "<<instances<<endl;
664 d_hinterBackend
=NULL
;
669 UeberBackend::handle::~handle()
674 bool UeberBackend::handle::get(DNSZoneRecord
&r
)
676 DLOG(g_log
<< "Ueber get() was called for a "<<qtype
.getName()<<" record" << endl
);
678 while(d_hinterBackend
&& !(isMore
=d_hinterBackend
->get(r
))) { // this backend out of answers
679 if(i
<parent
->backends
.size()) {
680 DLOG(g_log
<<"Backend #"<<i
<<" of "<<parent
->backends
.size()
681 <<" out of answers, taking next"<<endl
);
683 d_hinterBackend
=parent
->backends
[i
++];
684 d_hinterBackend
->lookup(qtype
,qname
,pkt_p
,parent
->d_domain_id
);
689 DLOG(g_log
<<"Now asking backend #"<<i
<<endl
);
692 if(!isMore
&& i
==parent
->backends
.size()) {
693 DLOG(g_log
<<"UeberBackend reached end of backends"<<endl
);
697 DLOG(g_log
<<"Found an answering backend - will not try another one"<<endl
);
698 i
=parent
->backends
.size(); // don't go on to the next backend