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

PowerDNS / pdns / 17765900968

16 Sep 2025 12:33PM UTC coverage: 65.987% (-0.04%) from 66.029%
17765900968

Pull #16108

github

web-flow
Merge 4059c5fe8 into 2e297650d
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42424 of 93030 branches covered (45.6%)

Branch coverage included in aggregate %.

9 of 135 new or added lines in 6 files covered. (6.67%)

34 existing lines in 8 files now uncovered.

128910 of 166619 relevant lines covered (77.37%)

5500581.66 hits per line

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

81.21
/pdns/dnsdistdist/dnsdist-cache.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
#include <cinttypes>
23

24
#include "dnsdist.hh"
25
#include "dolog.hh"
26
#include "dnsparser.hh"
27
#include "dnsdist-cache.hh"
28
#include "dnsdist-ecs.hh"
29
#include "ednssubnet.hh"
30
#include "packetcache.hh"
31
#include "base64.hh"
32

33
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters): too cumbersome to change at this point
34
DNSDistPacketCache::DNSDistPacketCache(CacheSettings settings) :
35
  d_settings(std::move(settings))
146✔
36
{
146✔
37
  if (d_settings.d_maxEntries == 0) {
146!
38
    throw std::runtime_error("Trying to create a 0-sized packet-cache");
×
39
  }
×
40

41
  if (d_settings.d_shardCount == 0) {
146!
42
    d_settings.d_shardCount = 1;
×
43
  }
×
44

45
  d_shards.resize(d_settings.d_shardCount);
146✔
46

47
  /* we reserve maxEntries + 1 to avoid rehashing from occurring
48
     when we get to maxEntries, as it means a load factor of 1 */
49
  for (auto& shard : d_shards) {
2,026✔
50
    shard.setSize((d_settings.d_maxEntries / d_settings.d_shardCount) + 1);
2,026✔
51
  }
2,026✔
52
}
146✔
53

54
bool DNSDistPacketCache::getClientSubnet(const PacketBuffer& packet, size_t qnameWireLength, boost::optional<Netmask>& subnet)
55
{
56✔
56
  uint16_t optRDPosition = 0;
56✔
57
  size_t remaining = 0;
56✔
58

59
  int res = dnsdist::getEDNSOptionsStart(packet, qnameWireLength, &optRDPosition, &remaining);
56✔
60

61
  if (res == 0) {
56✔
62
    size_t ecsOptionStartPosition = 0;
38✔
63
    size_t ecsOptionSize = 0;
38✔
64

65
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
66
    res = getEDNSOption(reinterpret_cast<const char*>(&packet.at(optRDPosition)), remaining, EDNSOptionCode::ECS, &ecsOptionStartPosition, &ecsOptionSize);
38✔
67

68
    if (res == 0 && ecsOptionSize > (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) {
38!
69

70
      EDNSSubnetOpts eso;
30✔
71
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
72
      if (EDNSSubnetOpts::getFromString(reinterpret_cast<const char*>(&packet.at(optRDPosition + ecsOptionStartPosition + (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))), ecsOptionSize - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), &eso)) {
30!
73
        subnet = eso.getSource();
30✔
74
        return true;
30✔
75
      }
30✔
76
    }
30✔
77
  }
38✔
78

79
  return false;
26✔
80
}
56✔
81

82
bool DNSDistPacketCache::cachedValueMatches(const CacheValue& cachedValue, uint16_t queryFlags, const DNSName& qname, uint16_t qtype, uint16_t qclass, bool receivedOverUDP, bool dnssecOK, const boost::optional<Netmask>& subnet) const
83
{
1,242,586✔
84
  if (cachedValue.queryFlags != queryFlags || cachedValue.dnssecOK != dnssecOK || cachedValue.receivedOverUDP != receivedOverUDP || cachedValue.qtype != qtype || cachedValue.qclass != qclass || cachedValue.qname != qname) {
1,242,586✔
85
    return false;
242✔
86
  }
242✔
87

88
  if (d_settings.d_parseECS && cachedValue.subnet != subnet) {
1,242,344✔
89
    return false;
2✔
90
  }
2✔
91

92
  return true;
1,242,342✔
93
}
1,242,344✔
94

