• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 21496291765

29 Jan 2026 10:18AM UTC coverage: 66.823% (-4.7%) from 71.534%
21496291765

push

github

web-flow
Merge pull request #16782 from rgacogne/ddist-210-a1

dnsdist: Update security polling zone and ChangeLog for 2.1.0-alpha1

57917 of 119990 branches covered (48.27%)

Branch coverage included in aggregate %.

131747 of 163841 relevant lines covered (80.41%)

9104990.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.5
/modules/bindbackend/bindbackend2.cc
1
/*
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
 */
22

23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
#include <cerrno>
27
#include <string>
28
#include <set>
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
#include <unistd.h>
32
#include <fstream>
33
#include <fcntl.h>
34
#include <sstream>
35
#include <boost/algorithm/string.hpp>
36
#include <system_error>
37
#include <unordered_map>
38
#include <unordered_set>
39

40
#include "pdns/dnsseckeeper.hh"
41
#include "pdns/dnssecinfra.hh"
42
#include "pdns/base32.hh"
43
#include "pdns/namespaces.hh"
44
#include "pdns/dns.hh"
45
#include "pdns/dnsbackend.hh"
46
#include "bindbackend2.hh"
47
#include "pdns/dnspacket.hh"
48
#include "pdns/zoneparser-tng.hh"
49
#include "pdns/bindparserclasses.hh"
50
#include "pdns/logger.hh"
51
#include "pdns/arguments.hh"
52
#include "pdns/qtype.hh"
53
#include "pdns/misc.hh"
54
#include "pdns/dynlistener.hh"
55
#include "pdns/lock.hh"
56
#include "pdns/auth-zonecache.hh"
57
#include "pdns/auth-caches.hh"
58

59
/*
60
   All instances of this backend share one s_state, which is indexed by zone name and zone id.
61
   The s_state is protected by a read/write lock, and the goal it to only interact with it briefly.
62
   When a query comes in, we take a read lock and COPY the best zone to answer from s_state (BB2DomainInfo object)
63
   All answers are served from this copy.
64

65
   To interact with s_state, use safeGetBBDomainInfo (search on name or id), safePutBBDomainInfo (to update)
66
   or safeRemoveBBDomainInfo. These all lock as they should.
67

68
   Several functions need to traverse s_state to get data for the rest of PowerDNS. When doing so,
69
   you need to manually take the lock (read).
70

71
   Parsing zones happens with parseZone(), which fills a BB2DomainInfo object. This can then be stored with safePutBBDomainInfo.
72

73
   Finally, the BB2DomainInfo contains all records as a LookButDontTouch object. This makes sure you only look, but don't touch, since
74
   the records might be in use in other places.
75
*/
76

77
SharedLockGuarded<Bind2Backend::state_t> Bind2Backend::s_state;
78
int Bind2Backend::s_first = 1;
79
bool Bind2Backend::s_ignore_broken_records = false;
80

81
std::mutex Bind2Backend::s_autosecondary_config_lock; // protects writes to config file
82
std::mutex Bind2Backend::s_startup_lock;
83
string Bind2Backend::s_binddirectory;
84

85
BB2DomainInfo::BB2DomainInfo()
86
{
14,775✔
87
  d_loaded = false;
14,775✔
88
  d_lastcheck = 0;
14,775✔
89
  d_checknow = false;
14,775✔
90
  d_status = "Unknown";
14,775✔
91
}
14,775✔
92

93
void BB2DomainInfo::setCheckInterval(time_t seconds)
94
{
2,650✔
95
  d_checkinterval = seconds;
2,650✔
96
}
2,650✔
97

98
bool BB2DomainInfo::current()
99
{
3,805✔
100
  if (d_checknow) {
3,805✔
101
    return false;
6✔
102
  }
6✔
103

104
  if (!d_checkinterval)
3,799!
105
    return true;
3,799✔
106

107
  if (time(nullptr) - d_lastcheck < d_checkinterval)
×
108
    return true;
×
109

110
  d_lastcheck = time(nullptr);
×
111
  // Our data is still current if all the files it has been obtained from have
×
112
  // their modification times unchanged since the last parse.
113
  return std::all_of(d_fileinfo.cbegin(), d_fileinfo.cend(),
×
114
                     [](const auto& fileinfo) { return getCtime(fileinfo.first) == fileinfo.second; });
×
115
}
116

117
time_t BB2DomainInfo::getCtime(const std::string& filename)
×
118
{
×
119
  struct stat buf;
120

×
121
  if (filename.empty() || stat(filename.c_str(), &buf) < 0) {
×
122
    return 0;
×
123
  }
×
124
  return buf.st_ctime;
×
125
}
126

127
void BB2DomainInfo::updateCtime()
2,656✔
128
{
2,656✔
129
  struct stat buf;
2,656!
130

×
131
  for (auto& fileinfo : d_fileinfo) {
2,656!
132
    if (stat(fileinfo.first.c_str(), &buf) < 0) {
2,656!
133
      buf.st_ctime = 0;
134
    }
135
    fileinfo.second = buf.st_ctime;
10,106✔
136
  }
10,106✔
137
}
10,106✔
138

10,106!
139
// NOLINTNEXTLINE(readability-identifier-length)
×
140
bool Bind2Backend::safeGetBBDomainInfo(domainid_t id, BB2DomainInfo* bbd)
×
141
{
10,106✔
142
  auto state = s_state.read_lock();
10,106✔
143
  state_t::const_iterator iter = state->find(id);
10,106✔
144
  if (iter == state->end()) {
×
145
    return false;
146
  }
4,373✔
147
  *bbd = *iter;
4,672✔
148
  return true;
4,672✔
149
}
4,672✔
150

4,672✔
151
bool Bind2Backend::safeGetBBDomainInfo(const ZoneName& name, BB2DomainInfo* bbd)
3,557✔
152
{
3,557✔
153
  auto state = s_state.read_lock();
1,414✔
154
  const auto& nameindex = boost::multi_index::get<NameTag>(*state);
1,115✔
155
  auto iter = nameindex.find(name);
4,373✔
156
  if (iter == nameindex.end()) {
299!
157
    return false;
158
  }
159
  *bbd = *iter;
×
160
  return true;
×
161
}
×
162

163
bool Bind2Backend::safeRemoveBBDomainInfo(const ZoneName& name)
164
{
×
165
  auto state = s_state.write_lock();
×
166
  using nameindex_t = state_t::index<NameTag>::type;
×
167
  nameindex_t& nameindex = boost::multi_index::get<NameTag>(*state);
×
168

×
169
  nameindex_t::iterator iter = nameindex.find(name);
×
170
  if (iter == nameindex.end()) {
×
171
    return false;
172
  }
2,792✔
173
  nameindex.erase(iter);
2,792✔
174
  return true;
2,792✔
175
}
2,792✔
176

177
void Bind2Backend::safePutBBDomainInfo(const BB2DomainInfo& bbd)
178
{
179
  auto state = s_state.write_lock();
180
  replacing_insert(*state, bbd);
×
181
}
×
182

×
183
// NOLINTNEXTLINE(readability-identifier-length)
×
184
void Bind2Backend::setNotified(domainid_t id, uint32_t serial)
×
185
{
186
  BB2DomainInfo bbd;
187
  if (!safeGetBBDomainInfo(id, &bbd))
68!
188
    return;
68✔
189
  bbd.d_lastnotified = serial;
68!
190
  safePutBBDomainInfo(bbd);
68✔
191
}
68✔
192

68!
193
// NOLINTNEXTLINE(readability-identifier-length)
68✔
194
void Bind2Backend::setLastCheck(domainid_t domain_id, time_t lastcheck)
195
{
196
  BB2DomainInfo bbd;
×
197
  if (safeGetBBDomainInfo(domain_id, &bbd)) {
×
198
    bbd.d_lastcheck = lastcheck;
199
    safePutBBDomainInfo(bbd);
200
  }
201
}
68✔
202

68✔
203
void Bind2Backend::setStale(domainid_t domain_id)
68✔
204
{
205
  Bind2Backend::setLastCheck(domain_id, 0);
206
}
208✔
207

208✔
208
void Bind2Backend::setFresh(domainid_t domain_id)
140✔
209
{
140✔
210
  Bind2Backend::setLastCheck(domain_id, time(nullptr));
140✔
211
}
140✔
212

68!
213
bool Bind2Backend::startTransaction(const ZoneName& qname, domainid_t domainId)
214
{
×
215
  if (domainId == UnknownDomainID) {
×
216
    d_transaction_tmpname.clear();
68!
217
    d_transaction_id = UnknownDomainID;
68✔
218
    // No support for domain contents deletion
68✔
219
    return false;
68!
220
  }
68✔
221
  if (domainId == 0) {
68!
222
    throw DBException("domain_id 0 is invalid for this backend.");
68!
223
  }
×
224

×
225
  d_transaction_id = domainId;
226
  d_transaction_qname = qname;
68!
227
  BB2DomainInfo bbd;
68!
228
  if (safeGetBBDomainInfo(domainId, &bbd)) {
×
229
    d_transaction_tmpname = bbd.main_filename() + "XXXXXX";
230
    int fd = mkstemp(&d_transaction_tmpname.at(0));
×
231
    if (fd == -1) {
×
232
      throw DBException("Unable to create a unique temporary zonefile '" + d_transaction_tmpname + "': " + stringerror());
×
233
    }
×
234

68✔
235
    d_of = std::make_unique<ofstream>(d_transaction_tmpname);
68✔
236
    if (!*d_of) {
×
237
      unlink(d_transaction_tmpname.c_str());
68✔
238
      close(fd);
68✔
239
      fd = -1;
68✔
240
      d_of.reset();
241
      throw DBException("Unable to open temporary zonefile '" + d_transaction_tmpname + "': " + stringerror());
68✔
242
    }
68✔
243
    close(fd);
×
244
    fd = -1;
68✔
245

246
    *d_of << "; Written by PowerDNS, don't edit!" << endl;
247
    *d_of << "; Zone '" << bbd.d_name << "' retrieved from primary " << endl
208✔
248
          << "; at " << nowTime() << endl; // insert primary info here again
208✔
249

140✔
250
    return true;
68✔
251
  }
252
  return false;
68✔
253
}
68!
254

