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
22 #include <boost/archive/binary_iarchive.hpp>
23 #include <boost/archive/binary_oarchive.hpp>
25 #include "packetcache.hh"
30 #endif // HAVE_CONFIG_H
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
;
57 string
UeberBackend::s_status
;
59 // initially we are blocked
60 bool UeberBackend::d_go
=false;
61 pthread_mutex_t
UeberBackend::d_mut
= PTHREAD_MUTEX_INITIALIZER
;
62 pthread_cond_t
UeberBackend::d_cond
= PTHREAD_COND_INITIALIZER
;
64 int UeberBackend::s_s
=-1; // ?
67 #define RTLD_NOW RTLD_LAZY
70 //! Loads a module and reports it to all UeberBackend threads
71 bool UeberBackend::loadmodule(const string
&name
)
73 // TODO: Implement dynamic loading?
75 void *dlib
=dlopen(name
.c_str(), RTLD_NOW
);
78 L
<<Logger::Warning
<<"Unable to load module '"<<name
<<"': "<<dlerror() << endl
;
79 if(name
.find("gsqlite3")!=string::npos
)
80 L
<<Logger::Warning
<<"Trying to load gsqlite3backend? Make sure pdns_server was compiled with sqlite3!" <<endl
;
87 L
<< Logger::Warning
<< "This version doesn't support dynamic loading (yet)." << endl
;
93 void UeberBackend::go(void)
95 pthread_mutex_lock(&d_mut
);
97 pthread_cond_broadcast(&d_cond
);
98 pthread_mutex_unlock(&d_mut
);
101 bool UeberBackend::getDomainInfo(const string
&domain
, DomainInfo
&di
)
103 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
104 if((*i
)->getDomainInfo(domain
, di
))
109 bool UeberBackend::createDomain(const string
&domain
)
111 BOOST_FOREACH(DNSBackend
* mydb
, backends
) {
112 if(mydb
->createDomain(domain
)) {
119 int UeberBackend::addDomainKey(const string
& name
, const KeyData
& key
)
122 BOOST_FOREACH(DNSBackend
* db
, backends
) {
123 if((ret
= db
->addDomainKey(name
, key
)) >= 0)
128 bool UeberBackend::getDomainKeys(const string
& name
, unsigned int kind
, std::vector
<KeyData
>& keys
)
130 BOOST_FOREACH(DNSBackend
* db
, backends
) {
131 if(db
->getDomainKeys(name
, kind
, keys
))
137 bool UeberBackend::getDomainMetadata(const string
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
139 BOOST_FOREACH(DNSBackend
* db
, backends
) {
140 if(db
->getDomainMetadata(name
, kind
, meta
))
146 bool UeberBackend::setDomainMetadata(const string
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
148 BOOST_FOREACH(DNSBackend
* db
, backends
) {
149 if(db
->setDomainMetadata(name
, kind
, meta
))
155 bool UeberBackend::activateDomainKey(const string
& name
, unsigned int id
)
157 BOOST_FOREACH(DNSBackend
* db
, backends
) {
158 if(db
->activateDomainKey(name
, id
))
164 bool UeberBackend::deactivateDomainKey(const string
& name
, unsigned int id
)
166 BOOST_FOREACH(DNSBackend
* db
, backends
) {
167 if(db
->deactivateDomainKey(name
, id
))
173 bool UeberBackend::removeDomainKey(const string
& name
, unsigned int id
)
175 BOOST_FOREACH(DNSBackend
* db
, backends
) {
176 if(db
->removeDomainKey(name
, id
))
183 bool UeberBackend::getTSIGKey(const string
& name
, string
* algorithm
, string
* content
)
185 BOOST_FOREACH(DNSBackend
* db
, backends
) {
186 if(db
->getTSIGKey(name
, algorithm
, content
))
193 bool UeberBackend::setTSIGKey(const string
& name
, const string
& algorithm
, const string
& content
)
195 BOOST_FOREACH(DNSBackend
* db
, backends
) {
196 if(db
->setTSIGKey(name
, algorithm
, content
))
202 bool UeberBackend::deleteTSIGKey(const string
& name
)
204 BOOST_FOREACH(DNSBackend
* db
, backends
) {
205 if(db
->deleteTSIGKey(name
))
211 bool UeberBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
213 BOOST_FOREACH(DNSBackend
* db
, backends
) {
214 db
->getTSIGKeys(keys
);
220 void UeberBackend::reload()
222 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
228 void UeberBackend::rediscover(string
*status
)
231 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
234 ( *i
)->rediscover(&tmpstr
);
236 *status
+=tmpstr
+ (i
!=backends
.begin() ? "\n" : "");
241 void UeberBackend::getUnfreshSlaveInfos(vector
<DomainInfo
>* domains
)
243 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
245 ( *i
)->getUnfreshSlaveInfos( domains
);
251 void UeberBackend::getUpdatedMasters(vector
<DomainInfo
>* domains
)
253 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
255 ( *i
)->getUpdatedMasters( domains
);
259 /** special trick - if sd.db is set to -1, the cache is ignored */
260 bool UeberBackend::getSOA(const string
&domain
, SOAData
&sd
, DNSPacket
*p
)
262 d_question
.qtype
=QType::SOA
;
263 d_question
.qname
=domain
;
264 d_question
.zoneId
=-1;
266 if(sd
.db
!=(DNSBackend
*)-1) {
267 int cstat
=cacheHas(d_question
,d_answers
);
268 if(cstat
==0) { // negative
271 else if(cstat
==1 && !d_answers
.empty()) {
272 fillSOAData(d_answers
[0].content
,sd
);
273 sd
.domain_id
=d_answers
[0].domain_id
;
274 sd
.ttl
=d_answers
[0].ttl
;
280 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
281 if((*i
)->getSOA(domain
, sd
, p
)) {
282 DNSResourceRecord rr
;
285 rr
.content
=serializeSOAData(sd
);
287 rr
.domain_id
=sd
.domain_id
;
288 vector
<DNSResourceRecord
> rrs
;
290 addCache(d_question
, rrs
);
294 addNegCache(d_question
);
298 bool UeberBackend::superMasterBackend(const string
&ip
, const string
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*account
, DNSBackend
**db
)
300 for(vector
<DNSBackend
*>::const_iterator i
=backends
.begin();i
!=backends
.end();++i
)
301 if((*i
)->superMasterBackend(ip
,domain
,nsset
,account
, db
))
307 void UeberBackend::setStatus(const string
&st
)
312 UeberBackend::UeberBackend(const string
&pname
)
314 pthread_mutex_lock(&instances_lock
);
315 instances
.push_back(this); // report to the static list of ourself
316 pthread_mutex_unlock(&instances_lock
);
321 backends
=BackendMakers().all(pname
=="key-only");
324 void UeberBackend::die()
330 void del(DNSBackend
* d
)
335 void UeberBackend::cleanup()
337 pthread_mutex_lock(&instances_lock
);
339 remove(instances
.begin(),instances
.end(),this);
340 instances
.resize(instances
.size()-1);
342 pthread_mutex_unlock(&instances_lock
);
344 for_each(backends
.begin(),backends
.end(),del
);
350 // returns -1 for miss, 0 for negative match, 1 for hit
351 int UeberBackend::cacheHas(const Question
&q
, vector
<DNSResourceRecord
> &rrs
)
353 extern PacketCache PC
;
354 static unsigned int *qcachehit
=S
.getPointer("query-cache-hit");
355 static unsigned int *qcachemiss
=S
.getPointer("query-cache-miss");
357 static int negqueryttl
=::arg().asNum("negquery-cache-ttl");
358 static int queryttl
=::arg().asNum("query-cache-ttl");
360 if(!negqueryttl
&& !queryttl
) {
366 // L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
368 bool ret
=PC
.getEntry(q
.qname
, q
.qtype
, PacketCache::QUERYCACHE
, content
, q
.zoneId
); // think about lowercasing here
374 if(content
.empty()) // negatively cached
377 std::istringstream
istr(content
);
378 boost::archive::binary_iarchive
boa(istr
, boost::archive::no_header
);
384 void UeberBackend::addNegCache(const Question
&q
)
386 extern PacketCache PC
;
387 static int negqueryttl
=::arg().asNum("negquery-cache-ttl");
390 // 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!
391 PC
.insert(q
.qname
, q
.qtype
, PacketCache::QUERYCACHE
, "", negqueryttl
, q
.zoneId
);
394 void UeberBackend::addCache(const Question
&q
, const vector
<DNSResourceRecord
> &rrs
)
396 extern PacketCache PC
;
397 static unsigned int queryttl
=::arg().asNum("query-cache-ttl");
398 unsigned int cachettl
;
403 // L<<Logger::Warning<<"inserting: "<<q.qname+"|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
404 std::ostringstream ostr
;
405 boost::archive::binary_oarchive
boa(ostr
, boost::archive::no_header
);
408 BOOST_FOREACH(DNSResourceRecord rr
, rrs
) {
409 if (rr
.ttl
< queryttl
)
416 PC
.insert(q
.qname
, q
.qtype
, PacketCache::QUERYCACHE
, ostr
.str(), cachettl
, q
.zoneId
);
419 void UeberBackend::alsoNotifies(const string
&domain
, set
<string
> *ips
)
421 for ( vector
< DNSBackend
* >::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
422 (*i
)->alsoNotifies(domain
,ips
);
425 UeberBackend::~UeberBackend()
427 DLOG(L
<<Logger::Error
<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl
);
431 // this handle is more magic than most
432 void UeberBackend::lookup(const QType
&qtype
,const string
&qname
, DNSPacket
*pkt_p
, int zoneId
)
435 L
<<Logger::Error
<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl
;
436 throw PDNSException("We are stale, please recycle");
439 DLOG(L
<<"UeberBackend received question for "<<qtype
.getName()<<" of "<<qname
<<endl
);
441 pthread_mutex_lock(&d_mut
);
442 while (d_go
==false) {
443 L
<<Logger::Error
<<"UeberBackend is blocked, waiting for 'go'"<<endl
;
444 pthread_cond_wait(&d_cond
, &d_mut
);
445 L
<<Logger::Error
<<"Broadcast received, unblocked"<<endl
;
447 pthread_mutex_unlock(&d_mut
);
453 d_handle
.qtype
=qtype
;
454 d_handle
.qname
=qname
;
455 d_handle
.pkt_p
=pkt_p
;
458 if(!backends
.size()) {
459 L
<<Logger::Error
<<Logger::NTLog
<<"No database backends available - unable to answer questions."<<endl
;
460 stale
=true; // please recycle us!
461 throw PDNSException("We are stale, please recycle");
464 d_question
.qtype
=qtype
;
465 d_question
.qname
=qname
;
466 d_question
.zoneId
=zoneId
;
467 int cstat
=cacheHas(d_question
, d_answers
);
468 if(cstat
<0) { // nothing
469 d_negcached
=d_cached
=false;
471 (d_handle
.d_hinterBackend
=backends
[d_handle
.i
++])->lookup(qtype
, qname
,pkt_p
,zoneId
);
481 d_cachehandleiter
= d_answers
.begin();
485 d_handle
.parent
=this;
488 void UeberBackend::getAllDomains(vector
<DomainInfo
> *domains
) {
489 for (vector
<DNSBackend
*>::iterator i
= backends
.begin(); i
!= backends
.end(); ++i
)
491 (*i
)->getAllDomains(domains
);
495 bool UeberBackend::get(DNSResourceRecord
&rr
)
502 if(d_cachehandleiter
!= d_answers
.end()) {
503 rr
=*d_cachehandleiter
++;;
508 if(!d_handle
.get(rr
)) {
509 if(!d_ancount
&& !d_handle
.qname
.empty()) // don't cache axfr
510 addNegCache(d_question
);
512 addCache(d_question
, d_answers
);
517 d_answers
.push_back(rr
);
521 bool UeberBackend::list(const string
&target
, int domain_id
)
523 L
<<Logger::Error
<<"UeberBackend::list called, should NEVER EVER HAPPEN"<<endl
;
529 AtomicCounter
UeberBackend::handle::instances(0);
531 UeberBackend::handle::handle()
533 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
537 UeberBackend::handle::~handle()
542 bool UeberBackend::handle::get(DNSResourceRecord
&r
)
544 DLOG(L
<< "Ueber get() was called for a "<<qtype
.getName()<<" record" << endl
);
546 while(d_hinterBackend
&& !(isMore
=d_hinterBackend
->get(r
))) { // this backend out of answers
547 if(i
<parent
->backends
.size()) {
548 DLOG(L
<<"Backend #"<<i
<<" of "<<parent
->backends
.size()
549 <<" out of answers, taking next"<<endl
);
551 d_hinterBackend
=parent
->backends
[i
++];
552 d_hinterBackend
->lookup(qtype
,qname
,pkt_p
,parent
->domain_id
);
557 DLOG(L
<<"Now asking backend #"<<i
<<endl
);
560 if(!isMore
&& i
==parent
->backends
.size()) {
561 DLOG(L
<<"UeberBackend reached end of backends"<<endl
);
565 DLOG(L
<<"Found an answering backend - will not try another one"<<endl
);
566 i
=parent
->backends
.size(); // don't go on to the next backend