]>
Commit | Line | Data |
---|---|---|
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 | ||
45 | extern StatBag S; | |
46 | ||
47 | vector<UeberBackend *>UeberBackend::instances; | |
48 | pthread_mutex_t UeberBackend::instances_lock=PTHREAD_MUTEX_INITIALIZER; | |
49 | ||
50 | sem_t UeberBackend::d_dynserialize; | |
51 | string UeberBackend::programname; | |
52 | string UeberBackend::s_status; | |
53 | ||
54 | // initially we are blocked | |
55 | bool UeberBackend::d_go=false; | |
56 | pthread_mutex_t UeberBackend::d_mut = PTHREAD_MUTEX_INITIALIZER; | |
57 | pthread_cond_t UeberBackend::d_cond = PTHREAD_COND_INITIALIZER; | |
58 | ||
59 | int 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 |
66 | bool 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 | ||
87 | void 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 | ||
95 | bool 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 | ||
103 | void UeberBackend::reload() | |
104 | { | |
105 | for ( vector< DNSBackend * >::iterator i = backends.begin(); i != backends.end(); ++i ) | |
106 | { | |
107 | ( *i )->reload(); | |
108 | } | |
109 | } | |
110 | ||
973ad2b5 | 111 | void 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 | ||
123 | void 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 | ||
133 | void 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 | ||
142 | bool 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 | ||
178 | bool 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 | ||
187 | void UeberBackend::setStatus(const string &st) | |
188 | { | |
189 | s_status=st; | |
190 | } | |
191 | ||
192 | UeberBackend::UeberBackend() | |
193 | { | |
194 | UeberBackend("default"); | |
195 | } | |
196 | ||
197 | UeberBackend::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 | ||
210 | void UeberBackend::die() | |
211 | { | |
212 | cleanup(); | |
213 | stale=true; | |
214 | } | |
215 | ||
216 | void del(DNSBackend* d) | |
217 | { | |
218 | delete d; | |
219 | } | |
220 | ||
221 | void 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 | ||
233 | int 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 | ||
262 | void 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 | ||
272 | void 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 | ||
283 | UeberBackend::~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 | |
290 | void 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 | ||
342 | bool 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 | ||
371 | bool 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 | ||
409 | int UeberBackend::handle::instances=0; | |
410 | ||
411 | UeberBackend::handle::handle() | |
412 | { | |
413 | // L<<Logger::Warning<<"Handle instances: "<<instances<<endl; | |
414 | instances++; | |
415 | } | |
416 | ||
417 | UeberBackend::handle::~handle() | |
418 | { | |
419 | instances--; | |
420 | } | |
421 | ||
422 | ||
423 | ||
424 | ||
425 | ||
426 | bool 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 | } |