95
bool DNSDistPacketCache::insertLocked(std::unordered_map<uint32_t, CacheValue>& map, uint32_t key, CacheValue& newValue)
96
{
845,211✔
97
  /* check again now that we hold the lock to prevent a race */
98
  if (map.size() >= (d_settings.d_maxEntries / d_settings.d_shardCount)) {
845,211!
99
    return false;
×
100
  }
×
101

102
  std::unordered_map<uint32_t, CacheValue>::iterator mapIt;
845,211✔
103
  bool result{false};
845,211✔
104
  std::tie(mapIt, result) = map.insert({key, newValue});
845,211✔
105

106
  if (result) {
845,211✔
107
    return true;
845,151✔
108
  }
845,151✔
109

110
  /* in case of collision, don't override the existing entry
111
     except if it has expired */
112
  CacheValue& value = mapIt->second;
60✔
113
  bool wasExpired = value.validity <= newValue.added;
60✔
114

115
  if (!wasExpired && !cachedValueMatches(value, newValue.queryFlags, newValue.qname, newValue.qtype, newValue.qclass, newValue.receivedOverUDP, newValue.dnssecOK, newValue.subnet)) {
60!
116
    ++d_insertCollisions;
49✔
117
    return false;
49✔
118
  }
49✔
119

120
  /* if the existing entry had a longer TTD, keep it */
121
  if (newValue.validity <= value.validity) {
11!
122
    return false;
×
123
  }
×
124

125
  value = newValue;
11✔
126
  return false;
11✔
127
}
11✔
128