68✔
255
bool Bind2Backend::commitTransaction()
×
256
{
68✔
257
  // d_transaction_id is only set to a valid domain id if we are actually
68✔
258
  // setting up a replacement zone file with the updated data.
259
  if (d_transaction_id == UnknownDomainID) {
68!
260
    return false;
72!
261
  }
68!
262
  d_of.reset();
68✔
263

264
  BB2DomainInfo bbd;
265
  if (safeGetBBDomainInfo(d_transaction_id, &bbd)) {
×
266
    if (rename(d_transaction_tmpname.c_str(), bbd.main_filename().c_str()) < 0) {
×
267
      throw DBException("Unable to commit (rename to: '" + bbd.main_filename() + "') AXFRed zone: " + stringerror());
268
    }
269
    queueReloadAndStore(bbd.d_id);
×
270
  }
271

272
  d_transaction_id = UnknownDomainID;
×
273

274
  return true;
275
}
×
276

×
277
bool Bind2Backend::abortTransaction()
278
{
279
  // d_transaction_id is only set to a valid domain id if we are actually
202,660✔
280
  // setting up a replacement zone file with the updated data.
202,660!
281
  if (d_transaction_id != UnknownDomainID) {
×
282
    unlink(d_transaction_tmpname.c_str());
×
283
    d_of.reset();
284
    d_transaction_id = UnknownDomainID;
202,660✔
285
  }
202,660!
286

✔
287
  return true;
×
288
}
202,660!
289

202,660✔
290
static bool ciEqual(const string& lhs, const string& rhs)
610✔
291
{
610✔
292
  if (lhs.size() != rhs.size()) {
202,050✔
293
    return false;
202,050✔
294
  }
202,050✔
295

202,050✔
296
  string::size_type pos = 0;
202,660✔
297
  const string::size_type epos = lhs.size();
×
298
  for (; pos < epos; ++pos) {
×
299
    if (dns_tolower(lhs[pos]) != dns_tolower(rhs[pos])) {
×
300
      return false;
301
    }
202,660✔
302
  }
202,660✔
303
  return true;
864!
304
}
305

202,660✔
306
/** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
56✔
307
static bool endsOn(const string& domain, const string& suffix)
76✔
308
{
208✔
309
  if (suffix.empty() || ciEqual(domain, suffix)) {
212!
310
    return true;
416✔
311
  }
416✔
312

313
  if (domain.size() <= suffix.size()) {
202,660✔
314
    return false;
202,660!
315
  }
202,660✔
316

202,660✔
317
  string::size_type dpos = domain.size() - suffix.size() - 1;
202,660✔
318
  string::size_type spos = 0;
202,660✔
319

202,660✔
320
  if (domain[dpos++] != '.') {
×
321
    return false;
322
  }
×
323

324
  for (; dpos < domain.size(); ++dpos, ++spos) {
×
325
    if (dns_tolower(domain[dpos]) != dns_tolower(suffix[spos])) {
×
326
      return false;
327
    }
×
328
  }
×
329

×
330
  return true;
331
}
332

✔
333
/** strips a domain suffix from a domain, returns true if it stripped */
×
334
static bool stripDomainSuffix(string* qname, const ZoneName& zonename)
×
335
{
336
  std::string domain = zonename.operator const DNSName&().toString();
✔
337

×
338
  if (!endsOn(*qname, domain)) {
×
339
    return false;
×
340
  }
×
341

342
  if (toLower(*qname) == toLower(domain)) {
×
343
    *qname = "@";
×
344
  }
×
345
  else {
×
346
    if ((*qname)[qname->size() - domain.size() - 1] != '.') {
×
347
      return false;
×
348
    }
349

350
    qname->resize(qname->size() - domain.size() - 1);
×
351
  }
×
352
  return true;
×
353
}
×
354

355
bool Bind2Backend::feedRecord(const DNSResourceRecord& rr, const DNSName& /* ordername */, bool /* ordernameIsNSEC3 */)
×
356
{
×
357
  if (d_transaction_id == UnknownDomainID) {
×
358
    throw DBException("Bind2Backend::feedRecord() called outside of transaction");
×
359
  }
×
360

✔
361
  string qname;
×
362
  if (d_transaction_qname.empty()) {
×
363
    qname = rr.qname.toString();
×
364
  }
365
  else if (rr.qname.isPartOf(d_transaction_qname)) {
×
366
    if (rr.qname == d_transaction_qname.operator const DNSName&()) {
81!
367
      qname = "@";
81✔
368
    }
369
    else {
370
      DNSName relName = rr.qname.makeRelative(d_transaction_qname);
81✔
371
      qname = relName.toStringNoDot();
81✔
372
    }
81✔
373
  }
374
  else {
241✔
375
    throw DBException("out-of-zone data '" + rr.qname.toLogString() + "' during AXFR of zone '" + d_transaction_qname.toLogString() + "'");
175✔
376
  }
175✔
377

175✔
378
  shared_ptr<DNSRecordContent> drc(DNSRecordContent::make(rr.qtype.getCode(), QClass::IN, rr.content));
175✔
379
  string content = drc->getZoneRepresentation();
175✔
380

175✔
381
  // SOA needs stripping too! XXX FIXME - also, this should not be here I think
175✔
382
  switch (rr.qtype.getCode()) {
175✔
383
  case QType::MX:
175!
384
  case QType::SRV:
81✔
385
  case QType::CNAME:
405,570!
386
  case QType::DNAME:
81✔
387
  case QType::NS:
1,340✔
388
    stripDomainSuffix(&content, d_transaction_qname);
389
    // fallthrough
1,340!
390
  default:
1,340!
391
    if (d_of && *d_of) {
×
392
      *d_of << qname << "\t" << rr.ttl << "\t" << rr.qtype.toString() << "\t" << content << endl;
393
    }
×
394
  }
×
395
  return true;
×
396
}
×
397

398
void Bind2Backend::getUpdatedPrimaries(vector<DomainInfo>& changedDomains, std::unordered_set<DNSName>& /* catalogs */, CatalogHashMap& /* catalogHashes */)
×
399
{
41!
400
  vector<DomainInfo> consider;
81✔
401
  {
402
    auto state = s_state.read_lock();
403

4✔
404
    for (const auto& i : *state) {
4!
405
      if (i.d_kind != DomainInfo::Primary && this->alsoNotify.empty() && i.d_also_notify.empty())
4!
406
        continue;
4✔
407

4✔
408
      DomainInfo di;
68✔
409
      di.id = i.d_id;
68!
410
      di.zone = i.d_name;
×
411
      di.last_check = i.d_lastcheck;
68✔
412
      di.notified_serial = i.d_lastnotified;
68✔
413
      di.backend = this;
68✔
414
      di.kind = DomainInfo::Primary;
68!
415
      consider.push_back(std::move(di));
68✔
416
    }
68✔
417
  }
68✔
418

68✔
419
  SOAData soadata;
68✔
420
  for (DomainInfo& di : consider) {
4!
421
    soadata.serial = 0;
4✔
422
    try {
×
423
      this->getSOA(di.zone, di.id, soadata); // we might not *have* a SOA yet, but this might trigger a load of it
68✔
424
    }
68!
425
    catch (...) {
68✔
426
      continue;
68✔
427
    }
68✔
428
    if (di.notified_serial != soadata.serial) {
68!
429
      BB2DomainInfo bbd;
68✔
430
      if (safeGetBBDomainInfo(di.id, &bbd)) {
68!
431
        bbd.d_lastnotified = soadata.serial;
68✔
432
        safePutBBDomainInfo(bbd);
68✔
433
      }
434
      if (di.notified_serial) { // don't do notification storm on startup
68!
435
        di.serial = soadata.serial;
68✔
436
        changedDomains.push_back(std::move(di));
68✔
437
      }
41✔
438
    }
37✔
439
  }
440
}
978✔
441

1,015✔
442
void Bind2Backend::getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool /* include_disabled */)
1,015✔
443
{
618✔
444
  SOAData soadata;
445

434✔
446
  // prevent deadlock by using getSOA() later on
397✔
447
  {
397✔
448
    auto state = s_state.read_lock();
397✔
449
    domains->reserve(state->size());
397✔
450

397✔
451
    for (const auto& i : *state) {
397!
452
      DomainInfo di;
397✔
453
      di.id = i.d_id;
129✔
454
      di.zone = i.d_name;
129✔
455
      di.last_check = i.d_lastcheck;
166✔
456
      di.kind = i.d_kind;
457
      di.primaries = i.d_primaries;
166✔
458
      di.backend = this;
803✔
459
      domains->push_back(std::move(di));
129✔
460
    };
803!
461
  }
742✔
462

129✔
463
  if (getSerial) {
73✔
464
    for (DomainInfo& di : *domains) {
397✔
465
      // do not corrupt di if domain supplied by another backend.
397✔
466
      if (di.backend != this)
1,410!
467
        continue;
468
      try {
4✔
469
        this->getSOA(di.zone, di.id, soadata);
470
      }
26!
471
      catch (...) {
37✔
472
        continue;
473
      }
474
      di.serial = soadata.serial;
4✔
475
    }
4!
476
  }
×
477
}
×
478

