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

PowerDNS / pdns / 19741624072

27 Nov 2025 03:45PM UTC coverage: 73.086% (+0.02%) from 73.065%
19741624072

Pull #16570

github

web-flow
Merge 08a2cdb1d into f94a3f63f
Pull Request #16570: rec: rewrite all unwrap calls in web.rs

38523 of 63408 branches covered (60.75%)

Branch coverage included in aggregate %.

128044 of 164496 relevant lines covered (77.84%)

6531485.83 hits per line

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

81.89
/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
DNSDistPacketCache::DNSDistPacketCache(CacheSettings settings) :
34
  d_settings(std::move(settings))
202✔
35
{
202✔
36
  if (d_settings.d_maxEntries == 0) {
202!
37
    throw std::runtime_error("Trying to create a 0-sized packet-cache");
×
38
  }
×
39

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

44
  d_shards.resize(d_settings.d_shardCount);
202✔
45

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

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

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

60
  if (res == 0) {
62✔
61
    size_t ecsOptionStartPosition = 0;
44✔
62
    size_t ecsOptionSize = 0;
44✔
63

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

67
    if (res == 0 && ecsOptionSize > (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) {
44!
68

69
      EDNSSubnetOpts eso;
33✔
70
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
71
      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)) {
33!
72
        subnet = eso.getSource();
33✔
73
        return true;
33✔
74
      }
33✔
75
    }
33✔
76
  }
44✔
77

78
  return false;
29✔
79
}
62✔
80

81
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
82
{
2,793,528✔
83
  if (cachedValue.queryFlags != queryFlags || cachedValue.dnssecOK != dnssecOK || cachedValue.receivedOverUDP != receivedOverUDP || cachedValue.qtype != qtype || cachedValue.qclass != qclass || cachedValue.qname != qname) {
2,800,235✔
84
    return false;
414✔
85
  }
414✔
86

87
  if (d_settings.d_parseECS && cachedValue.subnet != subnet) {
2,793,114✔
88
    return false;
3✔
89
  }
3✔
90

91
  return true;
2,793,111✔
92
}
2,793,114✔
93

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

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

105
  if (result) {
1,604,294✔
106
    return true;
1,604,192✔
107
  }
1,604,192✔
108

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

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

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

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

128
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)
129
{
2,053,676✔
130
  if (response.size() < sizeof(dnsheader) || response.size() > getMaximumEntrySize()) {
2,087,270✔
131
    return;
6✔
132
  }
6✔
133

134
  if (qtype == QType::AXFR || qtype == QType::IXFR) {
2,085,838✔
135
    return;
16✔
136
  }
16✔
137

138
  uint32_t minTTL{0};
2,053,654✔
139

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

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

165
    if (rcode == RCode::NXDomain || (rcode == RCode::NoError && seenAuthSOA)) {
2,089,524✔
166
      minTTL = std::min(minTTL, d_settings.d_maxNegativeTTL);
13✔
167
    }
13✔
168
    else if (minTTL > d_settings.d_maxTTL) {
2,053,609✔
169
      minTTL = d_settings.d_maxTTL;
2✔
170
    }
2✔
171

172
    if (minTTL < d_settings.d_minTTL) {
2,053,622✔
173
      ++d_ttlTooShorts;
4✔
174
      return;
4✔
175
    }
4✔
176
  }
2,053,622✔
177

178
  uint32_t shardIndex = getShardIndex(key);
2,053,639✔
179

180
  if (d_shards.at(shardIndex).d_entriesCount >= (d_settings.d_maxEntries / d_settings.d_shardCount)) {
2,053,639✔
181
    return;
2✔
182
  }
2✔
183

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

199
  auto& shard = d_shards.at(shardIndex);
2,053,637✔
200

201
  bool inserted = false;
2,053,637✔
202
  if (d_settings.d_deferrableInsertLock) {
2,083,380✔
203
    auto lock = shard.d_map.try_write_lock();
2,083,033✔
204

205
    if (!lock.owns_lock()) {
2,083,033✔
206
      ++d_deferredInserts;
491,217✔
207
      return;
491,217✔
208
    }
491,217✔
209
    inserted = insertLocked(*lock, key, newValue);
1,591,816✔
210
  }
1,591,816✔
211
  else {
4,294,967,294✔
212
    auto lock = shard.d_map.write_lock();
4,294,967,294✔
213

214
    inserted = insertLocked(*lock, key, newValue);
4,294,967,294✔
215
  }
4,294,967,294✔
216
  if (inserted) {
1,604,203✔
217
    ++shard.d_entriesCount;
1,604,192✔
218
  }
1,604,192✔
219
}
1,562,420✔
220