129
void DNSDistPacketCache::insert(uint32_t key, const boost::optional<Netmask>& subnet, uint16_t queryFlags, bool dnssecOK, const DNSName& qname, uint16_t qtype, uint16_t qclass, const PacketBuffer& response, bool receivedOverUDP, uint8_t rcode, boost::optional<uint32_t> tempFailureTTL)
130
{
1,193,706✔
131
  if (response.size() < sizeof(dnsheader) || response.size() > getMaximumEntrySize()) {
1,193,706✔
132
    return;
4✔
133
  }
4✔
134

135
  if (qtype == QType::AXFR || qtype == QType::IXFR) {
1,193,702✔
136
    return;
14✔
137
  }
14✔
138

139
  uint32_t minTTL{0};
1,193,688✔
140

141
  if (rcode == RCode::ServFail || rcode == RCode::Refused) {
1,193,688✔
142
    minTTL = tempFailureTTL == boost::none ? d_settings.d_tempFailureTTL : *tempFailureTTL;
22✔
143
    if (minTTL == 0) {
22✔
144
      return;
2✔
145
    }
2✔
146
  }
22✔
147
  else {
1,193,666✔
148
    bool seenAuthSOA = false;
1,193,666✔
149
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
150
    minTTL = getMinTTL(reinterpret_cast<const char*>(response.data()), response.size(), &seenAuthSOA);
1,193,666✔
151

152
    if (minTTL == std::numeric_limits<uint32_t>::max()) {
1,193,666✔
153
      /* no TTL found, we probably don't want to cache this
154
         unless it's an empty (no records) truncated answer,
155
         and we have been asked to cache these */
156
      if (d_settings.d_truncatedTTL == 0) {
10✔
157
        return;
6✔
158
      }
6✔
159
      dnsheader_aligned dh_aligned(response.data());
4✔
160
      if (dh_aligned->tc == 0) {
4!
161
        return;
×
162
      }
×
163
      minTTL = d_settings.d_truncatedTTL;
4✔
164
    }
4✔
165

166
    if (rcode == RCode::NXDomain || (rcode == RCode::NoError && seenAuthSOA)) {
1,193,660✔
167
      minTTL = std::min(minTTL, d_settings.d_maxNegativeTTL);
10✔
168
    }
10✔
169
    else if (minTTL > d_settings.d_maxTTL) {
1,193,650✔
170
      minTTL = d_settings.d_maxTTL;
2✔
171
    }
2✔
172

173
    if (minTTL < d_settings.d_minTTL) {
1,193,660✔
174
      ++d_ttlTooShorts;
4✔
175
      return;
4✔
176
    }
4✔
177
  }
1,193,660✔
178

179
  uint32_t shardIndex = getShardIndex(key);
1,193,676✔
180

181
  if (d_shards.at(shardIndex).d_entriesCount >= (d_settings.d_maxEntries / d_settings.d_shardCount)) {
1,193,676✔
182
    return;
2✔
183
  }
2✔
184

185
  const time_t now = time(nullptr);
1,193,674✔
186
  time_t newValidity = now + minTTL;
1,193,674✔
187
  CacheValue newValue;
1,193,674✔
188
  newValue.qname = qname;
1,193,674✔
189
  newValue.qtype = qtype;
1,193,674✔
190
  newValue.qclass = qclass;
1,193,674✔
191
  newValue.queryFlags = queryFlags;
1,193,674✔
192
  newValue.len = response.size();
1,193,674✔
193
  newValue.validity = newValidity;
1,193,674✔
194
  newValue.added = now;
1,193,674✔
195
  newValue.receivedOverUDP = receivedOverUDP;
1,193,674✔
196
  newValue.dnssecOK = dnssecOK;
1,193,674✔
197
  newValue.value = std::string(response.begin(), response.end());
1,193,674✔
198
  newValue.subnet = subnet;
1,193,674✔
199

200
  auto& shard = d_shards.at(shardIndex);
1,193,674✔
201

202
  bool inserted = false;
1,193,674✔
203
  if (d_settings.d_deferrableInsertLock) {
1,197,740✔
204
    auto lock = shard.d_map.try_write_lock();
1,196,289✔
205

206
    if (!lock.owns_lock()) {
1,196,289✔
207
      ++d_deferredInserts;
349,976✔
208
      return;
349,976✔
209
    }
349,976✔
210
    inserted = insertLocked(*lock, key, newValue);
846,313✔
211
  }
846,313✔
212
  else {
2,147,485,098✔
213
    auto lock = shard.d_map.write_lock();
2,147,485,098✔
214

215
    inserted = insertLocked(*lock, key, newValue);
2,147,485,098✔
216
  }
2,147,485,098✔
217
  if (inserted) {
847,093✔
218
    ++shard.d_entriesCount;
845,151✔
219
  }
845,151✔
220
}
843,698✔
221

222
bool DNSDistPacketCache::get(DNSQuestion& dnsQuestion, uint16_t queryId, uint32_t* keyOut, boost::optional<Netmask>& subnet, bool dnssecOK, bool receivedOverUDP, uint32_t allowExpired, bool skipAging, bool truncatedOK, bool recordMiss)
223
{
2,795,388✔
224
  if (dnsQuestion.ids.qtype == QType::AXFR || dnsQuestion.ids.qtype == QType::IXFR) {
2,795,388✔
225
    ++d_misses;
18✔
226
    return false;
18✔
227
  }
18✔
228

229
  const auto& dnsQName = dnsQuestion.ids.qname.getStorage();
2,795,370✔
230
  uint32_t key = getKey(dnsQName, dnsQuestion.ids.qname.wirelength(), dnsQuestion.getData(), receivedOverUDP);
2,795,370✔
231

232
  if (keyOut != nullptr) {
2,795,370✔
233
    *keyOut = key;
2,734,811✔
234
  }
2,734,811✔
235

236
  if (d_settings.d_parseECS) {
2,795,370✔
237
    getClientSubnet(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), subnet);
56✔
238
  }
56✔
239

