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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

70.58
/pdns/recursordist/rec-zonetocache.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
#include "rec-zonetocache.hh"
24

25
#include "syncres.hh"
26
#include "zoneparser-tng.hh"
27
#include "query-local-address.hh"
28
#include "axfr-retriever.hh"
29
#include "validate-recursor.hh"
30
#include "logging.hh"
31
#include "rec-lua-conf.hh"
32
#include "zonemd.hh"
33
#include "validate.hh"
34

35
#ifdef HAVE_LIBCURL
36
#include "minicurl.hh"
37
#endif
38

39
#include <fstream>
40

41
struct ZoneData
42
{
43
  ZoneData(const std::shared_ptr<Logr::Logger>& log, const std::string& zone) :
44
    d_log(log),
45
    d_zone(zone),
46
    d_now(time(nullptr)) {}
29✔
47

48
  // Potentially the two fields below could be merged into a single map. ATM it is not clear to me
49
  // if that would make the code easier to read.
50
  std::map<pair<DNSName, QType>, vector<DNSRecord>> d_all;
51
  std::map<pair<DNSName, QType>, vector<shared_ptr<const RRSIGRecordContent>>> d_sigs;
52

53
  // Maybe use a SuffixMatchTree?
54
  std::set<DNSName> d_delegations;
55

56
  std::shared_ptr<Logr::Logger> d_log;
57
  DNSName d_zone;
58
  time_t d_now;
59

60
  [[nodiscard]] bool isRRSetAuth(const DNSName& qname, QType qtype) const;
61
  void parseDRForCache(DNSRecord& resourceRecord);
62
  pdns::ZoneMD::Result getByAXFR(const RecZoneToCache::Config& config, pdns::ZoneMD& zonemd);
63
  pdns::ZoneMD::Result processLines(const std::vector<std::string>& lines, const RecZoneToCache::Config& config, pdns::ZoneMD& zonemd);
64
  void ZoneToCache(const RecZoneToCache::Config& config);
65
  vState dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const;
66
};
67

68
bool ZoneData::isRRSetAuth(const DNSName& qname, QType qtype) const
69
{
14,717✔
70
  DNSName delegatedZone(qname);
14,717✔
71
  if (qtype == QType::DS) {
14,717✔
72
    delegatedZone.chopOff();
1,355✔
73
  }
1,355✔
74
  bool isDelegated = false;
14,717✔
75
  for (;;) {
39,809✔
76
    if (d_delegations.count(delegatedZone) > 0) {
39,809✔
77
      isDelegated = true;
13,306✔
78
      break;
13,306✔
79
    }
13,306✔
80
    delegatedZone.chopOff();
26,503✔
81
    if (delegatedZone == g_rootdnsname || delegatedZone == d_zone) {
26,503✔
82
      break;
1,411✔
83
    }
1,411✔
84
  }
26,503✔
85
  return !isDelegated;
14,717✔
86
}
14,717✔
87