×
479
void Bind2Backend::getUnfreshSecondaryInfos(vector<DomainInfo>* unfreshDomains)
✔
480
{
4!
481
  vector<DomainInfo> domains;
4!
482
  {
×
483
    auto state = s_state.read_lock();
×
484
    domains.reserve(state->size());
×
485
    for (const auto& i : *state) {
×
486
      if (i.d_kind != DomainInfo::Secondary)
×
487
        continue;
×
488
      DomainInfo sd;
×
489
      sd.id = i.d_id;
4✔
490
      sd.zone = i.d_name;
491
      sd.primaries = i.d_primaries;
492
      sd.last_check = i.d_lastcheck;
493
      sd.backend = this;
2,718✔
494
      sd.kind = DomainInfo::Secondary;
2,718✔
495
      domains.push_back(std::move(sd));
2,718✔
496
    }
2,718!
497
  }
×
498
  unfreshDomains->reserve(domains.size());
×
499

×
500
  for (DomainInfo& sd : domains) {
2,718!
501
    SOAData soadata;
2,718✔
502
    soadata.refresh = 0;
503
    soadata.serial = 0;
2,718✔
504
    try {
2,718✔
505
      getSOA(sd.zone, sd.id, soadata); // we might not *have* a SOA yet
2,718!
506
    }
2,718✔
507
    catch (...) {
2,718✔
508
    }
2,718✔
509
    sd.serial = soadata.serial;
3,276,047✔
510
    // coverity[store_truncates_time_t]
3,273,329!
511
    if (sd.last_check + soadata.refresh < (unsigned int)time(nullptr))
289!
512
      unfreshDomains->push_back(std::move(sd));
289✔
513
  }
3,273,618✔
514
}
3,273,618✔
515

2,718✔
516
bool Bind2Backend::getDomainInfo(const ZoneName& domain, DomainInfo& info, bool getSerial)
2,718✔
517
{
2,718✔
518
  BB2DomainInfo bbd;
2,718✔
519
  if (!safeGetBBDomainInfo(domain, &bbd))
2,718!
520
    return false;
2,718✔
521

2,718✔
522
  info.id = bbd.d_id;
2,718✔
523
  info.zone = domain;
2,718✔
524
  info.primaries = bbd.d_primaries;
2,718✔
525
  info.last_check = bbd.d_lastcheck;
526
  info.backend = this;
527
  info.kind = bbd.d_kind;
528
  info.serial = 0;
529
  if (getSerial) {
3,277,860!
530
    try {
3,277,860✔
531
      SOAData sd;
3,277,860✔
532
      sd.serial = 0;
533

3,277,860!
534
      getSOA(bbd.d_name, bbd.d_id, sd); // we might not *have* a SOA yet
535
      info.serial = sd.serial;
3,277,860✔
536
    }
3,277,558✔
537
    catch (...) {
302✔
538
    }
302✔
539
  }
304!
540

302✔
541
  return true;
304!
542
}
302✔
543

×
544
void Bind2Backend::alsoNotifies(const ZoneName& domain, set<string>* ips)
545
{
304✔
546
  // combine global list with local list
2!
547
  for (const auto& i : this->alsoNotify) {
4!
548
    (*ips).insert(i);
549
  }
3,277,558✔
550
  // check metadata too if available
8,817✔
551
  vector<string> meta;
2✔
552
  if (getDomainMetadata(domain, "ALSO-NOTIFY", meta)) {
3,277,560!
553
    for (const auto& str : meta) {
3,277,558!
554
      (*ips).insert(str);
3,277,558!
555
    }
3,277,558✔
556
  }
557
  auto state = s_state.read_lock();
3,277,558✔
558
  for (const auto& i : *state) {
4,531!
559
    if (i.d_name == domain) {
3,273,027!
560
      for (const auto& it : i.d_also_notify) {
3,273,029!
561
        (*ips).insert(it);
562
      }
3,277,558✔
563
      return;
3,277,558✔
564
    }
3,277,558✔
565
  }
566
}
567

×
568
// only parses, does NOT add to s_state!
×
569
void Bind2Backend::parseZoneFile(BB2DomainInfo* bbd)
570
{
×
571
  NSEC3PARAMRecordContent ns3pr;
×
572
  bool nsec3zone = false;
×
573
  if (d_hybrid) {
×
574
    DNSSECKeeper dk;
×
575
    nsec3zone = dk.getNSEC3PARAM(bbd->d_name, &ns3pr);
×
576
  }
×
577
  else
×
578
    nsec3zone = getNSEC3PARAMuncached(bbd->d_name, &ns3pr);
×
579

×
580
  auto records = std::make_shared<recordstorage_t>();
✔
581
  ZoneParserTNG zpt(bbd->main_filename(), bbd->d_name, s_binddirectory, d_upgradeContent);
×
582
  zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
×
583
  zpt.setMaxIncludes(::arg().asNum("max-include-depth"));
584
  DNSResourceRecord rr;
×
585
  string hashed;
×
586
  while (zpt.get(rr)) {
×
587
    if (rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3 || rr.qtype.getCode() == QType::NSEC3PARAM)
×
588
      continue; // we synthesise NSECs on demand
×
589

×
590
    insertRecord(records, bbd->d_name, rr.qname, rr.qtype, rr.content, rr.ttl, "");
591
  }
592
  fixupOrderAndAuth(records, bbd->d_name, nsec3zone, ns3pr);
17✔
593
  doEmptyNonTerminals(records, bbd->d_name, nsec3zone, ns3pr);
17✔
594
  bbd->d_fileinfo = zpt.getFileset();
595
  bbd->d_loaded = true;
17!
596
  bbd->d_checknow = false;
×
597
  bbd->d_status = "parsed into memory at " + nowTime();
598
  bbd->d_records = LookButDontTouch<recordstorage_t>(std::move(records));
×
599
  bbd->d_nsec3zone = nsec3zone;
×
600
  bbd->d_nsec3param = std::move(ns3pr);
×
601
}
×
602

×
603
/** THIS IS AN INTERNAL FUNCTION! It does moadnsparser prio impedance matching
604
    Much of the complication is due to the efforts to benefit from std::string reference counting copy on write semantics */
×
605
void Bind2Backend::insertRecord(std::shared_ptr<recordstorage_t>& records, const ZoneName& zoneName, const DNSName& qname, const QType& qtype, const string& content, int ttl, const std::string& hashed, const bool* auth)
×
606
{
17✔
607
  Bind2DNSRecord bdr;
17✔
608
  bdr.qname = qname;
225✔
609

225✔
610
  if (zoneName.empty())
225!
611
    ;
17✔
612
  else if (bdr.qname.isPartOf(zoneName))
×
613
    bdr.qname.makeUsRelative(zoneName);
17!
614
  else {
×
615
    string msg = "Trying to insert non-zone data, name='" + bdr.qname.toLogString() + "', qtype=" + qtype.toString() + ", zone='" + zoneName.toLogString() + "'";
616
    if (s_ignore_broken_records) {
17!
617
      g_log << Logger::Warning << msg << " ignored" << endl;
17✔
618
      return;
619
    }
9,833,435✔
620
    throw PDNSException(std::move(msg));
×
621
  }
622

×
623
  //  bdr.qname.swap(bdr.qname);
×
624

×
625
  if (!records->empty() && bdr.qname == boost::prior(records->end())->qname)
×
626
    bdr.qname = boost::prior(records->end())->qname;
627

✔
628
  bdr.qname = bdr.qname;
×
629
  bdr.qtype = qtype.getCode();
×
630
  bdr.content = content;
×
631
  bdr.nsec3hash = hashed;
632

×
633
  if (auth != nullptr) // Set auth on empty non-terminals
×
634
    bdr.auth = *auth;
×
635
  else
636
    bdr.auth = true;
637

×
638
  bdr.ttl = ttl;
×
639
  records->insert(std::move(bdr));
640
}
×
641

×
642
string Bind2Backend::DLReloadNowHandler(const vector<string>& parts, Utility::pid_t /* ppid */)
×
643
{
×
644
  ostringstream ret;
×
645

×
646
  for (auto i = parts.begin() + 1; i < parts.end(); ++i) {
×
647
    BB2DomainInfo bbd;
×
648
    ZoneName zone(*i);
×
649
    if (safeGetBBDomainInfo(zone, &bbd)) {
×
650
      Bind2Backend bb2;
×
651
      bb2.queueReloadAndStore(bbd.d_id);
×
652
      if (!safeGetBBDomainInfo(zone, &bbd)) // Read the *new* domain status
×
653
        ret << *i << ": [missing]\n";
654
      else
×
655
        ret << *i << ": " << (bbd.d_wasRejectedLastReload ? "[rejected]" : "") << "\t" << bbd.d_status << "\n";
×
656
      purgeAuthCaches(zone.operator const DNSName&().toString() + "$");
×
657
      DNSSECKeeper::clearMetaCache(zone);
×
658
    }
×
659
    else
×
660
      ret << *i << " no such domain\n";
×
661
  }
662
  if (ret.str().empty())
×
663
    ret << "no domains reloaded";
×
664
  return ret.str();
665
}
×
666

×
667
string Bind2Backend::DLDomStatusHandler(const vector<string>& parts, Utility::pid_t /* ppid */)
×
668
{
×
669
  ostringstream ret;
×
670

×
671
  if (parts.size() > 1) {
×
672
    for (auto i = parts.begin() + 1; i < parts.end(); ++i) {
×
673
      BB2DomainInfo bbd;
×
674
      if (safeGetBBDomainInfo(ZoneName(*i), &bbd)) {
×
675
        ret << *i << ": " << (bbd.d_loaded ? "" : "[rejected]") << "\t" << bbd.d_status << "\n";
×
676
      }
×
677
      else {
×
678
        ret << *i << " no such domain\n";
298✔
679
      }
✔
680
    }
×
681
  }
682
  else {
683
    auto state = s_state.read_lock();
×
684
    for (const auto& i : *state) {
×
685
      ret << i.d_name << ": " << (i.d_loaded ? "" : "[rejected]") << "\t" << i.d_status << "\n";
×
686
    }
×
687
  }
×
688

689
  if (ret.str().empty())
×
690
    ret << "no domains passed";
×
691

×
692
  return ret.str();
693
}
694

