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