88
void ZoneData::parseDRForCache(DNSRecord& dnsRecord)
89
{
26,045✔
90
  if (dnsRecord.d_class != QClass::IN) {
26,045!
91
    return;
×
92
  }
×
93
  const auto key = pair(dnsRecord.d_name, dnsRecord.d_type);
26,045✔
94

95
  dnsRecord.d_ttl += d_now;
26,045✔
96

97
  switch (dnsRecord.d_type) {
26,045✔
98
  case QType::NSEC:
1,494✔
99
  case QType::NSEC3:
1,494!
100
    break;
1,494✔
101
  case QType::RRSIG: {
2,793✔
102
    const auto rrsig = getRR<RRSIGRecordContent>(dnsRecord);
2,793✔
103
    if (rrsig == nullptr) {
2,793!
104
      break;
×
105
    }
×
106
    const auto sigkey = pair(key.first, rrsig->d_type);
2,793✔
107
    auto found = d_sigs.find(sigkey);
2,793✔
108
    if (found != d_sigs.end()) {
2,793!
109
      found->second.push_back(rrsig);
×
110
    }
×
111
    else {
2,793✔
112
      vector<shared_ptr<const RRSIGRecordContent>> sigsrr;
2,793✔
113
      sigsrr.push_back(rrsig);
2,793✔
114
      d_sigs.insert({sigkey, sigsrr});
2,793✔
115
    }
2,793✔
116
    break;
2,793✔
117
  }
2,793✔
118
  case QType::NS:
8,092✔
119
    if (dnsRecord.d_name != d_zone) {
8,092✔
120
      d_delegations.insert(dnsRecord.d_name);
7,763✔
121
    }
7,763✔
122
    break;
8,092✔
123
  default:
13,666✔
124
    break;
13,666✔
125
  }
26,045✔
126

127
  auto found = d_all.find(key);
26,045✔
128
  if (found != d_all.end()) {
26,045✔
129
    found->second.push_back(dnsRecord);
8,168✔
130
  }
8,168✔
131
  else {
17,877✔
132
    vector<DNSRecord> dnsRecords;
17,877✔
133
    dnsRecords.push_back(dnsRecord);
17,877✔
134
    d_all.insert({key, dnsRecords});
17,877✔
135
  }
17,877✔
136
}
26,045✔
137

138
pdns::ZoneMD::Result ZoneData::getByAXFR(const RecZoneToCache::Config& config, pdns::ZoneMD& zonemd)
139
{
1✔
140
  ComboAddress primary = ComboAddress(config.d_sources.at(0), 53);
1✔
141
  uint16_t axfrTimeout = config.d_timeout;
1✔
142
  size_t maxReceivedBytes = config.d_maxReceivedBytes;
1✔
143
  const TSIGTriplet tsigTriplet = config.d_tt;
1✔
144
  ComboAddress local = config.d_local;
1✔
145
  if (local == ComboAddress()) {
1!
146
    local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
1✔
147
  }
1✔
148

149
  AXFRRetriever axfr(primary, d_zone, tsigTriplet, &local, maxReceivedBytes, axfrTimeout);
1✔
150
  Resolver::res_t nop;
1✔
151
  vector<DNSRecord> chunk;
1✔
152
  time_t axfrStart = time(nullptr);
1✔
153
  time_t axfrNow = time(nullptr);
1✔
154

155
  // coverity[store_truncates_time_t]
156
  while (axfr.getChunk(nop, &chunk, (axfrStart + axfrTimeout - axfrNow)) != 0) {
81✔
157
    for (auto& dnsRecord : chunk) {
25,101✔
158
      if (config.d_zonemd != pdns::ZoneMD::Config::Ignore) {
25,101!
159
        zonemd.readRecord(dnsRecord);
25,101✔
160
      }
25,101✔
161
      parseDRForCache(dnsRecord);
25,101✔
162
    }
25,101✔
163
    axfrNow = time(nullptr);
80✔
164
    if (axfrNow < axfrStart || axfrNow - axfrStart > axfrTimeout) {
80!
165
      throw std::runtime_error("Total AXFR time for zoneToCache exceeded!");
×
166
    }
×
167
  }
80✔
168
  if (config.d_zonemd != pdns::ZoneMD::Config::Ignore) {
1!
169
    bool validationDone = false;
1✔
170
    bool validationSuccess = false;
1✔
171
    zonemd.verify(validationDone, validationSuccess);
1✔
172
    d_log->info(Logr::Info, "ZONEMD digest validation", "validationDone", Logging::Loggable(validationDone),
1✔
173
                "validationSuccess", Logging::Loggable(validationSuccess));
1✔
174
    if (!validationDone) {
1!
175
      return pdns::ZoneMD::Result::NoValidationDone;
×
176
    }
×
177
    if (!validationSuccess) {
1!
178
      return pdns::ZoneMD::Result::ValidationFailure;
×
179
    }
×
180
  }
1✔
181
  return pdns::ZoneMD::Result::OK;
1✔
182
}
1✔
183