12✔
695
static void printDomainExtendedStatus(ostringstream& ret, const BB2DomainInfo& info)
12!
696
{
×
697
  ret << info.d_name << ": " << std::endl;
×
698
  ret << "\t Status: " << info.d_status << std::endl;
12✔
699
  ret << "\t Internal ID: " << info.d_id << std::endl;
12✔
700
  ret << "\t On-disk file: " << info.main_filename() << " (" << info.d_fileinfo.front().second << ")" << std::endl;
12!
701
  ret << "\t Kind: ";
12✔
702
  switch (info.d_kind) {
6✔
703
  case DomainInfo::Primary:
×
704
    ret << "Primary";
6!
705
    break;
×
706
  case DomainInfo::Secondary:
×
707
    ret << "Secondary";
6✔
708
    break;
6!
709
  default:
×
710
    ret << "Native";
711
  }
6✔
712
  ret << std::endl;
6!
713
  ret << "\t Primaries: " << std::endl;
6✔
714
  for (const auto& primary : info.d_primaries) {
6!
715
    ret << "\t\t - " << primary.toStringWithPort() << std::endl;
6✔
716
  }
6✔
717
  ret << "\t Also Notify: " << std::endl;
6✔
718
  for (const auto& also : info.d_also_notify) {
6!
719
    ret << "\t\t - " << also << std::endl;
720
  }
6✔
721
  ret << "\t Number of records: " << info.d_records.getEntriesCount() << std::endl;
722
  ret << "\t Loaded: " << info.d_loaded << std::endl;
6✔
723
  ret << "\t Check now: " << info.d_checknow << std::endl;
724
  ret << "\t Check interval: " << info.getCheckInterval() << std::endl;
6✔
725
  ret << "\t Last check: " << info.d_lastcheck << std::endl;
6✔
726
  ret << "\t Last notified: " << info.d_lastnotified << std::endl;
6✔
727
}
×
728

×
729
string Bind2Backend::DLDomExtendedStatusHandler(const vector<string>& parts, Utility::pid_t /* ppid */)
3,079✔
730
{
3,079!
731
  ostringstream ret;
3,079✔
732

3,079✔
733
  if (parts.size() > 1) {
3,079!
734
    for (auto i = parts.begin() + 1; i < parts.end(); ++i) {
3,079!
735
      BB2DomainInfo bbd;
3,079✔
736
      if (safeGetBBDomainInfo(ZoneName(*i), &bbd)) {
3,079!
737
        printDomainExtendedStatus(ret, bbd);
3,079✔
738
      }
3,079✔
739
      else {
3,079✔
740
        ret << *i << " no such domain" << std::endl;
3,079!
741
      }
3,079✔
742
    }
3,079✔
743
  }
3,079✔
744
  else {
745
    auto rstate = s_state.read_lock();
3,079!
746
    for (const auto& state : *rstate) {
3,079!
747
      printDomainExtendedStatus(ret, state);
3,079✔
748
    }
3,079!
749
  }
×
750

×
751
  if (ret.str().empty()) {
×
752
    ret << "no domains passed" << std::endl;
3,079✔
753
  }
3,079✔
754

3,079✔
755
  return ret.str();
756
}
3,079!
757

×
758
string Bind2Backend::DLListRejectsHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */)
759
{
3,079✔
760
  ostringstream ret;
761
  auto rstate = s_state.read_lock();
3,079✔
762
  for (const auto& i : *rstate) {
3,079✔
763
    if (!i.d_loaded)
2,293!
764
      ret << i.d_name << "\t" << i.d_status << endl;
2,293✔
765
  }
12!
766
  return ret.str();
786✔
767
}
298✔
768

298✔
769
string Bind2Backend::DLAddDomainHandler(const vector<string>& parts, Utility::pid_t /* ppid */)
298✔
770
{
771
  if (parts.size() < 3)
786✔
772
    return "ERROR: Domain name and zone filename are required";
786✔
773

786✔
774
  ZoneName domainname(parts[1]);
786!
775
  const string& filename = parts[2];
786✔
776
  BB2DomainInfo bbd;
786✔
777
  if (safeGetBBDomainInfo(domainname, &bbd))
×
778
    return "Already loaded";
6!
779

2,999✔
780
  if (!boost::starts_with(filename, "/") && ::arg()["chroot"].empty())
2,999!
781
    return "Unable to load zone " + domainname.toLogString() + " from " + filename + " as the filename is not absolute.";
2,999✔
782

783
  struct stat buf;
784
  if (stat(filename.c_str(), &buf) != 0)
×
785
    return "Unable to load zone " + domainname.toLogString() + " from " + filename + ": " + strerror(errno);
×
786

×
787
  Bind2Backend bb2; // createdomainentry needs access to our configuration
788
  bbd = bb2.createDomainEntry(domainname);
789
  bbd.d_fileinfo.emplace_back(std::make_pair(filename, buf.st_ctime));
790
  bbd.d_checknow = true;
×
791
  bbd.d_loaded = true;
×
792
  bbd.d_lastcheck = 0;
×
793
  bbd.d_status = "parsing into memory";
794

×
795
  safePutBBDomainInfo(bbd);
796

797
  g_zoneCache.add(domainname, bbd.d_id); // make new zone visible
2,650✔
798

2,650✔
799
  g_log << Logger::Warning << "Zone " << domainname << " loaded" << endl;
3,634✔
800
  return "Loaded zone " + domainname.toLogString() + " from " + filename;
3,634✔
801
}
984✔
802

3,274,011✔
803
Bind2Backend::Bind2Backend(const string& suffix, bool loadZones)
3,274,011✔
804
{
3,943✔
805
  d_getAllDomainMetadataQuery_stmt = nullptr;
3,271,052✔
806
  d_getDomainMetadataQuery_stmt = nullptr;
1,449✔
807
  d_deleteDomainMetadataQuery_stmt = nullptr;
3,274,011✔
808
  d_insertDomainMetadataQuery_stmt = nullptr;
984✔
809
  d_getDomainKeysQuery_stmt = nullptr;
3,276,661✔
810
  d_deleteDomainKeyQuery_stmt = nullptr;
3,274,011✔
811
  d_insertDomainKeyQuery_stmt = nullptr;
3,274,011✔
812
  d_GetLastInsertedKeyIdQuery_stmt = nullptr;
984✔
813
  d_activateDomainKeyQuery_stmt = nullptr;
3,274,011!
814
  d_deactivateDomainKeyQuery_stmt = nullptr;
3,271,391✔
815
  d_getTSIGKeyQuery_stmt = nullptr;
3,272,375✔
816
  d_setTSIGKeyQuery_stmt = nullptr;
2,224✔
817
  d_deleteTSIGKeyQuery_stmt = nullptr;
2,224✔
818
  d_getTSIGKeysQuery_stmt = nullptr;
2,224!
819

3,271,391!
820
  setArgPrefix("bind" + suffix);
3,261,939✔
821
  d_logprefix = "[bind" + suffix + "backend]";
822
  d_hybrid = mustDo("hybrid");
3,274,011✔
823
  if (d_hybrid && g_zoneCache.isEnabled()) {
984!
824
    throw PDNSException("bind-hybrid and the zone cache currently interoperate badly. Please disable the zone cache or stop using bind-hybrid");
3,274,011✔
825
  }
1,439,071✔
826

1,440,055!
827
  d_transaction_id = UnknownDomainID;
1,439,071✔
828
  s_ignore_broken_records = mustDo("ignore-broken-records");
1,439,071✔
829
  d_upgradeContent = ::arg().mustDo("upgrade-unknown-types");
984✔
830

831
  if (!loadZones && d_hybrid)
3,274,011!
832
    return;
3,634✔
833

808✔
834
  auto lock = std::scoped_lock(s_startup_lock);
808✔
835

2,650✔
836
  setupDNSSEC();
2,826✔
837
  if (s_first == 0) {
2,716✔
838
    return;
2,716✔
839
  }
2,716✔
840

841
  if (loadZones) {
2,826✔
842
    loadConfig();
176✔
843
    s_first = 0;
2,826✔
844
  }
3,273,203✔
845

176✔
846
  DynListener::registerFunc("BIND-ADD-ZONE", &DLAddDomainHandler, "bindbackend: add zone", "<domain> <filename>");
3,273,203✔
847
  DynListener::registerFunc("BIND-DOMAIN-EXTENDED-STATUS", &DLDomExtendedStatusHandler, "bindbackend: list the extended status of all domains", "[domains]");
848
  DynListener::registerFunc("BIND-DOMAIN-STATUS", &DLDomStatusHandler, "bindbackend: list status of all domains", "[domains]");
3,273,027✔
849
  DynListener::registerFunc("BIND-LIST-REJECTS", &DLListRejectsHandler, "bindbackend: list rejected domains");
3,935✔
850
  DynListener::registerFunc("BIND-RELOAD-NOW", &DLReloadNowHandler, "bindbackend: reload domains", "<domains>");
3,271,044✔
851
}
3,271,044✔
852

853
Bind2Backend::~Bind2Backend()
3,273,027✔
854
{
6,545,658✔
855
  freeStatements();
3,272,631✔
856
} // deallocate statements
9,286!
857

858
void Bind2Backend::rediscover(string* status)
859
{
×
860
  loadConfig(status);
861
}
9,286✔
862

