]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ueberbackend.cc
add OpenSSL exception to PowerDNS, Netherlabs, van Dijk and Hubert copyrights
[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 #include <boost/archive/binary_iarchive.hpp>
23 #include <boost/archive/binary_oarchive.hpp>
24
25 #include "packetcache.hh"
26 #include "utility.hh"
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif // HAVE_CONFIG_H
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 string UeberBackend::s_status;
58
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;
63
64 int UeberBackend::s_s=-1; // ?
65
66 #ifdef NEED_RTLD_NOW
67 #define RTLD_NOW RTLD_LAZY
68 #endif
69
70 //! Loads a module and reports it to all UeberBackend threads
71 bool UeberBackend::loadmodule(const string &name)
72 {
73 // TODO: Implement dynamic loading?
74 #if !defined(DARWIN)
75 void *dlib=dlopen(name.c_str(), RTLD_NOW);
76
77 if(dlib == NULL) {
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;
81 return false;
82 }
83
84 return true;
85
86 #else
87 L << Logger::Warning << "This version doesn't support dynamic loading (yet)." << endl;
88 return false;
89
90 #endif
91 }
92
93 void UeberBackend::go(void)
94 {
95 pthread_mutex_lock(&d_mut);
96 d_go=true;
97 pthread_cond_broadcast(&d_cond);
98 pthread_mutex_unlock(&d_mut);
99 }
100
101 bool UeberBackend::getDomainInfo(const string &domain, DomainInfo &di)
102 {
103 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
104 if((*i)->getDomainInfo(domain, di))
105 return true;
106 return false;
107 }
108
109 bool UeberBackend::createDomain(const string &domain)
110 {
111 BOOST_FOREACH(DNSBackend* mydb, backends) {
112 if(mydb->createDomain(domain)) {
113 return true;
114 }
115 }
116 return false;
117 }
118
119 int UeberBackend::addDomainKey(const string& name, const KeyData& key)
120 {
121 int ret;
122 BOOST_FOREACH(DNSBackend* db, backends) {
123 if((ret = db->addDomainKey(name, key)) >= 0)
124 return ret;
125 }
126 return -1;
127 }
128 bool UeberBackend::getDomainKeys(const string& name, unsigned int kind, std::vector<KeyData>& keys)
129 {
130 BOOST_FOREACH(DNSBackend* db, backends) {
131 if(db->getDomainKeys(name, kind, keys))
132 return true;
133 }
134 return false;
135 }
136
137 bool UeberBackend::getDomainMetadata(const string& name, const std::string& kind, std::vector<std::string>& meta)
138 {
139 BOOST_FOREACH(DNSBackend* db, backends) {
140 if(db->getDomainMetadata(name, kind, meta))
141 return true;
142 }
143 return false;
144 }
145
146 bool UeberBackend::setDomainMetadata(const string& name, const std::string& kind, const std::vector<std::string>& meta)
147 {
148 BOOST_FOREACH(DNSBackend* db, backends) {
149 if(db->setDomainMetadata(name, kind, meta))
150 return true;
151 }
152 return false;
153 }
154
155 bool UeberBackend::activateDomainKey(const string& name, unsigned int id)
156 {
157 BOOST_FOREACH(DNSBackend* db, backends) {
158 if(db->activateDomainKey(name, id))
159 return true;
160 }
161 return false;
162 }
163
164 bool UeberBackend::deactivateDomainKey(const string& name, unsigned int id)
165 {
166 BOOST_FOREACH(DNSBackend* db, backends) {
167 if(db->deactivateDomainKey(name, id))
168 return true;
169 }
170 return false;
171 }
172
173 bool UeberBackend::removeDomainKey(const string& name, unsigned int id)
174 {
175 BOOST_FOREACH(DNSBackend* db, backends) {
176 if(db->removeDomainKey(name, id))
177 return true;
178 }
179 return false;
180 }
181
182
183 bool UeberBackend::getTSIGKey(const string& name, string* algorithm, string* content)
184 {
185 BOOST_FOREACH(DNSBackend* db, backends) {
186 if(db->getTSIGKey(name, algorithm, content))
187 return true;
188 }
189 return false;
190 }
191
192
193 bool UeberBackend::setTSIGKey(const string& name, const string& algorithm, const string& content)
194 {
195 BOOST_FOREACH(DNSBackend* db, backends) {
196 if(db->setTSIGKey(name, algorithm, content))
197 return true;
198 }
199 return false;
200 }
201
202 bool UeberBackend::deleteTSIGKey(const string& name)
203 {
204 BOOST_FOREACH(DNSBackend* db, backends) {
205 if(db->deleteTSIGKey(name))
206 return true;
207 }
208 return false;
209 }
210
211 bool UeberBackend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
212 {
213 BOOST_FOREACH(DNSBackend* db, backends) {
214 db->getTSIGKeys(keys);
215 }
216 return true;
217 }
218
219
220 void UeberBackend::reload()
221 {
222 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
223 {
224 ( *i )->reload();
225 }
226 }
227
228 void UeberBackend::rediscover(string *status)
229 {
230
231 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
232 {
233 string tmpstr;
234 ( *i )->rediscover(&tmpstr);
235 if(status)
236 *status+=tmpstr + (i!=backends.begin() ? "\n" : "");
237 }
238 }
239
240
241 void UeberBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
242 {
243 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
244 {
245 ( *i )->getUnfreshSlaveInfos( domains );
246 }
247 }
248
249
250
251 void UeberBackend::getUpdatedMasters(vector<DomainInfo>* domains)
252 {
253 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
254 {
255 ( *i )->getUpdatedMasters( domains );
256 }
257 }
258
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)
261 {
262 d_question.qtype=QType::SOA;
263 d_question.qname=domain;
264 d_question.zoneId=-1;
265
266 if(sd.db!=(DNSBackend *)-1) {
267 int cstat=cacheHas(d_question,d_answers);
268 if(cstat==0) { // negative
269 return false;
270 }
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;
275 sd.db=0;
276 return true;
277 }
278 }
279
280 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
281 if((*i)->getSOA(domain, sd, p)) {
282 DNSResourceRecord rr;
283 rr.qname=domain;
284 rr.qtype=QType::SOA;
285 rr.content=serializeSOAData(sd);
286 rr.ttl=sd.ttl;
287 rr.domain_id=sd.domain_id;
288 vector<DNSResourceRecord> rrs;
289 rrs.push_back(rr);
290 addCache(d_question, rrs);
291 return true;
292 }
293
294 addNegCache(d_question);
295 return false;
296 }
297
298 bool UeberBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **db)
299 {
300 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
301 if((*i)->superMasterBackend(ip,domain,nsset,account, db))
302 return true;
303 return false;
304
305 }
306
307 void UeberBackend::setStatus(const string &st)
308 {
309 s_status=st;
310 }
311
312 UeberBackend::UeberBackend(const string &pname)
313 {
314 pthread_mutex_lock(&instances_lock);
315 instances.push_back(this); // report to the static list of ourself
316 pthread_mutex_unlock(&instances_lock);
317
318 tid=pthread_self();
319 stale=false;
320
321 backends=BackendMakers().all(pname=="key-only");
322 }
323
324 void UeberBackend::die()
325 {
326 cleanup();
327 stale=true;
328 }
329
330 void del(DNSBackend* d)
331 {
332 delete d;
333 }
334
335 void UeberBackend::cleanup()
336 {
337 pthread_mutex_lock(&instances_lock);
338
339 remove(instances.begin(),instances.end(),this);
340 instances.resize(instances.size()-1);
341
342 pthread_mutex_unlock(&instances_lock);
343
344 for_each(backends.begin(),backends.end(),del);
345 }
346
347 // silly Solaris fix
348 #undef PC
349
350 // returns -1 for miss, 0 for negative match, 1 for hit
351 int UeberBackend::cacheHas(const Question &q, vector<DNSResourceRecord> &rrs)
352 {
353 extern PacketCache PC;
354 static unsigned int *qcachehit=S.getPointer("query-cache-hit");
355 static unsigned int *qcachemiss=S.getPointer("query-cache-miss");
356
357 static int negqueryttl=::arg().asNum("negquery-cache-ttl");
358 static int queryttl=::arg().asNum("query-cache-ttl");
359
360 if(!negqueryttl && !queryttl) {
361 (*qcachemiss)++;
362 return -1;
363 }
364
365 string content;
366 // L<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
367
368 bool ret=PC.getEntry(q.qname, q.qtype, PacketCache::QUERYCACHE, content, q.zoneId); // think about lowercasing here
369 if(!ret) {
370 (*qcachemiss)++;
371 return -1;
372 }
373 (*qcachehit)++;
374 if(content.empty()) // negatively cached
375 return 0;
376
377 std::istringstream istr(content);
378 boost::archive::binary_iarchive boa(istr, boost::archive::no_header);
379 rrs.clear();
380 boa >> rrs;
381 return 1;
382 }
383
384 void UeberBackend::addNegCache(const Question &q)
385 {
386 extern PacketCache PC;
387 static int negqueryttl=::arg().asNum("negquery-cache-ttl");
388 if(!negqueryttl)
389 return;
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);
392 }
393
394 void UeberBackend::addCache(const Question &q, const vector<DNSResourceRecord> &rrs)
395 {
396 extern PacketCache PC;
397 static unsigned int queryttl=::arg().asNum("query-cache-ttl");
398 unsigned int cachettl;
399
400 if(!queryttl)
401 return;
402
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);
406
407 cachettl = queryttl;
408 BOOST_FOREACH(DNSResourceRecord rr, rrs) {
409 if (rr.ttl < queryttl)
410 cachettl = rr.ttl;
411 if (rr.scopeMask)
412 return;
413 }
414
415 boa << rrs;
416 PC.insert(q.qname, q.qtype, PacketCache::QUERYCACHE, ostr.str(), cachettl, q.zoneId);
417 }
418
419 void UeberBackend::alsoNotifies(const string &domain, set<string> *ips)
420 {
421 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
422 (*i)->alsoNotifies(domain,ips);
423 }
424
425 UeberBackend::~UeberBackend()
426 {
427 DLOG(L<<Logger::Error<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl);
428 cleanup();
429 }
430
431 // this handle is more magic than most
432 void UeberBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId)
433 {
434 if(stale) {
435 L<<Logger::Error<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl;
436 throw PDNSException("We are stale, please recycle");
437 }
438
439 DLOG(L<<"UeberBackend received question for "<<qtype.getName()<<" of "<<qname<<endl);
440 if(!d_go) {
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;
446 }
447 pthread_mutex_unlock(&d_mut);
448 }
449
450 domain_id=zoneId;
451
452 d_handle.i=0;
453 d_handle.qtype=qtype;
454 d_handle.qname=qname;
455 d_handle.pkt_p=pkt_p;
456 d_ancount=0;
457
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");
462 }
463 else {
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;
470 d_answers.clear();
471 (d_handle.d_hinterBackend=backends[d_handle.i++])->lookup(qtype, qname,pkt_p,zoneId);
472 }
473 else if(cstat==0) {
474 d_negcached=true;
475 d_cached=false;
476 d_answers.clear();
477 }
478 else {
479 d_negcached=false;
480 d_cached=true;
481 d_cachehandleiter = d_answers.begin();
482 }
483 }
484
485 d_handle.parent=this;
486 }
487
488 void UeberBackend::getAllDomains(vector<DomainInfo> *domains) {
489 for (vector<DNSBackend*>::iterator i = backends.begin(); i != backends.end(); ++i )
490 {
491 (*i)->getAllDomains(domains);
492 }
493 }
494
495 bool UeberBackend::get(DNSResourceRecord &rr)
496 {
497 if(d_negcached) {
498 return false;
499 }
500
501 if(d_cached) {
502 if(d_cachehandleiter != d_answers.end()) {
503 rr=*d_cachehandleiter++;;
504 return true;
505 }
506 return false;
507 }
508 if(!d_handle.get(rr)) {
509 if(!d_ancount && !d_handle.qname.empty()) // don't cache axfr
510 addNegCache(d_question);
511
512 addCache(d_question, d_answers);
513 d_answers.clear();
514 return false;
515 }
516 d_ancount++;
517 d_answers.push_back(rr);
518 return true;
519 }
520
521 bool UeberBackend::list(const string &target, int domain_id)
522 {
523 L<<Logger::Error<<"UeberBackend::list called, should NEVER EVER HAPPEN"<<endl;
524 exit(1);
525 return false;
526 }
527
528
529 AtomicCounter UeberBackend::handle::instances(0);
530
531 UeberBackend::handle::handle()
532 {
533 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
534 ++instances;
535 }
536
537 UeberBackend::handle::~handle()
538 {
539 --instances;
540 }
541
542 bool UeberBackend::handle::get(DNSResourceRecord &r)
543 {
544 DLOG(L << "Ueber get() was called for a "<<qtype.getName()<<" record" << endl);
545 bool isMore=false;
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);
550
551 d_hinterBackend=parent->backends[i++];
552 d_hinterBackend->lookup(qtype,qname,pkt_p,parent->domain_id);
553 }
554 else
555 break;
556
557 DLOG(L<<"Now asking backend #"<<i<<endl);
558 }
559
560 if(!isMore && i==parent->backends.size()) {
561 DLOG(L<<"UeberBackend reached end of backends"<<endl);
562 return false;
563 }
564
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
567 return true;
568 }