184
static std::vector<std::string> getLinesFromFile(const std::string& file)
185
{
28✔
186

187
  std::vector<std::string> lines;
28✔
188
  std::ifstream stream(file);
28✔
189
  if (!stream) {
28!
190
    throw std::runtime_error("Cannot read file: " + file);
×
191
  }
×
192
  std::string line;
28✔
193
  while (std::getline(stream, line)) {
972✔
194
    lines.push_back(line);
944✔
195
  }
944✔
196
  return lines;
28✔
197
}
28✔
198

199
static std::vector<std::string> getURL(const RecZoneToCache::Config& config)
200
{
×
201
  std::vector<std::string> lines;
×
202
#ifdef HAVE_LIBCURL
203
  MiniCurl miniCurl;
204
  ComboAddress local = config.d_local;
205
  std::string reply = miniCurl.getURL(config.d_sources.at(0), nullptr, local == ComboAddress() ? nullptr : &local, static_cast<int>(config.d_timeout), false, true);
×
206
  if (config.d_maxReceivedBytes > 0 && reply.size() > config.d_maxReceivedBytes) {
×
207
    // We should actually detect this *during* the GET
208
    throw std::runtime_error("Retrieved data exceeds maxReceivedBytes");
209
  }
210
  std::istringstream stream(reply);
211
  string line;
212
  while (std::getline(stream, line)) {
×
213
    lines.push_back(line);
214
  }
215
#else
216
  throw std::runtime_error("url method configured but libcurl not compiled in");
217
#endif
218
  return lines;
×
219
}
×
220

221
pdns::ZoneMD::Result ZoneData::processLines(const vector<string>& lines, const RecZoneToCache::Config& config, pdns::ZoneMD& zonemd)
222
{
28✔
223
  DNSResourceRecord drr;
28✔
224
  ZoneParserTNG zpt(lines, d_zone, true);
28✔
225
  zpt.setMaxGenerateSteps(1);
28✔
226
  zpt.setMaxIncludes(0);
28✔
227

228
  while (zpt.get(drr)) {
972✔
229
    DNSRecord dnsRecord(drr);
944✔
230
    if (config.d_zonemd != pdns::ZoneMD::Config::Ignore) {
944✔
231
      zonemd.readRecord(dnsRecord);
558✔
232
    }
558✔
233
    parseDRForCache(dnsRecord);
944✔
234
  }
944✔
235
  if (config.d_zonemd != pdns::ZoneMD::Config::Ignore) {
28✔
236
    bool validationDone = false;
18✔
237
    bool validationSuccess = false;
18✔
238
    zonemd.verify(validationDone, validationSuccess);
18✔
239
    d_log->info(Logr::Info, "ZONEMD digest validation", "validationDone", Logging::Loggable(validationDone),
18✔
240
                "validationSuccess", Logging::Loggable(validationSuccess));
18✔
241
    if (!validationDone) {
18✔
242
      return pdns::ZoneMD::Result::NoValidationDone;
6✔
243
    }
6✔
244
    if (!validationSuccess) {
12✔
245
      return pdns::ZoneMD::Result::ValidationFailure;
4✔
246
    }
4✔
247
  }
12✔
248
  return pdns::ZoneMD::Result::OK;
18✔
249
}
28✔
250

