]> git.ipfire.org Git - thirdparty/pdns.git/blame - modules/bindbackend/bindbackend2.cc
snap
[thirdparty/pdns.git] / modules / bindbackend / bindbackend2.cc
CommitLineData
0f310932 1 /*
b532a4b1 2 PowerDNS Versatile Database Driven Nameserver
b755d50a 3 Copyright (C) 2002 - 2014 PowerDNS.COM BV
b532a4b1
BH
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
f782fe38
MH
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
b532a4b1
BH
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
06bd9ccf 20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
b532a4b1 21*/
69ab97d1 22
870a0fe4
AT
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
b532a4b1
BH
26#include <errno.h>
27#include <string>
b532a4b1
BH
28#include <set>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <unistd.h>
f404b863 32#include <fstream>
82f7b3cc 33#include <fcntl.h>
f404b863 34#include <sstream>
82cc6877 35#include <boost/algorithm/string.hpp>
8e16b978 36#include <boost/foreach.hpp>
e53a4bf6
KM
37#include "pdns/dnsseckeeper.hh"
38#include "pdns/dnssecinfra.hh"
39#include "pdns/base32.hh"
40#include "pdns/namespaces.hh"
e53a4bf6
KM
41#include "pdns/dns.hh"
42#include "pdns/dnsbackend.hh"
b532a4b1 43#include "bindbackend2.hh"
e53a4bf6
KM
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"
b532a4b1 54
d8f47a4a 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*/
b532a4b1 72
f08339e3 73Bind2Backend::state_t Bind2Backend::s_state;
b532a4b1 74int Bind2Backend::s_first=1;
17c2b1a8 75bool Bind2Backend::s_ignore_broken_records=false;
d10f9034 76
b755d50a 77pthread_rwlock_t Bind2Backend::s_state_lock=PTHREAD_RWLOCK_INITIALIZER;
d8f47a4a 78pthread_mutex_t Bind2Backend::s_supermaster_config_lock=PTHREAD_MUTEX_INITIALIZER; // protects writes to config file
b755d50a 79pthread_mutex_t Bind2Backend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
10635124 80string Bind2Backend::s_binddirectory;
2717b8b3 81
b532a4b1
BH
82BB2DomainInfo::BB2DomainInfo()
83{
84 d_loaded=false;
c36285d0 85 d_lastcheck=0;
b532a4b1 86 d_checknow=false;
d10f9034 87 d_status="Unknown";
b532a4b1
BH
88}
89
90void BB2DomainInfo::setCheckInterval(time_t seconds)
91{
92 d_checkinterval=seconds;
93}
94
95bool BB2DomainInfo::current()
96{
aaf2e4a4 97 if(d_checknow) {
b532a4b1 98 return false;
aaf2e4a4 99 }
b532a4b1 100
c36285d0
BH
101 if(!d_checkinterval)
102 return true;
103
aaf2e4a4 104 if(time(0) - d_lastcheck < d_checkinterval)
c36285d0
BH
105 return true;
106
107 if(d_filename.empty())
b532a4b1
BH
108 return true;
109
110 return (getCtime()==d_ctime);
111}
112
113time_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
123void 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
a1c4d85c 131bool 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
141bool 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
6b947ad5 154bool 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
a1c4d85c 167void Bind2Backend::safePutBBDomainInfo(const BB2DomainInfo& bbd)
168{
169 WriteLock rl(&s_state_lock);
170 replacing_insert(s_state, bbd);
171}
172
092f210a 173void Bind2Backend::setNotified(uint32_t id, uint32_t serial)
b532a4b1 174{
f08339e3 175 BB2DomainInfo bbd;
176 safeGetBBDomainInfo(id, &bbd);
177 bbd.d_lastnotified = serial;
b755d50a 178 safePutBBDomainInfo(bbd);
b532a4b1
BH
179}
180
092f210a 181void Bind2Backend::setFresh(uint32_t domain_id)
b532a4b1 182{
f08339e3 183 BB2DomainInfo bbd;
184 if(safeGetBBDomainInfo(domain_id, &bbd)) {
185 bbd.d_lastcheck=time(0);
b755d50a 186 safePutBBDomainInfo(bbd);
f1cf0678 187 }
f1cf0678
BH
188}
189
675fa24c 190bool Bind2Backend::startTransaction(const DNSName &qname, int id)
b532a4b1 191{
340a213e
BH
192 if(id < 0) {
193 d_transaction_tmpname.clear();
194 d_transaction_id=id;
195 return true;
196 }
c919b27e
CH
197 if(id == 0) {
198 throw DBException("domain_id 0 is invalid for this backend.");
199 }
c99d9120 200
f08339e3 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;
b532a4b1 217 }
f08339e3 218 return false;
219}
b532a4b1 220
b532a4b1
BH
221bool Bind2Backend::commitTransaction()
222{
340a213e
BH
223 if(d_transaction_id < 0)
224 return true;
b532a4b1
BH
225 delete d_of;
226 d_of=0;
d10f9034 227
f08339e3 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());
aaf2e4a4 232 queueReloadAndStore(bbd.d_id);
f08339e3 233 }
016e4ae9 234
b532a4b1
BH
235 d_transaction_id=0;
236
237 return true;
238}
239
240bool Bind2Backend::abortTransaction()
241{
c919b27e
CH
242 // -1 = dnssec speciality
243 // 0 = invalid transact
244 // >0 = actual transaction
245 if(d_transaction_id > 0) {
b532a4b1
BH
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
f9cf6d92 255bool Bind2Backend::feedRecord(const DNSResourceRecord &r, string *ordername)
b532a4b1 256{
675fa24c 257 string qname=r.qname.toString();
c99d9120 258
f08339e3 259 BB2DomainInfo bbd;
260 safeGetBBDomainInfo(d_transaction_id, &bbd);
261
262 string domain = bbd.d_name;
b532a4b1
BH
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
69ab97d1 269 // SOA needs stripping too! XXX FIXME - also, this should not be here I think
b532a4b1 270 switch(r.qtype.getCode()) {
b532a4b1 271 case QType::MX:
f7fc87e6 272 case QType::SRV:
b532a4b1
BH
273 case QType::CNAME:
274 case QType::NS:
b33702d5 275 if(!stripDomainSuffix(&content, domain))
2bb4c288 276 content=stripDot(content)+".";
b532a4b1
BH
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 }
b532a4b1
BH
283 return true;
284}
285
286void Bind2Backend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
287{
c7287b67 288 vector<DomainInfo> consider;
9215e60f 289 {
290 ReadLock rl(&s_state_lock);
9215e60f 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())
2336e762
KM
294 continue;
295
9215e60f 296 DomainInfo di;
297 di.id=i->d_id;
9215e60f 298 di.zone=i->d_name;
299 di.last_check=i->d_lastcheck;
2336e762 300 di.notified_serial=i->d_lastnotified;
9215e60f 301 di.backend=this;
302 di.kind=DomainInfo::Master;
2336e762 303 consider.push_back(di);
fbdca370 304 }
9215e60f 305 }
c7287b67 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 }
2336e762
KM
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 }
c7287b67 326 }
b532a4b1
BH
327 }
328}
329
f08339e3 330void Bind2Backend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled)
331{
1325e8a2
PD
332 SOAData soadata;
333
d695df1a
AT
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) {
d695df1a 350 this->getSOA(di.zone, soadata);
1325e8a2 351 di.serial=soadata.serial;
1325e8a2
PD
352 }
353}
354
b532a4b1
BH
355void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
356{
d695df1a
AT
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) {
b532a4b1 375 SOAData soadata;
b532a4b1
BH
376 soadata.refresh=0;
377 soadata.serial=0;
b532a4b1 378 try {
d695df1a 379 getSOA(sd.zone,soadata); // we might not *have* a SOA yet
b532a4b1
BH
380 }
381 catch(...){}
382 sd.serial=soadata.serial;
d8f47a4a 383 if(sd.last_check+soadata.refresh < (unsigned int)time(0))
b532a4b1
BH
384 unfreshDomains->push_back(sd);
385 }
386}
387
388bool Bind2Backend::getDomainInfo(const string &domain, DomainInfo &di)
389{
79ff9306 390 BB2DomainInfo bbd;
391 if(!safeGetBBDomainInfo(domain, &bbd))
392 return false;
b532a4b1 393
79ff9306 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;
b532a4b1 407 }
79ff9306 408 catch(...){}
409
410 return true;
b532a4b1
BH
411}
412
27d94a79
BH
413void Bind2Backend::alsoNotifies(const string &domain, set<string> *ips)
414{
27d94a79
BH
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 }
a1c4d85c 419 ReadLock rl(&s_state_lock);
f08339e3 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++) {
27d94a79
BH
423 (*ips).insert(*it);
424 }
425 return;
426 }
427 }
428}
b532a4b1 429
a1c4d85c 430// only parses, does NOT add to s_state!
431void Bind2Backend::parseZoneFile(BB2DomainInfo *bbd)
40126001
PB
432{
433 NSEC3PARAMRecordContent ns3pr;
aebddbd2
KM
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);
aaf2e4a4 440
441 bbd->d_records = shared_ptr<recordstorage_t>(new recordstorage_t());
40126001
PB
442
443 ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
444 DNSResourceRecord rr;
445 string hashed;
b755d50a 446 while(zpt.get(rr)) {
40126001
PB
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 }
675fa24c 456 insertRecord(*bbd, rr.qname.toString(), rr.qtype, rr.content, rr.ttl, hashed);
40126001 457 }
f08339e3 458 fixupAuth(bbd->d_records.getWRITABLE());
459 doEmptyNonTerminals(*bbd, nsec3zone, ns3pr);
40126001 460 bbd->setCtime();
47fee74f 461 bbd->d_loaded=true;
aaf2e4a4 462 bbd->d_checknow=false;
40126001
PB
463 bbd->d_status="parsed into memory at "+nowTime();
464}
465
7c696097 466/** THIS IS AN INTERNAL FUNCTION! It does moadnsparser prio impedance matching
04361e50 467 Much of the complication is due to the efforts to benefit from std::string reference counting copy on write semantics */
b9bafae0 468void Bind2Backend::insertRecord(BB2DomainInfo& bb2, const string &qnameu, const QType &qtype, const string &content, int ttl, const std::string& hashed, bool *auth)
b532a4b1 469{
d9367e79 470 Bind2DNSRecord bdr;
f08339e3 471 shared_ptr<recordstorage_t> records = bb2.d_records.getWRITABLE();
b755d50a 472 bdr.qname=toLowerCanonic(qnameu);
79ff9306 473
5e6880ce
BH
474 if(bb2.d_name.empty())
475 ;
fad6e8da 476 else if(dottedEndsOn(bdr.qname, bb2.d_name))
0eeca6c0 477 bdr.qname.resize(max(0, static_cast<int>(bdr.qname.length() - (bb2.d_name.length() + 1))));
c3b85638
PB
478 else {
479 string msg = "Trying to insert non-zone data, name='"+bdr.qname+"', qtype="+qtype.getName()+", zone='"+bb2.d_name+"'";
0eeca6c0 480 if(s_ignore_broken_records) {
c3b85638
PB
481 L<<Logger::Warning<<msg<< " ignored" << endl;
482 return;
483 }
484 else
485 throw PDNSException(msg);
486 }
9b145472
BH
487
488 bdr.qname.swap(bdr.qname);
ced82662 489
f08339e3 490 if(!records->empty() && bdr.qname==boost::prior(records->end())->qname)
491 bdr.qname=boost::prior(records->end())->qname;
ced82662 492
8e16b978 493 bdr.qname=labelReverse(bdr.qname);
874300a8 494 bdr.qtype=qtype.getCode();
82cc6877 495 bdr.content=content;
772e8b10 496 bdr.nsec3hash = hashed;
b5baefaf 497
90f27487
KM
498 if (auth) // Set auth on empty non-terminals
499 bdr.auth=*auth;
fe5ecd7e
KM
500 else
501 bdr.auth=true;
82cc6877 502
9b145472 503 bdr.ttl=ttl;
f08339e3 504 records->insert(bdr);
b532a4b1
BH
505}
506
b532a4b1
BH
507string Bind2Backend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
508{
509 ostringstream ret;
b532a4b1 510
d10f9034 511 for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
f08339e3 512 BB2DomainInfo bbd;
513 if(safeGetBBDomainInfo(*i, &bbd)) {
2717b8b3 514 Bind2Backend bb2;
aaf2e4a4 515 bb2.queueReloadAndStore(bbd.d_id);
d10f9034 516 ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";
b532a4b1 517 }
d10f9034
BH
518 else
519 ret<< *i << " no such domain\n";
520 }
e9dd48f9 521 if(ret.str().empty())
04361e50 522 ret<<"no domains reloaded";
b532a4b1
BH
523 return ret.str();
524}
525
526
527string Bind2Backend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
528{
d10f9034 529 ostringstream ret;
3be60e6e 530
d10f9034
BH
531 if(parts.size() > 1) {
532 for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
f08339e3 533 BB2DomainInfo bbd;
534 if(safeGetBBDomainInfo(*i, &bbd)) {
4957a608 535 ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";
d10f9034
BH
536 }
537 else
4957a608 538 ret<< *i << " no such domain\n";
d10f9034 539 }
b532a4b1 540 }
f08339e3 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 }
d10f9034
BH
547
548 if(ret.str().empty())
549 ret<<"no domains passed";
550
551 return ret.str();
b532a4b1
BH
552}
553
b532a4b1
BH
554string Bind2Backend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
555{
556 ostringstream ret;
a1c4d85c 557 ReadLock rl(&s_state_lock);
f08339e3 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;
f08339e3 561 }
b532a4b1
BH
562 return ret.str();
563}
564
a31fe060
PB
565string Bind2Backend::DLAddDomainHandler(const vector<string>&parts, Utility::pid_t ppid)
566{
567 if(parts.size() < 3)
1c8fd613 568 return "ERROR: Domain name and zone filename are required";
a31fe060 569
b755d50a 570 string domainname = toLowerCanonic(parts[1]);
a31fe060 571 const string &filename = parts[2];
f08339e3 572 BB2DomainInfo bbd;
573 if(safeGetBBDomainInfo(domainname, &bbd))
a31fe060 574 return "Already loaded";
d8f47a4a 575 Bind2Backend bb2; // createdomainentry needs access to our configuration
b755d50a 576 bbd=bb2.createDomainEntry(domainname, filename);
a31fe060
PB
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";
f08339e3 582
b755d50a 583 safePutBBDomainInfo(bbd);
a31fe060
PB
584
585 L<<Logger::Warning<<"Zone "<<domainname<< " loaded"<<endl;
a31fe060
PB
586 return "Loaded zone " + domainname + " from " + filename;
587}
588
2717b8b3 589Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
b532a4b1 590{
0f310932
AT
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
d7dbeca9 605 setArgPrefix("bind"+suffix);
17c2b1a8 606 d_logprefix="[bind"+suffix+"backend]";
aebddbd2 607 d_hybrid=mustDo("hybrid");
17c2b1a8
PB
608 s_ignore_broken_records=mustDo("ignore-broken-records");
609
aebddbd2
KM
610 if (!loadZones && d_hybrid)
611 return;
612
b532a4b1 613 Lock l(&s_startup_lock);
2717b8b3 614
b532a4b1 615 d_transaction_id=0;
2717b8b3 616 setupDNSSEC();
b532a4b1
BH
617 if(!s_first) {
618 return;
619 }
2717b8b3 620
2717b8b3
BH
621 if(loadZones) {
622 loadConfig();
623 s_first=0;
624 }
625
b532a4b1 626 extern DynListener *dl;
17384177
PD
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");
a31fe060 630 dl->registerFunc("BIND-ADD-ZONE", &DLAddDomainHandler, "bindbackend: add zone", "<domain> <filename>");
b532a4b1
BH
631}
632
2a95d1df 633Bind2Backend::~Bind2Backend()
0f310932 634{ freeStatements(); } // deallocate statements
2a95d1df 635
b532a4b1
BH
636void Bind2Backend::rediscover(string *status)
637{
10635124 638 loadConfig(status);
b532a4b1 639}
82f7b3cc 640
a3047110
BH
641void Bind2Backend::reload()
642{
f08339e3 643 WriteLock rwl(&s_state_lock);
b755d50a 644 for(state_t::iterator i = s_state.begin(); i != s_state.end() ; ++i) {
6a347650 645 i->d_checknow=true; // being a bit cheeky here, don't index state_t on this (mutable)
f08339e3 646 }
82f7b3cc 647}
81c43517
BH
648
649void Bind2Backend::fixupAuth(shared_ptr<recordstorage_t> records)
650{
651 pair<recordstorage_t::const_iterator, recordstorage_t::const_iterator> range;
21a0a2ae
BH
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 }
81c43517
BH
659
660 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records) {
661 bdr.auth=true;
21a0a2ae 662
81c43517
BH
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;
21a0a2ae
BH
671 if(bdr.qtype == QType::NS || nssets.count(sqname)) { // NS records which are not apex are unauth by definition
672 bdr.auth=false;
81c43517
BH
673 }
674 } while(chopOff(sqname));
675 }
676}
677
f08339e3 678void Bind2Backend::doEmptyNonTerminals(BB2DomainInfo& bbd, bool nsec3zone, NSEC3PARAMRecordContent ns3pr)
b5baefaf 679{
9353b710 680 shared_ptr<const recordstorage_t> records = bbd.d_records.get();
90f27487
KM
681 bool auth, doent=true;
682 set<string> qnames;
683 map<string, bool> nonterm;
684 string shorter, hashed;
b5baefaf
PD
685
686 uint32_t maxent = ::arg().asNum("max-ent-entries");
687
f08339e3 688 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records)
90f27487 689 qnames.insert(labelReverse(bdr.qname));
b5baefaf 690
f08339e3 691 BOOST_FOREACH(const Bind2DNSRecord& bdr, *records) {
90f27487
KM
692 shorter=labelReverse(bdr.qname);
693
694 if (!bdr.auth && bdr.qtype == QType::NS)
fe5ecd7e 695 auth=(!nsec3zone || !ns3pr.d_flags);
90f27487
KM
696 else
697 auth=bdr.auth;
b5baefaf
PD
698
699 while(chopOff(shorter))
700 {
54c9247e 701 if(!qnames.count(shorter))
b5baefaf
PD
702 {
703 if(!(maxent))
704 {
f08339e3 705 L<<Logger::Error<<"Zone '"<<bbd.d_name<<"' has too many empty non terminals."<<endl;
b5baefaf
PD
706 doent=false;
707 break;
708 }
90f27487
KM
709
710 if (!nonterm.count(shorter)) {
711 nonterm.insert(pair<string, bool>(shorter, auth));
712 --maxent;
713 } else if (auth)
714 nonterm[shorter]=true;
b5baefaf
PD
715 }
716 }
717 if(!doent)
718 return;
719 }
720
721 DNSResourceRecord rr;
b563f71b 722 rr.qtype="#0";
b5baefaf
PD
723 rr.content="";
724 rr.ttl=0;
90f27487
KM
725 pair<string, bool> nt;
726 BOOST_FOREACH(nt, nonterm)
b5baefaf 727 {
f08339e3 728 rr.qname=nt.first+"."+bbd.d_name+".";
b5baefaf 729 if(nsec3zone)
fb484267 730 hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname));
675fa24c 731 insertRecord(bbd, rr.qname.toString(), rr.qtype, rr.content, rr.ttl, hashed, &nt.second);
b5baefaf
PD
732 }
733}
734
b532a4b1
BH
735void Bind2Backend::loadConfig(string* status)
736{
737 static int domain_id=1;
d9367e79 738
b532a4b1
BH
739 if(!getArg("config").empty()) {
740 BindParser BP;
741 try {
742 BP.parse(getArg("config"));
743 }
3f81d239 744 catch(PDNSException &ae) {
b532a4b1
BH
745 L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl;
746 throw;
747 }
b532a4b1
BH
748
749 vector<BindDomainInfo> domains=BP.getDomains();
27d94a79
BH
750 this->alsoNotify = BP.getAlsoNotify();
751
10635124 752 s_binddirectory=BP.getDirectory();
874300a8 753 // ZP.setDirectory(d_binddirectory);
04361e50 754
b532a4b1
BH
755 L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), will report when done"<<endl;
756
6b947ad5 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 }
b532a4b1
BH
764 int rejected=0;
765 int newdomains=0;
766
82f7b3cc
BH
767 struct stat st;
768
769 for(vector<BindDomainInfo>::iterator i=domains.begin(); i!=domains.end(); ++i)
770 {
c36285d0 771 if(stat(i->filename.c_str(), &st) == 0) {
4957a608
BH
772 i->d_dev = st.st_dev;
773 i->d_ino = st.st_ino;
c36285d0 774 }
82f7b3cc
BH
775 }
776
777 sort(domains.begin(), domains.end()); // put stuff in inode order
b532a4b1 778 for(vector<BindDomainInfo>::const_iterator i=domains.begin();
4957a608
BH
779 i!=domains.end();
780 ++i)
b532a4b1 781 {
4957a608
BH
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
f08339e3 787 BB2DomainInfo bbd;
4957a608 788
f08339e3 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;
4957a608
BH
794 }
795
4957a608 796 // overwrite what we knew about the domain
b755d50a 797 bbd.d_name=toLowerCanonic(i->name);
f08339e3 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;
ff0a23c2 802
803 newnames.insert(bbd.d_name);
f08339e3 804 if(filenameChanged || !bbd.d_loaded || !bbd.current()) {
4957a608 805 L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
d4d9ea3c 806
4957a608 807 try {
f08339e3 808 parseZoneFile(&bbd);
4957a608 809 }
3f81d239 810 catch(PDNSException &ae) {
4957a608
BH
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();
f08339e3 816 bbd.d_status=msg.str();
817
4957a608
BH
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();
f08339e3 827 bbd.d_status=msg.str();
4957a608
BH
828 L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
829 rejected++;
830 }
d8f47a4a 831 safePutBBDomainInfo(bbd);
ff0a23c2 832
4957a608 833 }
b532a4b1 834 }
b532a4b1 835 vector<string> diff;
f08339e3 836
b532a4b1 837 set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
ff0a23c2 838 unsigned int remdomains=diff.size();
6b947ad5 839
840 BOOST_FOREACH(const std::string& name, diff) {
841 safeRemoveBBDomainInfo(name);
842 }
cecc5075 843
04361e50 844 // count number of entirely new domains
6b947ad5 845 diff.clear();
846 set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff));
847 newdomains=diff.size();
e9dd48f9 848
b532a4b1
BH
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;
b532a4b1
BH
855 }
856}
857
aaf2e4a4 858void Bind2Backend::queueReloadAndStore(unsigned int id)
b532a4b1 859{
aaf2e4a4 860 BB2DomainInfo bbold;
b532a4b1 861 try {
aaf2e4a4 862 if(!safeGetBBDomainInfo(id, &bbold))
863 return;
864 parseZoneFile(&bbold);
865 bbold.d_checknow=false;
b755d50a 866 safePutBBDomainInfo(bbold);
aaf2e4a4 867 L<<Logger::Warning<<"Zone '"<<bbold.d_name<<"' ("<<bbold.d_filename<<") reloaded"<<endl;
b532a4b1 868 }
3f81d239 869 catch(PDNSException &ae) {
c733e4d5 870 ostringstream msg;
aaf2e4a4 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);
c733e4d5 874 }
5172cb78 875 catch(std::exception &ae) {
b532a4b1 876 ostringstream msg;
aaf2e4a4 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);
b532a4b1
BH
880 }
881}
882
675fa24c 883bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
8e16b978 884{
675fa24c 885 string domain=qname.toString();
f08339e3 886 shared_ptr<const recordstorage_t> records = bbd.d_records.get();
887 recordstorage_t::const_iterator iter = records->upper_bound(domain);
8e16b978 888
8c1a6d6d
KM
889 if (before.empty()){
890 //cout<<"starting before for: '"<<domain<<"'"<<endl;
f08339e3 891 iter = records->upper_bound(domain);
27045410 892
f08339e3 893 while(iter == records->end() || (iter->qname) > domain || (!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
8c1a6d6d 894 iter--;
8e16b978 895
8c1a6d6d
KM
896 before=iter->qname;
897 }
898 else {
899 before=domain;
900 }
9b145472 901
8c1a6d6d 902 //cerr<<"Now after"<<endl;
f08339e3 903 iter = records->upper_bound(domain);
f3c18f9d 904
f08339e3 905 if(iter == records->end()) {
21a0a2ae 906 //cerr<<"\tFound the end, begin storage: '"<<bbd.d_records->begin()->qname<<"', '"<<bbd.d_name<<"'"<<endl;
f3c18f9d 907 after.clear(); // this does the right thing (i.e. point to apex, which is sure to have auth records)
8e16b978 908 } else {
88717c79 909 //cerr<<"\tFound: '"<<(iter->qname)<<"' (nsec3hash='"<<(iter->nsec3hash)<<"')"<<endl;
27045410
PD
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.
b5baefaf 912 while((!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
f3c18f9d
PD
913 {
914 iter++;
f08339e3 915 if(iter == records->end())
f3c18f9d
PD
916 {
917 after.clear();
918 break;
919 }
920 }
75943e62 921 after = (iter)->qname;
8e16b978
BH
922 }
923
21a0a2ae 924 //cerr<<"Before: '"<<before<<"', after: '"<<after<<"'\n";
8e16b978 925 return true;
8e16b978 926}
9b145472 927
772e8b10
BH
928bool Bind2Backend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string& qname, std::string& unhashed, std::string& before, std::string& after)
929{
f08339e3 930 BB2DomainInfo bbd;
931 safeGetBBDomainInfo(id, &bbd);
932
772e8b10 933 NSEC3PARAMRecordContent ns3pr;
f08339e3 934 string auth=bbd.d_name;
51a3a4d4 935
aebddbd2
KM
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) {
21a0a2ae 944 //cerr<<"in bind2backend::getBeforeAndAfterAbsolute: no nsec3 for "<<auth<<endl;
772e8b10
BH
945 return findBeforeAndAfterUnhashed(bbd, qname, unhashed, before, after);
946
947 }
948 else {
949 string lqname = toLower(qname);
27045410 950 // cerr<<"\nin bind2backend::getBeforeAndAfterAbsolute: nsec3 HASH for "<<auth<<", asked for: "<<lqname<< " (auth: "<<auth<<".)"<<endl;
772e8b10 951 typedef recordstorage_t::index<HashedTag>::type records_by_hashindex_t;
b755d50a 952 records_by_hashindex_t& hashindex=boost::multi_index::get<HashedTag>(*bbd.d_records.getWRITABLE()); // needlessly dangerous
772e8b10 953
a711454e 954// BOOST_FOREACH(const Bind2DNSRecord& bdr, hashindex) {
8563d54a
BH
955// cerr<<"Hash: "<<bdr.nsec3hash<<"\t"<< (lqname < bdr.nsec3hash) <<endl;
956// }
27045410 957
8c1a6d6d
KM
958 records_by_hashindex_t::const_iterator iter;
959 bool wraponce;
27045410 960
8c1a6d6d
KM
961 if (before.empty()) {
962 iter = hashindex.upper_bound(lqname);
75a89ce6 963
8c1a6d6d
KM
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 }
ebc55fe7 988 }
7c85a891 989 }
27045410 990
8c1a6d6d
KM
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 }
27045410 998
27045410
PD
999 iter = hashindex.upper_bound(lqname);
1000 if(iter == hashindex.end())
1001 {
1002 iter = hashindex.begin();
1003 }
1004
8c1a6d6d 1005 wraponce = false;
b8adb30d 1006 while((!iter->auth && !(iter->qtype == QType::NS && !pdns_iequals(iter->qname, auth) && !ns3pr.d_flags)) || iter->nsec3hash.empty())
27045410
PD
1007 {
1008 iter++;
8c1a6d6d
KM
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 }
772e8b10
BH
1019 }
1020 }
27045410
PD
1021
1022 after = iter->nsec3hash;
1023 // cerr<<"after: "<<(iter->nsec3hash)<<"/"<<(iter->qname)<<endl;
21a0a2ae 1024 //cerr<<"Before: '"<<before<<"', after: '"<<after<<"'\n";
772e8b10
BH
1025 return true;
1026 }
1027}
1028
675fa24c 1029void Bind2Backend::lookup(const QType &qtype, const DNSName &qname, DNSPacket *pkt_p, int zoneId )
b532a4b1 1030{
95c22a8f 1031 d_handle.reset();
675fa24c 1032 string domain=qname.toString();
69ab97d1 1033
702156df 1034 static bool mustlog=::arg().mustDo("query-logging");
70524e9a
BH
1035 if(mustlog)
1036 L<<Logger::Warning<<"Lookup for '"<<qtype.getName()<<"' of '"<<domain<<"'"<<endl;
b755d50a 1037 bool found=false;
1038 BB2DomainInfo bbd;
b563f71b 1039 do {
b755d50a 1040 found = safeGetBBDomainInfo(domain, &bbd);
1041 } while ((!found || (zoneId != (int)bbd.d_id && zoneId != -1)) && chopOff(domain));
b532a4b1 1042
b755d50a 1043 if(!found) {
70524e9a
BH
1044 if(mustlog)
1045 L<<Logger::Warning<<"Found no authoritative zone for "<<qname<<endl;
7b308b7e 1046 d_handle.d_list=false;
b532a4b1
BH
1047 return;
1048 }
b755d50a 1049
70524e9a 1050 if(mustlog)
b755d50a 1051 L<<Logger::Warning<<"Found a zone '"<<domain<<"' (with id " << bbd.d_id<<") that might contain data "<<endl;
70524e9a 1052
b755d50a 1053 d_handle.id=bbd.d_id;
7b308b7e 1054
b532a4b1
BH
1055 DLOG(L<<"Bind2Backend constructing handle for search for "<<qtype.getName()<<" for "<<
1056 qname<<endl);
9b145472 1057
5e6880ce
BH
1058 if(domain.empty())
1059 d_handle.qname=qname;
1060 else if(strcasecmp(qname.c_str(),domain.c_str()))
945a9ad4
BH
1061 d_handle.qname=qname.substr(0,qname.size()-domain.length()-1); // strip domain name
1062
7b308b7e 1063 d_handle.qtype=qtype;
945a9ad4 1064 d_handle.domain=qname.substr(qname.size()-domain.length());
b755d50a 1065
ab9e6a6c
BH
1066 if(!bbd.d_loaded) {
1067 d_handle.reset();
1614d543 1068 throw DBException("Zone for '"+bbd.d_name+"' in '"+bbd.d_filename+"' temporarily not available (file missing, or master dead)"); // fsck
b532a4b1 1069 }
ab9e6a6c
BH
1070
1071 if(!bbd.current()) {
1072 L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
aaf2e4a4 1073 queueReloadAndStore(bbd.d_id);
9353b710
KM
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
b532a4b1 1076 }
ced82662 1077
f08339e3 1078 d_handle.d_records = bbd.d_records.get();
ab9e6a6c
BH
1079
1080 if(d_handle.d_records->empty())
1081 DLOG(L<<"Query with no results"<<endl);
38e655b6 1082
772e8b10 1083 pair<recordstorage_t::const_iterator, recordstorage_t::const_iterator> range;
ced82662 1084
8e16b978 1085 string lname=labelReverse(toLower(d_handle.qname));
21a0a2ae
BH
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;
702156df 1090 d_handle.mustlog = mustlog;
ced82662
BH
1091
1092 if(range.first==range.second) {
abc1d928 1093 // cerr<<"Found nothing!"<<endl;
7b308b7e 1094 d_handle.d_list=false;
76faefcf 1095 d_handle.d_iter = d_handle.d_end_iter = range.first;
ced82662
BH
1096 return;
1097 }
1098 else {
c6547e77 1099 // cerr<<"Found something!"<<endl;
7b308b7e
BH
1100 d_handle.d_iter=range.first;
1101 d_handle.d_end_iter=range.second;
ced82662
BH
1102 }
1103
7b308b7e 1104 d_handle.d_list=false;
b532a4b1
BH
1105}
1106
1107Bind2Backend::handle::handle()
1108{
38e655b6 1109 mustlog=false;
b532a4b1
BH
1110}
1111
1112bool Bind2Backend::get(DNSResourceRecord &r)
1113{
702156df
BH
1114 if(!d_handle.d_records) {
1115 if(d_handle.mustlog)
1116 L<<Logger::Warning<<"There were no answers"<<endl;
69ab97d1 1117 return false;
702156df 1118 }
69ab97d1 1119
7b308b7e 1120 if(!d_handle.get(r)) {
702156df
BH
1121 if(d_handle.mustlog)
1122 L<<Logger::Warning<<"End of answers"<<endl;
69ab97d1 1123
702156df 1124 d_handle.reset();
69ab97d1 1125
b532a4b1
BH
1126 return false;
1127 }
c1d02c0d 1128 if(d_handle.mustlog)
675fa24c 1129 L<<Logger::Warning<<"Returning: '"<<r.qtype.getName()<<"' of '"<<r.qname.toString()<<"', content: '"<<r.content<<"'"<<endl;
b532a4b1
BH
1130 return true;
1131}
1132
1133bool Bind2Backend::handle::get(DNSResourceRecord &r)
1134{
1135 if(d_list)
1136 return get_list(r);
1137 else
1138 return get_normal(r);
1139}
1140
a3047110
BH
1141void Bind2Backend::handle::reset()
1142{
1143 d_records.reset();
1144 qname.clear();
1145 mustlog=false;
1146}
1147
8e16b978 1148//#define DLOG(x) x
b532a4b1
BH
1149bool Bind2Backend::handle::get_normal(DNSResourceRecord &r)
1150{
06748762
BH
1151 DLOG(L << "Bind2Backend get() was called for "<<qtype.getName() << " record for '"<<
1152 qname<<"' - "<<d_records->size()<<" available in total!"<<endl);
b532a4b1 1153
ced82662 1154 if(d_iter==d_end_iter) {
ced82662
BH
1155 return false;
1156 }
9b145472 1157
ced82662 1158 while(d_iter!=d_end_iter && !(qtype.getCode()==QType::ANY || (d_iter)->qtype==qtype.getCode())) {
b532a4b1
BH
1159 DLOG(L<<Logger::Warning<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).getName()<<": '"<<d_iter->content<<"'"<<endl);
1160 d_iter++;
1161 }
ced82662 1162 if(d_iter==d_end_iter) {
b532a4b1
BH
1163 return false;
1164 }
1165 DLOG(L << "Bind2Backend get() returning a rr with a "<<QType(d_iter->qtype).getCode()<<endl);
1166
ced82662
BH
1167 r.qname=qname.empty() ? domain : (qname+"."+domain);
1168 r.domain_id=id;
1169 r.content=(d_iter)->content;
9b145472 1170 // r.domain_id=(d_iter)->domain_id;
b532a4b1
BH
1171 r.qtype=(d_iter)->qtype;
1172 r.ttl=(d_iter)->ttl;
8e16b978 1173
2717b8b3
BH
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;
8e16b978
BH
1176 r.auth = d_iter->auth;
1177
b532a4b1
BH
1178 d_iter++;
1179
1180 return true;
1181}
1182
cea26350 1183bool Bind2Backend::list(const string &target, int id, bool include_disabled)
b532a4b1 1184{
b755d50a 1185 BB2DomainInfo bbd;
1186
1187 if(!safeGetBBDomainInfo(id, &bbd))
b532a4b1
BH
1188 return false;
1189
e9dd48f9 1190 d_handle.reset();
b532a4b1
BH
1191 DLOG(L<<"Bind2Backend constructing handle for list of "<<id<<endl);
1192
b755d50a 1193 d_handle.d_records=bbd.d_records.get(); // give it a copy, which will stay around
d9367e79
BH
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>
b532a4b1 1196
7b308b7e
BH
1197 d_handle.id=id;
1198 d_handle.d_list=true;
b532a4b1 1199 return true;
b532a4b1
BH
1200}
1201
1202bool Bind2Backend::handle::get_list(DNSResourceRecord &r)
1203{
1204 if(d_qname_iter!=d_qname_end) {
8e16b978 1205 r.qname=d_qname_iter->qname.empty() ? domain : (labelReverse(d_qname_iter->qname)+"."+domain);
7b308b7e
BH
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;
8e16b978 1210 r.auth = d_qname_iter->auth;
b532a4b1
BH
1211 d_qname_iter++;
1212 return true;
1213 }
1214 return false;
b532a4b1
BH
1215}
1216
1217bool Bind2Backend::isMaster(const string &name, const string &ip)
1218{
b755d50a 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
b532a4b1
BH
1227 return false;
1228}
1229
719f9024 1230bool Bind2Backend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db)
f404b863
BH
1231{
1232 // Check whether we have a configfile available.
1233 if (getArg("supermaster-config").empty())
1234 return false;
1235
9e04108d 1236 ifstream c_if(getArg("supermasters").c_str(), std::ios::in); // this was nocreate?
f404b863
BH
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)) {
9e04108d 1246 std::istringstream ii(line);
f404b863
BH
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
b755d50a 1266BB2DomainInfo Bind2Backend::createDomainEntry(const string &domain, const string &filename)
f404b863 1267{
a31fe060 1268 int newid=1;
b755d50a 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 }
a31fe060
PB
1274 }
1275
b755d50a 1276 BB2DomainInfo bbd;
a31fe060
PB
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;
a31fe060
PB
1282 return bbd;
1283}
1284
719f9024 1285bool Bind2Backend::createSlaveDomain(const string &ip, const string &domain, const string &nameserver, const string &account)
f404b863 1286{
f404b863
BH
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;
8168957d
PB
1292
1293 {
1294 Lock l2(&s_supermaster_config_lock);
f404b863 1295
8168957d
PB
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();
f404b863 1310 }
8168957d 1311
b755d50a 1312 BB2DomainInfo bbd = createDomainEntry(toLowerCanonic(domain), filename);
e5b11b2f 1313 bbd.d_masters.push_back(ip);
b755d50a 1314 safePutBBDomainInfo(bbd);
f404b863
BH
1315 return true;
1316}
1317
b532a4b1
BH
1318class Bind2Factory : public BackendFactory
1319{
1320 public:
d7dbeca9 1321 Bind2Factory() : BackendFactory("bind") {}
b532a4b1
BH
1322
1323 void declareArguments(const string &suffix="")
1324 {
fb484267 1325 declare(suffix,"ignore-broken-records","Ignore records that are out-of-bound for the zone.","no");
b2e52bc5
PB
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", "");
aebddbd2 1332 declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no");
b532a4b1
BH
1333 }
1334
1335 DNSBackend *make(const string &suffix="")
1336 {
1337 return new Bind2Backend(suffix);
1338 }
2717b8b3
BH
1339
1340 DNSBackend *makeMetadataOnly(const string &suffix="")
1341 {
1342 return new Bind2Backend(suffix, false);
1343 }
b532a4b1
BH
1344};
1345
1346//! Magic class that is activated when the dynamic library is loaded
1347class Bind2Loader
1348{
1349public:
1350 Bind2Loader()
1351 {
1352 BackendMakers().report(new Bind2Factory);
5e6a3d93
PL
1353 L << Logger::Info << "[bind2backend] This is the bind backend version " << VERSION
1354#ifndef REPRODUCIBLE
1355 << " (" __DATE__ " " __TIME__ ")"
1356#endif
1357 << " reporting" << endl;
b532a4b1
BH
1358 }
1359};
1360static Bind2Loader bind2loader;