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 enmpty 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 j
->first
= sd
->qname
.wirelength();
335 if(sd
->qname
== shorter
) {
339 DLOG(g_log
<<Logger::Error
<<"no match for: "<<shorter
<<endl
);
345 if(i
== backends
.end()) {
347 DLOG(g_log
<<Logger::Error
<<"add neg cache entry:"<<shorter
<<endl
);
348 d_question
.qname
=shorter
;
349 addNegCache(d_question
);
352 } else if(d_cache_ttl
) {
353 DLOG(g_log
<<Logger::Error
<<"add pos cache entry: "<<sd
->qname
<<endl
);
354 d_question
.qtype
= QType::SOA
;
355 d_question
.qname
= sd
->qname
;
356 d_question
.zoneId
= -1;
359 rr
.dr
.d_name
= sd
->qname
;
360 rr
.dr
.d_type
= QType::SOA
;
361 rr
.dr
.d_content
= makeSOAContent(*sd
);
362 rr
.dr
.d_ttl
= sd
->ttl
;
363 rr
.domain_id
= sd
->domain_id
;
365 addCache(d_question
, {rr
});
370 if(found
== (qtype
== QType::DS
) || target
!= shorter
) {
371 DLOG(g_log
<<Logger::Error
<<"found: "<<sd
->qname
<<endl
);
374 DLOG(g_log
<<Logger::Error
<<"chasing next: "<<sd
->qname
<<endl
);
378 } while(shorter
.chopOff());
382 bool UeberBackend::getSOA(const DNSName
&domain
, SOAData
&sd
)
384 d_question
.qtype
=QType::SOA
;
385 d_question
.qname
=domain
;
386 d_question
.zoneId
=-1;
388 int cstat
=cacheHas(d_question
,d_answers
);
389 if(cstat
==0) { // negative
392 else if(cstat
==1 && !d_answers
.empty()) {
393 fillSOAData(d_answers
[0],sd
);
394 sd
.domain_id
=d_answers
[0].domain_id
;
395 sd
.ttl
=d_answers
[0].dr
.d_ttl
;
400 // not found in neg. or pos. cache, look it up
401 return getSOAUncached(domain
, sd
);
404 bool UeberBackend::getSOAUncached(const DNSName
&domain
, SOAData
&sd
)
406 d_question
.qtype
=QType::SOA
;
407 d_question
.qname
=domain
;
408 d_question
.zoneId
=-1;
410 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
411 if((*i
)->getSOA(domain
, sd
)) {
414 rr
.dr
.d_name
= sd
.qname
;
415 rr
.dr
.d_type
= QType::SOA
;
416 rr
.dr
.d_content
= makeSOAContent(sd
);
417 rr
.dr
.d_ttl
= sd
.ttl
;
418 rr
.domain_id
= sd
.domain_id
;
420 addCache(d_question
, {rr
});
427 addNegCache(d_question
);
431 bool UeberBackend::superMasterBackend(const string
&ip
, const DNSName
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**db
)
433 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
434 if((*i
)->superMasterBackend(ip
, domain
, nsset
, nameserver
, account
, db
))
439 UeberBackend::UeberBackend(const string
&pname
)
441 pthread_mutex_lock(&instances_lock
);
442 instances
.push_back(this); // report to the static list of ourself
443 pthread_mutex_unlock(&instances_lock
);
449 d_cache_ttl
= ::arg().asNum("query-cache-ttl");
450 d_negcache_ttl
= ::arg().asNum("negquery-cache-ttl");
452 d_tid
=pthread_self();
455 backends
=BackendMakers().all(pname
=="key-only");
458 void del(DNSBackend
* d
)
463 void UeberBackend::cleanup()
465 pthread_mutex_lock(&instances_lock
);
467 remove(instances
.begin(),instances
.end(),this);
468 instances
.resize(instances
.size()-1);
470 pthread_mutex_unlock(&instances_lock
);
472 for_each(backends
.begin(),backends
.end(),del
);
475 // returns -1 for miss, 0 for negative match, 1 for hit
476 int UeberBackend::cacheHas(const Question
&q
, vector
<DNSZoneRecord
> &rrs
)
478 extern AuthQueryCache QC
;
480 if(!d_cache_ttl
&& ! d_negcache_ttl
) {
485 // g_log<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
487 bool ret
=QC
.getEntry(q
.qname
, q
.qtype
, rrs
, q
.zoneId
); // think about lowercasing here
491 if(rrs
.empty()) // negatively cached
497 void UeberBackend::addNegCache(const Question
&q
)
499 extern AuthQueryCache QC
;
502 // 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!
503 QC
.insert(q
.qname
, q
.qtype
, vector
<DNSZoneRecord
>(), d_negcache_ttl
, q
.zoneId
);
506 void UeberBackend::addCache(const Question
&q
, const vector
<DNSZoneRecord
> &rrs
)
508 extern AuthQueryCache QC
;
513 unsigned int store_ttl
= d_cache_ttl
;
514 for(const auto& rr
: rrs
) {
515 if (rr
.dr
.d_ttl
< d_cache_ttl
)
516 store_ttl
= rr
.dr
.d_ttl
;
521 QC
.insert(q
.qname
, q
.qtype
, rrs
, store_ttl
, q
.zoneId
);
524 void UeberBackend::alsoNotifies(const DNSName
&domain
, set
<string
> *ips
)
526 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
527 (*i
)->alsoNotifies(domain
,ips
);
530 UeberBackend::~UeberBackend()
532 DLOG(g_log
<<Logger::Error
<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl
);
536 // this handle is more magic than most
537 void UeberBackend::lookup(const QType
&qtype
,const DNSName
&qname
, DNSPacket
*pkt_p
, int zoneId
)
540 g_log
<<Logger::Error
<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl
;
541 throw PDNSException("We are stale, please recycle");
544 DLOG(g_log
<<"UeberBackend received question for "<<qtype
.getName()<<" of "<<qname
<<endl
);
546 pthread_mutex_lock(&d_mut
);
547 while (d_go
==false) {
548 g_log
<<Logger::Error
<<"UeberBackend is blocked, waiting for 'go'"<<endl
;
549 pthread_cond_wait(&d_cond
, &d_mut
);
550 g_log
<<Logger::Error
<<"Broadcast received, unblocked"<<endl
;
552 pthread_mutex_unlock(&d_mut
);
558 d_handle
.qtype
=qtype
;
559 d_handle
.qname
=qname
;
560 d_handle
.pkt_p
=pkt_p
;
563 if(!backends
.size()) {
564 g_log
<<Logger::Error
<<"No database backends available - unable to answer questions."<<endl
;
565 d_stale
=true; // please recycle us!
566 throw PDNSException("We are stale, please recycle");
569 d_question
.qtype
=qtype
;
570 d_question
.qname
=qname
;
571 d_question
.zoneId
=zoneId
;
572 int cstat
=cacheHas(d_question
, d_answers
);
573 if(cstat
<0) { // nothing
574 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): uncached"<<endl;
575 d_negcached
=d_cached
=false;
577 (d_handle
.d_hinterBackend
=backends
[d_handle
.i
++])->lookup(qtype
, qname
,pkt_p
,zoneId
);
580 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
586 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
589 d_cachehandleiter
= d_answers
.begin();
593 d_handle
.parent
=this;
596 void UeberBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
) {
597 for (vector
<DNSBackend
*>::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
599 (*i
)->getAllDomains(domains
, include_disabled
);
603 bool UeberBackend::get(DNSZoneRecord
&rr
)
605 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
611 if(d_cachehandleiter
!= d_answers
.end()) {
612 rr
=*d_cachehandleiter
++;;
617 if(!d_handle
.get(rr
)) {
618 // cout<<"end of ueberbackend get, seeing if we should cache"<<endl;
619 if(!d_ancount
&& d_handle
.qname
.countLabels()) {// don't cache axfr
620 // cout<<"adding negcache"<<endl;
621 addNegCache(d_question
);
624 // cout<<"adding query cache"<<endl;
625 addCache(d_question
, d_answers
);
631 d_answers
.push_back(rr
);
635 bool UeberBackend::searchRecords(const string
& pattern
, int maxResults
, vector
<DNSResourceRecord
>& result
)
638 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); result
.size() < static_cast<vector
<DNSResourceRecord
>::size_type
>(maxResults
) && i
!= backends
.end(); ++i
)
639 if ((*i
)->searchRecords(pattern
, maxResults
- result
.size(), result
)) rc
= true;
643 bool UeberBackend::searchComments(const string
& pattern
, int maxResults
, vector
<Comment
>& result
)
646 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); result
.size() < static_cast<vector
<Comment
>::size_type
>(maxResults
) && i
!= backends
.end(); ++i
)
647 if ((*i
)->searchComments(pattern
, maxResults
- result
.size(), result
)) rc
= true;
651 AtomicCounter
UeberBackend::handle::instances(0);
653 UeberBackend::handle::handle()
655 // g_log<<Logger::Warning<<"Handle instances: "<<instances<<endl;
658 d_hinterBackend
=NULL
;
663 UeberBackend::handle::~handle()
668 bool UeberBackend::handle::get(DNSZoneRecord
&r
)
670 DLOG(g_log
<< "Ueber get() was called for a "<<qtype
.getName()<<" record" << endl
);
672 while(d_hinterBackend
&& !(isMore
=d_hinterBackend
->get(r
))) { // this backend out of answers
673 if(i
<parent
->backends
.size()) {
674 DLOG(g_log
<<"Backend #"<<i
<<" of "<<parent
->backends
.size()
675 <<" out of answers, taking next"<<endl
);
677 d_hinterBackend
=parent
->backends
[i
++];
678 d_hinterBackend
->lookup(qtype
,qname
,pkt_p
,parent
->d_domain_id
);
683 DLOG(g_log
<<"Now asking backend #"<<i
<<endl
);
686 if(!isMore
&& i
==parent
->backends
.size()) {
687 DLOG(g_log
<<"UeberBackend reached end of backends"<<endl
);
691 DLOG(g_log
<<"Found an answering backend - will not try another one"<<endl
);
692 i
=parent
->backends
.size(); // don't go on to the next backend