240
  uint32_t shardIndex = getShardIndex(key);
2,795,370✔
241
  time_t now = time(nullptr);
2,795,370✔
242
  time_t age{0};
2,795,370✔
243
  bool stale = false;
2,795,370✔
244
  auto& response = dnsQuestion.getMutableData();
2,795,370✔
245
  auto& shard = d_shards.at(shardIndex);
2,795,370✔
246
  {
2,795,370✔
247
    auto map = shard.d_map.try_read_lock();
2,795,370✔
248
    if (!map.owns_lock()) {
2,795,370✔
249
      ++d_deferredLookups;
200,919✔
250
      return false;
200,919✔
251
    }
200,919✔
252

253
    auto mapIt = map->find(key);
2,594,451✔
254
    if (mapIt == map->end()) {
2,594,451✔
255
      if (recordMiss) {
1,344,641✔
256
        ++d_misses;
1,344,575✔
257
      }
1,344,575✔
258
      return false;
1,344,527✔
259
    }
1,344,527✔
260

261
    const CacheValue& value = mapIt->second;
1,249,924✔
262
    if (value.validity <= now) {
1,249,924✔
263
      if ((now - value.validity) >= static_cast<time_t>(allowExpired)) {
16✔
264
        if (recordMiss) {
12!
265
          ++d_misses;
12✔
266
        }
12✔
267
        return false;
12✔
268
      }
12✔
269
      stale = true;
4✔
270
    }
4✔
271

272
    if (value.len < sizeof(dnsheader)) {
1,249,912!
273
      return false;
×
274
    }
×
275

276
    /* check for collision */
277
    if (!cachedValueMatches(value, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.ids.qclass, receivedOverUDP, dnssecOK, subnet)) {
1,249,912✔
278
      ++d_lookupCollisions;
195✔
279
      return false;
195✔
280
    }
195✔
281

282
    if (!truncatedOK) {
1,249,717✔
283
      dnsheader_aligned dh_aligned(value.value.data());
36✔
284
      if (dh_aligned->tc != 0) {
36✔
285
        return false;
2✔
286
      }
2✔
287
    }
36✔
288

289
    response.resize(value.len);
1,249,715✔
290
    memcpy(&response.at(0), &queryId, sizeof(queryId));
1,249,715✔
291
    memcpy(&response.at(sizeof(queryId)), &value.value.at(sizeof(queryId)), sizeof(dnsheader) - sizeof(queryId));
1,249,715✔
292

293
    if (value.len == sizeof(dnsheader)) {
1,249,715✔
294
      /* DNS header only, our work here is done */
295
      ++d_hits;
1✔
296
      return true;
1✔
297
    }
1✔
298

299
    const size_t dnsQNameLen = dnsQName.length();
1,249,714✔
300
    if (value.len < (sizeof(dnsheader) + dnsQNameLen)) {
1,249,714!
301
      return false;
×
302
    }
×
303

304
    memcpy(&response.at(sizeof(dnsheader)), dnsQName.c_str(), dnsQNameLen);
1,249,714✔
305
    if (value.len > (sizeof(dnsheader) + dnsQNameLen)) {
1,249,714✔
306
      memcpy(&response.at(sizeof(dnsheader) + dnsQNameLen), &value.value.at(sizeof(dnsheader) + dnsQNameLen), value.len - (sizeof(dnsheader) + dnsQNameLen));
1,245,204✔
307
    }
1,245,204✔
308

309
    if (!stale) {
1,249,714✔
310
      age = now - value.added;
1,243,053✔
311
    }
1,243,053✔
312
    else {
6,661✔
313
      age = (value.validity - value.added) - d_settings.d_staleTTL;
6,661✔
314
      dnsQuestion.ids.staleCacheHit = true;
6,661✔
315
    }
6,661✔
316
  }
1,249,714✔
317

