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

PowerDNS / pdns / 22225698541

20 Feb 2026 01:20PM UTC coverage: 70.913% (-0.7%) from 71.654%
22225698541

Pull #16693

github

web-flow
Merge 12ab81d89 into da25f7afd
Pull Request #16693: auth: plumbing for structured logging

45342 of 79966 branches covered (56.7%)

Branch coverage included in aggregate %.

892 of 2611 new or added lines in 70 files covered. (34.16%)

390 existing lines in 48 files now uncovered.

130608 of 168156 relevant lines covered (77.67%)

7371735.34 hits per line

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

36.77
/modules/ldapbackend/native.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 * originally authored by Norbert Sendetzky
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of version 2 of the GNU General Public License as
8
 * published by the Free Software Foundation.
9
 *
10
 * In addition, for the avoidance of any doubt, permission is granted to
11
 * link this program with OpenSSL and to (re)distribute the binaries
12
 * produced as the result of such linking.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
 */
23
#include "exceptions.hh"
24
#include "ldapbackend.hh"
25
#include <cstdlib>
26

27
/*
28
 *  Known DNS RR types
29
 *  Types which aren't active are currently not supported by PDNS
30
 */
31

32
static const char* ldap_attrany[] = { // NOLINT(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays)
33
  "associatedDomain", // needs to be first, code below depends on this
34
  "dNSTTL",
35
  "ALIASRecord",
36
  "aRecord",
37
  "nSRecord",
38
  "cNAMERecord",
39
  "sOARecord",
40
  "pTRRecord",
41
  "hInfoRecord",
42
  "mXRecord",
43
  "tXTRecord",
44
  "rPRecord",
45
  "aFSDBRecord",
46
  //  "SigRecord",
47
  "KeyRecord",
48
  //  "gPosRecord",
49
  "aAAARecord",
50
  "lOCRecord",
51
  "sRVRecord",
52
  "nAPTRRecord",
53
  "kXRecord",
54
  "certRecord",
55
  //  "a6Record",
56
  "dNameRecord",
57
  //  "aPLRecord",
58
  "dSRecord",
59
  "sSHFPRecord",
60
  "iPSecKeyRecord",
61
  "rRSIGRecord",
62
  "nSECRecord",
63
  "dNSKeyRecord",
64
  "dHCIDRecord",
65
  "nSEC3Record",
66
  "nSEC3PARAMRecord",
67
  "tLSARecord",
68
  "cDSRecord",
69
  "cDNSKeyRecord",
70
  "openPGPKeyRecord",
71
  "SVCBRecord",
72
  "HTTPSRecord",
73
  "sPFRecord",
74
  "EUI48Record",
75
  "EUI64Record",
76
  "tKeyRecord",
77
  "uRIRecord",
78
  "cAARecord",
79
  "TYPE65226Record",
80
  "TYPE65534Record",
81
  "modifyTimestamp",
82
  "PdnsRecordTTL",
83
  "PdnsRecordAuth",
84
  "PdnsRecordOrdername",
85
  nullptr};
86

87
bool LdapBackend::list(const ZoneName& target, domainid_t domain_id, bool /* include_disabled */)
88
{
×
89
  try {
×
90
    d_in_list = true;
×
91
    d_qname = target.operator const DNSName&();
×
92
    d_qtype = QType::ANY;
×
93
    d_results_cache.clear();
×
94

95
    return (this->*d_list_fcnt)(target, domain_id);
×
96
  }
×
97
  catch (LDAPTimeout& lt) {
×
NEW
98
    SLOG(g_log << Logger::Warning << d_myname << " Unable to get zone " << target << " from LDAP directory: " << lt.what() << endl,
×
NEW
99
         d_slog->error(Logr::Warning, lt.what(), "unable to get zone from LDAP directory", "zone", Logging::Loggable(target)));
×
100
    throw DBException("LDAP server timeout");
×
101
  }
×
102
  catch (LDAPNoConnection& lnc) {
×
NEW
103
    SLOG(g_log << Logger::Warning << d_myname << " Connection to LDAP lost, trying to reconnect" << endl,
×
NEW
104
         d_slog->info(Logr::Warning, "LDAP connection lost, trying to reconnect"));
×
105
    if (reconnect()) {
×
106
      return this->list(target, domain_id);
×
107
    }
×
108
    throw PDNSException("Failed to reconnect to LDAP server");
×
109
  }
×
110
  catch (LDAPException& le) {
×
NEW
111
    SLOG(g_log << Logger::Error << d_myname << " Unable to get zone " << target << " from LDAP directory: " << le.what() << endl,
×
NEW
112
         d_slog->error(Logr::Warning, le.what(), "unable to get zone from LDAP directory", "zone", Logging::Loggable(target)));
×
113
    throw PDNSException("LDAP server unreachable"); // try to reconnect to another server
×
114
  }
×
115
  catch (std::exception& e) {
×
NEW
116
    SLOG(g_log << Logger::Error << d_myname << " Caught STL exception for target " << target << ": " << e.what() << endl,
×
NEW
117
         d_slog->error(Logr::Warning, e.what(), "unable to get zone from LDAP directory, caught STL exception", "zone", Logging::Loggable(target)));
×
118
    throw DBException("STL exception");
×
119
  }
×
120

121
  return false;
×
122
}
×
123