221
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)
222
{
5,314,171✔
223
  if (dnsQuestion.ids.qtype == QType::AXFR || dnsQuestion.ids.qtype == QType::IXFR) {
5,395,867✔
224
    ++d_misses;
22✔
225
    return false;
22✔
226
  }
22✔
227

228
  const auto& dnsQName = dnsQuestion.ids.qname.getStorage();
5,314,149✔
229
  uint32_t key = getKey(dnsQName, dnsQuestion.ids.qname.wirelength(), dnsQuestion.getData(), receivedOverUDP);
5,314,149✔
230

231
  if (keyOut != nullptr) {
5,371,211✔
232
    *keyOut = key;
5,357,382✔
233
  }
5,357,382✔
234

235
  if (d_settings.d_parseECS) {
5,314,149✔
236
    getClientSubnet(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), subnet);
62✔
237
  }
62✔
238

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

252
    auto mapIt = map->find(key);
4,993,961✔
253
    if (mapIt == map->end()) {
4,993,961✔
254
      if (recordMiss) {
2,245,788✔
255
        ++d_misses;
2,245,684✔
256
      }
2,245,684✔
257
      return false;
2,245,145✔
258
    }
2,245,145✔
259

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

271
    if (value.len < sizeof(dnsheader)) {
2,748,804!
272
      return false;
×
273
    }
×
274

275
    /* check for collision */
276
    if (!cachedValueMatches(value, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.ids.qclass, receivedOverUDP, dnssecOK, subnet)) {
2,748,804✔
277
      ++d_lookupCollisions;
326✔
278
      return false;
326✔
279
    }
326✔
280

281
    if (!truncatedOK) {
2,748,478✔
282
      dnsheader_aligned dh_aligned(value.value.data());
37✔
283
      if (dh_aligned->tc != 0) {
37✔
284
        return false;
3✔
285
      }
3✔
286
    }
37✔
287

288
    response.resize(value.len);
2,748,475✔
289
    memcpy(&response.at(0), &queryId, sizeof(queryId));
2,748,475✔
290
    memcpy(&response.at(sizeof(queryId)), &value.value.at(sizeof(queryId)), sizeof(dnsheader) - sizeof(queryId));
2,748,475✔
291

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

298
    const size_t dnsQNameLen = dnsQName.length();
2,748,474✔
299
    if (value.len < (sizeof(dnsheader) + dnsQNameLen)) {
2,748,474!
300
      return false;
×
301
    }
×
302

303
    memcpy(&response.at(sizeof(dnsheader)), dnsQName.c_str(), dnsQNameLen);
2,748,474✔
304
    if (value.len > (sizeof(dnsheader) + dnsQNameLen)) {
2,814,879✔
305
      memcpy(&response.at(sizeof(dnsheader) + dnsQNameLen), &value.value.at(sizeof(dnsheader) + dnsQNameLen), value.len - (sizeof(dnsheader) + dnsQNameLen));
2,796,899✔
306
    }
2,796,899✔
307

308
    if (!stale) {
2,818,867✔
309
      age = now - value.added;
2,800,899✔
310
    }
2,800,899✔
311
    else {
2,147,501,615✔
312
      age = (value.validity - value.added) - d_settings.d_staleTTL;
2,147,501,615✔
313
      dnsQuestion.ids.staleCacheHit = true;
2,147,501,615✔
314
    }
2,147,501,615✔
315
  }
2,748,474✔
316

317
  if (!d_settings.d_dontAge && !skipAging) {
2,748,474✔
318
    if (!stale) {
1,601,758✔
319
      // coverity[store_truncates_time_t]
320
      dnsheader_aligned dh_aligned(response.data());
1,601,734✔
321
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
322
      ageDNSPacket(reinterpret_cast<char*>(response.data()), response.size(), age, dh_aligned);
1,601,734✔
323
    }
1,601,734✔
324
    else {
4,294,967,298✔
325
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
326
      editDNSPacketTTL(reinterpret_cast<char*>(response.data()), response.size(),
4,294,967,298✔
327
                       [staleTTL = d_settings.d_staleTTL](uint8_t /* section */, uint16_t /* class_ */, uint16_t /* type */, uint32_t /* ttl */) { return staleTTL; });
4,294,967,298✔
328
    }
4,294,967,298✔
329
  }
1,601,068✔
330

331
  if (d_settings.d_shuffle) {
2,748,474✔
332
    dnsheader_aligned dh_aligned(response.data());
899,976✔
333
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
334
    shuffleDNSPacket(reinterpret_cast<char*>(response.data()), response.size(), dh_aligned);
899,976✔
335
  }
899,976✔
336

337
  ++d_hits;
2,748,474✔
338
  return true;
2,748,474✔
339
}
2,748,474✔
340

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

350
  size_t removed = 0;
31✔
351

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

359
    size_t toRemove = map->size() - maxPerShard;
98✔
360

361
    for (auto it = map->begin(); toRemove > 0 && it != map->end();) {
1,199,458✔
362
      const CacheValue& value = it->second;
1,199,360✔
363

364
      if (value.validity <= now) {
1,199,360✔
365
        it = map->erase(it);
299,992✔
366
        --toRemove;
299,992✔
367
        --shard.d_entriesCount;
299,992✔
368
        ++removed;
299,992✔
369
      }
299,992✔
370
      else {
899,368✔
371
        ++it;
899,368✔
372
      }
899,368✔
373
    }