318
  if (!d_settings.d_dontAge && !skipAging) {
1,249,714✔
319
    if (!stale) {
844,254✔
320
      // coverity[store_truncates_time_t]
321
      dnsheader_aligned dh_aligned(response.data());
843,913✔
322
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
323
      ageDNSPacket(reinterpret_cast<char*>(response.data()), response.size(), age, dh_aligned);
843,913✔
324
    }
843,913✔
325
    else {
341✔
326
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
327
      editDNSPacketTTL(reinterpret_cast<char*>(response.data()), response.size(),
341✔
328
                       [staleTTL = d_settings.d_staleTTL](uint8_t /* section */, uint16_t /* class_ */, uint16_t /* type */, uint32_t /* ttl */) { return staleTTL; });
341✔
329
    }
341✔
330
  }
844,254✔
331

332
  if (d_settings.d_shuffle) {
1,249,714!
NEW
333
    dnsheader_aligned dh_aligned(response.data());
×
334
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
NEW
335
    shuffleDNSPacket(reinterpret_cast<char*>(response.data()), response.size(), dh_aligned);
×
NEW
336
  }
×
337

338
  ++d_hits;
1,249,714✔
339
  return true;
1,249,714✔
340
}
1,249,714✔
341

342
/* Remove expired entries, until the cache has at most
343
   upTo entries in it.
344
   If the cache has more than one shard, we will try hard
345
   to make sure that every shard has free space remaining.
346
*/
347
size_t DNSDistPacketCache::purgeExpired(size_t upTo, const time_t now)
348
{
19✔
349
  const size_t maxPerShard = upTo / d_settings.d_shardCount;
19✔
350

351
  size_t removed = 0;
19✔
352

353
  ++d_cleanupCount;
19✔
354
  for (auto& shard : d_shards) {
224✔
355
    auto map = shard.d_map.write_lock();
224✔
356
    if (map->size() <= maxPerShard) {
224✔
357
      continue;
160✔
358
    }
160✔
359

360
    size_t toRemove = map->size() - maxPerShard;
64✔
361

362
    for (auto it = map->begin(); toRemove > 0 && it != map->end();) {
599,846✔
363
      const CacheValue& value = it->second;
599,782✔
364

365
      if (value.validity <= now) {
599,782✔
366
        it = map->erase(it);
199,995✔
367
        --toRemove;
199,995✔
368
        --shard.d_entriesCount;
199,995✔
369
        ++removed;
199,995✔
370
      }
199,995✔
371
      else {
399,787✔
372
        ++it;
399,787✔
373
      }
399,787✔
374
    }
599,782✔
375
  }
64✔
376

377
  return removed;
19✔
378
}
19✔
379

380
/* Remove all entries, keeping only upTo
381
   entries in the cache.
382
   If the cache has more than one shard, we will try hard
383
   to make sure that every shard has free space remaining.
384
*/
385
size_t DNSDistPacketCache::expunge(size_t upTo)
386
{
1✔
387
  const size_t maxPerShard = upTo / d_settings.d_shardCount;
1✔
388

389
  size_t removed = 0;
1✔
390

391
  for (auto& shard : d_shards) {
20✔
392
    auto map = shard.d_map.write_lock();
20✔
393

394
    if (map->size() <= maxPerShard) {
20✔
395
      continue;
19✔
396
    }
19✔
397

398
    size_t toRemove = map->size() - maxPerShard;
1✔
399

400
    auto beginIt = map->begin();
1✔
401
    auto endIt = beginIt;
1✔
402

403
    if (map->size() >= toRemove) {
1!
404
      std::advance(endIt, toRemove);
1✔
405
      map->erase(beginIt, endIt);
1✔
406
      shard.d_entriesCount -= toRemove;
1✔
407
      removed += toRemove;
1✔
408
    }
1✔
409
    else {
×
410
      removed += map->size();
×
411
      map->clear();
×
412
      shard.d_entriesCount = 0;
×
413
    }
×
414
  }
1✔
415

416
  return removed;
1✔
417
}
1✔
418