124
bool LdapBackend::list_simple(const ZoneName& target, domainid_t /* domain_id */)
125
{
×
126
  string dn;
×
127
  string filter;
×
128
  string qesc;
×
129

130
  dn = getArg("basedn");
×
131
  qesc = toLower(d_pldap->escape(target.toStringRootDot()));
×
132

133
  // search for SOARecord of target
134
  filter = strbind(":target:", "&(associatedDomain=" + qesc + ")(sOARecord=*)", getArg("filter-axfr"));
×
135
  PowerLDAP::SearchResult::Ptr search = d_pldap->search(dn, LDAP_SCOPE_SUBTREE, filter, (const char**)ldap_attrany);
×
136
  if (!search->getNext(d_result, true))
×
137
    return false;
×
138

139
  if (d_result.count("dn") && !d_result["dn"].empty()) {
×
140
    if (!mustDo("basedn-axfr-override")) {
×
141
      dn = d_result["dn"][0];
×
142
    }
×
143
  }
×
144

145
  // If we have any records associated with this entry let's parse them here
146
  DNSResult soa_result;
×
147
  soa_result.ttl = d_default_ttl;
×
148
  soa_result.lastmod = 0;
×
149
  this->extract_common_attributes(soa_result);
×
150
  this->extract_entry_results(d_qname, soa_result, QType(uint16_t(QType::ANY)));
×
151

152
  filter = strbind(":target:", "associatedDomain=*." + qesc, getArg("filter-axfr"));
×
NEW
153
  SLOG(g_log << Logger::Debug << d_myname << " Search = basedn: " << dn << ", filter: " << filter << endl,
×
NEW
154
       d_slog->info(Logr::Debug, "search", "basedn", Logging::Loggable(dn), "filter", Logging::Loggable(filter)));
×
UNCOV
155
  d_search = d_pldap->search(dn, LDAP_SCOPE_SUBTREE, filter, (const char**)ldap_attrany);
×
156

157
  return true;
×
158
}
×
159

160
bool LdapBackend::list_strict(const ZoneName& target, domainid_t domain_id)
161
{
×
162
  static const DNSName inaddrarpa("in-addr.arpa");
×
163
  static const DNSName ip6arpa("ip6.arpa");
×
164
  if (target.isPartOf(inaddrarpa) || target.isPartOf(ip6arpa)) {
×
NEW
165
    SLOG(g_log << Logger::Warning << d_myname << " Request for reverse zone AXFR, but this is not supported in strict mode" << endl,
×
NEW
166
         d_slog->info(Logr::Warning, "Request for reverse zone AXFR is not supported in strict mode", "zone", Logging::Loggable(target)));
×
167
    return false; // AXFR isn't supported in strict mode. Use simple mode and additional PTR records
×
168
  }
×
169

170
  return list_simple(target, domain_id);
×
171
}
×
172