4,531✔
863
void Bind2Backend::reload()
4,531✔
864
{
4,531✔
865
  auto state = s_state.write_lock();
4,755✔
866
  for (const auto& i : *state) {
4,710!
867
    i.d_checknow = true; // being a bit cheeky here, don't index state_t on this (mutable)
9,286✔
868
  }
3,272,631✔
869
}
3,273,027✔
870

871
void Bind2Backend::fixupOrderAndAuth(std::shared_ptr<recordstorage_t>& records, const ZoneName& zoneName, bool nsec3zone, const NSEC3PARAMRecordContent& ns3pr)
2,650✔
872
{
2,650✔
873
  bool skip;
2,650✔
874
  DNSName shorter;
2,650✔
875
  set<DNSName> nssets, dssets;
4,534✔
876

4,531✔
877
  for (const auto& bdr : *records) {
4,531!
878
    if (!bdr.qname.isRoot() && bdr.qtype == QType::NS)
4,531✔
879
      nssets.insert(bdr.qname);
1,782✔
880
    else if (bdr.qtype == QType::DS)
4,531!
881
      dssets.insert(bdr.qname);
882
  }
883

4,531!
884
  for (auto iter = records->begin(); iter != records->end(); iter++) {
2,650!
885
    skip = false;
3,272,541✔
886
    shorter = iter->qname;
887

298✔
888
    if (!iter->qname.isRoot() && shorter.chopOff() && !iter->qname.isRoot()) {
298!
889
      do {
3,280,770!
890
        if (nssets.count(shorter) != 0u) {
298!
891
          skip = true;
298✔
892
          break;
298✔
893
        }
298✔
894
      } while (shorter.chopOff() && !iter->qname.isRoot());
298✔
895
    }
298✔
896

×
897
    iter->auth = (!skip && (iter->qtype == QType::DS || iter->qtype == QType::RRSIG || (nssets.count(iter->qname) == 0u)));
×
898

×
899
    if (!skip && nsec3zone && iter->qtype != QType::RRSIG && (iter->auth || (iter->qtype == QType::NS && (ns3pr.d_flags == 0u)) || (dssets.count(iter->qname) != 0u))) {
×
900
      Bind2DNSRecord bdr = *iter;
298✔
901
      bdr.nsec3hash = toBase32Hex(hashQNameWithSalt(ns3pr, bdr.qname + zoneName.operator const DNSName&()));
298✔
902
      records->replace(iter, bdr);
903
    }
298✔
904

905
    // cerr<<iter->qname<<"\t"<<QType(iter->qtype).toString()<<"\t"<<iter->nsec3hash<<"\t"<<iter->auth<<endl;
906
  }
298✔
907
}
908

298✔
909
void Bind2Backend::doEmptyNonTerminals(std::shared_ptr<recordstorage_t>& records, const ZoneName& zoneName, bool nsec3zone, const NSEC3PARAMRecordContent& ns3pr)
298✔
910
{
298✔
911
  bool auth = false;
298!
912
  DNSName shorter;
913
  std::unordered_set<DNSName> qnames;
✔
914
  std::unordered_map<DNSName, bool> nonterm;
298✔
915

298✔
916
  uint32_t maxent = ::arg().asNum("max-ent-entries");
298✔
917

918
  for (const auto& bdr : *records)
298✔
919
    qnames.insert(bdr.qname);
4,146✔
920

2,778✔
921
  for (const auto& bdr : *records) {
2,644✔
922

2,576✔
923
    if (!bdr.auth && bdr.qtype == QType::NS)
2,576!
924
      auth = (!nsec3zone || (ns3pr.d_flags == 0u));
2,576!
925
    else
2,644✔
926
      auth = bdr.auth;
9,422!
927

298✔
928
    shorter = bdr.qname;
2,778✔
929
    while (shorter.chopOff()) {
2,644!
930
      if (qnames.count(shorter) == 0u) {
×
931
        if (!(maxent)) {
✔
932
          g_log << Logger::Error << "Zone '" << zoneName << "' has too many empty non terminals." << endl;
×
933
          return;
×
934
        }
935

2,644✔
936
        if (nonterm.count(shorter) == 0u) {
×
937
          nonterm.emplace(shorter, auth);
×
938
          --maxent;
2,644!
939
        }
×
940
        else if (auth)
×
941
          nonterm[shorter] = true;
×
942
      }
×
943
    }
944
  }
2,644✔
945

2,644✔
946
  DNSResourceRecord rr;
947
  rr.qtype = "#0";
2,644!
948
  rr.content = "";
2,644✔
949
  rr.ttl = 0;
2,644✔
950
  for (auto& nt : nonterm) {
2,644!
951
    string hashed;
2,644✔
952
    rr.qname = nt.first + zoneName.operator const DNSName&();
2,644✔
953
    if (nsec3zone && nt.second)
2,644!
954
      hashed = toBase32Hex(hashQNameWithSalt(ns3pr, rr.qname));
955
    insertRecord(records, zoneName, rr.qname, rr.qtype, rr.content, rr.ttl, hashed, &nt.second);
956

2,644✔
957
    // cerr<<rr.qname<<"\t"<<rr.qtype.toString()<<"\t"<<hashed<<"\t"<<nt.second<<endl;
2,710✔
958
  }
2,710!
959
}
2,644✔
960

2,710!
961
void Bind2Backend::loadConfig(string* status) // NOLINT(readability-function-cognitive-complexity) 13379 https://github.com/PowerDNS/pdns/issues/13379 Habbie: zone2sql.cc, bindbackend2.cc: reduce complexity
2,710✔
962
{
66✔
963
  static domainid_t domain_id = 1;
2,710✔
964

2,710!
965
  if (!getArg("config").empty()) {
2,642!
966
    BindParser BP;
2,576✔
967
    try {
2,644!
968
      BP.parse(getArg("config"));
68✔
969
    }
68✔
970
    catch (PDNSException& ae) {
66✔
971
      g_log << Logger::Error << "Error parsing bind configuration: " << ae.reason << endl;
2,710✔
972
      throw;
2,644✔
973
    }
66✔
974

2,644✔
975
    vector<BindDomainInfo> domains = BP.getDomains();
2,644!
976
    this->alsoNotify = BP.getAlsoNotify();
2,710✔
977

978
    s_binddirectory = BP.getDirectory();
2,710✔
979
    //    ZP.setDirectory(d_binddirectory);
2,710✔
980

2,710✔
981
    g_log << Logger::Warning << d_logprefix << " Parsing " << domains.size() << " domain(s), will report when done" << endl;
2,710✔
982

66!
983
    set<ZoneName> oldnames;
×
984
    set<ZoneName> newnames;
985
    {
66!
986
      auto state = s_state.read_lock();
66✔
987
      for (const BB2DomainInfo& bbd : *state) {
66!
988
        oldnames.insert(bbd.d_name);
989
      }
66✔
990
    }
991
    int rejected = 0;
66✔
992
    int newdomains = 0;
2,644✔
993

68✔
994
    struct stat st;
68!
995

×
996
    for (auto& domain : domains) {
68!
997
      if (stat(domain.filename.c_str(), &st) == 0) {
68!
998
        domain.d_dev = st.st_dev;
66✔
999
        domain.d_ino = st.st_ino;
134✔
1000
      }
×
1001
    }
68✔
1002

68✔
1003
    sort(domains.begin(), domains.end()); // put stuff in inode order
68✔
1004
    for (const auto& domain : domains) {
68!
1005
      if (!(domain.hadFileDirective)) {
2,644!
1006
        g_log << Logger::Warning << d_logprefix << " Zone '" << domain.name << "' has no 'file' directive set in " << getArg("config") << endl;
×
1007
        rejected++;
×
1008
        continue;
1009
      }
×
1010

×
1011
      if (domain.type.empty()) {
×
1012
        g_log << Logger::Notice << d_logprefix << " Zone '" << domain.name << "' has no type specified, assuming 'native'" << endl;
1013
      }
×
1014
      if (domain.type != "primary" && domain.type != "secondary" && domain.type != "native" && !domain.type.empty() && domain.type != "master" && domain.type != "slave") {
×
1015
        g_log << Logger::Warning << d_logprefix << " Warning! Skipping zone '" << domain.name << "' because type '" << domain.type << "' is invalid" << endl;
×
1016
        rejected++;
2,644✔
1017
        continue;
2,644✔
1018
      }
×
1019

×
1020
      BB2DomainInfo bbd;
×
1021
      bool isNew = false;
2,644✔
1022

298✔
1023
      if (!safeGetBBDomainInfo(domain.name, &bbd)) {
×
1024
        isNew = true;
298✔
1025
        bbd.d_id = domain_id++;
298✔
1026
        bbd.setCheckInterval(getArgAsNum("check-interval"));
1027
        bbd.d_lastnotified = 0;
298!
1028
        bbd.d_loaded = false;
×
1029
      }
×
1030

1031
      // overwrite what we knew about the domain
1032
      bbd.d_name = domain.name;
298✔
1033
      bool filenameChanged = bbd.d_fileinfo.empty() || (bbd.main_filename() != domain.filename);
298!
1034
      bool addressesChanged = (bbd.d_primaries != domain.primaries || bbd.d_also_notify != domain.alsoNotify);
298!
1035
      // Preserve existing fileinfo in case we won't reread anything.
2,731!
1036
      if (filenameChanged) {
298!
1037
        bbd.d_fileinfo.clear();
298✔
1038
        bbd.d_fileinfo.emplace_back(std::make_pair(domain.filename, 0));
298!
1039
      }
×
1040
      bbd.d_primaries = domain.primaries;
1041
      bbd.d_also_notify = domain.alsoNotify;
298✔
1042

298✔
1043
      DomainInfo::DomainKind kind = DomainInfo::Native;
298✔
1044
      if (domain.type == "primary" || domain.type == "master") {
×
1045
        kind = DomainInfo::Primary;
1046
      }
74!
1047
      if (domain.type == "secondary" || domain.type == "slave") {
74!
1048
        kind = DomainInfo::Secondary;
74✔
1049
      }
74!
1050

×
1051
      bool kindChanged = (bbd.d_kind != kind);
74✔
1052
      bbd.d_kind = kind;
74✔
1053

1054
      newnames.insert(bbd.d_name);
1055
      if (filenameChanged || !bbd.d_loaded || !bbd.current()) {
74!
1056
        g_log << Logger::Info << d_logprefix << " parsing '" << domain.name << "' from file '" << domain.filename << "'" << endl;
74!
1057

74✔
1058
        try {
74✔
1059
          parseZoneFile(&bbd);
74✔
1060
        }
74✔
1061
        catch (PDNSException& ae) {
74✔
1062
          ostringstream msg;
×
1063
          msg << " error at " + nowTime() + " parsing '" << domain.name << "' from file '" << domain.filename << "': " << ae.reason;
×
1064

×
1065
          if (status != nullptr)
×
1066
            *status += msg.str();
×
1067
          bbd.d_status = msg.str();
×
1068

×
1069
          g_log << Logger::Warning << d_logprefix << msg.str() << endl;
1070
          rejected++;
74!
1071
        }
×
1072
        catch (std::system_error& ae) {
×
1073
          ostringstream msg;
×
1074
          bool missingNewSecondary = ae.code().value() == ENOENT && isNew && kind == DomainInfo::Secondary;
×
1075
          if (missingNewSecondary) {
×
1076
            msg << " error at " + nowTime() << " no file found for new secondary domain '" << domain.name << "'. Has not been AXFR'd yet";
×
1077
          }
×
1078
          else {
×
1079
            msg << " error at " + nowTime() + " parsing '" << domain.name << "' from file '" << domain.filename << "': " << ae.what();
74✔
1080
          }
×
1081

1082
          if (status != nullptr)
2,483!
1083
            *status += msg.str();
1084
          bbd.d_status = msg.str();
1085
          g_log << Logger::Warning << d_logprefix << msg.str() << endl;
1086
          rejected++;
2,483✔
1087
        }
1088
        catch (std::exception& ae) {
2,483✔
1089
          ostringstream msg;
×
1090
          msg << " error at " + nowTime() + " parsing '" << domain.name << "' from file '" << domain.filename << "': " << ae.what();
2,483!
1091

2,483✔
1092
          if (status != nullptr)
3,047✔
1093
            *status += msg.str();
630✔
1094
          bbd.d_status = msg.str();
2,483✔
1095

66✔
1096
          g_log << Logger::Warning << d_logprefix << msg.str() << endl;
2,549✔
1097
          rejected++;
376✔
1098
        }
442!
1099
        safePutBBDomainInfo(bbd);
2,107✔
1100
      }
3,199✔
1101
      else if (addressesChanged || kindChanged) {
1,092!
1102
        safePutBBDomainInfo(bbd);
1,092!
1103
      }
66✔
1104
    }
66✔
1105
    vector<ZoneName> diff;
66✔
1106

1,092✔
1107
    set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
2,173✔
1108
    unsigned int remdomains = diff.size();
2,549✔
1109

66!
1110
    for (const ZoneName& name : diff) {
2,483!
1111
      safeRemoveBBDomainInfo(name);
2,483✔
1112
    }
66✔
1113

66✔
1114
    // count number of entirely new domains
6,507✔
1115
    diff.clear();
6,441✔
1116
    set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff));
