]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ueberbackend.cc
lots of coolness!
[thirdparty/pdns.git] / pdns / ueberbackend.cc
CommitLineData
12c86877
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 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 as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
973ad2b5 19// $Id: ueberbackend.cc,v 1.6 2002/12/18 16:22:20 ahu Exp $
343546e5 20/* (C) Copyright 2002 PowerDNS.COM BV */
12c86877 21#include "utility.hh"
c6566265
BH
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif // HAVE_CONFIG_H
26
12c86877
BH
27#include <string>
28#include <map>
29#include <sys/types.h>
30
31#include <errno.h>
32#include <iostream>
33#include <sstream>
34#include <functional>
35
36#include "dns.hh"
37#include "arguments.hh"
38#include "dnsbackend.hh"
39#include "ueberbackend.hh"
40#include "dnspacket.hh"
41#include "logger.hh"
42#include "statbag.hh"
43#include "packetcache.hh"
44
45extern StatBag S;
46
47vector<UeberBackend *>UeberBackend::instances;
48pthread_mutex_t UeberBackend::instances_lock=PTHREAD_MUTEX_INITIALIZER;
49
50sem_t UeberBackend::d_dynserialize;
51string UeberBackend::programname;
52string UeberBackend::s_status;
53
54// initially we are blocked
55bool UeberBackend::d_go=false;
56pthread_mutex_t UeberBackend::d_mut = PTHREAD_MUTEX_INITIALIZER;
57pthread_cond_t UeberBackend::d_cond = PTHREAD_COND_INITIALIZER;
58
59int UeberBackend::s_s=-1; // ?
60
731f58b8
BH
61#ifdef NEED_RTLD_NOW
62#define RTLD_NOW RTLD_LAZY
63#endif
64
12c86877
BH
65//! Loads a module and reports it to all UeberBackend threads
66bool UeberBackend::loadmodule(const string &name)
67{
68 // TODO: Implement dynamic loading?
69#if !defined(WIN32) && !defined(DARWIN)
70 void *dlib=dlopen(name.c_str(), RTLD_NOW);
71
72 if(dlib == NULL) {
73 L<<Logger::Warning <<"Unable to load module '"<<name<<"': "<<dlerror() << endl;
74 return false;
75 }
76
77 return true;
78
79#else
80 L << Logger::Warning << "This version doesn't support dynamic loading (yet)." << endl;
81 return false;
82
83#endif // WIN32
84
85}
86
87void UeberBackend::go(void)
88{
89 pthread_mutex_lock(&d_mut);
90 d_go=true;
91 pthread_cond_broadcast(&d_cond);
92 pthread_mutex_unlock(&d_mut);
93}
94
95bool UeberBackend::getDomainInfo(const string &domain, DomainInfo &di)
96{
97 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
98 if((*i)->getDomainInfo(domain, di))
99 return true;
100 return false;
101}
102
103void UeberBackend::reload()
104{
105 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
106 {
107 ( *i )->reload();
108 }
109}
110
973ad2b5 111void UeberBackend::rediscover(string *status)
12c86877
BH
112{
113 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
114 {
973ad2b5
BH
115 string tmpstr;
116 ( *i )->rediscover(&tmpstr);
117 if(status)
118 *status+=tmpstr + (i!=backends.begin() ? "\n" : "");
12c86877
BH
119 }
120}
121
122
123void UeberBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
124{
125 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
126 {
127 ( *i )->getUnfreshSlaveInfos( domains );
128 }
129}
130
131
132
133void UeberBackend::getUpdatedMasters(vector<DomainInfo>* domains)
134{
135 for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i )
136 {
137 ( *i )->getUpdatedMasters( domains );
138 }
139}
140
141
142bool UeberBackend::getSOA(const string &domain, SOAData &sd)
143{
144 d_question.qtype=QType::SOA;
145 d_question.qname=domain;
146 d_question.zoneId=-1;
147
148 int cstat=cacheHas(d_question,d_answer);
149 if(cstat==0) {
150 return false;
151 }
152 else if(cstat==1) {
153 // ehm
154 DNSPacket::fillSOAData(d_answer.content,sd);
155 sd.domain_id=d_answer.domain_id;
156 sd.ttl=d_answer.ttl;
343546e5 157
12c86877
BH
158 return true;
159 }
160
161
162 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
163 if((*i)->getSOA(domain, sd)) {
164 DNSResourceRecord rr;
165 rr.qname=domain;
166 rr.qtype=QType::SOA;
167 rr.content=DNSPacket::serializeSOAData(sd);
168 rr.ttl=sd.ttl;
12c86877
BH
169 rr.domain_id=sd.domain_id;
170 addOneCache(d_question,rr);
171 return true;
172 }
173
174 addNegCache(d_question);
175 return false;
176}
177
178bool UeberBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **db)
179{
180 for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
181 if((*i)->superMasterBackend(ip,domain,nsset,account, db))
182 return true;
183 return false;
184
185}
186
187void UeberBackend::setStatus(const string &st)
188{
189 s_status=st;
190}
191
192UeberBackend::UeberBackend()
193{
194 UeberBackend("default");
195}
196
197UeberBackend::UeberBackend(const string &pname)
198{
199 programname=pname;
200 pthread_mutex_lock(&instances_lock);
201 instances.push_back(this); // report to the static list of ourself
202 pthread_mutex_unlock(&instances_lock);
203
204 tid=pthread_self();
205 stale=false;
206
207 backends=BackendMakers().all();
208}
209
210void UeberBackend::die()
211{
212 cleanup();
213 stale=true;
214}
215
216void del(DNSBackend* d)
217{
218 delete d;
219}
220
221void UeberBackend::cleanup()
222{
223 pthread_mutex_lock(&instances_lock);
224
225 remove(instances.begin(),instances.end(),this);
226 instances.resize(instances.size()-1);
227
228 pthread_mutex_unlock(&instances_lock);
229
230 for_each(backends.begin(),backends.end(),del);
231}
232
233int UeberBackend::cacheHas(const Question &q, DNSResourceRecord &rr)
234{
235 extern PacketCache PC;
236 static int *qcachehit=S.getPointer("query-cache-hit");
237 static int *qcachemiss=S.getPointer("query-cache-miss");
238
239 static int negqueryttl=arg().asNum("negquery-cache-ttl");
240 static int queryttl=arg().asNum("query-cache-ttl");
241 if(!negqueryttl && !queryttl) {
242 (*qcachemiss)++;
243 return -1;
244 }
245
246 string content;
247 // L<<Logger::Warning<<"looking up: "<<q.qname+"|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
248 bool ret=PC.getKey(q.qname+"|Q|"+q.qtype.getName()+"|"+itoa(q.zoneId), content); // think about lowercasing here
249
250 if(!ret) {
251 (*qcachemiss)++;
252 return -1;
253 }
254
255 (*qcachehit)++;
256 if(content.empty())
257 return 0;
258 rr.unSerialize(content);
259 return 1;
260}
261
262void UeberBackend::addNegCache(const Question &q)
263{
264 extern PacketCache PC;
265 static int negqueryttl=arg().asNum("negquery-cache-ttl");
266 if(!negqueryttl)
267 return;
268 // L<<Logger::Warning<<"negative inserting: "<<q.qname+"|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
269 PC.insert(q.qname+"|Q|"+q.qtype.getName()+"|"+itoa(q.zoneId),"",negqueryttl);
270}
271
272void UeberBackend::addOneCache(const Question &q, const DNSResourceRecord &rr)
273{
274 extern PacketCache PC;
275 static int queryttl=arg().asNum("query-cache-ttl");
276 if(!queryttl)
277 return;
278
279 PC.insert(q.qname+"|Q|"+q.qtype.getName()+"|"+itoa(q.zoneId),rr.serialize(),queryttl);
280}
281
282
283UeberBackend::~UeberBackend()
284{
285 DLOG(L<<Logger::Error<<"UeberBackend destructor called, removing ourselves from instances, and deleting our backends"<<endl);
286 cleanup();
287}
288
289// this handle is more magic than most
290void UeberBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId)
291{
292 if(stale) {
293 L<<Logger::Error<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl;
294 throw AhuException("We are stale, please recycle");
295 }
296
297 DLOG(L<<"UeberBackend received question for "<<qtype.getName()<<" of "<<qname<<endl);
298 if(!d_go) {
299 pthread_mutex_lock(&d_mut);
300 while (d_go==false) {
301 L<<Logger::Error<<"UeberBackend is blocked, waiting for 'go'"<<endl;
302 pthread_cond_wait(&d_cond, &d_mut);
303 L<<Logger::Error<<"Broadcast received, unblocked"<<endl;
304 }
305 pthread_mutex_unlock(&d_mut);
306 }
307
308 d_handle.i=0;
309 d_handle.qtype=qtype;
310 d_handle.qname=qname;
311 d_handle.pkt_p=pkt_p;
312 d_ancount=0;
313
314 if(!backends.size()) {
315 L<<Logger::Error<<Logger::NTLog<<"No database backends available - unable to answer questions."<<endl;
316 stale=true; // please recycle us!
317 throw AhuException("We are stale, please recycle");
318 }
319 else {
320 d_question.qtype=qtype;
321 d_question.qname=qname;
322 d_question.zoneId=zoneId;
323 int cstat=cacheHas(d_question,d_answer);
324 if(cstat<0) { // nothing
325 d_negcached=d_cached=false;
326 (d_handle.d_hinterBackend=backends[d_handle.i++])->lookup(qtype, qname,pkt_p,zoneId);
327 }
328 else if(cstat==0) {
329 d_negcached=true;
330 d_cached=false;
331 }
332 else {
333 d_negcached=false;
334 d_cached=true;
335 }
336 }
337
338 d_handle.parent=this;
339
340}
341
342bool UeberBackend::get(DNSResourceRecord &rr)
343{
344 if(d_negcached) {
345 return false;
346 }
347
348 if(d_cached) {
349 rr=d_answer;
350 d_negcached=true; // ugly, confusing
351 return true;
352 }
353
354 if(!d_handle.get(rr)) {
355 if(!d_ancount && !d_handle.qname.empty()) // don't cache axfr
356 addNegCache(d_question);
357
358 if(d_ancount==1) {
359 addOneCache(d_question,lastrr);
360 }
361
362 return false;
363 }
364
365 if(!d_ancount++) {
366 lastrr=rr;
367 }
368 return true;
369}
370
371bool UeberBackend::list(int domain_id)
372{
373 d_cached=d_negcached=false;
374 d_ancount=0;
375 if(stale) {
376 L<<Logger::Error<<"Stale ueberbackend received question, signalling that we want to be recycled"<<endl;
377 throw AhuException("We are stale, please recycle");
378 }
379
380 DLOG(L<<"UeberBackend received list request for domain id "<<domain_id<<endl);
381
382 pthread_mutex_lock(&d_mut);
383 while (d_go==false) {
384 L<<Logger::Error<<"UeberBackend is blocked, waiting for 'go'"<<endl;
385 pthread_cond_wait(&d_cond, &d_mut);
386 L<<Logger::Error<<"Broadcast received, unblocked"<<endl;
387 }
388
389 pthread_mutex_unlock(&d_mut);
390
391 d_handle.i=0;
392 d_handle.pkt_p=0;
393 d_handle.qname="";
394 d_negcached=false;
395
396 if(!backends.size()) {
397 return 0; // we failed
398 }
399 else {
400 while(!(d_handle.d_hinterBackend=backends[d_handle.i++])->list(domain_id) && d_handle.i<backends.size())
401 ;
402 }
403 d_handle.parent=this;
404
405 return true;
406}
407
408
409int UeberBackend::handle::instances=0;
410
411UeberBackend::handle::handle()
412{
413 // L<<Logger::Warning<<"Handle instances: "<<instances<<endl;
414 instances++;
415}
416
417UeberBackend::handle::~handle()
418{
419 instances--;
420}
421
422
423
424
425
426bool UeberBackend::handle::get(DNSResourceRecord &r)
427{
428 DLOG(L << "Ueber get() was called for a "<<qtype.getName()<<" record" << endl);
429 bool isMore=false;
430 while(d_hinterBackend && !(isMore=d_hinterBackend->get(r))) { // this backend out of answers
431 if(i<parent->backends.size()) {
432 DLOG(L<<"Backend #"<<i<<" of "<<parent->backends.size()
433 <<" out of answers, taking next"<<endl);
434
435 d_hinterBackend=parent->backends[i++];
436 d_hinterBackend->lookup(qtype,qname,pkt_p);
437 }
438 else
439 break;
440
441 DLOG(L<<"Now asking backend #"<<i<<endl);
442 }
443
444 if(!isMore && i==parent->backends.size()) {
445 DLOG(L<<"UeberBackend reached end of backends"<<endl);
446 return false;
447 }
448
449 DLOG(L<<"Found an answering backend - will not try another one"<<endl);
450 i=parent->backends.size(); // don't go on to the next backend
451 return true;
452}