173
void LdapBackend::lookup(const QType& qtype, const DNSName& qname, domainid_t zoneid, DNSPacket* dnspkt)
174
{
1,253✔
175
  try {
1,253✔
176
    d_in_list = false;
1,253✔
177
    d_qname = qname;
1,253✔
178
    d_qtype = qtype;
1,253✔
179
    d_results_cache.clear();
1,253✔
180

181
    if (d_qlog) {
1,253!
182
      SLOG(g_log.log("Query: '" + qname.toStringRootDot() + "|" + qtype.toString() + "'", Logger::Error),
1,253!
183
           d_slog->info(Logr::Error, "query", "name", Logging::Loggable(qname.toStringRootDot()), "type", Logging::Loggable(qtype)));
1,253✔
184
    }
1,253✔
185
    (this->*d_lookup_fcnt)(qtype, qname, dnspkt, zoneid);
1,253✔
186
  }
1,253✔
187
  catch (LDAPTimeout& lt) {
1,253✔
NEW
188
    SLOG(g_log << Logger::Warning << d_myname << " Unable to search LDAP directory: " << lt.what() << endl,
×
NEW
189
         d_slog->error(Logr::Warning, lt.what(), "unable to search LDAP directory"));
×
190
    throw DBException("LDAP server timeout");
×
191
  }
×
192
  catch (LDAPNoConnection& lnc) {
1,253✔
NEW
193
    SLOG(g_log << Logger::Warning << d_myname << " Connection to LDAP lost, trying to reconnect" << endl,
×
NEW
194
         d_slog->error(Logr::Warning, lnc.what(), "LDAP connection lost, trying to reconnect"));
×
195
    if (reconnect()) {
×
196
      this->lookup(qtype, qname, zoneid, dnspkt);
×
197
      return;
×
198
    }
×
199
    throw PDNSException("Failed to reconnect to LDAP server");
×
200
  }
×
201
  catch (LDAPException& le) {
1,253✔
NEW
202
    SLOG(g_log << Logger::Error << d_myname << " Unable to search LDAP directory: " << le.what() << endl,
×
NEW
203
         d_slog->error(Logr::Warning, le.what(), "unable to search LDAP directory"));
×
204
    throw PDNSException("LDAP server unreachable"); // try to reconnect to another server
×
205
  }
×
206
  catch (std::exception& e) {
1,253✔
NEW
207
    SLOG(g_log << Logger::Error << d_myname << " Caught STL exception for qname " << qname << ": " << e.what() << endl,
×
NEW
208
         d_slog->error(Logr::Warning, e.what(), "unable to search LDAP directory, caught STL exception"));
×
209
    throw DBException("STL exception");
×
210
  }
×
211
}
1,253✔
212

213
void LdapBackend::lookup_simple(const QType& qtype, const DNSName& qname, DNSPacket* /* dnspkt */, domainid_t /* zoneid */)
214
{
419✔
215
  string filter, attr, qesc;
419✔
216
  const char** attributes = ldap_attrany + 1; // skip associatedDomain
419✔
217
  const char* attronly[] = {NULL, "dNSTTL", "modifyTimestamp", "PdnsRecordTTL", "PdnsRecordAuth", "PdnsRecordOrdername", NULL};
419✔
218

219
  qesc = toLower(d_pldap->escape(qname.toStringRootDot()));
419✔
220
  filter = "associatedDomain=" + qesc;
419✔
221

222
  if (qtype.getCode() != QType::ANY) {
419✔
223
    attr = qtype.toString() + "Record";
197✔
224
    filter = "&(" + filter + ")(" + attr + "=*)";
197✔
225
    attronly[0] = attr.c_str();
197✔
226
    attributes = attronly;
197✔
227
  }
197✔
228

229
  filter = strbind(":target:", filter, getArg("filter-lookup"));
419✔
230

231
  SLOG(g_log << Logger::Debug << d_myname << " Search = basedn: " << getArg("basedn") << ", filter: " << filter << ", qtype: " << qtype.toString() << endl,
419!
232
       d_slog->info(Logr::Debug, "search", "basedn", Logging::Loggable(getArg("basedn")), "filter", Logging::Loggable(filter), "qtype", Logging::Loggable(qtype)));
419✔
233
  d_search = d_pldap->search(getArg("basedn"), LDAP_SCOPE_SUBTREE, filter, attributes);
419✔
234
}
419✔
235