1,199,360✔
374
  }
98✔
375

376
  return removed;
31✔
377
}
31✔
378

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

388
  size_t removed = 0;
1✔
389

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

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

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

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

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

415
  return removed;
1✔
416
}
1✔
417

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

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

425
    for (auto it = map->begin(); it != map->end();) {
60,568,091✔
426
      const CacheValue& value = it->second;
60,567,305✔
427

428
      if ((value.qname == name || (suffixMatch && value.qname.isPartOf(name))) && (qtype == QType::ANY || qtype == value.qtype)) {
60,567,305✔
429
        it = map->erase(it);
599,983✔
430
        --shard.d_entriesCount;
599,983✔
431
        ++removed;
599,983✔
432
      }
599,983✔
433
      else {
59,967,322✔
434
        ++it;
59,967,322✔
435
      }
59,967,322✔
436
    }
60,567,305✔
437
  }
786✔
438

439
  return removed;
615✔
440
}
615✔
441

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

447
uint64_t DNSDistPacketCache::getSize()
448
{
331✔
449
  uint64_t count = 0;
331✔
450

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

455
  return count;
331✔
456
}
331✔
457

458
uint32_t DNSDistPacketCache::getMinTTL(const char* packet, uint16_t length, bool* seenNoDataSOA)
459
{
2,079,451✔
460
  return getDNSPacketMinTTL(packet, length, seenNoDataSOA);
2,079,451✔
461
}
2,079,451✔
462

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

471
  result = burtle(&packet.at(2), sizeof(dnsheader) - 2, result);
5,264,399✔
472
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
473
  result = burtleCI(reinterpret_cast<const unsigned char*>(qname.c_str()), qname.length(), result);
5,264,399✔
474
  if (packet.size() < sizeof(dnsheader) + qnameWireLength) {
5,264,399!
475
    throw std::range_error("Computing packet cache key for an invalid packet (" + std::to_string(packet.size()) + " < " + std::to_string(sizeof(dnsheader) + qnameWireLength) + ")");
×
476
  }
×
477
  if (packet.size() > ((sizeof(dnsheader) + qnameWireLength))) {
5,375,272✔
478
    if (!d_settings.d_optionsToSkip.empty() || !d_settings.d_payloadRanks.empty()) {
5,381,672!
479
      /* skip EDNS options if any */
480
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
481
      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);
5,381,672✔
482
    }
5,381,672✔
483
    else {
4,294,967,294✔
484
      result = burtle(&packet.at(sizeof(dnsheader) + qnameWireLength), packet.size() - (sizeof(dnsheader) + qnameWireLength), result);
4,294,967,294✔
485
    }
4,294,967,294✔
486
  }
5,375,272✔
487
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
488
  result = burtle(reinterpret_cast<const unsigned char*>(&receivedOverUDP), sizeof(receivedOverUDP), result);
5,264,399✔
489
  return result;
5,264,399✔
490
}
5,264,399✔
491

492
uint32_t DNSDistPacketCache::getShardIndex(uint32_t key) const
493
{
7,352,104✔
494
  return key % d_settings.d_shardCount;
7,352,104✔
495
}
7,352,104✔
496

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

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

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

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

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

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

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

537
        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);
×
538

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

551
  return count;
×
552
}
×
553

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

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

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

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

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

574
        bool found = false;
126✔
575
        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) {
222✔
576
          if (qtype == QType::A && qclass == QClass::IN && addr.isIPv4() && rdatalength == 4 && rdata != nullptr) {
222!
577
            ComboAddress parsed;
75✔
578
            parsed.sin4.sin_family = AF_INET;
75✔
579
            memcpy(&parsed.sin4.sin_addr.s_addr, rdata, rdatalength);
75✔
580
            if (parsed == addr) {
75✔
581
              found = true;
18✔
582
              return true;
18✔
583
            }
18✔
584
          }
75✔
585
          else if (qtype == QType::AAAA && qclass == QClass::IN && addr.isIPv6() && rdatalength == 16 && rdata != nullptr) {
147!
586
            ComboAddress parsed;
30✔
587
            parsed.sin6.sin6_family = AF_INET6;
30✔
588
            memcpy(&parsed.sin6.sin6_addr.s6_addr, rdata, rdatalength);
30✔
589
            if (parsed == addr) {
30✔
590
              found = true;
12✔
591
              return true;
12✔
592
            }
12✔
593
          }
30✔
594

595
          return false;
192✔
596
        });
222✔
597

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

608
  return domains;
30✔
609
}
30✔
610

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

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

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

621
      try {
96✔
622
        if (value.qname != domain) {
96✔
623
          continue;
78✔
624
        }
78✔
625

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

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

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

649
          return false;
33✔
650
        });
33✔
651
      }
18✔
652
      catch (...) {
96✔
653
        continue;
×
654
      }
×
655
    }
96✔
656
  }
24✔
657

658
  return addresses;
24✔
659
}
24✔
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