251
vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
252
{
7✔
253
  pdns::validation::ValidationContext validationContext;
7✔
254
  validationContext.d_nsec3IterationsRemainingQuota = std::numeric_limits<decltype(validationContext.d_nsec3IterationsRemainingQuota)>::max();
7✔
255
  zonemdCount = 0;
7✔
256

257
  SyncRes resolver({d_now, 0});
7✔
258
  resolver.setDoDNSSEC(true);
7✔
259
  resolver.setDNSSECValidationRequested(true);
7✔
260

261
  dsset_t dsset;
7✔
262
  vState dsState = resolver.getDSRecords(d_zone, dsset, false, 0, "");
7✔
263
  if (dsState != vState::Secure) {
7!
264
    return dsState;
×
265
  }
×
266

267
  skeyset_t dnsKeys;
7✔
268
  sortedRecords_t records;
7✔
269
  if (zonemd.getDNSKEYs().empty()) {
7✔
270
    return vState::BogusUnableToGetDNSKEYs;
4✔
271
  }
4✔
272
  for (const auto& key : zonemd.getDNSKEYs()) {
7✔
273
    dnsKeys.emplace(key);
7✔
274
    records.emplace(key);
7✔
275
  }
7✔
276

277
  skeyset_t validKeys;
3✔
278
  vState dnsKeyState = validateDNSKeysAgainstDS(d_now, d_zone, dsset, dnsKeys, records, zonemd.getRRSIGs(QType::DNSKEY), validKeys, std::nullopt, validationContext);
3✔
279
  if (dnsKeyState != vState::Secure) {
3✔
280
    return dnsKeyState;
2✔
281
  }
2✔
282

283
  if (validKeys.empty()) {
1!
284
    return vState::BogusNoValidDNSKEY;
×
285
  }
×
286

287
  auto zonemdRecords = zonemd.getZONEMDs();
1✔
288
  zonemdCount = zonemdRecords.size();
1✔
289

290
  // De we need to do a denial validation?
291
  if (zonemdCount == 0) {
1!
292
    const auto& nsecs = zonemd.getNSECs();
×
293
    const auto& nsec3s = zonemd.getNSEC3s();
×
294
    cspmap_t csp;
×
295

296
    vState nsecValidationStatus = vState::Indeterminate;
×
297

298
    if (!nsecs.records.empty() && !nsecs.signatures.empty()) {
×
299
      // Valdidate the NSEC
300
      nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys, std::nullopt, validationContext);
×
301
      csp.emplace(std::pair(d_zone, QType::NSEC), nsecs);
×
302
    }
×
303
    else if (!nsec3s.records.empty() && !nsec3s.signatures.empty()) {
×
304
      // Validate NSEC3PARAMS
305
      records.clear();
×
306
      for (const auto& rec : zonemd.getNSEC3Params()) {
×
307
        records.emplace(rec);
×
308
      }
×
309
      nsecValidationStatus = validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(QType::NSEC3PARAM), validKeys, std::nullopt, validationContext);
×
310
      if (nsecValidationStatus != vState::Secure) {
×
311
        d_log->info(Logr::Warning, "NSEC3PARAMS records did not validate");
×
312
        return nsecValidationStatus;
×
313
      }
×
314
      // Valdidate the NSEC3
315
      nsecValidationStatus = validateWithKeySet(d_now, zonemd.getNSEC3Label(), nsec3s.records, nsec3s.signatures, validKeys, std::nullopt, validationContext);
×
316
      csp.emplace(std::pair(zonemd.getNSEC3Label(), QType::NSEC3), nsec3s);
×
317
    }
×
318
    else {
×
319
      d_log->info(Logr::Warning, "No NSEC(3) records and/or RRSIGS found to deny ZONEMD");
×
320
      return vState::BogusInvalidDenial;
×
321
    }
×
322

323
    if (nsecValidationStatus != vState::Secure) {
×
324
      d_log->info(Logr::Warning, "zone NSEC(3) record does not validate");
×
325
      return nsecValidationStatus;
×
326
    }
×
327

328
    auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, validationContext, std::nullopt, true);
×
329
    if (denial == dState::NXQTYPE) {
×
330
      d_log->info(Logr::Info, "Validated denial of existence of ZONEMD record");
×
331
      return vState::Secure;
×
332
    }
×
333
    d_log->info(Logr::Warning, "No ZONEMD record, but NSEC(3) record does not deny it");
×
334
    return vState::BogusInvalidDenial;
×
335
  }
×
336

337
  // Collect the ZONEMD records and validate them using the validated DNSSKEYs
338
  records.clear();
1✔
339
  for (const auto& rec : zonemdRecords) {
1✔
340
    records.emplace(rec);
1✔
341
  }
1✔
342
  return validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(QType::ZONEMD), validKeys, std::nullopt, validationContext);