236
void LdapBackend::lookup_strict(const QType& qtype, const DNSName& qname, DNSPacket* /* dnspkt */, domainid_t /* zoneid */)
237
{
418✔
238
  int len;
418✔
239
  vector<string> parts;
418✔
240
  string filter, attr, qesc;
418✔
241
  const char** attributes = ldap_attrany + 1; // skip associatedDomain
418✔
242
  const char* attronly[] = {NULL, "dNSTTL", "modifyTimestamp", "PdnsRecordTTL", "PdnsRecordAuth", "PdnsRecordOrdername", NULL};
418✔
243

244
  qesc = toLower(d_pldap->escape(qname.toStringRootDot()));
418✔
245
  stringtok(parts, qesc, ".");
418✔
246
  len = qesc.length();
418✔
247

248
  if (parts.size() == 6 && len > 13 && qesc.substr(len - 13, 13) == ".in-addr.arpa") // IPv4 reverse lookups
418!
249
  {
2✔
250
    filter = "aRecord=" + ptr2ip4(parts);
2✔
251
    attronly[0] = "associatedDomain";
2✔
252
    attributes = attronly;
2✔
253
  }
2✔
254
  else if (parts.size() == 34 && len > 9 && (qesc.substr(len - 9, 9) == ".ip6.arpa")) // IPv6 reverse lookups
416!
255
  {
×
256
    filter = "aAAARecord=" + ptr2ip6(parts);
×
257
    attronly[0] = "associatedDomain";
×
258
    attributes = attronly;
×
259
  }
×
260
  else // IPv4 and IPv6 lookups
416✔
261
  {
416✔
262
    filter = "associatedDomain=" + qesc;
416✔
263
  }
416✔
264

265
  if (qtype.getCode() != QType::ANY) {
418✔
266
    attr = qtype.toString() + "Record";
197✔
267
    filter = "&(" + filter + ")(" + attr + "=*)";
197✔
268
    attronly[0] = attr.c_str();
197✔
269
    attributes = attronly;
197✔
270
  }
197✔
271

272
  filter = strbind(":target:", filter, getArg("filter-lookup"));
418✔
273

274
  SLOG(g_log << Logger::Debug << d_myname << " Search = basedn: " << getArg("basedn") << ", filter: " << filter << ", qtype: " << qtype.toString() << endl,
418!
275
       d_slog->info(Logr::Debug, "search", "basedn", Logging::Loggable(getArg("basedn")), "filter", Logging::Loggable(filter), "qtype", Logging::Loggable(qtype)));
418✔
276
  d_search = d_pldap->search(getArg("basedn"), LDAP_SCOPE_SUBTREE, filter, attributes);
418✔
277
}
418✔
278

279
void LdapBackend::lookup_tree(const QType& qtype, const DNSName& qname, DNSPacket* /* dnspkt */, domainid_t /* zoneid */)
280
{
416✔
281
  string filter, attr, qesc, dn;
416✔
282
  const char** attributes = ldap_attrany + 1; // skip associatedDomain
416✔
283
  const char* attronly[] = {NULL, "dNSTTL", "modifyTimestamp", "PdnsRecordTTL", "PdnsRecordAuth", "PdnsRecordOrdername", NULL};
416✔
284
  vector<string> parts;
416✔
285

286
  qesc = toLower(d_pldap->escape(qname.toStringRootDot()));
416✔
287
  filter = "associatedDomain=" + qesc;
416✔
288

289
  if (qtype.getCode() != QType::ANY) {
416✔
290
    attr = qtype.toString() + "Record";
196✔
291
    filter = "&(" + filter + ")(" + attr + "=*)";
196✔
292
    attronly[0] = attr.c_str();
196✔
293
    attributes = attronly;
196✔
294
  }
196✔
295

296
  filter = strbind(":target:", filter, getArg("filter-lookup"));
416✔
297

298
  stringtok(parts, toLower(qname.toString()), ".");
416✔
299
  for (auto i = parts.crbegin(); i != parts.crend(); i++) {
2,877✔
300
    dn = "dc=" + *i + "," + dn;
2,461✔
301
  }
2,461✔
302

303
  SLOG(g_log << Logger::Debug << d_myname << " Search = basedn: " << dn + getArg("basedn") << ", filter: " << filter << ", qtype: " << qtype.toString() << endl,
416!
304
       d_slog->info(Logr::Debug, "search", "basedn", Logging::Loggable(dn), "filter", Logging::Loggable(filter), "qtype", Logging::Loggable(qtype)));
416✔
305
  d_search = d_pldap->search(dn + getArg("basedn"), LDAP_SCOPE_BASE, filter, attributes);
416✔
306
}
416✔
307

