]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ueberbackend.cc
Include config.h only in .cc files
[thirdparty/pdns.git] / pdns / ueberbackend.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2005 - 2011 PowerDNS.COM BV
4
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
8
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.
12
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.
17
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
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <boost/archive/binary_iarchive.hpp>
26 #include <boost/archive/binary_oarchive.hpp>
27
28 #include "packetcache.hh"
29 #include "utility.hh"
30
31
32 #include <string>
33 #include <map>
34 #include <sys/types.h>
35 #include <sstream>
36 #include <errno.h>
37 #include <iostream>
38 #include <sstream>
39 #include <functional>
40 #include <boost/foreach.hpp>
41 #include "dns.hh"
42 #include "arguments.hh"
43 #include "dnsbackend.hh"
44 #include "ueberbackend.hh"
45 #include "dnspacket.hh"
46 #include "logger.hh"
47 #include "statbag.hh"
48 #include <boost/serialization/vector.hpp>
49
50
51 extern StatBag S;
52
53 vector<UeberBackend *>UeberBackend::instances;
54 pthread_mutex_t UeberBackend::instances_lock=PTHREAD_MUTEX_INITIALIZER;
55
56 sem_t UeberBackend::d_dynserialize;
57
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;
62
63 #ifdef NEED_RTLD_NOW
64 #define RTLD_NOW RTLD_LAZY
65 #endif
66
67 //! Loads a module and reports it to all UeberBackend threads
68 bool UeberBackend::loadmodule(const string &name)
69 {
70 L<<Logger::Warning <<"Loading '"<<name<<"'" << endl;
71
72 void *dlib=dlopen(name.c_str(), RTLD_NOW);
73
74 if(dlib == NULL) {
75 L<<Logger::Error <<"Unable to load module '"<<name<<"': "<<dlerror() << endl;
76 return false;
77 }
78
79 return true;
80 }
81
82 void UeberBackend::go(void)
83 {
84 pthread_mutex_lock(&d_mut);
85 d_go=true;
86 pthread_cond_broadcast(&d_cond);
87 pthread_mutex_unlock(&d_mut);
88 }
89
90 bool UeberBackend::getDomainInfo(const string &domain, DomainInfo &di)
91 {
92 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
93 if((*i)->getDomainInfo(domain, di))
94 return true;
95 return false;
96 }
97
98 bool UeberBackend::createDomain(const string &domain)
99 {
100 BOOST_FOREACH(DNSBackend* mydb, backends) {
101 if(mydb->createDomain(domain)) {
102 return true;
103 }
104 }
105 return false;
106 }
107
108 int UeberBackend::addDomainKey(const string& name, const DNSBackend::KeyData& key)
109 {
110 int ret;
111 BOOST_FOREACH(DNSBackend* db, backends) {
112 if((ret = db->addDomainKey(name, key)) >= 0)
113 return ret;
114 }
115 return -1;
116 }
117 bool UeberBackend::getDomainKeys(const string& name, unsigned int kind, std::vector<DNSBackend::KeyData>& keys)
118 {
119 BOOST_FOREACH(DNSBackend* db, backends) {
120 if(db->getDomainKeys(name, kind, keys))
121 return true;
122 }
123 return false;
124 }
125
126 bool UeberBackend::getAllDomainMetadata(const string& name, std::map<std::string, std::vector<std::string> >& meta)
127 {
128 BOOST_FOREACH(DNSBackend* db, backends) {
129 if(db->getAllDomainMetadata(name, meta))
130 return true;
131 }
132 return false;
133 }
134
135 bool UeberBackend::getDomainMetadata(const string& name, const std::string& kind, std::vector<std::string>& meta)
136 {
137 BOOST_FOREACH(DNSBackend* db, backends) {
138 if(db->getDomainMetadata(name, kind, meta))
139 return true;
140 }
141 return false;
142 }
143
144 bool UeberBackend::setDomainMetadata(const string& name, const std::string& kind, const std::vector<std::string>& meta)
145 {
146 BOOST_FOREACH(DNSBackend* db, backends) {
147 if(db->setDomainMetadata(name, kind, meta))
148 return true;
149 }
150 return false;
151 }
152
153 bool UeberBackend::activateDomainKey(const string& name, unsigned int id)
154 {
155 BOOST_FOREACH(DNSBackend* db, backends) {
156 if(db->activateDomainKey(name, id))
157 return true;
158 }
159 return false;
160 }
161
162 bool UeberBackend::deactivateDomainKey(const string& name, unsigned int id)
163 {
164 BOOST_FOREACH(DNSBackend* db, backends) {
165 if(db->deactivateDomainKey(name, id))
166 return true;
167 }
168 return false;
169 }
170
171 bool UeberBackend::removeDomainKey(const string& name, unsigned int id)
172 {
173 BOOST_FOREACH(DNSBackend* db, backends) {
174 if(db->removeDomainKey(name, id))
175 return true;
176 }
177 return false;
178 }
179
180
181 bool UeberBackend::getTSIGKey(const string& name, string* algorithm, string* content)
182 {
183 BOOST_FOREACH(DNSBackend* db, backends) {
184 if(db->getTSIGKey(name, algorithm, content))
185 return true;
186 }
187 return false;
188 }
189
190
191 bool UeberBackend::setTSIGKey(const string& name, const string& algorithm, const string& content)
192 {
193 BOOST_FOREACH(DNSBackend* db, backends) {
194 if(db->setTSIGKey(name, algorithm, content))
195 return true;
196 }
197 return false;
198 }
199
200 bool UeberBackend::deleteTSIGKey(const string& name)
201 {
202 BOOST_FOREACH(DNSBackend* db, backends) {
203 if(db->deleteTSIGKey(name))
204 return true;
205 }
206 return false;
207 }
208
209 bool UeberBackend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
210 {
211 BOOST_FOREACH(DNSBackend* db, backends) {
212 db->getTSIGKeys(keys);
213 }
214 return true;
215 }
216
217 bool UeberBackend::getDirectNSECx(uint32_t id, const string &hashed, const QType &qtype, string &before, DNSResourceRecord &rr)
218 {
219 BOOST_FOREACH(DNSBackend* db, backends) {
220 if(db->getDirectNSECx(id, hashed, qtype, before, rr))
221 return true;
222 }
223 return false;
224 }
225
226 bool UeberBackend::getDirectRRSIGs(const string &signer, const string &qname, const QType &qtype, vector<DNSResourceRecord> &rrsigs)
227 {
228 BOOST_FOREACH(DNSBackend* db, backends) {
229 if(db->getDirectRRSIGs(signer, qname, qtype, rrsigs))
230 return true;
231 }
232 return false;
233 }
234
235 void UeberBackend::reload()
236 {
237 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
238 {
239 ( *i )->reload();
240 }
241 }
242
243 void UeberBackend::rediscover(string *status)
244 {
245
246 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
247 {
248 string tmpstr;
249 ( *i )->rediscover(&tmpstr);
250 if(status)
251 *status+=tmpstr + (i!=backends.begin() ? "\n" : "");
252 }
253 }
254
255
256 void UeberBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
257 {
258 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
259 {
260 ( *i )->getUnfreshSlaveInfos( domains );
261 }
262 }
263
264
265
266 void UeberBackend::getUpdatedMasters(vector<DomainInfo>* domains)
267 {
268 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
269 {
270 ( *i )->getUpdatedMasters( domains );
271 }
272 }
273
274 bool UeberBackend::getAuth(DNSPacket *p, SOAData *sd, const string &target)
275 {
276 int best_match_len = -1;
277 bool from_cache = false; // Was this result fetched from the cache?
278
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;
285 do {
286 d_question.qtype = QType::SOA;
287 d_question.qname = subdomain;
288 d_question.zoneId = -1;
289
290 cstat = cacheHas(d_question,d_answers);
291
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;
296 sd->db = 0;
297 sd->qname = subdomain;
298 //L<<Logger::Error<<"Best cache match: " << sd->qname << " itteration " << loops <<endl;
299
300 // Found first time round this must be the best match
301 if( loops == 0 )
302 return true;
303
304 from_cache = true;
305 best_match_len = sd->qname.length();
306
307 break;
308 }
309 loops++;
310 }
311 while( chopOff( subdomain ) ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
312 }
313
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();
317 from_cache = false;
318
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() )
322 goto auth_found;
323 }
324
325 if( best_match_len == -1 )
326 return false;
327
328 auth_found:
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;
335
336 DNSResourceRecord rr;
337 rr.qname = sd->qname;
338 rr.qtype = QType::SOA;
339 rr.content = serializeSOAData(*sd);
340 rr.ttl = sd->ttl;
341 rr.domain_id = sd->domain_id;
342 vector<DNSResourceRecord> rrs;
343 rrs.push_back(rr);
344 addCache(d_question, rrs);
345 }
346
347 return true;
348 }
349
350 bool UeberBackend::getSOA(const string &domain, SOAData &sd, DNSPacket *p)
351 {
352 d_question.qtype=QType::SOA;
353 d_question.qname=domain;
354 d_question.zoneId=-1;
355
356 int cstat=cacheHas(d_question,d_answers);
357 if(cstat==0) { // negative
358 return false;
359 }
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;
364 sd.db=0;
365 return true;
366 }
367
368 // not found in neg. or pos. cache, look it up
369 return getSOAUncached(domain, sd, p);
370 }
371
372 bool UeberBackend::getSOAUncached(const string &domain, SOAData &sd, DNSPacket *p)
373 {
374 d_question.qtype=QType::SOA;
375 d_question.qname=domain;
376 d_question.zoneId=-1;
377
378 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
379 if((*i)->getSOA(domain, sd, p)) {
380 if( d_cache_ttl ) {
381 DNSResourceRecord rr;
382 rr.qname=domain;
383 rr.qtype=QType::SOA;
384 rr.content=serializeSOAData(sd);
385 rr.ttl=sd.ttl;
386 rr.domain_id=sd.domain_id;
387 vector<DNSResourceRecord> rrs;
388 rrs.push_back(rr);
389 addCache(d_question, rrs);
390 }
391 return true;
392 }
393
394 addNegCache(d_question);
395 return false;
396 }
397
398 bool UeberBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db)
399 {
400 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
401 if((*i)->superMasterBackend(ip, domain, nsset, nameserver, account, db))
402 return true;
403 return false;
404 }
405
406 UeberBackend::UeberBackend(const string &pname)
407 {
408 pthread_mutex_lock(&instances_lock);
409 instances.push_back(this); // report to the static list of ourself
410 pthread_mutex_unlock(&instances_lock);
411
412 d_cache_ttl = ::arg().asNum("query-cache-ttl");
413 d_negcache_ttl = ::arg().asNum("negquery-cache-ttl");
414
415 tid=pthread_self();
416 stale=false;
417
418 backends=BackendMakers().all(pname=="key-only");
419 }
420
421 void del(DNSBackend* d)
422 {
423 delete d;
424 }
425
426 void UeberBackend::cleanup()
427 {
428 pthread_mutex_lock(&instances_lock);
429
430 remove(instances.begin(),instances.end(),this);
431 instances.resize(instances.size()-1);
432
433 pthread_mutex_unlock(&instances_lock);
434
435 for_each(backends.begin(),backends.end(),del);
436 }
437
438 // silly Solaris fix
439 #undef PC
440
441 // returns -1 for miss, 0 for negative match, 1 for hit
442 int UeberBackend::cacheHas(const Question &q, vector<DNSResourceRecord> &rrs)
443 {
444 extern PacketCache PC;
445 static AtomicCounter *qcachehit=S.getPointer("query-cache-hit");
446 static AtomicCounter *qcachemiss=S.getPointer("query-cache-miss");
447
448 if(!d_cache_ttl && ! d_negcache_ttl) {
449 (*qcachemiss)++;
450 return -1;
451 }
452
453 string content;
454 // L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
455
456 bool ret=PC.getEntry(q.qname, q.qtype, PacketCache::QUERYCACHE, content, q.zoneId); // think about lowercasing here
457 if(!ret) {
458 (*qcachemiss)++;
459 return -1;
460 }
461 (*qcachehit)++;
462 if(content.empty()) // negatively cached
463 return 0;
464
465 std::istringstream istr(content);
466 boost::archive::binary_iarchive boa(istr, boost::archive::no_header);
467 rrs.clear();
468 boa >> rrs;
469 return 1;
470 }
471
472 void UeberBackend::addNegCache(const Question &q)
473 {
474 extern PacketCache PC;
475 if(!d_negcache_ttl)
476 return;
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);
479 }
480
481 void UeberBackend::addCache(const Question &q, const vector<DNSResourceRecord> &rrs)
482 {
483 extern PacketCache PC;
484
485 if(!d_cache_ttl)
486 return;
487
488 unsigned int store_ttl = d_cache_ttl;
489
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);
493
494 BOOST_FOREACH(DNSResourceRecord rr, rrs) {
495 if (rr.ttl < d_cache_ttl)
496 store_ttl = rr.ttl;
497 if (rr.scopeMask)
498 return;
499 }
500
501 boa << rrs;
502 PC.insert(q.qname, q.qtype, PacketCache::QUERYCACHE, ostr.str(), store_ttl, q.zoneId);
503 }
504
505 void UeberBackend::alsoNotifies(const string &domain, set<string> *ips)
506 {
507 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
508 (*i)->alsoNotifies(domain,ips);
509 }
510
511 UeberBackend::~UeberBackend()
512 {
513 DLOG(L<<Logger::Error<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl);
514 cleanup();
515 }
516
517 // this handle is more magic than most
518 void UeberBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId)
519 {
520 if(stale) {
521 L<<Logger::Error<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl;
522 throw PDNSException("We are stale, please recycle");
523 }
524
525 DLOG(L<<"UeberBackend received question for "<<qtype.getName()<<" of "<<qname<<endl);
526 if(!d_go) {
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;
532 }
533 pthread_mutex_unlock(&d_mut);
534 }
535
536 domain_id=zoneId;
537
538 d_handle.i=0;
539 d_handle.qtype=qtype;
540 d_handle.qname=qname;
541 d_handle.pkt_p=pkt_p;
542 d_ancount=0;
543
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");
548 }
549 else {
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;
556 d_answers.clear();
557 (d_handle.d_hinterBackend=backends[d_handle.i++])->lookup(qtype, qname,pkt_p,zoneId);
558 }
559 else if(cstat==0) {
560 d_negcached=true;
561 d_cached=false;
562 d_answers.clear();
563 }
564 else {
565 d_negcached=false;
566 d_cached=true;
567 d_cachehandleiter = d_answers.begin();
568 }
569 }
570
571 d_handle.parent=this;
572 }
573
574 void UeberBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled) {
575 for (vector<DNSBackend*>::iterator i = backends.begin(); i != backends.end(); ++i )
576 {
577 (*i)->getAllDomains(domains, include_disabled);
578 }
579 }
580
581 bool UeberBackend::get(DNSResourceRecord &rr)
582 {
583 if(d_negcached) {
584 return false;
585 }
586
587 if(d_cached) {
588 if(d_cachehandleiter != d_answers.end()) {
589 rr=*d_cachehandleiter++;;
590 return true;
591 }
592 return false;
593 }
594 if(!d_handle.get(rr)) {
595 if(!d_ancount && !d_handle.qname.empty()) // don't cache axfr
596 addNegCache(d_question);
597
598 addCache(d_question, d_answers);
599 d_answers.clear();
600 return false;
601 }
602 d_ancount++;
603 d_answers.push_back(rr);
604 return true;
605 }
606
607 bool UeberBackend::list(const string &target, int domain_id, bool include_disabled)
608 {
609 L<<Logger::Error<<"UeberBackend::list called, should NEVER EVER HAPPEN"<<endl;
610 exit(1);
611 return false;
612 }
613
614
615 AtomicCounter UeberBackend::handle::instances(0);
616
617 UeberBackend::handle::handle()
618 {
619 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
620 ++instances;
621 }
622
623 UeberBackend::handle::~handle()
624 {
625 --instances;
626 }
627
628 bool UeberBackend::handle::get(DNSResourceRecord &r)
629 {
630 DLOG(L << "Ueber get() was called for a "<<qtype.getName()<<" record" << endl);
631 bool isMore=false;
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);
636
637 d_hinterBackend=parent->backends[i++];
638 d_hinterBackend->lookup(qtype,qname,pkt_p,parent->domain_id);
639 }
640 else
641 break;
642
643 DLOG(L<<"Now asking backend #"<<i<<endl);
644 }
645
646 if(!isMore && i==parent->backends.size()) {
647 DLOG(L<<"UeberBackend reached end of backends"<<endl);
648 return false;
649 }
650
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
653 return true;
654 }