]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/backends/bind/bindbackend.cc
a8b980a95d1cfbf3322a24b704994920366da8d6
[thirdparty/pdns.git] / pdns / backends / bind / bindbackend.cc
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 version 2 as
7 published by the Free Software Foundation;
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
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 // $Id: bindbackend.cc,v 1.9 2002/12/20 16:12:06 ahu Exp $
19 #include <errno.h>
20 #include <string>
21 #include <map>
22 #include <set>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 using namespace std;
28
29 #include "dns.hh"
30 #include "dnsbackend.hh"
31 #include "bindbackend.hh"
32 #include "dnspacket.hh"
33
34 #include "zoneparser.hh"
35 #include "bindparser.hh"
36 #include "logger.hh"
37 #include "arguments.hh"
38 #include "huffman.hh"
39 #include "qtype.hh"
40 #include "misc.hh"
41 #include "dynlistener.hh"
42 #include "lock.hh"
43 using namespace std;
44
45
46 cmap_t BindBackend::d_qnames;
47 map<int,vector<vector<BBResourceRecord>* > > BindBackend::d_zone_id_map;
48 set<string> BindBackend::s_contents;
49 HuffmanCodec BindBackend::s_hc;
50
51 map<unsigned int,BBDomainInfo> BindBackend::d_bbds;
52
53 int BindBackend::s_first=1;
54 pthread_mutex_t BindBackend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
55
56 BBDomainInfo::BBDomainInfo()
57 {
58 d_loaded=false;
59 d_last_check=0;
60 d_checknow=false;
61 d_rwlock=new pthread_rwlock_t;
62 d_status="Seen in bind configuration";
63 d_confcount=0;
64 //cout<<"Generated a new bbdomaininfo: "<<(void*)d_rwlock<<"/"<<getpid()<<endl;
65 pthread_rwlock_init(d_rwlock,0);
66 }
67
68 void BBDomainInfo::setCheckInterval(time_t seconds)
69 {
70 d_checkinterval=seconds;
71 }
72
73 bool BBDomainInfo::current()
74 {
75 if(d_checknow)
76 return false;
77
78 if(!d_checknow && !d_checkinterval || (time(0)-d_lastcheck<d_checkinterval) || d_filename.empty())
79 return true;
80
81 return (getCtime()==d_ctime);
82 }
83
84 time_t BBDomainInfo::getCtime()
85 {
86 struct stat buf;
87
88 if(d_filename.empty() || stat(d_filename.c_str(),&buf)<0)
89 return 0;
90 d_lastcheck=time(0);
91 return buf.st_ctime;
92 }
93
94 void BBDomainInfo::setCtime()
95 {
96 struct stat buf;
97 if(stat(d_filename.c_str(),&buf)<0)
98 return;
99 d_ctime=buf.st_ctime;
100 }
101
102 void BindBackend::setNotified(u_int32_t id, u_int32_t serial)
103 {
104 d_bbds[id].d_lastnotified=serial;
105 }
106
107 void BindBackend::setFresh(u_int32_t domain_id)
108 {
109 d_bbds[domain_id].d_last_check=time(0);
110 }
111
112 bool BindBackend::startTransaction(const string &qname, int id)
113 {
114 BBDomainInfo &bbd=d_bbds[d_transaction_id=id];
115 d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
116 d_of=new ofstream(d_transaction_tmpname.c_str());
117 if(!*d_of) {
118 throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
119 unlink(d_transaction_tmpname.c_str());
120 delete d_of;
121 d_of=0;
122 }
123
124 *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
125 *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<bbd.d_master<<endl<<"; at "<<nowTime()<<endl;
126 bbd.lock();
127 return true;
128 }
129
130 bool BindBackend::commitTransaction()
131 {
132 delete d_of;
133 d_of=0;
134 if(rename(d_transaction_tmpname.c_str(),d_bbds[d_transaction_id].d_filename.c_str())<0)
135 throw DBException("Unable to commit (rename to: '"+d_bbds[d_transaction_id].d_filename+"') AXFRed zone: "+stringerror());
136
137 queueReload(&d_bbds[d_transaction_id]);
138 d_bbds[d_transaction_id].unlock();
139 return true;
140 }
141
142 bool BindBackend::abortTransaction()
143 {
144 delete d_of;
145 d_of=0;
146 unlink(d_transaction_tmpname.c_str());
147 return true;
148 }
149
150
151
152 bool BindBackend::feedRecord(const DNSResourceRecord &r)
153 {
154 string qname=r.qname;
155 string domain=d_bbds[d_transaction_id].d_name;
156
157 if(!stripDomainSuffix(&qname,domain))
158 throw DBException("out-of-zone data '"+qname+"' during AXFR of zone '"+domain+"'");
159
160 string content=r.content;
161
162 // SOA needs stripping too! XXX FIXME
163 switch(r.qtype.getCode()) {
164 case QType::TXT:
165 *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t\""<<r.content<<"\""<<endl;
166 break;
167 case QType::MX:
168 if(!stripDomainSuffix(&content,domain))
169 content+=".";
170 *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.priority<<"\t"<<content<<endl;
171 break;
172 case QType::CNAME:
173 case QType::NS:
174 if(!stripDomainSuffix(&content,domain))
175 content+=".";
176 *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<content<<endl;
177 break;
178 default:
179 *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.content<<endl;
180 break;
181 }
182
183 return true;
184 }
185
186 void BindBackend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
187 {
188 SOAData soadata;
189 for(map<u_int32_t,BBDomainInfo>::iterator i=d_bbds.begin();i!=d_bbds.end();++i) {
190 if(!i->second.d_master.empty())
191 continue;
192 soadata.serial=0;
193 try {
194 getSOA(i->second.d_name,soadata); // we might not *have* a SOA yet
195 }
196 catch(...){}
197 DomainInfo di;
198 di.id=i->first;
199 di.serial=soadata.serial;
200 di.zone=i->second.d_name;
201 di.last_check=i->second.d_last_check;
202 di.backend=this;
203 di.kind=DomainInfo::Master;
204 if(!i->second.d_lastnotified) // don't do notification storm on startup
205 i->second.d_lastnotified=soadata.serial;
206 else
207 if(soadata.serial!=i->second.d_lastnotified)
208 changedDomains->push_back(di);
209
210 }
211 }
212
213 void BindBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
214 {
215 for(map<u_int32_t,BBDomainInfo>::const_iterator i=d_bbds.begin();i!=d_bbds.end();++i) {
216 if(i->second.d_master.empty())
217 continue;
218 DomainInfo sd;
219 sd.id=i->first;
220 sd.zone=i->second.d_name;
221 sd.master=i->second.d_master;
222 sd.last_check=i->second.d_last_check;
223 sd.backend=this;
224 sd.kind=DomainInfo::Slave;
225 SOAData soadata;
226 soadata.serial=0;
227 soadata.refresh=0;
228 soadata.serial=0;
229
230 try {
231 getSOA(i->second.d_name,soadata); // we might not *have* a SOA yet
232 }
233 catch(...){}
234 sd.serial=soadata.serial;
235 if(sd.last_check+soadata.refresh<(unsigned int)time(0))
236 unfreshDomains->push_back(sd);
237 }
238 }
239
240 bool BindBackend::getDomainInfo(const string &domain, DomainInfo &di)
241 {
242 for(map<u_int32_t,BBDomainInfo>::const_iterator i=d_bbds.begin();i!=d_bbds.end();++i) {
243 if(i->second.d_name==domain) {
244 di.id=i->first;
245 di.zone=domain;
246 di.master=i->second.d_master;
247 di.last_check=i->second.d_last_check;
248 di.backend=this;
249 di.kind=i->second.d_master.empty() ? DomainInfo::Master : DomainInfo::Slave;
250 di.serial=0;
251 try {
252 SOAData sd;
253 getSOA(i->second.d_name,sd); // we might not *have* a SOA yet
254 di.serial=sd.serial;
255 }
256 catch(...){}
257
258 return true;
259 }
260 }
261 return false;
262 }
263
264
265 static string canonic(string ret)
266 {
267 string::iterator i;
268
269 for(i=ret.begin();
270 i!=ret.end();
271 ++i)
272 *i=*i; //tolower(*i);
273
274
275 if(*(i-1)=='.')
276 ret.resize(i-ret.begin()-1);
277 return ret;
278 }
279
280 /** This function adds a record to a domain with a certain id. */
281 void BindBackend::insert(int id, const string &qnameu, const string &qtype, const string &content, int ttl=300, int prio=25)
282 {
283 static int s_count;
284 static unsigned int len;
285 static unsigned int ulen;
286 DLOG(
287 if(!((s_count++)%10000))
288 cerr<<"\r"<<s_count-1<<", "<<s_contents.size()<<" different contents, "<<d_qnames.size()<<" different qnames, "<<len/1000000<<"MB, saved: "<<
289 (ulen-len)/1000;
290 );
291 string compressed;
292 s_hc.encode(toLower(canonic(qnameu)),compressed);
293 // string(compressed).swap(compressed);
294 // cout<<"saved: "<<qnameu.size()-compressed.size()<<endl;
295
296 vector<BBResourceRecord>::const_iterator i;
297
298 if(d_qnames[compressed].empty()) { // NEW! NEW! NEW! in de top 40!
299 d_zone_id_map[id].push_back(&d_qnames[compressed]);
300 i=d_qnames[compressed].end();
301 }
302 else
303 for(i=d_qnames[compressed].begin();i!=d_qnames[compressed].end();++i)
304 if(((i)->qtype==QType::chartocode(qtype.c_str())))
305 if((*(i)->content==canonic(content)))
306 break;
307
308 // never saw this specific name/type/content triple before
309 if(i==d_qnames[compressed].end()) {
310 BBResourceRecord v=resourceMaker(id,qtype,canonic(content),ttl,prio);
311 v.qnameptr=&d_qnames.find(compressed)->first;
312 len+=compressed.size();
313 ulen+=qnameu.size();
314 d_qnames[compressed].push_back(v);
315
316 d_qnames[compressed].reserve(0);
317 // vector<BBResourceRecord>&tmp=d_qnames[compressed];
318 // vector<BBResourceRecord>(tmp).swap(tmp);
319 }
320 else {
321 s_count--;
322 }
323 }
324
325
326 /** Helper function that creates a BBResourceRecord and does s_content housekeeping */
327 BBResourceRecord BindBackend::resourceMaker(int id, const string &qtype, const string &content, int ttl, int prio)
328 {
329 BBResourceRecord make;
330
331 make.domain_id=id;
332
333 make.qtype=QType::chartocode(qtype.c_str());
334 if(!make.qtype)
335 throw AhuException("Unknown qtype '"+qtype+"'");
336
337 set<string>::const_iterator i=s_contents.find(content);
338 if(i==s_contents.end()) {
339 s_contents.insert(content);
340 i=s_contents.find(content);
341 }
342 make.content=&*i;
343 make.ttl=ttl;
344 make.priority=prio;
345 return make;
346 }
347
348 static BindBackend *us;
349
350 void BindBackend::reload()
351 {
352 for(map<u_int32_t,BBDomainInfo>::iterator i=us->d_bbds.begin();i!=us->d_bbds.end();++i)
353 i->second.d_checknow=true;
354 }
355
356 string BindBackend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
357 {
358 ostringstream ret;
359 bool doReload=false;
360 for(map<u_int32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j) {
361 doReload=false;
362 if(parts.size()==1)
363 doReload=true;
364 else
365 for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) // O(N) badness XXX FIXME
366 if(*i==j->second.d_name) {
367 doReload=true;
368 break;
369 }
370
371 if(doReload) {
372 j->second.lock();
373
374 us->queueReload(&j->second);
375 j->second.unlock();
376 ret<<j->second.d_name<< (j->second.d_loaded ? "": "[rejected]") <<"\t"<<j->second.d_status<<"\n";
377 }
378 doReload=false;
379 }
380
381 return ret.str();
382 }
383
384
385 string BindBackend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
386 {
387 ostringstream ret;
388 bool doPrint=false;
389 for(map<u_int32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j) {
390 doPrint=false;
391 if(parts.size()==1)
392 doPrint=true;
393 else
394 for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) // O(N) badness XXX FIXME
395 if(*i==j->second.d_name) {
396 doPrint=true;
397 break;
398 }
399
400 if(doPrint)
401 ret<<j->second.d_name<< (j->second.d_loaded ? "": "[rejected]") <<"\t"<<j->second.d_status<<"\n";
402 doPrint=false;
403 }
404
405 return ret.str();
406 }
407
408
409 string BindBackend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
410 {
411 ostringstream ret;
412 for(map<u_int32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j)
413 if(!j->second.d_loaded)
414 ret<<j->second.d_name<<"\t"<<j->second.d_status<<endl;
415
416 return ret.str();
417 }
418
419 static void callback(unsigned int domain_id, const string &domain, const string &qtype, const string &content, int ttl, int prio)
420 {
421 us->insert(domain_id,domain,qtype,content,ttl,prio);
422 }
423
424
425
426 BindBackend::BindBackend(const string &suffix)
427 {
428 d_logprefix="[bind"+suffix+"backend]";
429 setArgPrefix("bind"+suffix);
430 Lock l(&s_startup_lock);
431
432 if(!s_first) {
433 return;
434 }
435
436 s_first=0;
437 if(!mustDo("enable-huffman"))
438 s_hc.passthrough(true);
439
440 if(mustDo("example-zones")) {
441 insert(0,"www.example.com","A","1.2.3.4");
442 insert(0,"example.com","SOA","ns1.example.com hostmaster.example.com");
443 insert(0,"example.com","NS","ns1.example.com",86400);
444 insert(0,"example.com","NS","ns2.example.com",86400);
445 insert(0,"example.com","MX","mail.example.com",3600,25);
446 insert(0,"example.com","MX","mail1.example.com",3600,25);
447 insert(0,"mail.example.com","A","4.3.2.1");
448 insert(0,"mail1.example.com","A","5.4.3.2");
449 insert(0,"ns1.example.com","A","4.3.2.1");
450 insert(0,"ns2.example.com","A","5.4.3.2");
451
452 for(int i=0;i<1000;i++)
453 insert(0,"host-"+itoa(i)+".example.com","A","2.3.4.5");
454
455 BBDomainInfo bbd;
456 bbd.d_name="example.com";
457 bbd.d_filename="";
458 bbd.d_id=0;
459 d_bbds[0]=bbd;
460 d_bbds[0].d_loaded=true;
461 d_bbds[0].d_status="parsed into memory at "+nowTime();
462 }
463
464 loadConfig();
465
466
467 extern DynListener *dl;
468 us=this;
469 dl->registerFunc("BIND-RELOAD-NOW", &DLReloadNowHandler);
470 dl->registerFunc("BIND-DOMAIN-STATUS", &DLDomStatusHandler);
471 dl->registerFunc("BIND-LIST-REJECTS", &DLListRejectsHandler);
472 }
473
474
475 void BindBackend::rediscover(string *status)
476 {
477 loadConfig(status);
478 }
479
480 void BindBackend::loadConfig(string* status)
481 {
482 static int domain_id=1;
483
484 if(!getArg("config").empty()) {
485 BindParser BP;
486 try {
487 BP.parse(getArg("config"));
488 }
489 catch(AhuException &ae) {
490 L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl;
491 throw;
492 }
493
494 ZoneParser ZP;
495
496 vector<BindDomainInfo> domains=BP.getDomains();
497
498 us=this;
499
500 ZP.setDirectory(BP.getDirectory());
501 ZP.setCallback(&callback);
502 L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), will report when done"<<endl;
503
504 int rejected=0;
505 int newdomains=0;
506
507 map<unsigned int, BBDomainInfo> nbbds;
508
509 for(vector<BindDomainInfo>::const_iterator i=domains.begin();
510 i!=domains.end();
511 ++i)
512 {
513 BBDomainInfo bbd;
514 if(i->type!="master" && i->type!="slave") {
515 L<<Logger::Warning<<d_logprefix<<" Warning! Skipping '"<<i->type<<"' zone '"<<i->name<<"'"<<endl;
516 continue;
517 }
518 map<unsigned int, BBDomainInfo>::const_iterator j=d_bbds.begin();
519 for(;j!=d_bbds.end();++j)
520 if(j->second.d_name==i->name) {
521 bbd=j->second;
522 break;
523 }
524 if(j==d_bbds.end()) { // entirely new
525 bbd.d_id=domain_id++;
526
527 bbd.setCheckInterval(getArgAsNum("check-interval"));
528 bbd.d_lastnotified=0;
529 bbd.d_loaded=false;
530 }
531
532 bbd.d_name=i->name;
533 bbd.d_filename=i->filename;
534 bbd.d_master=i->master;
535
536 nbbds[bbd.d_id]=bbd;
537 if(!bbd.d_loaded) {
538 L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
539
540 try {
541 ZP.parse(i->filename,i->name,bbd.d_id); // calls callback for us
542 nbbds[bbd.d_id].setCtime();
543 nbbds[bbd.d_id].d_loaded=true; // does this perform locking for us?
544 nbbds[bbd.d_id].d_status="parsed into memory at "+nowTime();
545
546 }
547 catch(AhuException &ae) {
548 ostringstream msg;
549 msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<i->filename<<"': "<<ae.reason;
550 if(status)
551 *status+=msg.str();
552 nbbds[bbd.d_id].d_status=msg.str();
553 L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
554 rejected++;
555 }
556 }
557
558 vector<vector<BBResourceRecord> *>&tmp=d_zone_id_map[bbd.d_id]; // shrink trick
559 vector<vector<BBResourceRecord> *>(tmp).swap(tmp);
560 }
561
562
563 int remdomains=0;
564 set<string> oldnames, newnames;
565 for(map<unsigned int, BBDomainInfo>::const_iterator j=d_bbds.begin();j!=d_bbds.end();++j) {
566 oldnames.insert(j->second.d_name);
567 }
568 for(map<unsigned int, BBDomainInfo>::const_iterator j=nbbds.begin();j!=nbbds.end();++j) {
569 newnames.insert(j->second.d_name);
570 }
571
572 vector<string> diff;
573 set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
574 remdomains=diff.size();
575 for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
576 L<<Logger::Error<<"Removed: "<<*k<<endl;
577
578 for(map<unsigned int, BBDomainInfo>::iterator j=d_bbds.begin();j!=d_bbds.end();++j) { // O(N*M)
579 for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
580 if(j->second.d_name==*k) {
581 L<<Logger::Error<<"Removing records from zone '"<<j->second.d_name<<"' from memory"<<endl;
582 j->second.lock();
583 j->second.d_loaded=false;
584 nukeZoneRecords(&j->second);
585 j->second.unlock();
586 break;
587 }
588 }
589
590 vector<string> diff2;
591 set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff2));
592 newdomains=diff2.size();
593
594 d_bbds.swap(nbbds); // commit
595 ostringstream msg;
596 msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed";
597 if(status)
598 *status=msg.str();
599
600 L<<Logger::Error<<d_logprefix<<msg.str()<<endl;
601 L<<Logger::Info<<d_logprefix<<" Number of hash buckets: "<<d_qnames.bucket_count()<<", number of entries: "<<d_qnames.size()<< endl;
602 }
603 }
604
605 /** nuke all records from memory, keep bbd intact though. Must be called with bbd lock held already! */
606 void BindBackend::nukeZoneRecords(BBDomainInfo *bbd)
607 {
608 bbd->d_loaded=0; // block further access
609
610 // this emtpies all d_qnames vectors belonging to this domain. We find these vectors via d_zone_id_map
611 for(vector<vector<BBResourceRecord> *>::iterator i=d_zone_id_map[bbd->d_id].begin();
612 i!=d_zone_id_map[bbd->d_id].end();++i) {
613 (*i)->clear();
614 }
615
616 // empty our d_zone_id_map of the references to the now empty vectors (which are not gone from d_qnames, btw)
617 d_zone_id_map[bbd->d_id].clear();
618 }
619
620 /** Must be called with bbd locked already. Will not be unlocked on return, is your own problem.
621 Does not throw errors or anything, may update d_status however */
622
623
624 void BindBackend::queueReload(BBDomainInfo *bbd)
625 {
626 // we reload *now* for the time being
627 //cout<<"unlock domain"<<endl;
628 bbd->unlock();
629 //cout<<"lock it again"<<endl;
630
631 bbd->lock();
632 try {
633 nukeZoneRecords(bbd);
634
635 ZoneParser ZP;
636 us=this;
637 ZP.setCallback(&callback);
638 ZP.parse(bbd->d_filename,bbd->d_name,bbd->d_id);
639 bbd->setCtime();
640 // and raise d_loaded again!
641 bbd->d_loaded=1;
642 bbd->d_checknow=0;
643 bbd->d_status="parsed into memory at "+nowTime();
644 L<<Logger::Warning<<"Zone '"<<bbd->d_name<<"' ("<<bbd->d_filename<<") reloaded"<<endl;
645 }
646 catch(AhuException &ae) {
647 ostringstream msg;
648 msg<<" error at "+nowTime()+" parsing '"<<bbd->d_name<<"' from file '"<<bbd->d_filename<<"': "<<ae.reason;
649 bbd->d_status=msg.str();
650 }
651 }
652
653 void BindBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId )
654 {
655 d_handle=new BindBackend::handle;
656 DLOG(L<<"BindBackend constructing handle for search for "<<qtype.getName()<<" for "<<
657 qname<<endl);
658
659 d_handle->qname=qname;
660 d_handle->parent=this;
661 d_handle->qtype=qtype;
662 string compressed;
663 s_hc.encode(toLower(qname),compressed);
664 d_handle->d_records=d_qnames[compressed];
665 d_handle->d_bbd=0;
666 if(!d_handle->d_records.empty()) {
667 BBDomainInfo& bbd=d_bbds[d_handle->d_records.begin()->domain_id];
668 if(!bbd.d_loaded) {
669 delete d_handle;
670 throw DBException("Zone temporarily not available (file missing, or master dead)"); // fuck
671 }
672
673 if(!bbd.tryRLock()) {
674 L<<Logger::Warning<<"Can't get read lock on zone '"<<bbd.d_name<<"'"<<endl;
675 delete d_handle;
676 throw DBException("Temporarily unavailable due to a zone lock"); // fuck
677 }
678
679
680 if(!bbd.current()) {
681 L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
682 queueReload(&bbd);
683 }
684 d_handle->d_bbd=&bbd;
685 }
686 else {
687 DLOG(L<<"Query with no results"<<endl);
688 }
689 d_handle->d_iter=d_handle->d_records.begin();
690 d_handle->d_list=false;
691 }
692
693 BindBackend::handle::handle()
694 {
695 d_bbd=0;
696 count=0;
697 }
698
699 bool BindBackend::get(DNSResourceRecord &r)
700 {
701 if(!d_handle->get(r)) {
702 delete d_handle;
703 d_handle=0;
704 return false;
705 }
706 return true;
707 }
708
709 bool BindBackend::handle::get(DNSResourceRecord &r)
710 {
711 if(d_list)
712 return get_list(r);
713 else
714 return get_normal(r);
715 }
716
717 bool BindBackend::handle::get_normal(DNSResourceRecord &r)
718 {
719 DLOG(L << "BindBackend get() was called for "<<qtype.getName() << " record for "<<
720 qname<<"- "<<d_records.size()<<" available!"<<endl);
721
722
723 while(d_iter!=d_records.end() && !(qtype=="ANY" || (d_iter)->qtype==QType(qtype).getCode())) {
724 DLOG(L<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).getName()<<": '"<<*d_iter->content<<"'"<<endl);
725 d_iter++;
726 }
727 if(d_iter==d_records.end()) { // we've reached the end
728 if(d_bbd) {
729 d_bbd->unlock();
730 d_bbd=0;
731 }
732 return false;
733 }
734
735 DLOG(L << "BindBackend get() returning a rr with a "<<QType(d_iter->qtype).getCode()<<endl);
736
737 r.qname=qname; // fill this in
738
739 r.content=*(d_iter)->content;
740 r.domain_id=(d_iter)->domain_id;
741 r.qtype=(d_iter)->qtype;
742 r.ttl=(d_iter)->ttl;
743 r.priority=(d_iter)->priority;
744 d_iter++;
745
746 return true;
747 }
748
749 bool BindBackend::list(int id)
750 {
751 d_handle=new BindBackend::handle;
752 DLOG(L<<"BindBackend constructing handle for list of "<<id<<endl);
753
754 d_handle->d_qname_iter=d_zone_id_map[id].begin();
755 d_handle->d_qname_end=d_zone_id_map[id].end(); // iter now points to a vector of pointers to vector<BBResourceRecords>
756 d_handle->d_riter=(*(d_handle->d_qname_iter))->begin();
757 d_handle->d_rend=(*(d_handle->d_qname_iter))->end();
758 // rend?
759 d_handle->parent=this;
760 d_handle->id=id;
761 d_handle->d_list=true;
762 return true;
763 }
764
765 // naam -> naamnummer
766 // naamnummer -> vector<BBResourceRecords>, BBResourceRecord bevat ook een pointer naar
767
768 bool BindBackend::handle::get_list(DNSResourceRecord &r)
769 {
770 DLOG(L << "BindBackend get_list()"<<endl);
771
772 while(d_riter==d_rend) {
773 DLOG(L<<"Starting new record"<<endl);
774 d_qname_iter++;
775 if(d_qname_iter==d_qname_end) { // we've reached the end of recordsets for this id
776 DLOG(L<<"Really stop!"<<endl);
777 return false;
778 }
779 d_riter=(*(d_qname_iter))->begin();
780 d_rend=(*(d_qname_iter))->end();
781 }
782 // d_riter points to a pointer to BBResourceRecord
783
784 // r.qname=qname; // fill this in HOW?!
785
786 r.qname=parent->s_hc.decode(*d_riter->qnameptr);
787
788 r.content=*(d_riter)->content;
789 r.domain_id=(d_riter)->domain_id;
790 r.qtype=(d_riter)->qtype;
791 r.ttl=(d_riter)->ttl;
792 r.priority=(d_riter)->priority;
793 d_riter++;
794 return true;
795 }
796
797 class BindFactory : public BackendFactory
798 {
799 public:
800 BindFactory() : BackendFactory("bind") {}
801
802 void declareArguments(const string &suffix="")
803 {
804 declare(suffix,"config","Location of named.conf","");
805 declare(suffix,"example-zones","Install example zones","no");
806 declare(suffix,"enable-huffman","Enable huffman compression","no");
807 declare(suffix,"check-interval","Interval for zonefile changes","0");
808 }
809
810 DNSBackend *make(const string &suffix="")
811 {
812 return new BindBackend(suffix);
813 }
814 };
815
816
817 //! Magic class that is activated when the dynamic library is loaded
818 class BindLoader
819 {
820 public:
821 BindLoader()
822 {
823 BackendMakers().report(new BindFactory);
824 L<<Logger::Notice<<"[BindBackend] This is the bind backend version "VERSION" ("__DATE__", "__TIME__") reporting"<<endl;
825 }
826 };
827 static BindLoader bindloader;