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::publishDomainKey(const DNSName
& name
, unsigned int id
)
195 for(DNSBackend
* db
: backends
) {
196 if(db
->publishDomainKey(name
, id
))
202 bool UeberBackend::unpublishDomainKey(const DNSName
& name
, unsigned int id
)
204 for(DNSBackend
* db
: backends
) {
205 if(db
->unpublishDomainKey(name
, id
))
212 bool UeberBackend::removeDomainKey(const DNSName
& name
, unsigned int id
)
214 for(DNSBackend
* db
: backends
) {
215 if(db
->removeDomainKey(name
, id
))
222 bool UeberBackend::getTSIGKey(const DNSName
& name
, DNSName
* algorithm
, string
* content
)
224 for(DNSBackend
* db
: backends
) {
225 if(db
->getTSIGKey(name
, algorithm
, content
))
232 bool UeberBackend::setTSIGKey(const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
234 for(DNSBackend
* db
: backends
) {
235 if(db
->setTSIGKey(name
, algorithm
, content
))
241 bool UeberBackend::deleteTSIGKey(const DNSName
& name
)
243 for(DNSBackend
* db
: backends
) {
244 if(db
->deleteTSIGKey(name
))
250 bool UeberBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
252 for(DNSBackend
* db
: backends
) {
253 db
->getTSIGKeys(keys
);
258 void UeberBackend::reload()
260 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
266 void UeberBackend::rediscover(string
*status
)
269 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
272 ( *i
)->rediscover(&tmpstr
);
274 *status
+=tmpstr
+ (i
!=backends
.begin() ? "\n" : "");
279 void UeberBackend::getUnfreshSlaveInfos(vector
<DomainInfo
>* domains
)
281 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
283 ( *i
)->getUnfreshSlaveInfos( domains
);
289 void UeberBackend::getUpdatedMasters(vector
<DomainInfo
>* domains
)
291 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
293 ( *i
)->getUpdatedMasters( domains
);
297 bool UeberBackend::getAuth(const DNSName
&target
, const QType
& qtype
, SOAData
* sd
, bool cachedOk
)
299 // A backend can respond to our authority request with the 'best' match it
300 // has. For example, when asked for a.b.c.example.com. it might respond with
301 // com. We then store that and keep querying the other backends in case one
302 // of them has a more specific zone but don't bother asking this specific
303 // backend again for b.c.example.com., c.example.com. and example.com.
304 // If a backend has no match it may respond with an empty qname.
308 DNSName
shorter(target
);
309 vector
<pair
<size_t, SOAData
> > bestmatch (backends
.size(), make_pair(target
.wirelength()+1, SOAData()));
313 if(cachedOk
&& (d_cache_ttl
|| d_negcache_ttl
)) {
314 d_question
.qtype
= QType::SOA
;
315 d_question
.qname
= shorter
;
316 d_question
.zoneId
= -1;
318 cstat
= cacheHas(d_question
,d_answers
);
320 if(cstat
== 1 && !d_answers
.empty() && d_cache_ttl
) {
321 DLOG(g_log
<<Logger::Error
<<"has pos cache entry: "<<shorter
<<endl
);
322 fillSOAData(d_answers
[0], *sd
);
327 } else if(cstat
== 0 && d_negcache_ttl
) {
328 DLOG(g_log
<<Logger::Error
<<"has neg cache entry: "<<shorter
<<endl
);
335 vector
<DNSBackend
*>::const_iterator i
= backends
.begin();
336 vector
<pair
<size_t, SOAData
> >::iterator j
= bestmatch
.begin();
337 for(; i
!= backends
.end() && j
!= bestmatch
.end(); ++i
, ++j
) {
339 DLOG(g_log
<<Logger::Error
<<"backend: "<<i
-backends
.begin()<<", qname: "<<shorter
<<endl
);
341 if(j
->first
< shorter
.wirelength()) {
342 DLOG(g_log
<<Logger::Error
<<"skipped, we already found a shorter best match in this backend: "<<j
->second
.qname
<<endl
);
344 } else if(j
->first
== shorter
.wirelength()) {
345 DLOG(g_log
<<Logger::Error
<<"use shorter best match: "<<j
->second
.qname
<<endl
);
349 DLOG(g_log
<<Logger::Error
<<"lookup: "<<shorter
<<endl
);
350 if((*i
)->getAuth(shorter
, sd
)) {
351 DLOG(g_log
<<Logger::Error
<<"got: "<<sd
->qname
<<endl
);
352 if(!sd
->qname
.empty() && !shorter
.isPartOf(sd
->qname
)) {
353 throw PDNSException("getAuth() returned an SOA for the wrong zone. Zone '"+sd
->qname
.toLogString()+"' is not part of '"+shorter
.toLogString()+"'");
355 j
->first
= sd
->qname
.wirelength();
357 if(sd
->qname
== shorter
) {
361 DLOG(g_log
<<Logger::Error
<<"no match for: "<<shorter
<<endl
);
367 if(i
== backends
.end()) {
369 DLOG(g_log
<<Logger::Error
<<"add neg cache entry:"<<shorter
<<endl
);
370 d_question
.qname
=shorter
;
371 addNegCache(d_question
);
374 } else if(d_cache_ttl
) {
375 DLOG(g_log
<<Logger::Error
<<"add pos cache entry: "<<sd
->qname
<<endl
);
376 d_question
.qtype
= QType::SOA
;
377 d_question
.qname
= sd
->qname
;
378 d_question
.zoneId
= -1;
381 rr
.dr
.d_name
= sd
->qname
;
382 rr
.dr
.d_type
= QType::SOA
;
383 rr
.dr
.d_content
= makeSOAContent(*sd
);
384 rr
.dr
.d_ttl
= sd
->ttl
;
385 rr
.domain_id
= sd
->domain_id
;
387 addCache(d_question
, {rr
});
392 if(found
== (qtype
== QType::DS
) || target
!= shorter
) {
393 DLOG(g_log
<<Logger::Error
<<"found: "<<sd
->qname
<<endl
);
396 DLOG(g_log
<<Logger::Error
<<"chasing next: "<<sd
->qname
<<endl
);
400 } while(shorter
.chopOff());
404 bool UeberBackend::getSOA(const DNSName
&domain
, SOAData
&sd
)
406 d_question
.qtype
=QType::SOA
;
407 d_question
.qname
=domain
;
408 d_question
.zoneId
=-1;
410 int cstat
=cacheHas(d_question
,d_answers
);
411 if(cstat
==0) { // negative
414 else if(cstat
==1 && !d_answers
.empty()) {
415 fillSOAData(d_answers
[0],sd
);
416 sd
.domain_id
=d_answers
[0].domain_id
;
417 sd
.ttl
=d_answers
[0].dr
.d_ttl
;
422 // not found in neg. or pos. cache, look it up
423 return getSOAUncached(domain
, sd
);
426 bool UeberBackend::getSOAUncached(const DNSName
&domain
, SOAData
&sd
)
428 d_question
.qtype
=QType::SOA
;
429 d_question
.qname
=domain
;
430 d_question
.zoneId
=-1;
432 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
433 if((*i
)->getSOA(domain
, sd
)) {
434 if(domain
!= sd
.qname
) {
435 throw PDNSException("getSOA() returned an SOA for the wrong zone. Question: '"+domain
.toLogString()+"', answer: '"+sd
.qname
.toLogString()+"'");
439 rr
.dr
.d_name
= sd
.qname
;
440 rr
.dr
.d_type
= QType::SOA
;
441 rr
.dr
.d_content
= makeSOAContent(sd
);
442 rr
.dr
.d_ttl
= sd
.ttl
;
443 rr
.domain_id
= sd
.domain_id
;
445 addCache(d_question
, {rr
});
452 addNegCache(d_question
);
456 bool UeberBackend::superMasterBackend(const string
&ip
, const DNSName
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**db
)
458 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
459 if((*i
)->superMasterBackend(ip
, domain
, nsset
, nameserver
, account
, db
))
464 UeberBackend::UeberBackend(const string
&pname
)
466 pthread_mutex_lock(&instances_lock
);
467 instances
.push_back(this); // report to the static list of ourself
468 pthread_mutex_unlock(&instances_lock
);
474 d_cache_ttl
= ::arg().asNum("query-cache-ttl");
475 d_negcache_ttl
= ::arg().asNum("negquery-cache-ttl");
477 d_tid
=pthread_self();
480 backends
=BackendMakers().all(pname
=="key-only");
483 static void del(DNSBackend
* d
)
488 void UeberBackend::cleanup()
490 pthread_mutex_lock(&instances_lock
);
492 remove(instances
.begin(),instances
.end(),this);
493 instances
.resize(instances
.size()-1);
495 pthread_mutex_unlock(&instances_lock
);
497 for_each(backends
.begin(),backends
.end(),del
);
500 // returns -1 for miss, 0 for negative match, 1 for hit
501 int UeberBackend::cacheHas(const Question
&q
, vector
<DNSZoneRecord
> &rrs
)
503 extern AuthQueryCache QC
;
505 if(!d_cache_ttl
&& ! d_negcache_ttl
) {
510 // g_log<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
512 bool ret
=QC
.getEntry(q
.qname
, q
.qtype
, rrs
, q
.zoneId
); // think about lowercasing here
516 if(rrs
.empty()) // negatively cached
522 void UeberBackend::addNegCache(const Question
&q
)
524 extern AuthQueryCache QC
;
527 // 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!
528 QC
.insert(q
.qname
, q
.qtype
, vector
<DNSZoneRecord
>(), d_negcache_ttl
, q
.zoneId
);
531 void UeberBackend::addCache(const Question
&q
, vector
<DNSZoneRecord
> &&rrs
)
533 extern AuthQueryCache QC
;
538 unsigned int store_ttl
= d_cache_ttl
;
539 for(const auto& rr
: rrs
) {
540 if (rr
.dr
.d_ttl
< d_cache_ttl
)
541 store_ttl
= rr
.dr
.d_ttl
;
546 QC
.insert(q
.qname
, q
.qtype
, std::move(rrs
), store_ttl
, q
.zoneId
);
549 void UeberBackend::alsoNotifies(const DNSName
&domain
, set
<string
> *ips
)
551 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
552 (*i
)->alsoNotifies(domain
,ips
);
555 UeberBackend::~UeberBackend()
557 DLOG(g_log
<<Logger::Error
<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl
);
561 // this handle is more magic than most
562 void UeberBackend::lookup(const QType
&qtype
,const DNSName
&qname
, int zoneId
, DNSPacket
*pkt_p
)
565 g_log
<<Logger::Error
<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl
;
566 throw PDNSException("We are stale, please recycle");
569 DLOG(g_log
<<"UeberBackend received question for "<<qtype
.getName()<<" of "<<qname
<<endl
);
571 pthread_mutex_lock(&d_mut
);
572 while (d_go
==false) {
573 g_log
<<Logger::Error
<<"UeberBackend is blocked, waiting for 'go'"<<endl
;
574 pthread_cond_wait(&d_cond
, &d_mut
);
575 g_log
<<Logger::Error
<<"Broadcast received, unblocked"<<endl
;
577 pthread_mutex_unlock(&d_mut
);
583 d_handle
.qtype
=qtype
;
584 d_handle
.qname
=qname
;
585 d_handle
.pkt_p
=pkt_p
;
588 if(!backends
.size()) {
589 g_log
<<Logger::Error
<<"No database backends available - unable to answer questions."<<endl
;
590 d_stale
=true; // please recycle us!
591 throw PDNSException("We are stale, please recycle");
594 d_question
.qtype
=qtype
;
595 d_question
.qname
=qname
;
596 d_question
.zoneId
=zoneId
;
597 int cstat
=cacheHas(d_question
, d_answers
);
598 if(cstat
<0) { // nothing
599 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): uncached"<<endl;
600 d_negcached
=d_cached
=false;
602 (d_handle
.d_hinterBackend
=backends
[d_handle
.i
++])->lookup(qtype
, qname
,zoneId
,pkt_p
);
605 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
611 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
614 d_cachehandleiter
= d_answers
.begin();
618 d_handle
.parent
=this;
621 void UeberBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
) {
622 for (vector
<DNSBackend
*>::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
624 (*i
)->getAllDomains(domains
, include_disabled
);
628 bool UeberBackend::get(DNSZoneRecord
&rr
)
630 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
636 if(d_cachehandleiter
!= d_answers
.end()) {
637 rr
=*d_cachehandleiter
++;;
642 if(!d_handle
.get(rr
)) {
643 // cout<<"end of ueberbackend get, seeing if we should cache"<<endl;
644 if(!d_ancount
&& d_handle
.qname
.countLabels()) {// don't cache axfr
645 // cout<<"adding negcache"<<endl;
646 addNegCache(d_question
);
649 // cout<<"adding query cache"<<endl;
650 addCache(d_question
, std::move(d_answers
));
656 rr
.dr
.d_place
=DNSResourceRecord::ANSWER
;
659 d_answers
.push_back(rr
);
663 bool UeberBackend::searchRecords(const string
& pattern
, int maxResults
, vector
<DNSResourceRecord
>& result
)
666 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); result
.size() < static_cast<vector
<DNSResourceRecord
>::size_type
>(maxResults
) && i
!= backends
.end(); ++i
)
667 if ((*i
)->searchRecords(pattern
, maxResults
- result
.size(), result
)) rc
= true;
671 bool UeberBackend::searchComments(const string
& pattern
, int maxResults
, vector
<Comment
>& result
)
674 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); result
.size() < static_cast<vector
<Comment
>::size_type
>(maxResults
) && i
!= backends
.end(); ++i
)
675 if ((*i
)->searchComments(pattern
, maxResults
- result
.size(), result
)) rc
= true;
679 AtomicCounter
UeberBackend::handle::instances(0);
681 UeberBackend::handle::handle()
683 // g_log<<Logger::Warning<<"Handle instances: "<<instances<<endl;
686 d_hinterBackend
=NULL
;
691 UeberBackend::handle::~handle()
696 bool UeberBackend::handle::get(DNSZoneRecord
&r
)
698 DLOG(g_log
<< "Ueber get() was called for a "<<qtype
.getName()<<" record" << endl
);
700 while(d_hinterBackend
&& !(isMore
=d_hinterBackend
->get(r
))) { // this backend out of answers
701 if(i
<parent
->backends
.size()) {
702 DLOG(g_log
<<"Backend #"<<i
<<" of "<<parent
->backends
.size()
703 <<" out of answers, taking next"<<endl
);
705 d_hinterBackend
=parent
->backends
[i
++];
706 d_hinterBackend
->lookup(qtype
,qname
,parent
->d_domain_id
,pkt_p
);
711 DLOG(g_log
<<"Now asking backend #"<<i
<<endl
);
714 if(!isMore
&& i
==parent
->backends
.size()) {
715 DLOG(g_log
<<"UeberBackend reached end of backends"<<endl
);
719 DLOG(g_log
<<"Found an answering backend - will not try another one"<<endl
);
720 i
=parent
->backends
.size(); // don't go on to the next backend