419
size_t DNSDistPacketCache::expungeByName(const DNSName& name, uint16_t qtype, bool suffixMatch)
420
{
211✔
421
  size_t removed = 0;
211✔
422

423
  for (auto& shard : d_shards) {
382✔
424
    auto map = shard.d_map.write_lock();
382✔
425

426
    for (auto it = map->begin(); it != map->end();) {
20,189,503✔
427
      const CacheValue& value = it->second;
20,189,121✔
428

429
      if ((value.qname == name || (suffixMatch && value.qname.isPartOf(name))) && (qtype == QType::ANY || qtype == value.qtype)) {
20,189,121✔
430
        it = map->erase(it);
199,999✔
431
        --shard.d_entriesCount;
199,999✔
432
        ++removed;
199,999✔
433
      }
199,999✔
434
      else {
19,989,122✔
435
        ++it;
19,989,122✔
436
      }
19,989,122✔
437
    }
20,189,121✔
438
  }
382✔
439

440
  return removed;
211✔
441
}
211✔
442

443
bool DNSDistPacketCache::isFull()
444
{
×
445
  return (getSize() >= d_settings.d_maxEntries);
×
446
}
×
447

448
uint64_t DNSDistPacketCache::getSize()
449
{
260✔
450
  uint64_t count = 0;
260✔
451

452
  for (auto& shard : d_shards) {
4,264✔
453
    count += shard.d_entriesCount;
4,264✔
454
  }
4,264✔
455

456
  return count;
260✔
457
}
260✔
458

459
uint32_t DNSDistPacketCache::getMinTTL(const char* packet, uint16_t length, bool* seenNoDataSOA)
460
{
1,161,902✔
461
  return getDNSPacketMinTTL(packet, length, seenNoDataSOA);
1,161,902✔
462
}
1,161,902✔
463

464
uint32_t DNSDistPacketCache::getKey(const DNSName::string_t& qname, size_t qnameWireLength, const PacketBuffer& packet, bool receivedOverUDP)
465
{
2,760,637✔
466
  uint32_t result = 0;
2,760,637✔
467
  /* skip the query ID */
468
  if (packet.size() < sizeof(dnsheader)) {
2,760,637!
469
    throw std::range_error("Computing packet cache key for an invalid packet size (" + std::to_string(packet.size()) + ")");
×
470
  }
×
471

472
  result = burtle(&packet.at(2), sizeof(dnsheader) - 2, result);
2,760,637✔
473
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
474
  result = burtleCI(reinterpret_cast<const unsigned char*>(qname.c_str()), qname.length(), result);
2,760,637✔
475
  if (packet.size() < sizeof(dnsheader) + qnameWireLength) {
2,760,637!
476
    throw std::range_error("Computing packet cache key for an invalid packet (" + std::to_string(packet.size()) + " < " + std::to_string(sizeof(dnsheader) + qnameWireLength) + ")");
×
477
  }
×
478
  if (packet.size() > ((sizeof(dnsheader) + qnameWireLength))) {
2,774,505✔
479
    if (!d_settings.d_optionsToSkip.empty() || !d_settings.d_payloadRanks.empty()) {
2,776,303!
480
      /* skip EDNS options if any */
481
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
482
      result = PacketCache::hashAfterQname(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()), result, sizeof(dnsheader) + qnameWireLength, d_settings.d_optionsToSkip, d_settings.d_payloadRanks);
2,776,275✔
483
    }
2,776,275✔
484
    else {
4,294,967,322✔
485
      result = burtle(&packet.at(sizeof(dnsheader) + qnameWireLength), packet.size() - (sizeof(dnsheader) + qnameWireLength), result);
4,294,967,322✔
486
    }
4,294,967,322✔
487
  }
2,773,882✔
488
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
489
  result = burtle(reinterpret_cast<const unsigned char*>(&receivedOverUDP), sizeof(receivedOverUDP), result);
2,760,637✔
490
  return result;
2,760,637✔
491
}
2,760,637✔
492

