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