]> git.ipfire.org Git - thirdparty/pdns.git/blob - modules/bindbackend/bindbackend2.cc
snap
[thirdparty/pdns.git] / modules / bindbackend / bindbackend2.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2014 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
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <errno.h>
27 #include <string>
28 #include <set>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <fstream>
33 #include <fcntl.h>
34 #include <sstream>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/foreach.hpp>
37 #include "pdns/dnsseckeeper.hh"
38 #include "pdns/dnssecinfra.hh"
39 #include "pdns/base32.hh"
40 #include "pdns/namespaces.hh"
41 #include "pdns/dns.hh"
42 #include "pdns/dnsbackend.hh"
43 #include "bindbackend2.hh"
44 #include "pdns/dnspacket.hh"
45 #include "pdns/zoneparser-tng.hh"
46 #include "pdns/bindparserclasses.hh"
47 #include "pdns/logger.hh"
48 #include "pdns/arguments.hh"
49 #include "pdns/qtype.hh"
50 #include "pdns/misc.hh"
51 #include "pdns/dynlistener.hh"
52 #include "pdns/lock.hh"
53 #include "pdns/namespaces.hh"
54
55 /*
56 All instances of this backend share one s_state, which is indexed by zone name and zone id.
57 The s_state is protected by a read/write lock, and the goal it to only interact with it briefly.
58 When a query comes in, we take a read lock and COPY the best zone to answer from s_state (BB2DomainInfo object)
59 All answers are served from this copy.
60
61 To interact with s_state, use safeGetBBDomainInfo (search on name or id), safePutBBDomainInfo (to update)
62 or safeRemoveBBDomainInfo. These all lock as they should.
63
64 Several functions need to traverse s_state to get data for the rest of PowerDNS. When doing so,
65 you need to manually take the s_state_lock (read).
66
67 Parsing zones happens with parseZone(), which fills a BB2DomainInfo object. This can then be stored with safePutBBDomainInfo.
68
69 Finally, the BB2DomainInfo contains all records as a LookButDontTouch object. This makes sure you only look, but don't touch, since
70 the records might be in use in other places.
71 */
72
73 Bind2Backend::state_t Bind2Backend::s_state;
74 int Bind2Backend::s_first=1;
75 bool Bind2Backend::s_ignore_broken_records=false;
76
77 pthread_rwlock_t Bind2Backend::s_state_lock=PTHREAD_RWLOCK_INITIALIZER;
78 pthread_mutex_t Bind2Backend::s_supermaster_config_lock=PTHREAD_MUTEX_INITIALIZER; // protects writes to config file
79 pthread_mutex_t Bind2Backend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
80 string Bind2Backend::s_binddirectory;
81
82 BB2DomainInfo::BB2DomainInfo()
83 {
84 d_loaded=false;
85 d_lastcheck=0;
86 d_checknow=false;
87 d_status="Unknown";
88 }
89
90 void BB2DomainInfo::setCheckInterval(time_t seconds)
91 {
92 d_checkinterval=seconds;
93 }
94
95 bool BB2DomainInfo::current()
96 {
97 if(d_checknow) {
98 return false;
99 }
100
101 if(!d_checkinterval)
102 return true;
103
104 if(time(0) - d_lastcheck < d_checkinterval)
105 return true;
106
107 if(d_filename.empty())
108 return true;
109
110 return (getCtime()==d_ctime);
111 }
112
113 time_t BB2DomainInfo::getCtime()
114 {
115 struct stat buf;
116
117 if(d_filename.empty() || stat(d_filename.c_str(),&buf)<0)
118 return 0;
119 d_lastcheck=time(0);
120 return buf.st_ctime;
121 }
122
123 void BB2DomainInfo::setCtime()
124 {
125 struct stat buf;
126 if(stat(d_filename.c_str(),&buf)<0)
127 return;
128 d_ctime=buf.st_ctime;
129 }
130
131 bool Bind2Backend::safeGetBBDomainInfo(int id, BB2DomainInfo* bbd)
132 {
133 ReadLock rl(&s_state_lock);
134 state_t::const_iterator iter = s_state.find(id);
135 if(iter == s_state.end())
136 return false;
137 *bbd=*iter;
138 return true;
139 }
140
141 bool Bind2Backend::safeGetBBDomainInfo(const std::string& name, BB2DomainInfo* bbd)
142 {
143 ReadLock rl(&s_state_lock);
144 typedef state_t::index<NameTag>::type nameindex_t;
145 nameindex_t& nameindex = boost::multi_index::get<NameTag>(s_state);
146
147 nameindex_t::const_iterator iter = nameindex.find(name);
148 if(iter == nameindex.end())
149 return false;
150 *bbd=*iter;
151 return true;
152 }
153
154 bool Bind2Backend::safeRemoveBBDomainInfo(const std::string& name)
155 {
156 WriteLock rl(&s_state_lock);
157 typedef state_t::index<NameTag>::type nameindex_t;
158 nameindex_t& nameindex = boost::multi_index::get<NameTag>(s_state);
159
160 nameindex_t::iterator iter = nameindex.find(name);
161 if(iter == nameindex.end())
162 return false;
163 nameindex.erase(iter);
164 return true;
165 }
166
167 void Bind2Backend::safePutBBDomainInfo(const BB2DomainInfo& bbd)
168 {
169 WriteLock rl(&s_state_lock);
170 replacing_insert(s_state, bbd);
171 }
172
173 void Bind2Backend::setNotified(uint32_t id, uint32_t serial)
174 {
175 BB2DomainInfo bbd;
176 safeGetBBDomainInfo(id, &bbd);
177 bbd.d_lastnotified = serial;
178 safePutBBDomainInfo(bbd);
179 }
180
181 void Bind2Backend::setFresh(uint32_t domain_id)
182 {
183 BB2DomainInfo bbd;
184 if(safeGetBBDomainInfo(domain_id, &bbd)) {
185 bbd.d_lastcheck=time(0);
186 safePutBBDomainInfo(bbd);
187 }
188 }
189
190 bool Bind2Backend::startTransaction(const DNSName &qname, int id)
191 {
192 if(id < 0) {
193 d_transaction_tmpname.clear();
194 d_transaction_id=id;
195 return true;
196 }
197 if(id == 0) {
198 throw DBException("domain_id 0 is invalid for this backend.");
199 }
200
201 d_transaction_id=id;
202 BB2DomainInfo bbd;
203 if(safeGetBBDomainInfo(id, &bbd)) {
204 d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
205 d_of=new ofstream(d_transaction_tmpname.c_str());
206 if(!*d_of) {
207 throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
208 unlink(d_transaction_tmpname.c_str());
209 delete d_of;
210 d_of=0;
211 }
212
213 *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
214 *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<endl<<"; at "<<nowTime()<<endl; // insert master info here again
215
216 return true;
217 }
218 return false;
219 }
220
221 bool Bind2Backend::commitTransaction()
222 {
223 if(d_transaction_id < 0)
224 return true;
225 delete d_of;
226 d_of=0;
227
228 BB2DomainInfo bbd;
229 if(safeGetBBDomainInfo(d_transaction_id, &bbd)) {
230 if(rename(d_transaction_tmpname.c_str(), bbd.d_filename.c_str())<0)
231 throw DBException("Unable to commit (rename to: '" + bbd.d_filename+"') AXFRed zone: "+stringerror());
232 queueReloadAndStore(bbd.d_id);
233 }
234
235 d_transaction_id=0;
236
237 return true;
238 }
239
240 bool Bind2Backend::abortTransaction()
241 {
242 // -1 = dnssec speciality
243 // 0 = invalid transact
244 // >0 = actual transaction
245 if(d_transaction_id > 0) {
246 delete d_of;
247 d_of=0;
248 unlink(d_transaction_tmpname.c_str());
249 d_transaction_id=0;
250 }
251
252 return true;
253 }
254
255 bool Bind2Backend::feedRecord(const DNSResourceRecord &r, string *ordername)
256 {
257 string qname=r.qname.toString();
258
259 BB2DomainInfo bbd;
260 safeGetBBDomainInfo(d_transaction_id, &bbd);
261
262 string domain = bbd.d_name;
263
264 if(!stripDomainSuffix(&qname,domain))
265 throw DBException("out-of-zone data '"+qname+"' during AXFR of zone '"+domain+"'");
266
267 string content=r.content;
268
269 // SOA needs stripping too! XXX FIXME - also, this should not be here I think
270 switch(r.qtype.getCode()) {
271 case QType::MX:
272 case QType::SRV:
273 case QType::CNAME:
274 case QType::NS:
275 if(!stripDomainSuffix(&content, domain))
276 content=stripDot(content)+".";
277 *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<content<<endl;
278 break;
279 default:
280 *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.content<<endl;
281 break;
282 }
283 return true;
284 }
285
286 void Bind2Backend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
287 {
288 vector<DomainInfo> consider;
289 {
290 ReadLock rl(&s_state_lock);
291
292 for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
293 if(!i->d_masters.empty() && this->alsoNotify.empty() && i->d_also_notify.empty())
294 continue;
295
296 DomainInfo di;
297 di.id=i->d_id;
298 di.zone=i->d_name;
299 di.last_check=i->d_lastcheck;
300 di.notified_serial=i->d_lastnotified;
301 di.backend=this;
302 di.kind=DomainInfo::Master;
303 consider.push_back(di);
304 }
305 }
306
307 SOAData soadata;
308 BOOST_FOREACH(DomainInfo& di, consider) {
309 soadata.serial=0;
310 try {
311 this->getSOA(di.zone, soadata); // we might not *have* a SOA yet, but this might trigger a load of it
312 }
313 catch(...) {
314 continue;
315 }
316 if(di.notified_serial != soadata.serial) {
317 BB2DomainInfo bbd;
318 if(safeGetBBDomainInfo(di.id, &bbd)) {
319 bbd.d_lastnotified=soadata.serial;
320 safePutBBDomainInfo(bbd);
321 }
322 if(di.notified_serial) { // don't do notification storm on startup
323 di.serial=soadata.serial;
324 changedDomains->push_back(di);
325 }
326 }
327 }
328 }
329
330 void Bind2Backend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled)
331 {
332 SOAData soadata;
333
334 // prevent deadlock by using getSOA() later on
335 {
336 ReadLock rl(&s_state_lock);
337
338 for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
339 DomainInfo di;
340 di.id=i->d_id;
341 di.zone=i->d_name;
342 di.last_check=i->d_lastcheck;
343 di.kind=i->d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave; //TODO: what about Native?
344 di.backend=this;
345 domains->push_back(di);
346 };
347 }
348
349 BOOST_FOREACH(DomainInfo &di, *domains) {
350 this->getSOA(di.zone, soadata);
351 di.serial=soadata.serial;
352 }
353 }
354
355 void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
356 {
357 vector<DomainInfo> domains;
358 {
359 ReadLock rl(&s_state_lock);
360 for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
361 if(i->d_masters.empty())
362 continue;
363 DomainInfo sd;
364 sd.id=i->d_id;
365 sd.zone=i->d_name;
366 sd.masters=i->d_masters;
367 sd.last_check=i->d_lastcheck;
368 sd.backend=this;
369 sd.kind=DomainInfo::Slave;
370 domains.push_back(sd);
371 }
372 }
373
374 BOOST_FOREACH(DomainInfo &sd, domains) {
375 SOAData soadata;
376 soadata.refresh=0;
377 soadata.serial=0;
378 try {
379 getSOA(sd.zone,soadata); // we might not *have* a SOA yet
380 }
381 catch(...){}
382 sd.serial=soadata.serial;
383 if(sd.last_check+soadata.refresh < (unsigned int)time(0))
384 unfreshDomains->push_back(sd);
385 }
386 }
387
388 bool Bind2Backend::getDomainInfo(const string &domain, DomainInfo &di)
389 {
390 BB2DomainInfo bbd;
391 if(!safeGetBBDomainInfo(domain, &bbd))
392 return false;
393
394 di.id=bbd.d_id;
395 di.zone=domain;
396 di.masters=bbd.d_masters;
397 di.last_check=bbd.d_lastcheck;
398 di.backend=this;
399 di.kind=bbd.d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave;
400 di.serial=0;
401 try {
402 SOAData sd;
403 sd.serial=0;
404
405 getSOA(bbd.d_name,sd); // we might not *have* a SOA yet
406 di.serial=sd.serial;
407 }
408 catch(...){}
409
410 return true;
411 }
412
413 void Bind2Backend::alsoNotifies(const string &domain, set<string> *ips)
414 {
415 // combine global list with local list
416 for(set<string>::iterator i = this->alsoNotify.begin(); i != this->alsoNotify.end(); i++) {
417 (*ips).insert(*i);
418 }
419 ReadLock rl(&s_state_lock);
420 for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
421 if(pdns_iequals(i->d_name,domain)) {
422 for(set<string>::iterator it = i->d_also_notify.begin(); it != i->d_also_notify.end(); it++) {
423 (*ips).insert(*it);
424 }
425 return;
426 }
427 }
428 }
429
430 // only parses, does NOT add to s_state!
431 void Bind2Backend::parseZoneFile(BB2DomainInfo *bbd)
432 {
433 NSEC3PARAMRecordContent ns3pr;
434 bool nsec3zone;
435 if (d_hybrid) {
436 DNSSECKeeper dk;
437 nsec3zone=dk.getNSEC3PARAM(bbd->d_name, &ns3pr);
438 } else
439 nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr);
440
441 bbd->d_records = shared_ptr<recordstorage_t>(new recordstorage_t());
442
443 ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
444 DNSResourceRecord rr;
445 string hashed;
446 while(zpt.get(rr)) {
447 if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3)
448 continue; // we synthesise NSECs on demand
449
450 if(nsec3zone) {
451 if(rr.qtype.getCode() != QType::NSEC3 && rr.qtype.getCode() != QType::RRSIG)
452 hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname));
453 else
454 hashed="";
455 }
456 insertRecord(*bbd, rr.qname.toString(), rr.qtype, rr.content, rr.ttl, hashed);
457 }
458 fixupAuth(bbd->d_records.getWRITABLE());
459 doEmptyNonTerminals(*bbd, nsec3zone, ns3pr);
460 bbd->setCtime();
461 bbd->d_loaded=true;
462 bbd->d_checknow=false;
463 bbd->d_status="parsed into memory at "+nowTime();
464 }
465
466 /** THIS IS AN INTERNAL FUNCTION! It does moadnsparser prio impedance matching
467 Much of the complication is due to the efforts to benefit from std::string reference counting copy on write semantics */
468 void Bind2Backend::insertRecord(BB2DomainInfo& bb2, const string &qnameu, const QType &qtype, const string &content, int ttl, const std::string& hashed, bool *auth)
469 {
470 Bind2DNSRecord bdr;
471 shared_ptr<recordstorage_t> records = bb2.d_records.getWRITABLE();
472 bdr.qname=toLowerCanonic(qnameu);
473
474 if(bb2.d_name.empty())
475 ;
476 else if(dottedEndsOn(bdr.qname, bb2.d_name))
477 bdr.qname.resize(max(0, static_cast<int>(bdr.qname.length() - (bb2.d_name.length() + 1))));
478 else {
479 string msg = "Trying to insert non-zone data, name='"+bdr.qname+"', qtype="+qtype.getName()+", zone='"+bb2.d_name+"'";
480 if(s_ignore_broken_records) {
481 L<<Logger::Warning<<msg<< " ignored" << endl;
482 return;
483 }
484 else
485 throw PDNSException(msg);
486 }
487
488 bdr.qname.swap(bdr.qname);
489
490 if(!records->empty() && bdr.qname==boost::prior(records->end())->qname)
491 bdr.qname=boost::prior(records->end())->qname;
492
493 bdr.qname=labelReverse(bdr.qname);
494 bdr.qtype=qtype.getCode();
495 bdr.content=content;
496 bdr.nsec3hash = hashed;
497
498 if (auth) // Set auth on empty non-terminals
499 bdr.auth=*auth;
500 else
501 bdr.auth=true;
502
503 bdr.ttl=ttl;
504 records->insert(bdr);
505 }
506
507 string Bind2Backend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
508 {
509 ostringstream ret;
510
511 for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
512 BB2DomainInfo bbd;
513 if(safeGetBBDomainInfo(*i, &bbd)) {
514 Bind2Backend bb2;
515 bb2.queueReloadAndStore(bbd.d_id);
516 ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";
517 }
518 else
519 ret<< *i << " no such domain\n";
520 }
521 if(ret.str().empty())
522 ret<<"no domains reloaded";
523 return ret.str();
524 }
525
526
527 string Bind2Backend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
528 {
529 ostringstream ret;
530
531 if(parts.size() > 1) {
532 for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
533 BB2DomainInfo bbd;
534 if(safeGetBBDomainInfo(*i, &bbd)) {
535 ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";
536 }
537 else
538 ret<< *i << " no such domain\n";
539 }
540 }
541 else {
542 ReadLock rl(&s_state_lock);
543 for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
544 ret<< i->d_name << ": "<< (i->d_loaded ? "": "[rejected]") <<"\t"<<i->d_status<<"\n";
545 }
546 }
547
548 if(ret.str().empty())
549 ret<<"no domains passed";
550
551 return ret.str();
552 }
553
554 string Bind2Backend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
555 {
556 ostringstream ret;
557 ReadLock rl(&s_state_lock);
558 for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
559 if(!i->d_loaded)
560 ret<<i->d_name<<"\t"<<i->d_status<<endl;
561 }
562 return ret.str();
563 }
564
565 string Bind2Backend::DLAddDomainHandler(const vector<string>&parts, Utility::pid_t ppid)
566 {
567 if(parts.size() < 3)
568 return "ERROR: Domain name and zone filename are required";
569
570 string domainname = toLowerCanonic(parts[1]);
571 const string &filename = parts[2];
572 BB2DomainInfo bbd;
573 if(safeGetBBDomainInfo(domainname, &bbd))
574 return "Already loaded";
575 Bind2Backend bb2; // createdomainentry needs access to our configuration
576 bbd=bb2.createDomainEntry(domainname, filename);
577 bbd.d_filename=filename;
578 bbd.d_checknow=true;
579 bbd.d_loaded=true;
580 bbd.d_lastcheck=0;
581 bbd.d_status="parsing into memory";
582
583 safePutBBDomainInfo(bbd);
584
585 L<<Logger::Warning<<"Zone "<<domainname<< " loaded"<<endl;
586 return "Loaded zone " + domainname + " from " + filename;
587 }
588
589 Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
590 {
591 d_getAllDomainMetadataQuery_stmt = NULL;
592 d_getDomainMetadataQuery_stmt = NULL;
593 d_deleteDomainMetadataQuery_stmt = NULL;
594 d_insertDomainMetadataQuery_stmt = NULL;
595 d_getDomainKeysQuery_stmt = NULL;
596 d_deleteDomainKeyQuery_stmt = NULL;
597 d_insertDomainKeyQuery_stmt = NULL;
598 d_activateDomainKeyQuery_stmt = NULL;
599 d_deactivateDomainKeyQuery_stmt = NULL;
600 d_getTSIGKeyQuery_stmt = NULL;
601 d_setTSIGKeyQuery_stmt = NULL;
602 d_deleteTSIGKeyQuery_stmt = NULL;
603 d_getTSIGKeysQuery_stmt = NULL;
604
605 setArgPrefix("bind"+suffix);
606 d_logprefix="[bind"+suffix+"backend]";
607 d_hybrid=mustDo("hybrid");
608 s_ignore_broken_records=mustDo("ignore-broken-records");
609
610 if (!loadZones && d_hybrid)
611 return;
612
613 Lock l(&s_startup_lock);
614
615 d_transaction_id=0;
616 setupDNSSEC();
617 if(!s_first) {
618 return;
619 }
620
621 if(loadZones) {
622 loadConfig();
623 s_first=0;
624 }
625
626 extern DynListener *dl;
627 dl->registerFunc("BIND-RELOAD-NOW", &DLReloadNowHandler, "bindbackend: reload domains", "<domains>");
628 dl->registerFunc("BIND-DOMAIN-STATUS", &DLDomStatusHandler, "bindbackend: list status of all domains", "[domains]");
629 dl->registerFunc("BIND-LIST-REJECTS", &DLListRejectsHandler, "bindbackend: list rejected domains");
630 dl->registerFunc("BIND-ADD-ZONE", &DLAddDomainHandler, "bindbackend: add zone", "<domain> <filename>");
631 }
632
633 Bind2Backend::~Bind2Backend()
634 { freeStatements(); } // deallocate statements
635
636 void Bind2Backend::rediscover(string *status)
637 {
638 loadConfig(status);
639 }
640
641 void Bind2Backend::reload()
642 {
643 WriteLock rwl(&s_state_lock);
644 for(state_t::iterator i = s_state.begin(); i != s_state.end() ; ++i) {
645 i->d_checknow=true; // being a bit cheeky here, don't index state_t on this (mutable)
646 }
647 }
648
649 void Bind2Backend::fixupAuth(shared_ptr<recordstorage_t> records)
650 {
651 pair<recordstorage_t::const_iterator, recordstorage_t::const_iterator> range;
652 string sqname;
653
654 recordstorage_t nssets;
655 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records) {
656 if(bdr.qtype==QType::NS)
657 nssets.insert(bdr);
658 }
659
660 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records) {
661 bdr.auth=true;
662
663 if(bdr.qtype == QType::DS) // as are delegation signer records
664 continue;
665
666 sqname = labelReverse(bdr.qname);
667
668 do {
669 if(sqname.empty()) // this is auth of course!
670 continue;
671 if(bdr.qtype == QType::NS || nssets.count(sqname)) { // NS records which are not apex are unauth by definition
672 bdr.auth=false;
673 }
674 } while(chopOff(sqname));
675 }
676 }
677
678 void Bind2Backend::doEmptyNonTerminals(BB2DomainInfo& bbd, bool nsec3zone, NSEC3PARAMRecordContent ns3pr)
679 {
680 shared_ptr<const recordstorage_t> records = bbd.d_records.get();
681 bool auth, doent=true;
682 set<string> qnames;
683 map<string, bool> nonterm;
684 string shorter, hashed;
685
686 uint32_t maxent = ::arg().asNum("max-ent-entries");
687
688 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records)
689 qnames.insert(labelReverse(bdr.qname));
690
691 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records) {
692 shorter=labelReverse(bdr.qname);
693
694 if (!bdr.auth && bdr.qtype == QType::NS)
695 auth=(!nsec3zone || !ns3pr.d_flags);
696 else
697 auth=bdr.auth;
698
699 while(chopOff(shorter))
700 {
701 if(!qnames.count(shorter))
702 {
703 if(!(maxent))
704 {
705 L<<Logger::Error<<"Zone '"<<bbd.d_name<<"' has too many empty non terminals."<<endl;
706 doent=false;
707 break;
708 }
709
710 if (!nonterm.count(shorter)) {
711 nonterm.insert(pair<string, bool>(shorter, auth));
712 --maxent;
713 } else if (auth)
714 nonterm[shorter]=true;
715 }
716 }
717 if(!doent)
718 return;
719 }
720
721 DNSResourceRecord rr;
722 rr.qtype="#0";
723 rr.content="";
724 rr.ttl=0;
725 pair<string, bool> nt;
726 BOOST_FOREACH(nt, nonterm)
727 {
728 rr.qname=nt.first+"."+bbd.d_name+".";
729 if(nsec3zone)
730 hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname));
731 insertRecord(bbd, rr.qname.toString(), rr.qtype, rr.content, rr.ttl, hashed, &nt.second);
732 }
733 }
734
735 void Bind2Backend::loadConfig(string* status)
736 {
737 static int domain_id=1;
738
739 if(!getArg("config").empty()) {
740 BindParser BP;
741 try {
742 BP.parse(getArg("config"));
743 }
744 catch(PDNSException &ae) {
745 L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl;
746 throw;
747 }
748
749 vector<BindDomainInfo> domains=BP.getDomains();
750 this->alsoNotify = BP.getAlsoNotify();
751
752 s_binddirectory=BP.getDirectory();
753 // ZP.setDirectory(d_binddirectory);
754
755 L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), will report when done"<<endl;
756
757 set<string> oldnames, newnames;
758 {
759 ReadLock rl(&s_state_lock);
760 BOOST_FOREACH(const BB2DomainInfo& bbd, s_state) {
761 oldnames.insert(bbd.d_name);
762 }
763 }
764 int rejected=0;
765 int newdomains=0;
766
767 struct stat st;
768
769 for(vector<BindDomainInfo>::iterator i=domains.begin(); i!=domains.end(); ++i)
770 {
771 if(stat(i->filename.c_str(), &st) == 0) {
772 i->d_dev = st.st_dev;
773 i->d_ino = st.st_ino;
774 }
775 }
776
777 sort(domains.begin(), domains.end()); // put stuff in inode order
778 for(vector<BindDomainInfo>::const_iterator i=domains.begin();
779 i!=domains.end();
780 ++i)
781 {
782 if(i->type!="master" && i->type!="slave") {
783 L<<Logger::Warning<<d_logprefix<<" Warning! Skipping '"<<i->type<<"' zone '"<<i->name<<"'"<<endl;
784 continue;
785 }
786
787 BB2DomainInfo bbd;
788
789 if(!safeGetBBDomainInfo(i->name, &bbd)) {
790 bbd.d_id=domain_id++;
791 bbd.setCheckInterval(getArgAsNum("check-interval"));
792 bbd.d_lastnotified=0;
793 bbd.d_loaded=false;
794 }
795
796 // overwrite what we knew about the domain
797 bbd.d_name=toLowerCanonic(i->name);
798 bool filenameChanged = (bbd.d_filename!=i->filename);
799 bbd.d_filename=i->filename;
800 bbd.d_masters=i->masters;
801 bbd.d_also_notify=i->alsoNotify;
802
803 newnames.insert(bbd.d_name);
804 if(filenameChanged || !bbd.d_loaded || !bbd.current()) {
805 L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
806
807 try {
808 parseZoneFile(&bbd);
809 }
810 catch(PDNSException &ae) {
811 ostringstream msg;
812 msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<i->filename<<"': "<<ae.reason;
813
814 if(status)
815 *status+=msg.str();
816 bbd.d_status=msg.str();
817
818 L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
819 rejected++;
820 }
821 catch(std::exception &ae) {
822 ostringstream msg;
823 msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<i->filename<<"': "<<ae.what();
824
825 if(status)
826 *status+=msg.str();
827 bbd.d_status=msg.str();
828 L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
829 rejected++;
830 }
831 safePutBBDomainInfo(bbd);
832
833 }
834 }
835 vector<string> diff;
836
837 set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
838 unsigned int remdomains=diff.size();
839
840 BOOST_FOREACH(const std::string& name, diff) {
841 safeRemoveBBDomainInfo(name);
842 }
843
844 // count number of entirely new domains
845 diff.clear();
846 set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff));
847 newdomains=diff.size();
848
849 ostringstream msg;
850 msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed";
851 if(status)
852 *status=msg.str();
853
854 L<<Logger::Error<<d_logprefix<<msg.str()<<endl;
855 }
856 }
857
858 void Bind2Backend::queueReloadAndStore(unsigned int id)
859 {
860 BB2DomainInfo bbold;
861 try {
862 if(!safeGetBBDomainInfo(id, &bbold))
863 return;
864 parseZoneFile(&bbold);
865 bbold.d_checknow=false;
866 safePutBBDomainInfo(bbold);
867 L<<Logger::Warning<<"Zone '"<<bbold.d_name<<"' ("<<bbold.d_filename<<") reloaded"<<endl;
868 }
869 catch(PDNSException &ae) {
870 ostringstream msg;
871 msg<<" error at "+nowTime()+" parsing '"<<bbold.d_name<<"' from file '"<<bbold.d_filename<<"': "<<ae.reason;
872 bbold.d_status=msg.str();
873 safePutBBDomainInfo(bbold);
874 }
875 catch(std::exception &ae) {
876 ostringstream msg;
877 msg<<" error at "+nowTime()+" parsing '"<<bbold.d_name<<"' from file '"<<bbold.d_filename<<"': "<<ae.what();
878 bbold.d_status=msg.str();
879 safePutBBDomainInfo(bbold);
880 }
881 }
882
883 bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
884 {
885 string domain=qname.toString();
886 shared_ptr<const recordstorage_t> records = bbd.d_records.get();
887 recordstorage_t::const_iterator iter = records->upper_bound(domain);
888
889 if (before.empty()){
890 //cout<<"starting before for: '"<<domain<<"'"<<endl;
891 iter = records->upper_bound(domain);
892
893 while(iter == records->end() || (iter->qname) > domain || (!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
894 iter--;
895
896 before=iter->qname;
897 }
898 else {
899 before=domain;
900 }
901
902 //cerr<<"Now after"<<endl;
903 iter = records->upper_bound(domain);
904
905 if(iter == records->end()) {
906 //cerr<<"\tFound the end, begin storage: '"<<bbd.d_records->begin()->qname<<"', '"<<bbd.d_name<<"'"<<endl;
907 after.clear(); // this does the right thing (i.e. point to apex, which is sure to have auth records)
908 } else {
909 //cerr<<"\tFound: '"<<(iter->qname)<<"' (nsec3hash='"<<(iter->nsec3hash)<<"')"<<endl;
910 // this iteration is theoretically unnecessary - glue always sorts right behind a delegation
911 // so we will never get here. But let's do it anyway.
912 while((!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
913 {
914 iter++;
915 if(iter == records->end())
916 {
917 after.clear();
918 break;
919 }
920 }
921 after = (iter)->qname;
922 }
923
924 //cerr<<"Before: '"<<before<<"', after: '"<<after<<"'\n";
925 return true;
926 }
927
928 bool Bind2Backend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string& qname, std::string& unhashed, std::string& before, std::string& after)
929 {
930 BB2DomainInfo bbd;
931 safeGetBBDomainInfo(id, &bbd);
932
933 NSEC3PARAMRecordContent ns3pr;
934 string auth=bbd.d_name;
935
936 bool nsec3zone;
937 if (d_hybrid) {
938 DNSSECKeeper dk;
939 nsec3zone=dk.getNSEC3PARAM(auth, &ns3pr);
940 } else
941 nsec3zone=getNSEC3PARAM(auth, &ns3pr);
942
943 if(!nsec3zone) {
944 //cerr<<"in bind2backend::getBeforeAndAfterAbsolute: no nsec3 for "<<auth<<endl;
945 return findBeforeAndAfterUnhashed(bbd, qname, unhashed, before, after);
946
947 }
948 else {
949 string lqname = toLower(qname);
950 // cerr<<"\nin bind2backend::getBeforeAndAfterAbsolute: nsec3 HASH for "<<auth<<", asked for: "<<lqname<< " (auth: "<<auth<<".)"<<endl;
951 typedef recordstorage_t::index<HashedTag>::type records_by_hashindex_t;
952 records_by_hashindex_t& hashindex=boost::multi_index::get<HashedTag>(*bbd.d_records.getWRITABLE()); // needlessly dangerous
953
954 // BOOST_FOREACH(const Bind2DNSRecord& bdr, hashindex) {
955 // cerr<<"Hash: "<<bdr.nsec3hash<<"\t"<< (lqname < bdr.nsec3hash) <<endl;
956 // }
957
958 records_by_hashindex_t::const_iterator iter;
959 bool wraponce;
960
961 if (before.empty()) {
962 iter = hashindex.upper_bound(lqname);
963
964 if(iter != hashindex.begin() && (iter == hashindex.end() || iter->nsec3hash > lqname))
965 {
966 iter--;
967 }
968
969 if(iter == hashindex.begin() && (iter->nsec3hash > lqname))
970 {
971 iter = hashindex.end();
972 }
973
974 wraponce = false;
975 while(iter == hashindex.end() || (!iter->auth && !(iter->qtype == QType::NS && !pdns_iequals(iter->qname, auth) && !ns3pr.d_flags)) || iter->nsec3hash.empty())
976 {
977 iter--;
978 if(iter == hashindex.begin()) {
979 if (!wraponce) {
980 iter = hashindex.end();
981 wraponce = true;
982 }
983 else {
984 before.clear();
985 after.clear();
986 return false;
987 }
988 }
989 }
990
991 before = iter->nsec3hash;
992 unhashed = dotConcat(labelReverse(iter->qname), auth);
993 // cerr<<"before: "<<(iter->nsec3hash)<<"/"<<(iter->qname)<<endl;
994 }
995 else {
996 before = lqname;
997 }
998
999 iter = hashindex.upper_bound(lqname);
1000 if(iter == hashindex.end())
1001 {
1002 iter = hashindex.begin();
1003 }
1004
1005 wraponce = false;
1006 while((!iter->auth && !(iter->qtype == QType::NS && !pdns_iequals(iter->qname, auth) && !ns3pr.d_flags)) || iter->nsec3hash.empty())
1007 {
1008 iter++;
1009 if(iter == hashindex.end()) {
1010 if (!wraponce) {
1011 iter = hashindex.begin();
1012 wraponce = true;
1013 }
1014 else {
1015 before.clear();
1016 after.clear();
1017 return false;
1018 }
1019 }
1020 }
1021
1022 after = iter->nsec3hash;
1023 // cerr<<"after: "<<(iter->nsec3hash)<<"/"<<(iter->qname)<<endl;
1024 //cerr<<"Before: '"<<before<<"', after: '"<<after<<"'\n";
1025 return true;
1026 }
1027 }
1028
1029 void Bind2Backend::lookup(const QType &qtype, const DNSName &qname, DNSPacket *pkt_p, int zoneId )
1030 {
1031 d_handle.reset();
1032 string domain=qname.toString();
1033
1034 static bool mustlog=::arg().mustDo("query-logging");
1035 if(mustlog)
1036 L<<Logger::Warning<<"Lookup for '"<<qtype.getName()<<"' of '"<<domain<<"'"<<endl;
1037 bool found=false;
1038 BB2DomainInfo bbd;
1039 do {
1040 found = safeGetBBDomainInfo(domain, &bbd);
1041 } while ((!found || (zoneId != (int)bbd.d_id && zoneId != -1)) && chopOff(domain));
1042
1043 if(!found) {
1044 if(mustlog)
1045 L<<Logger::Warning<<"Found no authoritative zone for "<<qname<<endl;
1046 d_handle.d_list=false;
1047 return;
1048 }
1049
1050 if(mustlog)
1051 L<<Logger::Warning<<"Found a zone '"<<domain<<"' (with id " << bbd.d_id<<") that might contain data "<<endl;
1052
1053 d_handle.id=bbd.d_id;
1054
1055 DLOG(L<<"Bind2Backend constructing handle for search for "<<qtype.getName()<<" for "<<
1056 qname<<endl);
1057
1058 if(domain.empty())
1059 d_handle.qname=qname;
1060 else if(strcasecmp(qname.c_str(),domain.c_str()))
1061 d_handle.qname=qname.substr(0,qname.size()-domain.length()-1); // strip domain name
1062
1063 d_handle.qtype=qtype;
1064 d_handle.domain=qname.substr(qname.size()-domain.length());
1065
1066 if(!bbd.d_loaded) {
1067 d_handle.reset();
1068 throw DBException("Zone for '"+bbd.d_name+"' in '"+bbd.d_filename+"' temporarily not available (file missing, or master dead)"); // fsck
1069 }
1070
1071 if(!bbd.current()) {
1072 L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
1073 queueReloadAndStore(bbd.d_id);
1074 if (!safeGetBBDomainInfo(domain, &bbd))
1075 throw DBException("Zone '"+bbd.d_name+"' ("+bbd.d_filename+") gone after reload"); // if we don't throw here, we crash for some reason
1076 }
1077
1078 d_handle.d_records = bbd.d_records.get();
1079
1080 if(d_handle.d_records->empty())
1081 DLOG(L<<"Query with no results"<<endl);
1082
1083 pair<recordstorage_t::const_iterator, recordstorage_t::const_iterator> range;
1084
1085 string lname=labelReverse(toLower(d_handle.qname));
1086 //cout<<"starting equal range for: '"<<d_handle.qname<<"', search is for: '"<<lname<<"'"<<endl;
1087
1088 range = d_handle.d_records->equal_range(lname);
1089 //cout<<"End equal range"<<endl;
1090 d_handle.mustlog = mustlog;
1091
1092 if(range.first==range.second) {
1093 // cerr<<"Found nothing!"<<endl;
1094 d_handle.d_list=false;
1095 d_handle.d_iter = d_handle.d_end_iter = range.first;
1096 return;
1097 }
1098 else {
1099 // cerr<<"Found something!"<<endl;
1100 d_handle.d_iter=range.first;
1101 d_handle.d_end_iter=range.second;
1102 }
1103
1104 d_handle.d_list=false;
1105 }
1106
1107 Bind2Backend::handle::handle()
1108 {
1109 mustlog=false;
1110 }
1111
1112 bool Bind2Backend::get(DNSResourceRecord &r)
1113 {
1114 if(!d_handle.d_records) {
1115 if(d_handle.mustlog)
1116 L<<Logger::Warning<<"There were no answers"<<endl;
1117 return false;
1118 }
1119
1120 if(!d_handle.get(r)) {
1121 if(d_handle.mustlog)
1122 L<<Logger::Warning<<"End of answers"<<endl;
1123
1124 d_handle.reset();
1125
1126 return false;
1127 }
1128 if(d_handle.mustlog)
1129 L<<Logger::Warning<<"Returning: '"<<r.qtype.getName()<<"' of '"<<r.qname.toString()<<"', content: '"<<r.content<<"'"<<endl;
1130 return true;
1131 }
1132
1133 bool Bind2Backend::handle::get(DNSResourceRecord &r)
1134 {
1135 if(d_list)
1136 return get_list(r);
1137 else
1138 return get_normal(r);
1139 }
1140
1141 void Bind2Backend::handle::reset()
1142 {
1143 d_records.reset();
1144 qname.clear();
1145 mustlog=false;
1146 }
1147
1148 //#define DLOG(x) x
1149 bool Bind2Backend::handle::get_normal(DNSResourceRecord &r)
1150 {
1151 DLOG(L << "Bind2Backend get() was called for "<<qtype.getName() << " record for '"<<
1152 qname<<"' - "<<d_records->size()<<" available in total!"<<endl);
1153
1154 if(d_iter==d_end_iter) {
1155 return false;
1156 }
1157
1158 while(d_iter!=d_end_iter && !(qtype.getCode()==QType::ANY || (d_iter)->qtype==qtype.getCode())) {
1159 DLOG(L<<Logger::Warning<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).getName()<<": '"<<d_iter->content<<"'"<<endl);
1160 d_iter++;
1161 }
1162 if(d_iter==d_end_iter) {
1163 return false;
1164 }
1165 DLOG(L << "Bind2Backend get() returning a rr with a "<<QType(d_iter->qtype).getCode()<<endl);
1166
1167 r.qname=qname.empty() ? domain : (qname+"."+domain);
1168 r.domain_id=id;
1169 r.content=(d_iter)->content;
1170 // r.domain_id=(d_iter)->domain_id;
1171 r.qtype=(d_iter)->qtype;
1172 r.ttl=(d_iter)->ttl;
1173
1174 //if(!d_iter->auth && r.qtype.getCode() != QType::A && r.qtype.getCode()!=QType::AAAA && r.qtype.getCode() != QType::NS)
1175 // cerr<<"Warning! Unauth response for qtype "<< r.qtype.getName() << " for '"<<r.qname<<"'"<<endl;
1176 r.auth = d_iter->auth;
1177
1178 d_iter++;
1179
1180 return true;
1181 }
1182
1183 bool Bind2Backend::list(const string &target, int id, bool include_disabled)
1184 {
1185 BB2DomainInfo bbd;
1186
1187 if(!safeGetBBDomainInfo(id, &bbd))
1188 return false;
1189
1190 d_handle.reset();
1191 DLOG(L<<"Bind2Backend constructing handle for list of "<<id<<endl);
1192
1193 d_handle.d_records=bbd.d_records.get(); // give it a copy, which will stay around
1194 d_handle.d_qname_iter= d_handle.d_records->begin();
1195 d_handle.d_qname_end=d_handle.d_records->end(); // iter now points to a vector of pointers to vector<BBResourceRecords>
1196
1197 d_handle.id=id;
1198 d_handle.d_list=true;
1199 return true;
1200 }
1201
1202 bool Bind2Backend::handle::get_list(DNSResourceRecord &r)
1203 {
1204 if(d_qname_iter!=d_qname_end) {
1205 r.qname=d_qname_iter->qname.empty() ? domain : (labelReverse(d_qname_iter->qname)+"."+domain);
1206 r.domain_id=id;
1207 r.content=(d_qname_iter)->content;
1208 r.qtype=(d_qname_iter)->qtype;
1209 r.ttl=(d_qname_iter)->ttl;
1210 r.auth = d_qname_iter->auth;
1211 d_qname_iter++;
1212 return true;
1213 }
1214 return false;
1215 }
1216
1217 bool Bind2Backend::isMaster(const string &name, const string &ip)
1218 {
1219 BB2DomainInfo bbd;
1220 if(!safeGetBBDomainInfo(name, &bbd))
1221 return false;
1222
1223 for(vector<string>::const_iterator iter = bbd.d_masters.begin(); iter != bbd.d_masters.end(); ++iter)
1224 if(*iter==ip)
1225 return true;
1226
1227 return false;
1228 }
1229
1230 bool Bind2Backend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db)
1231 {
1232 // Check whether we have a configfile available.
1233 if (getArg("supermaster-config").empty())
1234 return false;
1235
1236 ifstream c_if(getArg("supermasters").c_str(), std::ios::in); // this was nocreate?
1237 if (!c_if) {
1238 L << Logger::Error << "Unable to open supermasters file for read: " << stringerror() << endl;
1239 return false;
1240 }
1241
1242 // Format:
1243 // <ip> <accountname>
1244 string line, sip, saccount;
1245 while (getline(c_if, line)) {
1246 std::istringstream ii(line);
1247 ii >> sip;
1248 if (sip == ip) {
1249 ii >> saccount;
1250 break;
1251 }
1252 }
1253 c_if.close();
1254
1255 if (sip != ip) // ip not found in authorization list - reject
1256 return false;
1257
1258 // ip authorized as supermaster - accept
1259 *db = this;
1260 if (saccount.length() > 0)
1261 *account = saccount.c_str();
1262
1263 return true;
1264 }
1265
1266 BB2DomainInfo Bind2Backend::createDomainEntry(const string &domain, const string &filename)
1267 {
1268 int newid=1;
1269 { // Find a free zone id nr.
1270 ReadLock rl(&s_state_lock);
1271 if (!s_state.empty()) {
1272 newid = s_state.rbegin()->d_id+1;
1273 }
1274 }
1275
1276 BB2DomainInfo bbd;
1277 bbd.d_id = newid;
1278 bbd.d_records = shared_ptr<recordstorage_t >(new recordstorage_t);
1279 bbd.d_name = domain;
1280 bbd.setCheckInterval(getArgAsNum("check-interval"));
1281 bbd.d_filename = filename;
1282 return bbd;
1283 }
1284
1285 bool Bind2Backend::createSlaveDomain(const string &ip, const string &domain, const string &nameserver, const string &account)
1286 {
1287 string filename = getArg("supermaster-destdir")+'/'+domain;
1288
1289 L << Logger::Warning << d_logprefix
1290 << " Writing bind config zone statement for superslave zone '" << domain
1291 << "' from supermaster " << ip << endl;
1292
1293 {
1294 Lock l2(&s_supermaster_config_lock);
1295
1296 ofstream c_of(getArg("supermaster-config").c_str(), std::ios::app);
1297 if (!c_of) {
1298 L << Logger::Error << "Unable to open supermaster configfile for append: " << stringerror() << endl;
1299 throw DBException("Unable to open supermaster configfile for append: "+stringerror());
1300 }
1301
1302 c_of << endl;
1303 c_of << "# Superslave zone " << domain << " (added: " << nowTime() << ") (account: " << account << ')' << endl;
1304 c_of << "zone \"" << domain << "\" {" << endl;
1305 c_of << "\ttype slave;" << endl;
1306 c_of << "\tfile \"" << filename << "\";" << endl;
1307 c_of << "\tmasters { " << ip << "; };" << endl;
1308 c_of << "};" << endl;
1309 c_of.close();
1310 }
1311
1312 BB2DomainInfo bbd = createDomainEntry(toLowerCanonic(domain), filename);
1313 bbd.d_masters.push_back(ip);
1314 safePutBBDomainInfo(bbd);
1315 return true;
1316 }
1317
1318 class Bind2Factory : public BackendFactory
1319 {
1320 public:
1321 Bind2Factory() : BackendFactory("bind") {}
1322
1323 void declareArguments(const string &suffix="")
1324 {
1325 declare(suffix,"ignore-broken-records","Ignore records that are out-of-bound for the zone.","no");
1326 declare(suffix,"config","Location of named.conf","");
1327 declare(suffix,"check-interval","Interval for zonefile changes","0");
1328 declare(suffix,"supermaster-config","Location of (part of) named.conf where pdns can write zone-statements to","");
1329 declare(suffix,"supermasters","List of IP-addresses of supermasters","");
1330 declare(suffix,"supermaster-destdir","Destination directory for newly added slave zones",::arg()["config-dir"]);
1331 declare(suffix,"dnssec-db","Filename to store & access our DNSSEC metadatabase, empty for none", "");
1332 declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no");
1333 }
1334
1335 DNSBackend *make(const string &suffix="")
1336 {
1337 return new Bind2Backend(suffix);
1338 }
1339
1340 DNSBackend *makeMetadataOnly(const string &suffix="")
1341 {
1342 return new Bind2Backend(suffix, false);
1343 }
1344 };
1345
1346 //! Magic class that is activated when the dynamic library is loaded
1347 class Bind2Loader
1348 {
1349 public:
1350 Bind2Loader()
1351 {
1352 BackendMakers().report(new Bind2Factory);
1353 L << Logger::Info << "[bind2backend] This is the bind backend version " << VERSION
1354 #ifndef REPRODUCIBLE
1355 << " (" __DATE__ " " __TIME__ ")"
1356 #endif
1357 << " reporting" << endl;
1358 }
1359 };
1360 static Bind2Loader bind2loader;