493
uint32_t DNSDistPacketCache::getShardIndex(uint32_t key) const
494
{
3,946,297✔
495
  return key % d_settings.d_shardCount;
3,946,297✔
496
}
3,946,297✔
497

498
string DNSDistPacketCache::toString()
499
{
×
500
  return std::to_string(getSize()) + "/" + std::to_string(d_settings.d_maxEntries);
×
501
}
×
502

503
uint64_t DNSDistPacketCache::getEntriesCount()
504
{
212✔
505
  return getSize();
212✔
506
}
212✔
507

508
uint64_t DNSDistPacketCache::dump(int fileDesc, bool rawResponse)
509
{
×
510
  auto fileDescDuplicated = dup(fileDesc);
×
511
  if (fileDescDuplicated < 0) {
×
512
    return 0;
×
513
  }
×
514
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDescDuplicated, "w"));
×
515
  if (filePtr == nullptr) {
×
516
    return 0;
×
517
  }
×
518

519
  fprintf(filePtr.get(), "; dnsdist's packet cache dump follows\n;\n");
×
520

521
  uint64_t count = 0;
×
522
  time_t now = time(nullptr);
×
523
  for (auto& shard : d_shards) {
×
524
    auto map = shard.d_map.read_lock();
×
525

526
    for (const auto& entry : *map) {
×
527
      const CacheValue& value = entry.second;
×
528
      count++;
×
529

530
      try {
×
531
        uint8_t rcode = 0;
×
532
        if (value.len >= sizeof(dnsheader)) {
×
533
          dnsheader dnsHeader{};
×
534
          memcpy(&dnsHeader, value.value.data(), sizeof(dnsheader));
×
535
          rcode = dnsHeader.rcode;
×
536
        }
×
537

538
        fprintf(filePtr.get(), "%s %" PRId64 " %s %s ; ecs %s, rcode %" PRIu8 ", key %" PRIu32 ", length %" PRIu16 ", received over UDP %d, added %" PRId64 ", dnssecOK %d, raw query flags %" PRIu16, value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QClass(value.qclass).toString().c_str(), QType(value.qtype).toString().c_str(), value.subnet ? value.subnet.get().toString().c_str() : "empty", rcode, entry.first, value.len, value.receivedOverUDP ? 1 : 0, static_cast<int64_t>(value.added), value.dnssecOK ? 1 : 0, value.queryFlags);
×
539

540
        if (rawResponse) {
×
541
          std::string rawDataResponse = Base64Encode(value.value);
×
542
          fprintf(filePtr.get(), ", base64response %s", rawDataResponse.c_str());
×
543
        }
×
544
        fprintf(filePtr.get(), "\n");
×
545
      }
×
546
      catch (...) {
×
547
        fprintf(filePtr.get(), "; error printing '%s'\n", value.qname.empty() ? "EMPTY" : value.qname.toString().c_str());
×
548
      }
×
549
    }
×
550
  }
×
551

552
  return count;
×
553
}
×
554

