2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2005 - 2011 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <boost/archive/binary_iarchive.hpp>
26 #include <boost/archive/binary_oarchive.hpp>
28 #include "packetcache.hh"
34 #include <sys/types.h>
40 #include <boost/foreach.hpp>
42 #include "arguments.hh"
43 #include "dnsbackend.hh"
44 #include "ueberbackend.hh"
45 #include "dnspacket.hh"
48 #include <boost/serialization/vector.hpp>
53 vector
<UeberBackend
*>UeberBackend::instances
;
54 pthread_mutex_t
UeberBackend::instances_lock
=PTHREAD_MUTEX_INITIALIZER
;
56 sem_t
UeberBackend::d_dynserialize
;
58 // initially we are blocked
59 bool UeberBackend::d_go
=false;
60 pthread_mutex_t
UeberBackend::d_mut
= PTHREAD_MUTEX_INITIALIZER
;
61 pthread_cond_t
UeberBackend::d_cond
= PTHREAD_COND_INITIALIZER
;
64 #define RTLD_NOW RTLD_LAZY
67 //! Loads a module and reports it to all UeberBackend threads
68 bool UeberBackend::loadmodule(const string
&name
)
70 L
<<Logger::Warning
<<"Loading '"<<name
<<"'" << endl
;
72 void *dlib
=dlopen(name
.c_str(), RTLD_NOW
);
75 L
<<Logger::Error
<<"Unable to load module '"<<name
<<"': "<<dlerror() << endl
;
82 void UeberBackend::go(void)
84 pthread_mutex_lock(&d_mut
);
86 pthread_cond_broadcast(&d_cond
);
87 pthread_mutex_unlock(&d_mut
);
90 bool UeberBackend::getDomainInfo(const string
&domain
, DomainInfo
&di
)
92 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
93 if((*i
)->getDomainInfo(domain
, di
))
98 bool UeberBackend::createDomain(const string
&domain
)
100 BOOST_FOREACH(DNSBackend
* mydb
, backends
) {
101 if(mydb
->createDomain(domain
)) {
108 int UeberBackend::addDomainKey(const string
& name
, const DNSBackend::KeyData
& key
)
111 BOOST_FOREACH(DNSBackend
* db
, backends
) {
112 if((ret
= db
->addDomainKey(name
, key
)) >= 0)
117 bool UeberBackend::getDomainKeys(const string
& name
, unsigned int kind
, std::vector
<DNSBackend::KeyData
>& keys
)
119 BOOST_FOREACH(DNSBackend
* db
, backends
) {
120 if(db
->getDomainKeys(name
, kind
, keys
))
126 bool UeberBackend::getAllDomainMetadata(const string
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
128 BOOST_FOREACH(DNSBackend
* db
, backends
) {
129 if(db
->getAllDomainMetadata(name
, meta
))
135 bool UeberBackend::getDomainMetadata(const string
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
137 BOOST_FOREACH(DNSBackend
* db
, backends
) {
138 if(db
->getDomainMetadata(name
, kind
, meta
))
144 bool UeberBackend::setDomainMetadata(const string
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
146 BOOST_FOREACH(DNSBackend
* db
, backends
) {
147 if(db
->setDomainMetadata(name
, kind
, meta
))
153 bool UeberBackend::activateDomainKey(const string
& name
, unsigned int id
)
155 BOOST_FOREACH(DNSBackend
* db
, backends
) {
156 if(db
->activateDomainKey(name
, id
))
162 bool UeberBackend::deactivateDomainKey(const string
& name
, unsigned int id
)
164 BOOST_FOREACH(DNSBackend
* db
, backends
) {
165 if(db
->deactivateDomainKey(name
, id
))
171 bool UeberBackend::removeDomainKey(const string
& name
, unsigned int id
)
173 BOOST_FOREACH(DNSBackend
* db
, backends
) {
174 if(db
->removeDomainKey(name
, id
))
181 bool UeberBackend::getTSIGKey(const string
& name
, string
* algorithm
, string
* content
)
183 BOOST_FOREACH(DNSBackend
* db
, backends
) {
184 if(db
->getTSIGKey(name
, algorithm
, content
))
191 bool UeberBackend::setTSIGKey(const string
& name
, const string
& algorithm
, const string
& content
)
193 BOOST_FOREACH(DNSBackend
* db
, backends
) {
194 if(db
->setTSIGKey(name
, algorithm
, content
))
200 bool UeberBackend::deleteTSIGKey(const string
& name
)
202 BOOST_FOREACH(DNSBackend
* db
, backends
) {
203 if(db
->deleteTSIGKey(name
))
209 bool UeberBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
211 BOOST_FOREACH(DNSBackend
* db
, backends
) {
212 db
->getTSIGKeys(keys
);
217 bool UeberBackend::getDirectNSECx(uint32_t id
, const string
&hashed
, const QType
&qtype
, string
&before
, DNSResourceRecord
&rr
)
219 BOOST_FOREACH(DNSBackend
* db
, backends
) {
220 if(db
->getDirectNSECx(id
, hashed
, qtype
, before
, rr
))
226 bool UeberBackend::getDirectRRSIGs(const string
&signer
, const string
&qname
, const QType
&qtype
, vector
<DNSResourceRecord
> &rrsigs
)
228 BOOST_FOREACH(DNSBackend
* db
, backends
) {
229 if(db
->getDirectRRSIGs(signer
, qname
, qtype
, rrsigs
))
235 void UeberBackend::reload()
237 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
243 void UeberBackend::rediscover(string
*status
)
246 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
249 ( *i
)->rediscover(&tmpstr
);
251 *status
+=tmpstr
+ (i
!=backends
.begin() ? "\n" : "");
256 void UeberBackend::getUnfreshSlaveInfos(vector
<DomainInfo
>* domains
)
258 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
260 ( *i
)->getUnfreshSlaveInfos( domains
);
266 void UeberBackend::getUpdatedMasters(vector
<DomainInfo
>* domains
)
268 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
270 ( *i
)->getUpdatedMasters( domains
);
274 bool UeberBackend::getAuth(DNSPacket
*p
, SOAData
*sd
, const string
&target
)
276 int best_match_len
= -1;
277 bool from_cache
= false; // Was this result fetched from the cache?
279 // If not special case of caching explicitly disabled (sd->db = -1), first
280 // find the best match from the cache. If DS then we need to find parent so
281 // dont bother with caching as it confuses matters.
282 if( sd
->db
!= (DNSBackend
*)-1 && d_cache_ttl
&& p
->qtype
!= QType::DS
) {
283 string
subdomain(target
);
284 int cstat
, loops
= 0;
286 d_question
.qtype
= QType::SOA
;
287 d_question
.qname
= subdomain
;
288 d_question
.zoneId
= -1;
290 cstat
= cacheHas(d_question
,d_answers
);
292 if(cstat
==1 && !d_answers
.empty()) {
293 fillSOAData(d_answers
[0].content
,*sd
);
294 sd
->domain_id
= d_answers
[0].domain_id
;
295 sd
->ttl
= d_answers
[0].ttl
;
297 sd
->qname
= subdomain
;
298 //L<<Logger::Error<<"Best cache match: " << sd->qname << " itteration " << loops <<endl;
300 // Found first time round this must be the best match
305 best_match_len
= sd
->qname
.length();
311 while( chopOff( subdomain
) ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
314 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin(); i
!=backends
.end();++i
)
315 if((*i
)->getAuth(p
, sd
, target
, best_match_len
)) {
316 best_match_len
= sd
->qname
.length();
319 // Shortcut for the case that we got a direct hit - no need to go
320 // through the other backends then.
321 if( best_match_len
== (int)target
.length() )
325 if( best_match_len
== -1 )
329 // Insert into cache. Don't cache if the query was a DS
330 if( d_cache_ttl
&& ! from_cache
&& p
->qtype
!= QType::DS
) {
331 //L<<Logger::Error<<"Saving auth cache for " << sd->qname <<endl;
332 d_question
.qtype
= QType::SOA
;
333 d_question
.qname
= sd
->qname
;
334 d_question
.zoneId
= -1;
336 DNSResourceRecord rr
;
337 rr
.qname
= sd
->qname
;
338 rr
.qtype
= QType::SOA
;
339 rr
.content
= serializeSOAData(*sd
);
341 rr
.domain_id
= sd
->domain_id
;
342 vector
<DNSResourceRecord
> rrs
;
344 addCache(d_question
, rrs
);
350 bool UeberBackend::getSOA(const string
&domain
, SOAData
&sd
, DNSPacket
*p
)
352 d_question
.qtype
=QType::SOA
;
353 d_question
.qname
=domain
;
354 d_question
.zoneId
=-1;
356 int cstat
=cacheHas(d_question
,d_answers
);
357 if(cstat
==0) { // negative
360 else if(cstat
==1 && !d_answers
.empty()) {
361 fillSOAData(d_answers
[0].content
,sd
);
362 sd
.domain_id
=d_answers
[0].domain_id
;
363 sd
.ttl
=d_answers
[0].ttl
;
368 // not found in neg. or pos. cache, look it up
369 return getSOAUncached(domain
, sd
, p
);
372 bool UeberBackend::getSOAUncached(const string
&domain
, SOAData
&sd
, DNSPacket
*p
)
374 d_question
.qtype
=QType::SOA
;
375 d_question
.qname
=domain
;
376 d_question
.zoneId
=-1;
378 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
379 if((*i
)->getSOA(domain
, sd
, p
)) {
381 DNSResourceRecord rr
;
384 rr
.content
=serializeSOAData(sd
);
386 rr
.domain_id
=sd
.domain_id
;
387 vector
<DNSResourceRecord
> rrs
;
389 addCache(d_question
, rrs
);
394 addNegCache(d_question
);
398 bool UeberBackend::superMasterBackend(const string
&ip
, const string
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**db
)
400 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
401 if((*i
)->superMasterBackend(ip
, domain
, nsset
, nameserver
, account
, db
))
406 UeberBackend::UeberBackend(const string
&pname
)
408 pthread_mutex_lock(&instances_lock
);
409 instances
.push_back(this); // report to the static list of ourself
410 pthread_mutex_unlock(&instances_lock
);
412 d_cache_ttl
= ::arg().asNum("query-cache-ttl");
413 d_negcache_ttl
= ::arg().asNum("negquery-cache-ttl");
418 backends
=BackendMakers().all(pname
=="key-only");
421 void del(DNSBackend
* d
)
426 void UeberBackend::cleanup()
428 pthread_mutex_lock(&instances_lock
);
430 remove(instances
.begin(),instances
.end(),this);
431 instances
.resize(instances
.size()-1);
433 pthread_mutex_unlock(&instances_lock
);
435 for_each(backends
.begin(),backends
.end(),del
);
441 // returns -1 for miss, 0 for negative match, 1 for hit
442 int UeberBackend::cacheHas(const Question
&q
, vector
<DNSResourceRecord
> &rrs
)
444 extern PacketCache PC
;
445 static AtomicCounter
*qcachehit
=S
.getPointer("query-cache-hit");
446 static AtomicCounter
*qcachemiss
=S
.getPointer("query-cache-miss");
448 if(!d_cache_ttl
&& ! d_negcache_ttl
) {
454 // L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
456 bool ret
=PC
.getEntry(q
.qname
, q
.qtype
, PacketCache::QUERYCACHE
, content
, q
.zoneId
); // think about lowercasing here
462 if(content
.empty()) // negatively cached
465 std::istringstream
istr(content
);
466 boost::archive::binary_iarchive
boa(istr
, boost::archive::no_header
);
472 void UeberBackend::addNegCache(const Question
&q
)
474 extern PacketCache PC
;
477 // 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!
478 PC
.insert(q
.qname
, q
.qtype
, PacketCache::QUERYCACHE
, "", d_negcache_ttl
, q
.zoneId
);
481 void UeberBackend::addCache(const Question
&q
, const vector
<DNSResourceRecord
> &rrs
)
483 extern PacketCache PC
;
488 unsigned int store_ttl
= d_cache_ttl
;
490 // L<<Logger::Warning<<"inserting: "<<q.qname+"|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
491 std::ostringstream ostr
;
492 boost::archive::binary_oarchive
boa(ostr
, boost::archive::no_header
);
494 BOOST_FOREACH(DNSResourceRecord rr
, rrs
) {
495 if (rr
.ttl
< d_cache_ttl
)
502 PC
.insert(q
.qname
, q
.qtype
, PacketCache::QUERYCACHE
, ostr
.str(), store_ttl
, q
.zoneId
);
505 void UeberBackend::alsoNotifies(const string
&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 string
&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
<<Logger::NTLog
<<"No database backends available - unable to answer questions."<<endl
;
546 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 d_negcached
=d_cached
=false;
557 (d_handle
.d_hinterBackend
=backends
[d_handle
.i
++])->lookup(qtype
, qname
,pkt_p
,zoneId
);
567 d_cachehandleiter
= d_answers
.begin();
571 d_handle
.parent
=this;
574 void UeberBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
) {
575 for (vector
<DNSBackend
*>::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
577 (*i
)->getAllDomains(domains
, include_disabled
);
581 bool UeberBackend::get(DNSResourceRecord
&rr
)
588 if(d_cachehandleiter
!= d_answers
.end()) {
589 rr
=*d_cachehandleiter
++;;
594 if(!d_handle
.get(rr
)) {
595 if(!d_ancount
&& !d_handle
.qname
.empty()) // don't cache axfr
596 addNegCache(d_question
);
598 addCache(d_question
, d_answers
);
603 d_answers
.push_back(rr
);
607 bool UeberBackend::list(const string
&target
, int domain_id
, bool include_disabled
)
609 L
<<Logger::Error
<<"UeberBackend::list called, should NEVER EVER HAPPEN"<<endl
;
615 AtomicCounter
UeberBackend::handle::instances(0);
617 UeberBackend::handle::handle()
619 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
623 UeberBackend::handle::~handle()
628 bool UeberBackend::handle::get(DNSResourceRecord
&r
)
630 DLOG(L
<< "Ueber get() was called for a "<<qtype
.getName()<<" record" << endl
);
632 while(d_hinterBackend
&& !(isMore
=d_hinterBackend
->get(r
))) { // this backend out of answers
633 if(i
<parent
->backends
.size()) {
634 DLOG(L
<<"Backend #"<<i
<<" of "<<parent
->backends
.size()
635 <<" out of answers, taking next"<<endl
);
637 d_hinterBackend
=parent
->backends
[i
++];
638 d_hinterBackend
->lookup(qtype
,qname
,pkt_p
,parent
->domain_id
);
643 DLOG(L
<<"Now asking backend #"<<i
<<endl
);
646 if(!isMore
&& i
==parent
->backends
.size()) {
647 DLOG(L
<<"UeberBackend reached end of backends"<<endl
);
651 DLOG(L
<<"Found an answering backend - will not try another one"<<endl
);
652 i
=parent
->backends
.size(); // don't go on to the next backend