1✔
343
}
1✔
344

345
void ZoneData::ZoneToCache(const RecZoneToCache::Config& config)
346
{
29✔
347
  if (config.d_sources.size() > 1) {
29!
348
    d_log->info(Logr::Warning, "Multiple sources not yet supported, using first");
×
349
  }
×
350

351
  if (config.d_dnssec == pdns::ZoneMD::Config::Require && (g_dnssecmode == DNSSECMode::Off || g_dnssecmode == DNSSECMode::ProcessNoValidate)) {
29!
352
    throw PDNSException("ZONEMD DNSSEC validation failure: DNSSEC validation is switched off but required by ZoneToCache");
×
353
  }
×
354

355
  // First scan all records collecting info about delegations and sigs
356
  // A this moment, we ignore NSEC and NSEC3 records. It is not clear to me yet under which conditions
357
  // they could be entered in into the (neg)cache.
358

359
  auto zonemd = pdns::ZoneMD(DNSName(config.d_zone));
29✔
360
  pdns::ZoneMD::Result result = pdns::ZoneMD::Result::OK;
29✔
361
  if (config.d_method == "axfr") {
29✔
362
    d_log->info(Logr::Info, "Getting zone by AXFR");
1✔
363
    result = getByAXFR(config, zonemd);
1✔
364
  }
1✔
365
  else {
28✔
366
    vector<string> lines;
28✔
367
    if (config.d_method == "url") {
28!
368
      d_log->info(Logr::Info, "Getting zone by URL");
×
369
      lines = getURL(config);
×
370
    }
×
371
    else if (config.d_method == "file") {
28!
372
      d_log->info(Logr::Info, "Getting zone from file");
28✔
373
      lines = getLinesFromFile(config.d_sources.at(0));
28✔
374
    }
28✔
375
    result = processLines(lines, config, zonemd);
28✔
376
  }
28✔
377

378
  // Validate DNSKEYs and ZONEMD, rest of records are validated on-demand by SyncRes
379
  if (config.d_dnssec == pdns::ZoneMD::Config::Require || (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate && config.d_dnssec != pdns::ZoneMD::Config::Ignore)) {
29!
380
    size_t zonemdCount = 0;
7✔
381
    auto validationStatus = dnssecValidate(zonemd, zonemdCount);
7✔
382
    d_log->info(Logr::Info, "ZONEMD record related DNSSEC validation", "validationStatus", Logging::Loggable(validationStatus),
7✔
383
                "zonemdCount", Logging::Loggable(zonemdCount));
7✔
384
    if (config.d_dnssec == pdns::ZoneMD::Config::Require && validationStatus != vState::Secure) {
7!
385
      throw PDNSException("ZONEMD required DNSSEC validation failed");
2✔
386
    }
2✔
387
    if (validationStatus != vState::Secure && validationStatus != vState::Insecure) {
5!
388
      throw PDNSException("ZONEMD record DNSSEC validation failed");
4✔
389
    }
4✔
390
  }
5✔
391

392
  if (config.d_zonemd == pdns::ZoneMD::Config::Require && result != pdns::ZoneMD::Result::OK) {
23✔
393
    // We do not accept NoValidationDone in this case
394
    throw PDNSException("ZONEMD digest validation failure");
6✔
395
    return;
×
396
  }
6✔
397
  if (config.d_zonemd == pdns::ZoneMD::Config::Validate && result == pdns::ZoneMD::Result::ValidationFailure) {
17✔
398
    throw PDNSException("ZONEMD digest validation failure");
2✔
399
    return;
×
400
  }
2✔
401

402
  // Rerun, now inserting the rrsets into the cache with associated sigs
403
  d_now = time(nullptr);
15✔
404
  for (const auto& [key, v] : d_all) {
17,633✔
405
    const auto& [qname, qtype] = key;
17,633✔
406
    if (qname.isWildcard()) {
17,633!
407
      continue;
×
408
    }
×
409
    switch (qtype) {
17,633✔
410
    case QType::NSEC:
1,470✔
411
    case QType::NSEC3:
1,470!
412
    case QType::RRSIG:
2,916✔
413
      break;
2,916✔
414
    default: {
14,717✔
415
      vector<shared_ptr<const RRSIGRecordContent>> sigsrr;
14,717✔
416
      auto iter = d_sigs.find(key);
14,717✔
417
      if (iter != d_sigs.end()) {
14,717✔
418
        sigsrr = iter->second;
1,347✔
419
      }
1,347✔
420
      bool auth = isRRSetAuth(qname, qtype);
14,717✔
421
      // Same decision as updateCacheFromRecords() (we do not test for NSEC since we skip those completely)
422
      if (auth || (qtype == QType::NS || qtype == QType::A || qtype == QType::AAAA || qtype == QType::DS)) {
14,717!
423
        g_recCache->replace(d_now, qname, qtype, v, sigsrr,
14,717✔
424
                            std::vector<std::shared_ptr<DNSRecord>>(), auth, d_zone);
14,717✔
425
      }
14,717✔
426
      break;
14,717✔
427
    }
1,470✔
428
    }
17,633✔
429
  }
17,633✔
430
}
15✔
431