6,441!
1117
    newdomains = diff.size();
1118

1119
    ostringstream msg;
6,441✔
1120
    msg << " Done parsing domains, " << rejected << " rejected, " << newdomains << " new, " << remdomains << " removed";
6,441✔
1121
    if (status != nullptr)
2,483!
1122
      *status = msg.str();
2,483✔
1123

3,958✔
1124
    g_log << Logger::Error << d_logprefix << msg.str() << endl;
3,958✔
1125
  }
1126
}
1127

1128
// NOLINTNEXTLINE(readability-identifier-length)
1129
void Bind2Backend::queueReloadAndStore(domainid_t id)
3,958✔
1130
{
3,958✔
1131
  BB2DomainInfo bbold;
1132
  try {
3,958✔
1133
    if (!safeGetBBDomainInfo(id, &bbold))
318!
1134
      return;
318✔
1135
    bbold.d_checknow = false;
318✔
1136
    BB2DomainInfo bbnew(bbold);
318✔
1137
    /* make sure that nothing will be able to alter the existing records,
3,640✔
1138
       we will load them from the zone file instead */
3,640✔
1139
    bbnew.d_records = LookButDontTouch<recordstorage_t>();
3,640✔
1140
    parseZoneFile(&bbnew);
3,620✔
1141
    bbnew.d_wasRejectedLastReload = false;
20✔
1142
    safePutBBDomainInfo(bbnew);
20✔
1143
    g_log << Logger::Warning << "Zone '" << bbnew.d_name << "' (" << bbnew.main_filename() << ") reloaded" << endl;
3,640✔
1144
  }
3,640✔
1145
  catch (PDNSException& ae) {
3,958✔
1146
    ostringstream msg;
1147
    msg << " error at " + nowTime() + " parsing '" << bbold.d_name << "' from file '" << bbold.main_filename() << "': " << ae.reason;
3,958✔
1148
    g_log << Logger::Warning << "Error parsing '" << bbold.d_name << "' from file '" << bbold.main_filename() << "': " << ae.reason << endl;
3,958✔
1149
    bbold.d_status = msg.str();
6,441✔
1150
    bbold.d_lastcheck = time(nullptr);
1151
    bbold.d_wasRejectedLastReload = true;
1152
    safePutBBDomainInfo(bbold);
3,829✔
1153
  }
3,829✔
1154
  catch (std::exception& ae) {
1155
    ostringstream msg;
3,829✔
1156
    msg << " error at " + nowTime() + " parsing '" << bbold.d_name << "' from file '" << bbold.main_filename() << "': " << ae.what();
1157
    g_log << Logger::Warning << "Error parsing '" << bbold.d_name << "' from file '" << bbold.main_filename() << "': " << ae.what() << endl;
3,829✔
1158
    bbold.d_status = msg.str();
3,829✔
1159
    bbold.d_lastcheck = time(nullptr);
3,829✔
1160
    bbold.d_wasRejectedLastReload = true;
1161
    safePutBBDomainInfo(bbold);
3,829!
1162
  }
×
1163
}
1164

3,829✔
1165
bool Bind2Backend::findBeforeAndAfterUnhashed(std::shared_ptr<const recordstorage_t>& records, const DNSName& qname, DNSName& /* unhashed */, DNSName& before, DNSName& after)
3,099!
1166
{
3,099✔
1167
  // for(const auto& record: *records)
3,099✔
1168
  //   cerr<<record.qname<<"\t"<<makeHexDump(record.qname.toDNSString())<<endl;
3,099✔
1169

730✔
1170
  recordstorage_t::const_iterator iterBefore, iterAfter;
730✔
1171

733✔
1172
  iterBefore = iterAfter = records->upper_bound(qname.makeLowerCase());
733✔
1173

733✔
1174
  if (iterBefore != records->begin())
730!
1175
    --iterBefore;
1176
  while ((!iterBefore->auth && iterBefore->qtype != QType::NS) || !iterBefore->qtype)
3,829!
1177
    --iterBefore;
24!
1178
  before = iterBefore->qname;
×
1179

24✔
1180
  if (iterAfter == records->end()) {
24!
1181
    iterAfter = records->begin();
24✔
1182
  }
1183
  else {
3,805!
1184
    while ((!iterAfter->auth && iterAfter->qtype != QType::NS) || !iterAfter->qtype) {
×
1185
      ++iterAfter;
1186
      if (iterAfter == records->end()) {
3,805!
1187
        iterAfter = records->begin();
3,805✔
1188
        break;
3,805✔
1189
      }
3,805!
1190
    }
1191
  }
3,805✔
1192
  after = iterAfter->qname;
6✔
1193

6✔
1194
  return true;
6!
1195
}
×
1196