308
bool LdapBackend::get(DNSResourceRecord& rr)
309
{
2,091✔
310
  if (d_results_cache.empty()) {
2,091✔
311
    while (d_results_cache.empty()) {
2,012✔
312
      bool exhausted = false;
1,628✔
313
      bool valid_entry_found = false;
1,628✔
314

315
      while (!valid_entry_found && !exhausted) {
3,256✔
316
        try {
1,628✔
317
          exhausted = !d_search->getNext(d_result, true);
1,628✔
318
        }
1,628✔
319
        catch (LDAPException& le) {
1,628✔
NEW
320
          SLOG(g_log << Logger::Error << d_myname << " Failed to get next result: " << le.what() << endl,
×
NEW
321
               d_slog->error(Logr::Error, le.what(), "Failed to get next result"));
×
322
          throw PDNSException("Get next result impossible");
×
323
        }
×
324

325
        if (!exhausted) {
1,628✔
326
          if (!d_in_list) {
384!
327
            // All entries are valid here
328
            valid_entry_found = true;
384✔
329
          }
384✔
330
          else {
×
331
            // If we're called after list() then the entry *must* contain
332
            // associatedDomain, otherwise let's just skip it
333
            if (d_result.count("associatedDomain"))
×
334
              valid_entry_found = true;
×
335
          }
×
336
        }
384✔
337
      }
1,628✔
338

339
      if (exhausted) {
1,628✔
340
        break;
1,244✔
341
      }
1,244✔
342

343
      DNSResult result_template;
384✔
344
      result_template.ttl = d_default_ttl;
384✔
345
      result_template.lastmod = 0;
384✔
346
      this->extract_common_attributes(result_template);
384✔
347

348
      std::vector<std::string> associatedDomains;
384✔
349

350
      if (d_result.count("associatedDomain")) {
384✔
351
        if (d_in_list) {
3!
352
          // We can have more than one associatedDomain in the entry, so for each of them we have to check
353
          // that they are indeed under the domain we've been asked to list (nothing enforces this, so you
354
          // can have one associatedDomain set to "host.first-domain.com" and another one set to
355
          // "host.second-domain.com"). Better not return the latter I guess :)
356
          // We also have to generate one DNSResult per DNS-relevant attribute. As we've asked only for them
357
          // and the others above we've already cleaned it's just a matter of iterating over them.
358

359
          unsigned int axfrqlen = d_qname.toStringRootDot().length();
×
360
          for (auto i = d_result["associatedDomain"].begin(); i != d_result["associatedDomain"].end(); ++i) {
×
361
            // Sanity checks: is this associatedDomain attribute under the requested domain?
362
            if (i->size() >= axfrqlen && i->substr(i->size() - axfrqlen, axfrqlen) == d_qname.toStringRootDot())
×
363
              associatedDomains.push_back(*i);
×
364
          }
×
365
        }
×
366
        else {
3✔
367
          // This was a lookup in strict mode, so we add the reverse lookup
368
          // information manually.
369
          d_result["pTRRecord"] = d_result["associatedDomain"];
3✔
370
        }
3✔
371
      }
3✔
372

373
      if (d_in_list) {
384!
374
        for (const auto& domain : associatedDomains)
×
375
          this->extract_entry_results(DNSName(domain), result_template, QType(uint16_t(QType::ANY)));
×
376
      }
×
377
      else {
384✔
378
        this->extract_entry_results(d_qname, result_template, QType(uint16_t(QType::ANY)));
384✔
379
      }
384✔
380
    }
384✔
381

382
    if (d_results_cache.empty())
1,628✔
383
      return false;
1,244✔
384
  }
1,628✔
385

386
  DNSResult result = d_results_cache.back();
847✔
387
  d_results_cache.pop_back();
847✔
388
  rr.qtype = result.qtype;
847✔
389
  rr.qname = result.qname;
847✔
390
  rr.ttl = result.ttl;
847✔
391
  rr.last_modified = 0;
847✔
392
  rr.content = result.value;
847✔
393
  rr.auth = result.auth;
847✔
394

395
  SLOG(g_log << Logger::Debug << d_myname << " Record = qname: " << rr.qname << ", qtype: " << (rr.qtype).toString() << ", ttl: " << rr.ttl << ", content: " << rr.content << endl,
847!
396
       d_slog->info(Logr::Debug, "record", "qname", Logging::Loggable(rr.qname), "qtype", Logging::Loggable(rr.qtype), "ttl", Logging::Loggable(rr.ttl), "content", Logging::Loggable(rr.content)));
847✔
397
  return true;
847✔
398
}
2,091✔
399

400
void LdapBackend::lookupEnd()
401
{
9✔
402
  d_results_cache.clear();
9✔
403
}
9✔
404