432
void RecZoneToCache::maintainStates(const map<DNSName, Config>& configs, map<DNSName, State>& states, uint64_t mygeneration)
433
{
140✔
434
  // Delete states that have no config
435
  for (auto it = states.begin(); it != states.end();) {
140!
436
    if (configs.find(it->first) == configs.end()) {
×
437
      it = states.erase(it);
×
438
    }
×
439
    else {
×
440
      it = ++it;
×
441
    }
×
442
  }
×
443
  // Reset states for which the config generation changed and create new states for new configs
444
  for (const auto& config : configs) {
140✔
445
    auto state = states.find(config.first);
1✔
446
    if (state != states.end()) {
1!
447
      if (state->second.d_generation != mygeneration) {
×
448
        state->second = {0, 0, mygeneration};
×
449
      }
×
450
    }
×
451
    else {
1✔
452
      states.emplace(config.first, State{0, 0, mygeneration});
1✔
453
    }
1✔
454
  }
1✔
455
}
140✔
456

457
void RecZoneToCache::ZoneToCache(const RecZoneToCache::Config& config, RecZoneToCache::State& state)
458
{
29✔
459
  if (state.d_waittime == 0 && state.d_lastrun > 0) {
29!
460
    // single shot
461
    return;
×
462
  }
×
463
  if (state.d_lastrun > 0 && state.d_lastrun + state.d_waittime > time(nullptr)) {
29!
464
    return;
×
465
  }
×
466
  auto log = g_slog->withName("ztc")->withValues("zone", Logging::Loggable(config.d_zone));
29✔
467

468
  state.d_waittime = config.d_retryOnError;
29✔
469
  try {
29✔
470
    ZoneData data(log, config.d_zone);
29✔
471
    data.ZoneToCache(config);
29✔
472
    state.d_waittime = config.d_refreshPeriod;
29✔
473
    log->info(Logr::Info, "Loaded zone into cache", "refresh", Logging::Loggable(state.d_waittime));
29✔
474
  }
29✔
475
  catch (const PDNSException& e) {
29✔
476
    log->error(Logr::Error, e.reason, "Unable to load zone into cache, will retry", "exception", Logging::Loggable("PDNSException"), "refresh", Logging::Loggable(state.d_waittime));
14✔
477
  }
14✔
478
  catch (const std::runtime_error& e) {
29✔
479
    log->error(Logr::Error, e.what(), "Unable to load zone into cache, will retry", "exception", Logging::Loggable("std::runtime_error"), "refresh", Logging::Loggable(state.d_waittime));
×
480
  }
×
481
  catch (...) {
29✔
482
    log->info(Logr::Error, "Unable to load zone into cache, will retry", "refresh", Logging::Loggable(state.d_waittime));
×
483
  }
×
484
  state.d_lastrun = time(nullptr);
29✔
485
}
29✔
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

© 2025 Coveralls, Inc