555
std::set<DNSName> DNSDistPacketCache::getDomainsContainingRecords(const ComboAddress& addr)
556
{
20✔
557
  std::set<DNSName> domains;
20✔
558

559
  for (auto& shard : d_shards) {
20✔
560
    auto map = shard.d_map.read_lock();
20✔
561

562
    for (const auto& entry : *map) {
84✔
563
      const CacheValue& value = entry.second;
84✔
564

565
      try {
84✔
566
        if (value.len < sizeof(dnsheader)) {
84!
567
          continue;
×
568
        }
×
569

570
        dnsheader_aligned dnsHeader(value.value.data());
84✔
571
        if (dnsHeader->rcode != RCode::NoError || (dnsHeader->ancount == 0 && dnsHeader->nscount == 0 && dnsHeader->arcount == 0)) {
84!
572
          continue;
×
573
        }
×
574

575
        bool found = false;
84✔
576
        bool valid = visitDNSPacket(value.value, [addr, &found](uint8_t /* section */, uint16_t qclass, uint16_t qtype, uint32_t /* ttl */, uint16_t rdatalength, const char* rdata) {
148✔
577
          if (qtype == QType::A && qclass == QClass::IN && addr.isIPv4() && rdatalength == 4 && rdata != nullptr) {
148!
578
            ComboAddress parsed;
50✔
579
            parsed.sin4.sin_family = AF_INET;
50✔
580
            memcpy(&parsed.sin4.sin_addr.s_addr, rdata, rdatalength);
50✔
581
            if (parsed == addr) {
50✔
582
              found = true;
12✔
583
              return true;
12✔
584
            }
12✔
585
          }
50✔
586
          else if (qtype == QType::AAAA && qclass == QClass::IN && addr.isIPv6() && rdatalength == 16 && rdata != nullptr) {
98!
587
            ComboAddress parsed;
20✔
588
            parsed.sin6.sin6_family = AF_INET6;
20✔
589
            memcpy(&parsed.sin6.sin6_addr.s6_addr, rdata, rdatalength);
20✔
590
            if (parsed == addr) {
20✔
591
              found = true;
8✔
592
              return true;
8✔
593
            }
8✔
594
          }
20✔
595

596
          return false;
128✔
597
        });
148✔
598

599
        if (valid && found) {
84!
600
          domains.insert(value.qname);
20✔
601
        }
20✔
602
      }
84✔
603
      catch (...) {
84✔
604
        continue;
×
605
      }
×
606
    }
84✔
607
  }
20✔
608

609
  return domains;
20✔
610
}
20✔
611

612
std::set<ComboAddress> DNSDistPacketCache::getRecordsForDomain(const DNSName& domain)
613
{
16✔
614
  std::set<ComboAddress> addresses;
16✔
615

616
  for (auto& shard : d_shards) {
16✔
617
    auto map = shard.d_map.read_lock();
16✔
618

619
    for (const auto& entry : *map) {
64✔
620
      const CacheValue& value = entry.second;
64✔
621

622
      try {
64✔
623
        if (value.qname != domain) {
64✔
624
          continue;
52✔
625
        }
52✔
626

627
        if (value.len < sizeof(dnsheader)) {
12!
628
          continue;
×
629
        }
×
630

631
        dnsheader_aligned dnsHeader(value.value.data());
12✔
632
        if (dnsHeader->rcode != RCode::NoError || (dnsHeader->ancount == 0 && dnsHeader->nscount == 0 && dnsHeader->arcount == 0)) {
12!
633
          continue;
×
634
        }
×
635

636
        visitDNSPacket(value.value, [&addresses](uint8_t /* section */, uint16_t qclass, uint16_t qtype, uint32_t /* ttl */, uint16_t rdatalength, const char* rdata) {
22✔
637
          if (qtype == QType::A && qclass == QClass::IN && rdatalength == 4 && rdata != nullptr) {
22!
638
            ComboAddress parsed;
12✔
639
            parsed.sin4.sin_family = AF_INET;
12✔
640
            memcpy(&parsed.sin4.sin_addr.s_addr, rdata, rdatalength);
12✔
641
            addresses.insert(parsed);
12✔
642
          }
12✔
643
          else if (qtype == QType::AAAA && qclass == QClass::IN && rdatalength == 16 && rdata != nullptr) {
10!
644
            ComboAddress parsed;
8✔
645
            parsed.sin6.sin6_family = AF_INET6;
8✔
646
            memcpy(&parsed.sin6.sin6_addr.s6_addr, rdata, rdatalength);
8✔
647
            addresses.insert(parsed);
8✔
648
          }
8✔
649

650
          return false;
22✔
651
        });
22✔
652
      }
12✔
653
      catch (...) {
64✔
654
        continue;
×
655
      }
×
656
    }
64✔
657
  }
16✔
658

659
  return addresses;
16✔
660
}
16✔
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