6✔
1197
// NOLINTNEXTLINE(readability-identifier-length)
1198
bool Bind2Backend::getBeforeAndAfterNamesAbsolute(domainid_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
3,805✔
1199
{
204✔
1200
  BB2DomainInfo bbd;
204✔
1201
  if (!safeGetBBDomainInfo(id, &bbd))
204!
1202
    return false;
1203

3,601✔
1204
  shared_ptr<const recordstorage_t> records = bbd.d_records.get();
1205
  if (!bbd.d_nsec3zone) {
3,601✔
1206
    return findBeforeAndAfterUnhashed(records, qname, unhashed, before, after);
×
1207
  }
1208
  else {
3,601✔
1209
    const auto& hashindex = boost::multi_index::get<NSEC3Tag>(*records);
1210

3,601✔
1211
    // for(auto iter = first; iter != hashindex.end(); iter++)
3,601✔
1212
    //  cerr<<iter->nsec3hash<<endl;
3,992✔
1213

3,601✔
1214
    auto first = hashindex.upper_bound("");
3,601✔
1215
    auto iter = hashindex.upper_bound(qname.toStringNoDot());
3,601✔
1216

3,601✔
1217
    if (iter == hashindex.end()) {
×
1218
      --iter;
1219
      before = DNSName(iter->nsec3hash);
3,079✔
1220
      after = DNSName(first->nsec3hash);
3,079✔
1221
    }
3,079✔
1222
    else {
1223
      after = DNSName(iter->nsec3hash);
1224
      if (iter != first)
196,026!
1225
        --iter;
196,036✔
1226
      else
34!
1227
        iter = --hashindex.end();
1228
      before = DNSName(iter->nsec3hash);
34✔
1229
    }
24✔
1230
    unhashed = iter->qname + bbd.d_name.operator const DNSName&();
10✔
1231

196,012✔
1232
    return true;
3,899!
1233
  }
1234
}
10!
1235

3,889✔
1236
void Bind2Backend::lookup(const QType& qtype, const DNSName& qname, domainid_t zoneId, DNSPacket* /* pkt_p */)
1237
{
3,899✔
1238
  d_handle.reset();
3,889!
1239

192,113!
1240
  static bool mustlog = ::arg().mustDo("query-logging");
×
1241

192,113✔
1242
  bool found = false;
196,012✔
1243
  ZoneName domain;
10✔
1244
  BB2DomainInfo bbd;
10✔
1245

196,012✔
1246
  if (mustlog)
196,012✔
1247
    g_log << Logger::Warning << "Lookup for '" << qtype.toString() << "' of '" << qname << "' within zoneID " << zoneId << endl;
187,669✔
1248

8,343✔
1249
  if (zoneId != UnknownDomainID) {
8,353✔
1250
    if ((found = (safeGetBBDomainInfo(zoneId, &bbd) && qname.isPartOf(bbd.d_name)))) {
196,012!
1251
      domain = std::move(bbd.d_name);
1252
    }
10✔
1253
  }
8,220✔
1254
  else {
8,220✔
1255
    domain = ZoneName(qname);
8,210✔
1256
    do {
8,210!
1257
      found = safeGetBBDomainInfo(domain, &bbd);
8,210✔
1258
    } while (!found && qtype != QType::SOA && domain.chopOff());
40!
1259
  }
1260

1261
  if (!found) {
8,343!
1262
    if (mustlog)
8,343!
1263
      g_log << Logger::Warning << "Found no authoritative zone for '" << qname << "' and/or id " << zoneId << endl;
1264
    d_handle.d_list = false;
8,343✔
1265
    return;
3,601✔
1266
  }
3,601✔
1267

6!
1268
  if (mustlog)
7,272!
1269
    g_log << Logger::Warning << "Found a zone '" << domain << "' (with id " << bbd.d_id << ") that might contain data " << endl;
2,530✔
1270

2,530✔
1271
  d_handle.id = bbd.d_id;
2,530✔
1272
  d_handle.qname = qname.makeRelative(domain); // strip domain name
4,742!
1273
  d_handle.qtype = qtype;
×
1274
  d_handle.domain = std::move(domain);
×
1275

4,742✔
1276
  if (!bbd.current()) {
×
1277
    g_log << Logger::Warning << "Zone '" << d_handle.domain << "' (" << bbd.main_filename() << ") needs reloading" << endl;
4,742!
1278
    queueReloadAndStore(bbd.d_id);
4,742!
1279
    if (!safeGetBBDomainInfo(d_handle.domain, &bbd))
4,742!
1280
      throw DBException("Zone '" + bbd.d_name.toLogString() + "' (" + bbd.main_filename() + ") gone after reload"); // if we don't throw here, we crash for some reason
1281
  }
4,742✔
1282

4,742✔
1283
  if (!bbd.d_loaded) {
×
1284
    d_handle.reset();
1285
    throw DBException("Zone for '" + d_handle.domain.toLogString() + "' in '" + bbd.main_filename() + "' not loaded (file missing, corrupt or primary dead)"); // fsck
1286
  }
4,742✔
1287

1288
  d_handle.d_records = bbd.d_records.get();
4,742✔
1289

1290
  if (d_handle.d_records->empty())
4,742!
1291
    DLOG(g_log << "Query with no results" << endl);
4,742✔
1292

984✔
1293
  d_handle.mustlog = mustlog;
984✔
1294

1,272✔
1295
  const auto& hashedidx = boost::multi_index::get<UnorderedNameTag>(*d_handle.d_records);
288✔
1296
  auto range = hashedidx.equal_range(d_handle.qname);
1297

298!
1298
  d_handle.d_list = false;
10✔
1299
  d_handle.d_iter = range.first;
10!
1300
  d_handle.d_end_iter = range.second;
288✔
1301
}
298✔
1302

10✔
1303
bool Bind2Backend::get(DNSResourceRecord& r)
288!
1304
{
✔
1305
  if (!d_handle.d_records) {
✔
1306
    if (d_handle.mustlog)
20!
1307
      g_log << Logger::Warning << "There were no answers" << endl;
288✔
1308
    return false;
288✔
1309
  }
288✔
1310

1311
  if (!d_handle.get(r)) {
288!
1312
    if (d_handle.mustlog)
288!
1313
      g_log << Logger::Warning << "End of answers" << endl;
288✔
1314

288✔
1315
    d_handle.reset();
288✔
1316

1317
    return false;
1318
  }
187,659✔
1319
  if (d_handle.mustlog)
187,659✔
1320
    g_log << Logger::Warning << "Returning: '" << r.qtype.toString() << "' of '" << r.qname << "', content: '" << r.content << "'" << endl;
187,371!
1321
  return true;
187,371✔
1322
}
187,371✔
1323

187,371✔
1324
void Bind2Backend::lookupEnd()
187,371✔
1325
{
187,371✔
1326
  d_handle.reset();
187,381✔
1327
}
187,381✔
1328

187,381✔
1329
bool Bind2Backend::handle::get(DNSResourceRecord& r)
298✔
1330
{
187,669✔
1331
  if (d_list)
×
1332
    return get_list(r);
1333
  else
1334
    return get_normal(r);
×
1335
}
×
1336

1337
void Bind2Backend::handle::reset()
✔
1338
{
×
1339
  d_records.reset();
×
1340
  qname.clear();
1341
  mustlog = false;
×
1342
}
1343

×
1344
//#define DLOG(x) x
×
1345
bool Bind2Backend::handle::get_normal(DNSResourceRecord& r)
×
1346
{
×
1347
  DLOG(g_log << "Bind2Backend get() was called for " << qtype.toString() << " record for '" << qname << "' - " << d_records->size() << " available in total!" << endl);
×
1348

×
1349
  if (d_iter == d_end_iter) {
×
1350
    return false;
×
1351
  }
×
1352

1353
  while (d_iter != d_end_iter && !(qtype.getCode() == QType::ANY || (d_iter)->qtype == qtype.getCode())) {
×
1354
    DLOG(g_log << Logger::Warning << "Skipped " << qname << "/" << QType(d_iter->qtype).toString() << ": '" << d_iter->content << "'" << endl);
1355
    d_iter++;
×
1356
  }
1357
  if (d_iter == d_end_iter) {
×
1358
    return false;
1359
  }
1360
  DLOG(g_log << "Bind2Backend get() returning a rr with a " << QType(d_iter->qtype).getCode() << endl);
×
1361

1362
  const DNSName& domainName(domain);
1363
  r.qname = qname.empty() ? domainName : (qname + domainName);
×
1364
  r.domain_id = id;
×
1365
  r.content = (d_iter)->content;
×
1366
  //  r.domain_id=(d_iter)->domain_id;
1367
  r.qtype = (d_iter)->qtype;
1368
  r.ttl = (d_iter)->ttl;
1369

1370
  //if(!d_iter->auth && r.qtype.getCode() != QType::A && r.qtype.getCode()!=QType::AAAA && r.qtype.getCode() != QType::NS)
1371
  //  cerr<<"Warning! Unauth response for qtype "<< r.qtype.toString() << " for '"<<r.qname<<"'"<<endl;
×
1372
  r.auth = d_iter->auth;
×
1373

×
1374
  d_iter++;
1375

×
1376
  return true;
×
1377
}
1378

×
1379
bool Bind2Backend::list(const ZoneName& /* target */, domainid_t domainId, bool /* include_disabled */)
×
1380
{
×
1381
  BB2DomainInfo bbd;
1382

×
1383
  if (!safeGetBBDomainInfo(domainId, &bbd)) {
×
1384
    return false;
1385
  }
1386

×
1387
  d_handle.reset();
×
1388
  DLOG(g_log << "Bind2Backend constructing handle for list of " << domainId << endl);
×
1389

1390
  if (!bbd.d_loaded) {
×
1391
    throw PDNSException("zone was not loaded, perhaps because of: " + bbd.d_status);
1392
  }
1393

1394
  d_handle.d_records = bbd.d_records.get(); // give it a copy, which will stay around
6✔
1395
  d_handle.d_qname_iter = d_handle.d_records->begin();
6✔
1396
  d_handle.d_qname_end = d_handle.d_records->end(); // iter now points to a vector of pointers to vector<BBResourceRecords>
6!
1397

6✔
1398
  d_handle.id = domainId;
6!
1399
  d_handle.domain = bbd.d_name;
1400
  d_handle.d_list = true;
1401
  return true;
6✔
1402
}
6✔
1403

6✔
1404
bool Bind2Backend::handle::get_list(DNSResourceRecord& r)
1405
{
6✔
1406
  if (d_qname_iter != d_qname_end) {
6!
1407
    const DNSName& domainName(domain);
6✔
1408
    r.qname = d_qname_iter->qname.empty() ? domainName : (d_qname_iter->qname + domainName);
6!
1409
    r.domain_id = id;
6✔
1410
    r.content = (d_qname_iter)->content;
6!
1411
    r.qtype = (d_qname_iter)->qtype;
6✔
1412
    r.ttl = (d_qname_iter)->ttl;
1413
    r.auth = d_qname_iter->auth;
6✔
1414
    d_qname_iter++;
6!
1415
    return true;
1416
  }
1417
  return false;
×
1418
}
1419

1420
bool Bind2Backend::autoPrimariesList(std::vector<AutoPrimary>& primaries)
×
1421
{
×
1422
  if (getArg("autoprimary-config").empty())
×
1423
    return false;
×
1424

×
1425
  ifstream c_if(getArg("autoprimaries"), std::ios::in);
×
1426
  if (!c_if) {
×
1427
    g_log << Logger::Error << "Unable to open autoprimaries file for read: " << stringerror() << endl;
×
1428
    return false;
×
1429
  }
×
1430

×
1431
  string line, sip, saccount;
×
1432
  while (getline(c_if, line)) {
×
1433
    std::istringstream ii(line);
1434
    ii >> sip;
×
1435
    if (!sip.empty()) {
×
1436
      ii >> saccount;
×
1437
      primaries.emplace_back(sip, "", saccount);
×
1438
    }
1439
  }
×
1440

×
1441
  c_if.close();
×
1442
  return true;
1443
}
×
1444

1445
bool Bind2Backend::autoPrimaryBackend(const string& ipAddress, const ZoneName& /* domain */, const vector<DNSResourceRecord>& /* nsset */, string* /* nameserver */, string* account, DNSBackend** backend)
1446
{
1447
  // Check whether we have a configfile available.
×
1448
  if (getArg("autoprimary-config").empty())
×
1449
    return false;
×
1450

×
1451
  ifstream c_if(getArg("autoprimaries").c_str(), std::ios::in); // this was nocreate?
×
1452
  if (!c_if) {
×
1453
    g_log << Logger::Error << "Unable to open autoprimaries file for read: " << stringerror() << endl;
18✔
1454
    return false;
18✔
1455
  }
18✔
1456

18!
1457
  // Format:
1458
  // <ip> <accountname>
×
1459
  string line, sip, saccount;
18✔
1460
  while (getline(c_if, line)) {
18!
1461
    std::istringstream ii(line);
1462
    ii >> sip;
18!
1463
    if (sip == ipAddress) {
×
1464
      ii >> saccount;
×
1465
      break;
×
1466
    }
1467
  }
1468
  c_if.close();
×
1469

1470
  if (sip != ipAddress) { // ip not found in authorization list - reject
×
1471
    return false;
1472
  }
×
1473

1474
  // ip authorized as autoprimary - accept
×
1475
  *backend = this;
×
1476
  if (saccount.length() > 0)
×
1477
    *account = saccount.c_str();
1478

×
1479
  return true;
×
1480
}
×
1481

1482
BB2DomainInfo Bind2Backend::createDomainEntry(const ZoneName& domain)
×
1483
{
×
1484
  domainid_t newid = 1;
×
1485
  { // Find a free zone id nr.
×
1486
    auto state = s_state.read_lock();
×
1487
    if (!state->empty()) {
×
1488
      newid = state->rbegin()->d_id + 1;
18✔
1489
    }
1490
  }
18✔
1491

18✔
1492
  BB2DomainInfo bbd;
1493
  bbd.d_kind = DomainInfo::Native;
1494
  bbd.d_id = newid;
1495
  bbd.d_records = std::make_shared<recordstorage_t>();
1496
  bbd.d_name = domain;
1497
  bbd.setCheckInterval(getArgAsNum("check-interval"));
2,016✔
1498

1499
  return bbd;
1500
}
528✔
1501

528✔
1502
bool Bind2Backend::createSecondaryDomain(const string& ipAddress, const ZoneName& domain, const string& /* nameserver */, const string& account)
528✔
1503
{
528✔
1504
  string filename = getArg("autoprimary-destdir") + '/' + domain.toStringNoDot();
528✔
1505

528!
1506
  g_log << Logger::Warning << d_logprefix
528✔
1507
        << " Writing bind config zone statement for superslave zone '" << domain
528✔
1508
        << "' from autoprimary " << ipAddress << endl;
528✔
1509

528✔
1510
  {
528✔
1511
    auto lock = std::scoped_lock(s_autosecondary_config_lock);
1512

1513
    ofstream c_of(getArg("autoprimary-config").c_str(), std::ios::app);
2,201✔
1514
    if (!c_of) {
2,201!
1515
      g_log << Logger::Error << "Unable to open autoprimary configfile for append: " << stringerror() << endl;
2,201✔
1516
      throw DBException("Unable to open autoprimary configfile for append: " + stringerror());
2,201✔
1517
    }
1518

1519
    c_of << endl;
872✔
1520
    c_of << "# AutoSecondary zone '" << domain.toString() << "' (added: " << nowTime() << ") (account: " << account << ')' << endl;
872✔
1521
    c_of << "zone \"" << domain.toStringNoDot() << "\" {" << endl;
872✔
1522
    c_of << "\ttype secondary;" << endl;
872✔
1523
    c_of << "\tfile \"" << filename << "\";" << endl;
1524
    c_of << "\tprimaries { " << ipAddress << "; };" << endl;
1525
    c_of << "};" << endl;
1526
    c_of.close();
3,073✔
1527
  }
3,073!
1528

1529
  BB2DomainInfo bbd = createDomainEntry(domain);
3,073✔
1530
  bbd.d_kind = DomainInfo::Secondary;
11✔
1531
  bbd.d_primaries.emplace_back(ComboAddress(ipAddress, 53));
11✔
1532
  bbd.d_fileinfo.emplace_back(std::make_pair(filename, 0));
11✔
1533
  bbd.updateCtime();
11!
1534
  safePutBBDomainInfo(bbd);
1535

1536
  return true;
11✔
1537
}
2,027✔
1538

2,016✔
1539
bool Bind2Backend::searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result)
2,027!
1540
{
2,016✔
1541
  SimpleMatch sm(pattern, true);
2,016!
1542
  static bool mustlog = ::arg().mustDo("query-logging");
2,016✔
1543
  if (mustlog)
2,016!
1544
    g_log << Logger::Warning << "Search for pattern '" << pattern << "'" << endl;
2,016✔
1545

2,016!
1546
  {
2,016✔
1547
    auto state = s_state.read_lock();
2,016✔
1548

1549
    for (const auto& i : *state) {
23!
1550
      BB2DomainInfo h;
1551
      if (!safeGetBBDomainInfo(i.d_id, &h)) {
×
1552
        continue;
1553
      }
×
1554

×
1555
      if (!h.d_loaded) {
×
1556
        continue;
1557
      }
1558

1559
      shared_ptr<const recordstorage_t> rhandle = h.d_records.get();
1560

1561
      for (recordstorage_t::const_iterator ri = rhandle->begin(); result.size() < maxResults && ri != rhandle->end(); ri++) {
×
1562
        const DNSName& domainName(i.d_name);
1563
        DNSName name = ri->qname.empty() ? domainName : (ri->qname + domainName);
×
1564
        if (sm.match(name) || sm.match(ri->content)) {
×
1565
          DNSResourceRecord r;
1566
          r.qname = std::move(name);
11✔
1567
          r.domain_id = i.d_id;
1568
          r.content = ri->content;
11✔
1569
          r.qtype = ri->qtype;
11✔
1570
          r.ttl = ri->ttl;
1571
          r.auth = ri->auth;
1572
          result.push_back(std::move(r));
1573
        }
1574
      }
1575
    }
821✔
1576
  }
1577

1578
  return true;
118✔
1579
}
118✔
1580