405
bool LdapBackend::getDomainInfo(const ZoneName& domain, DomainInfo& info, bool /* getSerial */)
406
{
×
407
  string filter;
×
408
  SOAData sd;
×
409
  PowerLDAP::sentry_t result;
×
410
  const char* attronly[] = {
×
411
    "sOARecord",
×
412
    "PdnsDomainId",
×
413
    "PdnsDomainNotifiedSerial",
×
414
    "PdnsDomainLastCheck",
×
415
    "PdnsDomainMaster",
×
416
    "PdnsDomainType",
×
417
    NULL};
×
418

419
  try {
×
420
    // search for SOARecord of domain
421
    filter = "(&(associatedDomain=" + toLower(d_pldap->escape(domain.toStringRootDot())) + ")(SOARecord=*))";
×
422
    d_search = d_pldap->search(getArg("basedn"), LDAP_SCOPE_SUBTREE, filter, attronly);
×
423
    if (!d_search->getNext(result)) {
×
424
      return false;
×
425
    }
×
426
  }
×
427
  catch (LDAPTimeout& lt) {
×
NEW
428
    SLOG(g_log << Logger::Warning << d_myname << " Unable to search LDAP directory: " << lt.what() << endl,
×
NEW
429
         d_slog->error(Logr::Warning, lt.what(), "unable to search LDAP directory"));
×
430
    throw DBException("LDAP server timeout");
×
431
  }
×
432
  catch (LDAPNoConnection& lnc) {
×
NEW
433
    SLOG(g_log << Logger::Warning << d_myname << " Connection to LDAP lost, trying to reconnect" << endl,
×
NEW
434
         d_slog->error(Logr::Warning, lnc.what(), "LDAP connection lost, trying to reconnect"));
×
435
    if (reconnect()) {
×
436
      return this->getDomainInfo(domain, info);
×
437
    }
×
438
    throw PDNSException("Failed to reconnect to LDAP server");
×
439
  }
×
440
  catch (LDAPException& le) {
×
NEW
441
    SLOG(g_log << Logger::Error << d_myname << " Unable to search LDAP directory: " << le.what() << endl,
×
NEW
442
         d_slog->error(Logr::Warning, le.what(), "unable to search LDAP directory"));
×
443
    throw PDNSException("LDAP server unreachable"); // try to reconnect to another server
×
444
  }
×
445
  catch (std::exception& e) {
×
NEW
446
    SLOG(g_log << Logger::Error << d_myname << " Caught STL exception: " << e.what() << endl,
×
NEW
447
         d_slog->error(Logr::Warning, e.what(), "unable to search LDAP directory, caught STL exception"));
×
448
    throw DBException("STL exception");
×
449
  }
×
450

451
  if (result.count("sOARecord") && !result["sOARecord"].empty()) {
×
452
    sd.serial = 0;
×
453
    fillSOAData(result["sOARecord"][0], sd);
×
454

455
    if (result.count("PdnsDomainId") && !result["PdnsDomainId"].empty())
×
456
      info.id = static_cast<domainid_t>(std::stoll(result["PdnsDomainId"][0]));
×
457
    else
×
458
      info.id = UnknownDomainID;
×
459

460
    info.serial = sd.serial;
×
461
    info.zone = domain;
×
462

463
    if (result.count("PdnsDomainLastCheck") && !result["PdnsDomainLastCheck"].empty())
×
464
      pdns::checked_stoi_into(info.last_check, result["PdnsDomainLastCheck"][0]);
×
465
    else
×
466
      info.last_check = 0;
×
467

468
    if (result.count("PdnsDomainNotifiedSerial") && !result["PdnsDomainNotifiedSerial"].empty())
×
469
      pdns::checked_stoi_into(info.notified_serial, result["PdnsDomainNotifiedSerial"][0]);
×
470
    else
×
471
      info.notified_serial = 0;
×
472

473
    if (result.count("PdnsDomainMaster") && !result["PdnsDomainMaster"].empty()) {
×
474
      for (const auto& m : result["PdnsDomainMaster"])
×
475
        info.primaries.emplace_back(m, 53);
×
476
    }
×
477

478
    if (result.count("PdnsDomainType") && !result["PdnsDomainType"].empty()) {
×
479
      string kind = result["PdnsDomainType"][0];
×
480
      if (kind == "master")
×
481
        info.kind = DomainInfo::Primary;
×
482
      else if (kind == "slave")
×
483
        info.kind = DomainInfo::Secondary;
×
484
      else
×
485
        info.kind = DomainInfo::Native;
×
486
    }
×
487
    else {
×
488
      info.kind = DomainInfo::Native;
×
489
    }
×
490

491
    info.backend = this;
×
492
    return true;
×
493
  }
×
494

495
  return false;
×
496
}
×
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