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 L
<<Logger::Warning
<<"Loading '"<<name
<<"'" << endl
;
65 void *dlib
=dlopen(name
.c_str(), RTLD_NOW
);
68 L
<<Logger::Error
<<"Unable to load module '"<<name
<<"': "<<dlerror() << endl
;
75 void UeberBackend::go(void)
77 pthread_mutex_lock(&d_mut
);
79 pthread_cond_broadcast(&d_cond
);
80 pthread_mutex_unlock(&d_mut
);
83 bool UeberBackend::getDomainInfo(const DNSName
&domain
, DomainInfo
&di
)
85 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
86 if((*i
)->getDomainInfo(domain
, di
))
91 bool UeberBackend::createDomain(const DNSName
&domain
)
93 for(DNSBackend
* mydb
: backends
) {
94 if(mydb
->createDomain(domain
)) {
101 bool UeberBackend::doesDNSSEC()
103 for(auto* db
: backends
) {
110 bool UeberBackend::addDomainKey(const DNSName
& name
, const DNSBackend::KeyData
& key
, int64_t& id
)
113 for(DNSBackend
* db
: backends
) {
114 if(db
->addDomainKey(name
, key
, id
))
119 bool UeberBackend::getDomainKeys(const DNSName
& name
, std::vector
<DNSBackend::KeyData
>& keys
)
121 for(DNSBackend
* db
: backends
) {
122 if(db
->getDomainKeys(name
, keys
))
128 bool UeberBackend::getAllDomainMetadata(const DNSName
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
130 for(DNSBackend
* db
: backends
) {
131 if(db
->getAllDomainMetadata(name
, meta
))
137 bool UeberBackend::getDomainMetadata(const DNSName
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
139 for(DNSBackend
* db
: backends
) {
140 if(db
->getDomainMetadata(name
, kind
, meta
))
146 bool UeberBackend::setDomainMetadata(const DNSName
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
148 for(DNSBackend
* db
: backends
) {
149 if(db
->setDomainMetadata(name
, kind
, meta
))
155 bool UeberBackend::activateDomainKey(const DNSName
& name
, unsigned int id
)
157 for(DNSBackend
* db
: backends
) {
158 if(db
->activateDomainKey(name
, id
))
164 bool UeberBackend::deactivateDomainKey(const DNSName
& name
, unsigned int id
)
166 for(DNSBackend
* db
: backends
) {
167 if(db
->deactivateDomainKey(name
, id
))
173 bool UeberBackend::removeDomainKey(const DNSName
& name
, unsigned int id
)
175 for(DNSBackend
* db
: backends
) {
176 if(db
->removeDomainKey(name
, id
))
183 bool UeberBackend::getTSIGKey(const DNSName
& name
, DNSName
* algorithm
, string
* content
)
185 for(DNSBackend
* db
: backends
) {
186 if(db
->getTSIGKey(name
, algorithm
, content
))
193 bool UeberBackend::setTSIGKey(const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
195 for(DNSBackend
* db
: backends
) {
196 if(db
->setTSIGKey(name
, algorithm
, content
))
202 bool UeberBackend::deleteTSIGKey(const DNSName
& name
)
204 for(DNSBackend
* db
: backends
) {
205 if(db
->deleteTSIGKey(name
))
211 bool UeberBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
213 for(DNSBackend
* db
: backends
) {
214 db
->getTSIGKeys(keys
);
219 void UeberBackend::reload()
221 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
227 void UeberBackend::rediscover(string
*status
)
230 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
233 ( *i
)->rediscover(&tmpstr
);
235 *status
+=tmpstr
+ (i
!=backends
.begin() ? "\n" : "");
240 void UeberBackend::getUnfreshSlaveInfos(vector
<DomainInfo
>* domains
)
242 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
244 ( *i
)->getUnfreshSlaveInfos( domains
);
250 void UeberBackend::getUpdatedMasters(vector
<DomainInfo
>* domains
)
252 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
254 ( *i
)->getUpdatedMasters( domains
);
258 bool UeberBackend::getAuth(const DNSName
&target
, const QType
& qtype
, SOAData
* sd
, bool cachedOk
)
262 DNSName
choppedOff(target
);
263 vector
<pair
<size_t, SOAData
> > bestmatch (backends
.size(), make_pair(target
.wirelength()+1, SOAData()));
267 if(cachedOk
&& (d_cache_ttl
|| d_negcache_ttl
)) {
268 d_question
.qtype
= QType::SOA
;
269 d_question
.qname
= choppedOff
;
270 d_question
.zoneId
= -1;
272 cstat
= cacheHas(d_question
,d_answers
);
274 if(cstat
== 1 && !d_answers
.empty() && d_cache_ttl
) {
275 DLOG(L
<<Logger::Error
<<"has pos cache entry: "<<choppedOff
<<endl
);
276 fillSOAData(d_answers
[0], *sd
);
279 sd
->qname
= choppedOff
;
281 } else if(cstat
== 0 && d_negcache_ttl
) {
282 DLOG(L
<<Logger::Error
<<"has neg cache entry: "<<choppedOff
<<endl
);
288 // A backend can respond to our SOA request with the 'best'
289 // match it has. For example, when asked the SOA for a.b.c.powerdns.com.
290 // it might respond with the SOA for powerdns.com.
291 // We then store that, keep querying the other backends in case
292 // one of them has a more specific SOA but don't bother
293 // asking this specific backend again for b.c.powerdns.com. or c.powerdns.com.
295 vector
<DNSBackend
*>::const_iterator i
= backends
.begin();
296 vector
<pair
<size_t, SOAData
> >::iterator j
= bestmatch
.begin();
297 for(; i
!= backends
.end() && j
!= bestmatch
.end(); ++i
, ++j
) {
299 DLOG(L
<<Logger::Error
<<"backend: "<<i
-backends
.begin()<<", qname: "<<choppedOff
<<endl
);
301 if(j
->first
< choppedOff
.wirelength()) {
302 DLOG(L
<<Logger::Error
<<"skip this backend, we already know the 'higher' match: "<<j
->second
.qname
<<endl
);
304 } else if(j
->first
== choppedOff
.wirelength()) {
305 DLOG(L
<<Logger::Error
<<"use 'higher' match: "<<j
->second
.qname
<<endl
);
309 DLOG(L
<<Logger::Error
<<"lookup: "<<choppedOff
<<endl
);
310 if((*i
)->getAuth(choppedOff
, sd
)) {
311 DLOG(L
<<Logger::Error
<<"got: "<<sd
->qname
<<endl
);
312 j
->first
= sd
->qname
.wirelength();
314 if(sd
->qname
== choppedOff
) {
318 DLOG(L
<<Logger::Error
<<"no match for: "<<choppedOff
<<endl
);
324 if(i
== backends
.end()) {
326 DLOG(L
<<Logger::Error
<<"add neg cache entry:"<<choppedOff
<<endl
);
327 d_question
.qname
=choppedOff
;
328 addNegCache(d_question
);
331 } else if(d_cache_ttl
) {
332 DLOG(L
<<Logger::Error
<<"add pos cache entry: "<<sd
->qname
<<endl
);
333 d_question
.qtype
= QType::SOA
;
334 d_question
.qname
= sd
->qname
;
335 d_question
.zoneId
= -1;
338 rr
.dr
.d_name
= sd
->qname
;
339 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
;
345 addCache(d_question
, {rr
});
350 if(found
== (qtype
== QType::DS
)){
351 DLOG(L
<<Logger::Error
<<"found: "<<sd
->qname
<<endl
);
354 DLOG(L
<<Logger::Error
<<"chasing next: "<<sd
->qname
<<endl
);
358 } while(choppedOff
.chopOff());
362 bool UeberBackend::getSOA(const DNSName
&domain
, SOAData
&sd
)
364 d_question
.qtype
=QType::SOA
;
365 d_question
.qname
=domain
;
366 d_question
.zoneId
=-1;
368 int cstat
=cacheHas(d_question
,d_answers
);
369 if(cstat
==0) { // negative
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
;
380 // not found in neg. or pos. cache, look it up
381 return getSOAUncached(domain
, sd
);
384 bool UeberBackend::getSOAUncached(const DNSName
&domain
, SOAData
&sd
)
386 d_question
.qtype
=QType::SOA
;
387 d_question
.qname
=domain
;
388 d_question
.zoneId
=-1;
390 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
391 if((*i
)->getSOA(domain
, sd
)) {
394 rr
.dr
.d_name
= sd
.qname
;
395 rr
.dr
.d_type
= QType::SOA
;
397 rr
.dr
.d_content
= makeSOAContent(sd
);
398 rr
.dr
.d_ttl
= sd
.ttl
;
399 rr
.domain_id
= sd
.domain_id
;
401 addCache(d_question
, {rr
});
408 addNegCache(d_question
);
412 bool UeberBackend::superMasterBackend(const string
&ip
, const DNSName
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**db
)
414 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
415 if((*i
)->superMasterBackend(ip
, domain
, nsset
, nameserver
, account
, db
))
420 UeberBackend::UeberBackend(const string
&pname
)
422 pthread_mutex_lock(&instances_lock
);
423 instances
.push_back(this); // report to the static list of ourself
424 pthread_mutex_unlock(&instances_lock
);
430 d_cache_ttl
= ::arg().asNum("query-cache-ttl");
431 d_negcache_ttl
= ::arg().asNum("negquery-cache-ttl");
433 d_tid
=pthread_self();
436 backends
=BackendMakers().all(pname
=="key-only");
439 void del(DNSBackend
* d
)
444 void UeberBackend::cleanup()
446 pthread_mutex_lock(&instances_lock
);
448 remove(instances
.begin(),instances
.end(),this);
449 instances
.resize(instances
.size()-1);
451 pthread_mutex_unlock(&instances_lock
);
453 for_each(backends
.begin(),backends
.end(),del
);
456 // returns -1 for miss, 0 for negative match, 1 for hit
457 int UeberBackend::cacheHas(const Question
&q
, vector
<DNSZoneRecord
> &rrs
)
459 extern AuthQueryCache QC
;
461 if(!d_cache_ttl
&& ! d_negcache_ttl
) {
466 // L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
468 bool ret
=QC
.getEntry(q
.qname
, q
.qtype
, rrs
, q
.zoneId
); // think about lowercasing here
472 if(rrs
.empty()) // negatively cached
478 void UeberBackend::addNegCache(const Question
&q
)
480 extern AuthQueryCache QC
;
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
);
487 void UeberBackend::addCache(const Question
&q
, const vector
<DNSZoneRecord
> &rrs
)
489 extern AuthQueryCache QC
;
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
;
502 QC
.insert(q
.qname
, q
.qtype
, rrs
, store_ttl
, q
.zoneId
);
505 void UeberBackend::alsoNotifies(const DNSName
&domain
, set
<string
> *ips
)
507 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
508 (*i
)->alsoNotifies(domain
,ips
);
511 UeberBackend::~UeberBackend()
513 DLOG(L
<<Logger::Error
<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl
);
517 // this handle is more magic than most
518 void UeberBackend::lookup(const QType
&qtype
,const DNSName
&qname
, DNSPacket
*pkt_p
, int zoneId
)
521 L
<<Logger::Error
<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl
;
522 throw PDNSException("We are stale, please recycle");
525 DLOG(L
<<"UeberBackend received question for "<<qtype
.getName()<<" of "<<qname
<<endl
);
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
;
533 pthread_mutex_unlock(&d_mut
);
539 d_handle
.qtype
=qtype
;
540 d_handle
.qname
=qname
;
541 d_handle
.pkt_p
=pkt_p
;
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");
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;
558 (d_handle
.d_hinterBackend
=backends
[d_handle
.i
++])->lookup(qtype
, qname
,pkt_p
,zoneId
);
561 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
567 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
570 d_cachehandleiter
= d_answers
.begin();
574 d_handle
.parent
=this;
577 void UeberBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
) {
578 for (vector
<DNSBackend
*>::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
580 (*i
)->getAllDomains(domains
, include_disabled
);
584 bool UeberBackend::get(DNSZoneRecord
&rr
)
586 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
592 if(d_cachehandleiter
!= d_answers
.end()) {
593 rr
=*d_cachehandleiter
++;;
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
);
605 // cout<<"adding query cache"<<endl;
606 addCache(d_question
, d_answers
);
612 d_answers
.push_back(rr
);
616 bool UeberBackend::searchRecords(const string
& pattern
, int maxResults
, vector
<DNSResourceRecord
>& result
)
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;
624 bool UeberBackend::searchComments(const string
& pattern
, int maxResults
, vector
<Comment
>& result
)
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;
632 AtomicCounter
UeberBackend::handle::instances(0);
634 UeberBackend::handle::handle()
636 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
639 d_hinterBackend
=NULL
;
644 UeberBackend::handle::~handle()
649 bool UeberBackend::handle::get(DNSZoneRecord
&r
)
651 DLOG(L
<< "Ueber get() was called for a "<<qtype
.getName()<<" record" << endl
);
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
);
658 d_hinterBackend
=parent
->backends
[i
++];
659 d_hinterBackend
->lookup(qtype
,qname
,pkt_p
,parent
->d_domain_id
);
664 DLOG(L
<<"Now asking backend #"<<i
<<endl
);
667 if(!isMore
&& i
==parent
->backends
.size()) {
668 DLOG(L
<<"UeberBackend reached end of backends"<<endl
);
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