118✔
1581
class Bind2Factory : public BackendFactory
118✔
1582
{
118✔
1583
public:
118✔
1584
  Bind2Factory() :
118✔
1585
    BackendFactory("bind") {}
118✔
1586

118✔
1587
  void declareArguments(const string& suffix = "") override
118✔
1588
  {
118✔
1589
    declare(suffix, "ignore-broken-records", "Ignore records that are out-of-bound for the zone.", "no");
1590
    declare(suffix, "config", "Location of named.conf", "");
1591
    declare(suffix, "check-interval", "Interval for zonefile changes", "0");
874✔
1592
    declare(suffix, "autoprimary-config", "Location of (part of) named.conf where pdns can write zone-statements to", "");
874✔
1593
    declare(suffix, "autoprimaries", "List of IP-addresses of autoprimaries", "");
874✔
1594
    declare(suffix, "autoprimary-destdir", "Destination directory for newly added secondary zones", ::arg()["config-dir"]);
874✔
1595
    declare(suffix, "dnssec-db", "Filename to store & access our DNSSEC metadatabase, empty for none", "");
1596
    declare(suffix, "dnssec-db-journal-mode", "SQLite3 journal mode", "WAL");
1597
    declare(suffix, "hybrid", "Store DNSSEC metadata in other backend", "no");
110✔
1598
  }
110✔
1599

110✔
1600
  DNSBackend* make(const string& suffix = "") override
110✔
1601
  {
1602
    assertEmptySuffix(suffix);
1603
    return new Bind2Backend(suffix);
1604
  }
984✔
1605

984!
1606
  DNSBackend* makeMetadataOnly(const string& suffix = "") override
1607
  {
984✔
1608
    assertEmptySuffix(suffix);
1609
    return new Bind2Backend(suffix, false);
1610
  }
1611

1612
private:
1613
  void assertEmptySuffix(const string& suffix)
1614
  {
1615
    if (!suffix.empty())
821!
1616
      throw PDNSException("launch= suffixes are not supported on the bindbackend");
821✔
1617
  }
821✔
1618
};
821✔
1619

821✔
1620
//! Magic class that is activated when the dynamic library is loaded
821✔
1621
class Bind2Loader
821✔
1622
{
821✔
1623
public:
821✔
1624
  Bind2Loader()
821✔
1625
  {
821✔
1626
    BackendMakers().report(std::make_unique<Bind2Factory>());
1627
    g_log << Logger::Info << "[bind2backend] This is the bind backend version " << VERSION
1628
#ifndef REPRODUCIBLE
1629
          << " (" __DATE__ " " __TIME__ ")"
1630
#endif
1631
#ifdef HAVE_SQLITE3
1632
          << " (with bind-dnssec-db support)"
1633
#endif
1634
          << " reporting" << endl;
1635
  }
1636
};
1637
static Bind2Loader bind2loader;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc