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

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

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

0.0
/pdns/recursordist/rec-xfr.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-xfr.hh"
24
#include "arguments.hh"
25
#include "logging.hh"
26
#include "threadname.hh"
27
#include "rec-lua-conf.hh"
28
#include "query-local-address.hh"
29
#include "axfr-retriever.hh"
30
#include "ixfr.hh"
31
#include "dnsrecords.hh"
32
#include "rec-system-resolve.hh"
33

34
static const DNSName cZones("zones");
35
static const DNSName cVersion("version");
36

37
// TODO: cleanup files if not in catalogzones?
38

39
static bool validate(const DNSRecord& record, Logr::log_t logger)
UNCOV
40
{
×
UNCOV
41
  if (record.d_name.empty()) {
×
42
    logger->info(Logr::Warning, "Record is not part of zone, skipping", "name", Logging::Loggable(record.d_name));
×
43
    return false;
×
44
  }
×
UNCOV
45
  if (record.d_class != QClass::IN) {
×
46
    logger->info(Logr::Warning, "Record class is not IN, skipping", "name", Logging::Loggable(record.d_name));
×
47
    return false;
×
48
  }
×
49

UNCOV
50
  if (record.d_name.getLastLabel() != cZones && record.d_name != cVersion) {
×
51
    logger->info(Logr::Warning, "Record is not a catalog zone entry, skipping", "name", Logging::Loggable(record.d_name));
×
52
    return false;
×
53
  }
×
UNCOV
54
  return true;
×
UNCOV
55
}
×
56

57
void CatalogZone::add(const DNSRecord& record, Logr::log_t logger)
UNCOV
58
{
×
UNCOV
59
  if (!validate(record, logger)) {
×
60
    return;
×
61
  }
×
UNCOV
62
  const auto& key = record.d_name;
×
UNCOV
63
  logger->info(Logr::Debug, "Adding cat zone entry", "name", Logging::Loggable(key), "qtype", Logging::Loggable(QType(record.d_type)));
×
UNCOV
64
  d_records.emplace(std::make_pair(key, record.d_type), record);
×
UNCOV
65
}
×
66

67
void CatalogZone::remove(const DNSRecord& record, Logr::log_t logger)
UNCOV
68
{
×
UNCOV
69
  if (!validate(record, logger)) {
×
70
    return;
×
71
  }
×
UNCOV
72
  const auto& key = record.d_name;
×
UNCOV
73
  logger->info(Logr::Debug, "Removing cat zone entry", "name", Logging::Loggable(key), "qtype", Logging::Loggable(QType(record.d_type)));
×
UNCOV
74
  d_records.erase(std::make_pair(key, record.d_type));
×
UNCOV
75
}
×
76

77
void CatalogZone::registerForwarders(const FWCatalogZone& params, Logr::log_t logger) const
UNCOV
78
{
×
UNCOV
79
  const string zonesFile = ::arg()["api-config-dir"] + "/catzone." + d_name.toString();
×
UNCOV
80
  ::rust::Vec<::pdns::rust::settings::rec::ForwardZone> forwards;
×
81

UNCOV
82
  for (const auto& record : d_records) {
×
UNCOV
83
    if (record.first.second != QType::PTR) {
×
UNCOV
84
      continue;
×
UNCOV
85
    }
×
UNCOV
86
    if (const auto ptr = getRR<PTRRecordContent>(record.second)) {
×
UNCOV
87
      auto defsIter = params.d_defaults.find("");
×
UNCOV
88
      const auto& name = record.first.first;
×
UNCOV
89
      const auto& target = ptr->getContent();
×
UNCOV
90
      auto groupKey = name;
×
UNCOV
91
      groupKey.prependRawLabel("group");
×
92
      // Look for group records matching the member
UNCOV
93
      auto range = d_records.equal_range(std::make_pair(groupKey, QType::TXT));
×
UNCOV
94
      for (auto groupIter = range.first; groupIter != range.second; ++groupIter) {
×
UNCOV
95
        if (const auto txt = getRR<TXTRecordContent>(groupIter->second); txt != nullptr) {
×
UNCOV
96
          auto groupName = txt->d_text;
×
UNCOV
97
          groupName = unquotify(groupName);
×
UNCOV
98
          auto iter = params.d_defaults.find(groupName);
×
UNCOV
99
          if (iter == params.d_defaults.end()) {
×
100
            logger->info(Logr::Error, "No match for group in YAML config", "name", Logging::Loggable(name), "groupName", Logging::Loggable(groupName), "target", Logging::Loggable(target));
×
101
            continue;
×
102
          }
×
UNCOV
103
          logger->info(Logr::Debug, "Match for group in YAML config", "name", Logging::Loggable(name), "groupName", Logging::Loggable(groupName), "target", Logging::Loggable(target));
×
UNCOV
104
          defsIter = iter;
×
UNCOV
105
          break;
×
UNCOV
106
        }
×
UNCOV
107
      }
×
UNCOV
108
      if (defsIter == params.d_defaults.end()) {
×
109
        logger->info(Logr::Error, "No match for default group in YAML config", "name", Logging::Loggable(name), "target", Logging::Loggable(target));
×
110
        continue;
×
111
      }
×
UNCOV
112
      pdns::rust::settings::rec::ForwardZone forward;
×
UNCOV
113
      forward.zone = target.toString();
×
UNCOV
114
      forward.recurse = defsIter->second.recurse;
×
UNCOV
115
      forward.notify_allowed = defsIter->second.notify_allowed;
×
UNCOV
116
      for (const auto& value : defsIter->second.forwarders) {
×
UNCOV
117
        forward.forwarders.emplace_back(value);
×
UNCOV
118
      }
×
UNCOV
119
      forward.validate("catz");
×
UNCOV
120
      forwards.emplace_back(std::move(forward));
×
UNCOV
121
    }
×
UNCOV
122
  }
×
123

124
  // Simple approach: just replace everything. Keeping track of changes to members is a bit involved as the
125
  // members themselves can change, be added ot deleted, but also the group records.
UNCOV
126
  pdns::rust::settings::rec::api_delete_zones(zonesFile);
×
UNCOV
127
  pdns::rust::settings::rec::api_add_forward_zones(zonesFile, forwards);
×
UNCOV
128
  reloadZoneConfiguration(true);
×
UNCOV
129
}
×
130

131
bool CatalogZone::versionCheck() const
UNCOV
132
{
×
UNCOV
133
  auto records = d_records.equal_range(std::make_pair(cVersion, QType::TXT));
×
UNCOV
134
  bool invalid = false;
×
UNCOV
135
  size_t count = 0;
×
UNCOV
136
  for (auto record = records.first; record != records.second; ++record) {
×
UNCOV
137
    const auto txt = getRR<TXTRecordContent>(record->second);
×
UNCOV
138
    if (txt == nullptr) {
×
139
      invalid = true;
×
140
      continue;
×
141
    }
×
UNCOV
142
    if (txt->d_text != "\"2\"") {
×
143
      invalid = true;
×
144
      continue;
×
145
    }
×
UNCOV
146
    ++count;
×
UNCOV
147
  }
×
UNCOV
148
  return !invalid && count == 1;
×
UNCOV
149
}
×
150

151
bool CatalogZone::dupsCheck() const
UNCOV
152
{
×
UNCOV
153
  std::unordered_set<DNSName> values;
×
UNCOV
154
  bool invalid = false;
×
UNCOV
155
  for (const auto& [key, record] : d_records) {
×
UNCOV
156
    if (key.second != QType::PTR) {
×
UNCOV
157
      continue;
×
UNCOV
158
    }
×
UNCOV
159
    const auto ptr = getRR<PTRRecordContent>(record);
×
UNCOV
160
    if (ptr == nullptr) {
×
161
      invalid = true;
×
162
      continue;
×
163
    }
×
UNCOV
164
    if (!values.emplace(ptr->getContent()).second) {
×
165
      invalid = true;
×
166
      break;
×
167
    }
×
UNCOV
168
  }
×
UNCOV
169
  return !invalid;
×
UNCOV
170
}
×
171

172
static shared_ptr<const SOARecordContent> loadZoneFromServer(Logr::log_t plogger, const ComboAddress& primary, const DNSName& zoneName, shared_ptr<CatalogZone>& zone, const TSIGTriplet& tsigTriplet, size_t maxReceivedBytes, const ComboAddress& localAddress, uint16_t axfrTimeout)
UNCOV
173
{
×
174

UNCOV
175
  auto logger = plogger->withValues("primary", Logging::Loggable(primary));
×
UNCOV
176
  logger->info(Logr::Info, "Loading zone from nameserver");
×
UNCOV
177
  if (!tsigTriplet.name.empty()) {
×
178
    logger->info(Logr::Info, "Using TSIG key for authentication", "tsig_key_name", Logging::Loggable(tsigTriplet.name), "tsig_key_algorithm", Logging::Loggable(tsigTriplet.algo));
×
179
  }
×
180

UNCOV
181
  ComboAddress local(localAddress);
×
UNCOV
182
  if (local == ComboAddress()) {
×
UNCOV
183
    local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
×
UNCOV
184
  }
×
185

UNCOV
186
  AXFRRetriever axfr(primary, zoneName, tsigTriplet, &local, maxReceivedBytes, axfrTimeout);
×
UNCOV
187
  unsigned int nrecords = 0;
×
UNCOV
188
  Resolver::res_t nop;
×
UNCOV
189
  vector<DNSRecord> chunk;
×
UNCOV
190
  time_t last = 0;
×
UNCOV
191
  const time_t axfrStart = time(nullptr);
×
UNCOV
192
  time_t axfrNow = axfrStart;
×
UNCOV
193
  shared_ptr<const SOARecordContent> soaRecordContent;
×
194
  // coverity[store_truncates_time_t]
UNCOV
195
  while (axfr.getChunk(nop, &chunk, (axfrStart + axfrTimeout - axfrNow)) != 0) {
×
UNCOV
196
    for (auto& dnsRecord : chunk) {
×
UNCOV
197
      if (dnsRecord.d_type == QType::NS || dnsRecord.d_type == QType::TSIG) {
×
198
        continue;
×
199
      }
×
200

UNCOV
201
      dnsRecord.d_name.makeUsRelative(zoneName);
×
UNCOV
202
      if (dnsRecord.d_type == QType::SOA) {
×
UNCOV
203
        soaRecordContent = getRR<SOARecordContent>(dnsRecord);
×
UNCOV
204
        continue;
×
UNCOV
205
      }
×
206

UNCOV
207
      zone->add(dnsRecord, logger);
×
UNCOV
208
      nrecords++;
×
UNCOV
209
    }
×
UNCOV
210
    axfrNow = time(nullptr);
×
UNCOV
211
    if (axfrNow < axfrStart || axfrNow - axfrStart > axfrTimeout) {
×
212
      throw PDNSException("Total AXFR time exceeded!");
×
213
    }
×
UNCOV
214
    if (last != time(nullptr)) {
×
UNCOV
215
      logger->info(Logr::Info, "Zone load in progress", "nrecords", Logging::Loggable(nrecords));
×
UNCOV
216
      last = time(nullptr);
×
UNCOV
217
    }
×
UNCOV
218
  }
×
219

UNCOV
220
  if (!zone->versionCheck()) {
×
221
    zone->clear();
×
222
    throw PDNSException("no valid version record in catalog zone");
×
223
  }
×
UNCOV
224
  if (!zone->dupsCheck()) {
×
225
    zone->clear();
×
226
    throw PDNSException("duplicate PTR values in catalog zone");
×
227
  }
×
UNCOV
228
  logger->info(Logr::Info, "Zone load completed", "nrecords", Logging::Loggable(nrecords), "soa", Logging::Loggable(soaRecordContent->getZoneRepresentation()));
×
UNCOV
229
  return soaRecordContent;
×
UNCOV
230
}
×
231

232
void FWCatZoneXFR::preloadZoneFile(const DNSName& zoneName, const std::shared_ptr<const CatalogZone>& oldZone, uint32_t& refresh, uint64_t configGeneration, ZoneWaiter& waiter, Logr::log_t logger)
UNCOV
233
{
×
UNCOV
234
  while (!d_params.soaRecordContent) {
×
235
    /* if we received an empty sr, the zone was not really preloaded */
236

237
    /* full copy, as promised */
UNCOV
238
    auto newZone = std::make_shared<CatalogZone>(*oldZone);
×
UNCOV
239
    for (const auto& nameOrIp : d_params.primaries) {
×
UNCOV
240
      try {
×
UNCOV
241
        auto primary = pdns::fromNameOrIP(nameOrIp, 53, logger);
×
UNCOV
242
        d_params.soaRecordContent = loadZoneFromServer(logger, primary, zoneName, newZone, d_params.tsigtriplet, d_params.maxReceivedMBytes, d_params.localAddress, d_params.xfrTimeout);
×
UNCOV
243
        newZone->setSerial(d_params.soaRecordContent->d_st.serial);
×
UNCOV
244
        newZone->setRefresh(d_params.soaRecordContent->d_st.refresh);
×
UNCOV
245
        refresh = std::max(d_params.refreshFromConf != 0 ? d_params.refreshFromConf : newZone->getRefresh(), 1U);
×
246
        // XXX Stats
247

UNCOV
248
        g_luaconfs.modify([zoneIdx = d_params.zoneIdx, &newZone](LuaConfigItems& lci) {
×
UNCOV
249
          lci.catalogzones.at(zoneIdx).d_catz = newZone;
×
UNCOV
250
        });
×
251

UNCOV
252
        auto lci = g_luaconfs.getLocal();
×
UNCOV
253
        newZone->registerForwarders(lci->catalogzones.at(d_params.zoneIdx), logger);
×
254
        /* no need to try another primary */
UNCOV
255
        break;
×
UNCOV
256
      }
×
UNCOV
257
      catch (const std::exception& e) {
×
258
        logger->error(Logr::Warning, e.what(), "Unable to load zone, will retry", "from", Logging::Loggable(nameOrIp), "exception", Logging::Loggable("std::exception"), "refresh", Logging::Loggable(refresh));
×
259
        // XXX Stats
260
      }
×
UNCOV
261
      catch (const PDNSException& e) {
×
UNCOV
262
        logger->error(Logr::Warning, e.reason, "Unable to load zone, will retry", "from", Logging::Loggable(nameOrIp), "exception", Logging::Loggable("PDNSException"), "refresh", Logging::Loggable(refresh));
×
263
        // XXX Stats
UNCOV
264
      }
×
UNCOV
265
    }
×
266
    // Release newZone before (long) sleep to reduce memory usage
UNCOV
267
    newZone = nullptr;
×
UNCOV
268
    if (!d_params.soaRecordContent) {
×
269
      std::unique_lock lock(waiter.mutex);
×
270
      waiter.condVar.wait_for(lock, std::chrono::seconds(refresh),
×
271
                              [&stop = waiter.stop] { return stop.load(); });
×
272
    }
×
UNCOV
273
    waiter.stop = false;
×
UNCOV
274
    auto luaconfsLocal = g_luaconfs.getLocal();
×
275

UNCOV
276
    if (luaconfsLocal->generation != configGeneration) {
×
277
      /* the configuration has been reloaded, meaning that a new thread
278
         has been started to handle that zone and we are now obsolete.
279
      */
280
      return;
×
281
    }
×
UNCOV
282
  }
×
UNCOV
283
}
×
284

285
bool FWCatZoneXFR::zoneTrackerIteration(const DNSName& zoneName, std::shared_ptr<const CatalogZone>& oldZone, uint32_t& refresh, bool& skipRefreshDelay, uint64_t configGeneration, ZoneWaiter& waiter, Logr::log_t logger)
UNCOV
286
{
×
287
  // Don't hold on to oldZone, it well be re-assigned after sleep in the try block
UNCOV
288
  oldZone = nullptr;
×
UNCOV
289
  DNSRecord soaRecord;
×
UNCOV
290
  soaRecord.setContent(d_params.soaRecordContent);
×
291

UNCOV
292
  if (skipRefreshDelay) {
×
293
    skipRefreshDelay = false;
×
294
  }
×
UNCOV
295
  else {
×
UNCOV
296
    const time_t minimumTimeBetweenRefreshes = std::min(refresh, 5U);
×
UNCOV
297
    const time_t startTime = time(nullptr);
×
UNCOV
298
    time_t wakeTime = startTime;
×
UNCOV
299
    while (wakeTime - startTime < minimumTimeBetweenRefreshes) {
×
UNCOV
300
      std::unique_lock lock(waiter.mutex);
×
UNCOV
301
      time_t remaining = refresh - (wakeTime - startTime);
×
UNCOV
302
      if (remaining <= 0) {
×
303
        break;
×
304
      }
×
UNCOV
305
      waiter.condVar.wait_for(lock, std::chrono::seconds(remaining),
×
UNCOV
306
                              [&stop = waiter.stop] { return stop.load(); });
×
UNCOV
307
      waiter.stop = false;
×
UNCOV
308
      wakeTime = time(nullptr);
×
UNCOV
309
    }
×
UNCOV
310
  }
×
UNCOV
311
  auto luaconfsLocal = g_luaconfs.getLocal();
×
312

UNCOV
313
  if (luaconfsLocal->generation != configGeneration) {
×
314
    /* the configuration has been reloaded, meaning that a new thread
315
       has been started to handle that zone and we are now obsolete.
316
    */
317
    logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing zone update thread");
×
318
    return false;
×
319
  }
×
320

UNCOV
321
  vector<pair<vector<DNSRecord>, vector<DNSRecord>>> deltas;
×
UNCOV
322
  for (const auto& nameOrIp : d_params.primaries) {
×
UNCOV
323
    auto primary = pdns::fromNameOrIP(nameOrIp, 53, logger);
×
UNCOV
324
    auto soa = getRR<SOARecordContent>(soaRecord);
×
UNCOV
325
    auto serial = soa ? soa->d_st.serial : 0;
×
UNCOV
326
    logger->info(Logr::Info, "Getting IXFR deltas", "address", Logging::Loggable(primary), "ourserial", Logging::Loggable(serial));
×
327

UNCOV
328
    ComboAddress local(d_params.localAddress);
×
UNCOV
329
    if (local == ComboAddress()) {
×
UNCOV
330
      local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
×
UNCOV
331
    }
×
332

UNCOV
333
    try {
×
UNCOV
334
      deltas = getIXFRDeltas(primary, zoneName, soaRecord, d_params.xfrTimeout, true, d_params.tsigtriplet, &local, d_params.maxReceivedMBytes);
×
335

336
      /* no need to try another primary */
UNCOV
337
      break;
×
UNCOV
338
    }
×
UNCOV
339
    catch (const std::runtime_error& e) {
×
UNCOV
340
      logger->error(Logr::Warning, e.what(), "Exception during retrieval of delta", "exception", Logging::Loggable("std::runtime_error"));
×
UNCOV
341
      if (oldZone) {
×
342
        // XXX Stats
343
      }
×
UNCOV
344
      continue;
×
UNCOV
345
    }
×
UNCOV
346
  }
×
347

UNCOV
348
  if (deltas.empty()) {
×
UNCOV
349
    return true;
×
UNCOV
350
  }
×
351

UNCOV
352
  try {
×
UNCOV
353
    logger->info(Logr::Info, "Processing deltas", "size", Logging::Loggable(deltas.size()));
×
354

UNCOV
355
    if (luaconfsLocal->generation != configGeneration) {
×
356
      logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing zone update thread");
×
357
      return false;
×
358
    }
×
UNCOV
359
    oldZone = luaconfsLocal->catalogzones.at(d_params.zoneIdx).d_catz;
×
UNCOV
360
    if (!oldZone || oldZone->getName() != zoneName) {
×
361
      logger->info(Logr::Info, "This policy is no more, stopping the existing zone update thread");
×
362
      return false;
×
363
    }
×
364
    /* we need to make a _full copy_ of the zone we are going to work on */
UNCOV
365
    auto newZone = std::make_shared<CatalogZone>(*oldZone);
×
366
    /* initialize the current serial to the last one */
UNCOV
367
    std::shared_ptr<const SOARecordContent> currentSR = d_params.soaRecordContent;
×
368

UNCOV
369
    int removed = 0;
×
UNCOV
370
    int added = 0;
×
371

UNCOV
372
    for (const auto& delta : deltas) {
×
UNCOV
373
      const auto& remove = delta.first;
×
UNCOV
374
      const auto& add = delta.second;
×
UNCOV
375
      if (remove.empty()) {
×
UNCOV
376
        logger->info(Logr::Warning, "IXFR update is a whole new zone");
×
UNCOV
377
        newZone->clear();
×
UNCOV
378
      }
×
UNCOV
379
      for (const auto& resourceRecord : remove) { // should always contain the SOA
×
UNCOV
380
        if (resourceRecord.d_type == QType::NS) {
×
381
          continue;
×
382
        }
×
UNCOV
383
        if (resourceRecord.d_type == QType::SOA) {
×
UNCOV
384
          auto oldsr = getRR<SOARecordContent>(resourceRecord);
×
UNCOV
385
          if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) {
×
386
            //        Got good removal of SOA serial, no work to be done
UNCOV
387
          }
×
388
          else {
×
389
            if (!oldsr) {
×
390
              throw std::runtime_error("Unable to extract serial from SOA record while processing the removal part of an update");
×
391
            }
×
392
            throw std::runtime_error("Received an unexpected serial (" + std::to_string(oldsr->d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update");
×
393
          }
×
UNCOV
394
        }
×
UNCOV
395
        else {
×
UNCOV
396
          removed++;
×
UNCOV
397
          logger->info(Logr::Debug, "Remove from zone", "name", Logging::Loggable(resourceRecord.d_name));
×
UNCOV
398
          newZone->remove(resourceRecord, logger);
×
UNCOV
399
        }
×
UNCOV
400
      }
×
401

UNCOV
402
      for (const auto& resourceRecord : add) { // should always contain the new SOA
×
UNCOV
403
        if (resourceRecord.d_type == QType::NS) {
×
404
          continue;
×
405
        }
×
UNCOV
406
        if (resourceRecord.d_type == QType::SOA) {
×
UNCOV
407
          auto tempSR = getRR<SOARecordContent>(resourceRecord);
×
UNCOV
408
          if (tempSR) {
×
UNCOV
409
            currentSR = std::move(tempSR);
×
UNCOV
410
          }
×
UNCOV
411
        }
×
UNCOV
412
        else {
×
UNCOV
413
          added++;
×
UNCOV
414
          logger->info(Logr::Debug, "Addition to zone", "name", Logging::Loggable(resourceRecord.d_name));
×
UNCOV
415
          newZone->add(resourceRecord, logger);
×
UNCOV
416
        }
×
UNCOV
417
      }
×
UNCOV
418
    }
×
UNCOV
419
    if (!newZone->versionCheck()) {
×
UNCOV
420
      throw PDNSException("no valid version record in catalog zone");
×
UNCOV
421
    }
×
UNCOV
422
    if (!newZone->dupsCheck()) {
×
423
      throw PDNSException("duplicate PTR values in catalog zone");
×
424
    }
×
425

426
    /* only update sr now that all changes have been converted */
UNCOV
427
    if (currentSR) {
×
UNCOV
428
      d_params.soaRecordContent = std::move(currentSR);
×
UNCOV
429
    }
×
UNCOV
430
    logger->info(Logr::Info, "Zone mutations", "removals", Logging::Loggable(removed), "additions", Logging::Loggable(added), "newserial", Logging::Loggable(d_params.soaRecordContent->d_st.serial));
×
UNCOV
431
    newZone->setSerial(d_params.soaRecordContent->d_st.serial);
×
UNCOV
432
    newZone->setRefresh(d_params.soaRecordContent->d_st.refresh);
×
433
    // XXX Stats
434

435
    /* we need to replace the existing zone with the new one,
436
       but we don't want to touch anything else, especially other zones,
437
       since they might have been updated by another Zone IXFR tracker thread.
438
    */
UNCOV
439
    if (luaconfsLocal->generation != configGeneration) {
×
440
      logger->info(Logr::Info, "A more recent configuration has been found, stopping the existing zone update thread");
×
441
      return false;
×
442
    }
×
UNCOV
443
    g_luaconfs.modify([zoneIdx = d_params.zoneIdx, &newZone](LuaConfigItems& lci) {
×
UNCOV
444
      lci.catalogzones.at(zoneIdx).d_catz = newZone;
×
UNCOV
445
    });
×
446

UNCOV
447
    auto lci = g_luaconfs.getLocal();
×
UNCOV
448
    newZone->registerForwarders(lci->catalogzones.at(d_params.zoneIdx), logger);
×
UNCOV
449
    refresh = std::max(d_params.refreshFromConf != 0 ? d_params.refreshFromConf : newZone->getRefresh(), 1U);
×
UNCOV
450
  }
×
UNCOV
451
  catch (const std::exception& e) {
×
452
    logger->error(Logr::Error, e.what(), "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("std::exception"));
×
453
  }
×
UNCOV
454
  catch (const PDNSException& e) {
×
UNCOV
455
    logger->error(Logr::Error, e.reason, "Exception while applying the update received over XFR, skipping", "exception", Logging::Loggable("PDNSException"));
×
UNCOV
456
  }
×
UNCOV
457
  return true;
×
UNCOV
458
}
×
459

460
// coverity[pass_by_value] params is intended to be a copy, as this is the main function of a thread
461
void FWCatZoneXFR::zoneXFRTracker(ZoneXFRParams params, uint64_t configGeneration) // NOLINT(performance-unnecessary-value-param)
UNCOV
462
{
×
UNCOV
463
  setThreadName("rec/fwcatzixfr");
×
UNCOV
464
  bool isPreloaded = params.soaRecordContent != nullptr;
×
UNCOV
465
  auto logger = g_slog->withName("fwcatzixfr");
×
UNCOV
466
  ZoneWaiter waiter(std::this_thread::get_id());
×
467

468
  /* we can _never_ modify this zone directly, we need to do a full copy then replace the existing zone */
UNCOV
469
  std::shared_ptr<const CatalogZone> oldZone;
×
UNCOV
470
  if (params.zoneIdx < g_luaconfs.getLocal()->catalogzones.size()) {
×
UNCOV
471
    oldZone = g_luaconfs.getLocal()->catalogzones.at(params.zoneIdx).d_catz;
×
UNCOV
472
  }
×
UNCOV
473
  if (!oldZone) {
×
474
    logger->error(Logr::Error, "Unable to retrieve catalog zone from configuration", "index", Logging::Loggable(params.zoneIdx));
×
475
    return;
×
476
  }
×
477

478
  // If oldZone failed to load its getRefresh() returns 0, protect against that
UNCOV
479
  uint32_t refresh = std::max(params.refreshFromConf != 0 ? params.refreshFromConf : oldZone->getRefresh(), 10U);
×
UNCOV
480
  DNSName zoneName = oldZone->getName();
×
481

482
  // Now that we know the name, set it in the logger
UNCOV
483
  logger = logger->withValues("zone", Logging::Loggable(zoneName));
×
484

UNCOV
485
  insertZoneTracker(zoneName, waiter);
×
486

UNCOV
487
  FWCatZoneXFR xfrObject(std::move(params));
×
UNCOV
488
  xfrObject.preloadZoneFile(zoneName, oldZone, refresh, configGeneration, waiter, logger);
×
UNCOV
489
  bool skipRefreshDelay = isPreloaded;
×
UNCOV
490
  while (xfrObject.zoneTrackerIteration(zoneName, oldZone, refresh, skipRefreshDelay, configGeneration, waiter, logger)) {
×
491
    // empty
UNCOV
492
  }
×
493

UNCOV
494
  clearZoneTracker(zoneName);
×
UNCOV
495
}
×
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