Coverage Report

Created: 2025-12-31 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dnsdist-cache.cc
Line
Count
Source
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
2.29k
  d_settings(std::move(settings))
35
2.29k
{
36
2.29k
  if (d_settings.d_maxEntries == 0) {
37
0
    throw std::runtime_error("Trying to create a 0-sized packet-cache");
38
0
  }
39
40
2.29k
  if (d_settings.d_shardCount == 0) {
41
0
    d_settings.d_shardCount = 1;
42
0
  }
43
44
2.29k
  d_shards.resize(d_settings.d_shardCount);
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
2.29k
  for (auto& shard : d_shards) {
49
2.29k
    shard.setSize((d_settings.d_maxEntries / d_settings.d_shardCount) + 1);
50
2.29k
  }
51
2.29k
}
52
53
bool DNSDistPacketCache::getClientSubnet(const PacketBuffer& packet, size_t qnameWireLength, std::optional<Netmask>& subnet)
54
918
{
55
918
  uint16_t optRDPosition = 0;
56
918
  size_t remaining = 0;
57
58
918
  int res = dnsdist::getEDNSOptionsStart(packet, qnameWireLength, &optRDPosition, &remaining);
59
60
918
  if (res == 0) {
61
502
    size_t ecsOptionStartPosition = 0;
62
502
    size_t ecsOptionSize = 0;
63
64
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
65
502
    res = getEDNSOption(reinterpret_cast<const char*>(&packet.at(optRDPosition)), remaining, EDNSOptionCode::ECS, &ecsOptionStartPosition, &ecsOptionSize);
66
67
502
    if (res == 0 && ecsOptionSize > (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) {
68
69
206
      EDNSSubnetOpts eso;
70
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
71
206
      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)) {
72
62
        subnet = eso.getSource();
73
62
        return true;
74
62
      }
75
206
    }
76
502
  }
77
78
856
  return false;
79
918
}
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 std::optional<Netmask>& subnet) const
82
0
{
83
0
  if (cachedValue.queryFlags != queryFlags || cachedValue.dnssecOK != dnssecOK || cachedValue.receivedOverUDP != receivedOverUDP || cachedValue.qtype != qtype || cachedValue.qclass != qclass || cachedValue.qname != qname) {
84
0
    return false;
85
0
  }
86
87
0
  if (d_settings.d_parseECS && cachedValue.subnet != subnet) {
88
0
    return false;
89
0
  }
90
91
0
  return true;
92
0
}
93
94
bool DNSDistPacketCache::insertLocked(std::unordered_map<uint32_t, CacheValue>& map, uint32_t key, CacheValue& newValue)
95
0
{
96
  /* check again now that we hold the lock to prevent a race */
97
0
  if (map.size() >= (d_settings.d_maxEntries / d_settings.d_shardCount)) {
98
0
    return false;
99
0
  }
100
101
0
  std::unordered_map<uint32_t, CacheValue>::iterator mapIt;
102
0
  bool result{false};
103
0
  std::tie(mapIt, result) = map.insert({key, newValue});
104
105
0
  if (result) {
106
0
    return true;
107
0
  }
108
109
  /* in case of collision, don't override the existing entry
110
     except if it has expired */
111
0
  CacheValue& value = mapIt->second;
112
0
  bool wasExpired = value.validity <= newValue.added;
113
114
0
  if (!wasExpired && !cachedValueMatches(value, newValue.queryFlags, newValue.qname, newValue.qtype, newValue.qclass, newValue.receivedOverUDP, newValue.dnssecOK, newValue.subnet)) {
115
0
    ++d_insertCollisions;
116
0
    return false;
117
0
  }
118
119
  /* if the existing entry had a longer TTD, keep it */
120
0
  if (newValue.validity <= value.validity) {
121
0
    return false;
122
0
  }
123
124
0
  value = newValue;
125
0
  return false;
126
0
}
127
128
void DNSDistPacketCache::insert(uint32_t key, const std::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, std::optional<uint32_t> tempFailureTTL)
129
0
{
130
0
  if (response.size() < sizeof(dnsheader) || response.size() > getMaximumEntrySize()) {
131
0
    return;
132
0
  }
133
134
0
  if (qtype == QType::AXFR || qtype == QType::IXFR) {
135
0
    return;
136
0
  }
137
138
0
  uint32_t minTTL{0};
139
140
0
  if (rcode == RCode::ServFail || rcode == RCode::Refused) {
141
0
    minTTL = tempFailureTTL == std::nullopt ? d_settings.d_tempFailureTTL : *tempFailureTTL;
142
0
    if (minTTL == 0) {
143
0
      return;
144
0
    }
145
0
  }
146
0
  else {
147
0
    bool seenAuthSOA = false;
148
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
149
0
    minTTL = getMinTTL(reinterpret_cast<const char*>(response.data()), response.size(), &seenAuthSOA);
150
151
0
    if (minTTL == std::numeric_limits<uint32_t>::max()) {
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
0
      if (d_settings.d_truncatedTTL == 0) {
156
0
        return;
157
0
      }
158
0
      dnsheader_aligned dh_aligned(response.data());
159
0
      if (dh_aligned->tc == 0) {
160
0
        return;
161
0
      }
162
0
      minTTL = d_settings.d_truncatedTTL;
163
0
    }
164
165
0
    if (rcode == RCode::NXDomain || (rcode == RCode::NoError && seenAuthSOA)) {
166
0
      minTTL = std::min(minTTL, d_settings.d_maxNegativeTTL);
167
0
    }
168
0
    else if (minTTL > d_settings.d_maxTTL) {
169
0
      minTTL = d_settings.d_maxTTL;
170
0
    }
171
172
0
    if (minTTL < d_settings.d_minTTL) {
173
0
      ++d_ttlTooShorts;
174
0
      return;
175
0
    }
176
0
  }
177
178
0
  uint32_t shardIndex = getShardIndex(key);
179
180
0
  if (d_shards.at(shardIndex).d_entriesCount >= (d_settings.d_maxEntries / d_settings.d_shardCount)) {
181
0
    return;
182
0
  }
183
184
0
  const time_t now = time(nullptr);
185
0
  time_t newValidity = now + minTTL;
186
0
  CacheValue newValue;
187
0
  newValue.qname = qname;
188
0
  newValue.qtype = qtype;
189
0
  newValue.qclass = qclass;
190
0
  newValue.queryFlags = queryFlags;
191
0
  newValue.len = response.size();
192
0
  newValue.validity = newValidity;
193
0
  newValue.added = now;
194
0
  newValue.receivedOverUDP = receivedOverUDP;
195
0
  newValue.dnssecOK = dnssecOK;
196
0
  newValue.value = std::string(response.begin(), response.end());
197
0
  newValue.subnet = subnet;
198
199
0
  auto& shard = d_shards.at(shardIndex);
200
201
0
  bool inserted = false;
202
0
  if (d_settings.d_deferrableInsertLock) {
203
0
    auto lock = shard.d_map.try_write_lock();
204
205
0
    if (!lock.owns_lock()) {
206
0
      ++d_deferredInserts;
207
0
      return;
208
0
    }
209
0
    inserted = insertLocked(*lock, key, newValue);
210
0
  }
211
0
  else {
212
0
    auto lock = shard.d_map.write_lock();
213
214
0
    inserted = insertLocked(*lock, key, newValue);
215
0
  }
216
0
  if (inserted) {
217
0
    ++shard.d_entriesCount;
218
0
  }
219
0
}
220
221
bool DNSDistPacketCache::get(DNSQuestion& dnsQuestion, uint16_t queryId, uint32_t* keyOut, std::optional<Netmask>& subnet, bool dnssecOK, bool receivedOverUDP, uint32_t allowExpired, bool skipAging, bool truncatedOK, bool recordMiss)
222
0
{
223
0
  if (dnsQuestion.ids.qtype == QType::AXFR || dnsQuestion.ids.qtype == QType::IXFR) {
224
0
    ++d_misses;
225
0
    return false;
226
0
  }
227
228
0
  const auto& dnsQName = dnsQuestion.ids.qname.getStorage();
229
0
  uint32_t key = getKey(dnsQName, dnsQuestion.ids.qname.wirelength(), dnsQuestion.getData(), receivedOverUDP);
230
231
0
  if (keyOut != nullptr) {
232
0
    *keyOut = key;
233
0
  }
234
235
0
  if (d_settings.d_parseECS) {
236
0
    getClientSubnet(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), subnet);
237
0
  }
238
239
0
  uint32_t shardIndex = getShardIndex(key);
240
0
  time_t now = time(nullptr);
241
0
  time_t age{0};
242
0
  bool stale = false;
243
0
  auto& response = dnsQuestion.getMutableData();
244
0
  auto& shard = d_shards.at(shardIndex);
245
0
  {
246
0
    auto map = shard.d_map.try_read_lock();
247
0
    if (!map.owns_lock()) {
248
0
      ++d_deferredLookups;
249
0
      return false;
250
0
    }
251
252
0
    auto mapIt = map->find(key);
253
0
    if (mapIt == map->end()) {
254
0
      if (recordMiss) {
255
0
        ++d_misses;
256
0
      }
257
0
      return false;
258
0
    }
259
260
0
    const CacheValue& value = mapIt->second;
261
0
    if (value.validity <= now) {
262
0
      if ((now - value.validity) >= static_cast<time_t>(allowExpired)) {
263
0
        if (recordMiss) {
264
0
          ++d_misses;
265
0
        }
266
0
        return false;
267
0
      }
268
0
      stale = true;
269
0
    }
270
271
0
    if (value.len < sizeof(dnsheader)) {
272
0
      return false;
273
0
    }
274
275
    /* check for collision */
276
0
    if (!cachedValueMatches(value, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.ids.qclass, receivedOverUDP, dnssecOK, subnet)) {
277
0
      ++d_lookupCollisions;
278
0
      return false;
279
0
    }
280
281
0
    if (!truncatedOK) {
282
0
      dnsheader_aligned dh_aligned(value.value.data());
283
0
      if (dh_aligned->tc != 0) {
284
0
        return false;
285
0
      }
286
0
    }
287
288
0
    response.resize(value.len);
289
0
    memcpy(&response.at(0), &queryId, sizeof(queryId));
290
0
    memcpy(&response.at(sizeof(queryId)), &value.value.at(sizeof(queryId)), sizeof(dnsheader) - sizeof(queryId));
291
292
0
    if (value.len == sizeof(dnsheader)) {
293
      /* DNS header only, our work here is done */
294
0
      ++d_hits;
295
0
      return true;
296
0
    }
297
298
0
    const size_t dnsQNameLen = dnsQName.length();
299
0
    if (value.len < (sizeof(dnsheader) + dnsQNameLen)) {
300
0
      return false;
301
0
    }
302
303
0
    memcpy(&response.at(sizeof(dnsheader)), dnsQName.c_str(), dnsQNameLen);
304
0
    if (value.len > (sizeof(dnsheader) + dnsQNameLen)) {
305
0
      memcpy(&response.at(sizeof(dnsheader) + dnsQNameLen), &value.value.at(sizeof(dnsheader) + dnsQNameLen), value.len - (sizeof(dnsheader) + dnsQNameLen));
306
0
    }
307
308
0
    if (!stale) {
309
0
      age = now - value.added;
310
0
    }
311
0
    else {
312
0
      age = (value.validity - value.added) - d_settings.d_staleTTL;
313
0
      dnsQuestion.ids.staleCacheHit = true;
314
0
    }
315
0
  }
316
317
0
  if (!d_settings.d_dontAge && !skipAging) {
318
0
    if (!stale) {
319
      // coverity[store_truncates_time_t]
320
0
      dnsheader_aligned dh_aligned(response.data());
321
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
322
0
      ageDNSPacket(reinterpret_cast<char*>(response.data()), response.size(), age, dh_aligned);
323
0
    }
324
0
    else {
325
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
326
0
      editDNSPacketTTL(reinterpret_cast<char*>(response.data()), response.size(),
327
0
                       [staleTTL = d_settings.d_staleTTL](uint8_t /* section */, uint16_t /* class_ */, uint16_t /* type */, uint32_t /* ttl */) { return staleTTL; });
328
0
    }
329
0
  }
330
331
0
  if (d_settings.d_shuffle) {
332
0
    dnsheader_aligned dh_aligned(response.data());
333
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
334
0
    shuffleDNSPacket(reinterpret_cast<char*>(response.data()), response.size(), dh_aligned);
335
0
  }
336
337
0
  ++d_hits;
338
0
  return true;
339
0
}
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
0
{
348
0
  const size_t maxPerShard = upTo / d_settings.d_shardCount;
349
350
0
  size_t removed = 0;
351
352
0
  ++d_cleanupCount;
353
0
  for (auto& shard : d_shards) {
354
0
    auto map = shard.d_map.write_lock();
355
0
    if (map->size() <= maxPerShard) {
356
0
      continue;
357
0
    }
358
359
0
    size_t toRemove = map->size() - maxPerShard;
360
361
0
    for (auto it = map->begin(); toRemove > 0 && it != map->end();) {
362
0
      const CacheValue& value = it->second;
363
364
0
      if (value.validity <= now) {
365
0
        it = map->erase(it);
366
0
        --toRemove;
367
0
        --shard.d_entriesCount;
368
0
        ++removed;
369
0
      }
370
0
      else {
371
0
        ++it;
372
0
      }
373
0
    }
374
0
  }
375
376
0
  return removed;
377
0
}
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
0
{
386
0
  const size_t maxPerShard = upTo / d_settings.d_shardCount;
387
388
0
  size_t removed = 0;
389
390
0
  for (auto& shard : d_shards) {
391
0
    auto map = shard.d_map.write_lock();
392
393
0
    if (map->size() <= maxPerShard) {
394
0
      continue;
395
0
    }
396
397
0
    size_t toRemove = map->size() - maxPerShard;
398
399
0
    auto beginIt = map->begin();
400
0
    auto endIt = beginIt;
401
402
0
    if (map->size() >= toRemove) {
403
0
      std::advance(endIt, toRemove);
404
0
      map->erase(beginIt, endIt);
405
0
      shard.d_entriesCount -= toRemove;
406
0
      removed += toRemove;
407
0
    }
408
0
    else {
409
0
      removed += map->size();
410
0
      map->clear();
411
0
      shard.d_entriesCount = 0;
412
0
    }
413
0
  }
414
415
0
  return removed;
416
0
}
417
418
size_t DNSDistPacketCache::expungeByName(const DNSName& name, uint16_t qtype, bool suffixMatch)
419
0
{
420
0
  size_t removed = 0;
421
422
0
  for (auto& shard : d_shards) {
423
0
    auto map = shard.d_map.write_lock();
424
425
0
    for (auto it = map->begin(); it != map->end();) {
426
0
      const CacheValue& value = it->second;
427
428
0
      if ((value.qname == name || (suffixMatch && value.qname.isPartOf(name))) && (qtype == QType::ANY || qtype == value.qtype)) {
429
0
        it = map->erase(it);
430
0
        --shard.d_entriesCount;
431
0
        ++removed;
432
0
      }
433
0
      else {
434
0
        ++it;
435
0
      }
436
0
    }
437
0
  }
438
439
0
  return removed;
440
0
}
441
442
bool DNSDistPacketCache::isFull()
443
0
{
444
0
  return (getSize() >= d_settings.d_maxEntries);
445
0
}
446
447
uint64_t DNSDistPacketCache::getSize()
448
0
{
449
0
  uint64_t count = 0;
450
451
0
  for (auto& shard : d_shards) {
452
0
    count += shard.d_entriesCount;
453
0
  }
454
455
0
  return count;
456
0
}
457
458
uint32_t DNSDistPacketCache::getMinTTL(const char* packet, uint16_t length, bool* seenNoDataSOA)
459
0
{
460
0
  return getDNSPacketMinTTL(packet, length, seenNoDataSOA);
461
0
}
462
463
uint32_t DNSDistPacketCache::getKey(const DNSName::string_t& qname, size_t qnameWireLength, const PacketBuffer& packet, bool receivedOverUDP) const
464
1.83k
{
465
1.83k
  uint32_t result = 0;
466
  /* skip the query ID */
467
1.83k
  if (packet.size() < sizeof(dnsheader)) {
468
0
    throw std::range_error("Computing packet cache key for an invalid packet size (" + std::to_string(packet.size()) + ")");
469
0
  }
470
471
1.83k
  result = burtle(&packet.at(2), sizeof(dnsheader) - 2, result);
472
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
473
1.83k
  result = burtleCI(reinterpret_cast<const unsigned char*>(qname.c_str()), qname.length(), result);
474
1.83k
  if (packet.size() < sizeof(dnsheader) + qnameWireLength) {
475
0
    throw std::range_error("Computing packet cache key for an invalid packet (" + std::to_string(packet.size()) + " < " + std::to_string(sizeof(dnsheader) + qnameWireLength) + ")");
476
0
  }
477
1.83k
  if (packet.size() > ((sizeof(dnsheader) + qnameWireLength))) {
478
1.83k
    if (!d_settings.d_optionsToSkip.empty() || !d_settings.d_payloadRanks.empty()) {
479
      /* skip EDNS options if any */
480
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
481
918
      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);
482
918
    }
483
918
    else {
484
918
      result = burtle(&packet.at(sizeof(dnsheader) + qnameWireLength), packet.size() - (sizeof(dnsheader) + qnameWireLength), result);
485
918
    }
486
1.83k
  }
487
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
488
1.83k
  result = burtle(reinterpret_cast<const unsigned char*>(&receivedOverUDP), sizeof(receivedOverUDP), result);
489
1.83k
  return result;
490
1.83k
}
491
492
uint32_t DNSDistPacketCache::getShardIndex(uint32_t key) const
493
0
{
494
0
  return key % d_settings.d_shardCount;
495
0
}
496
497
string DNSDistPacketCache::toString()
498
0
{
499
0
  return std::to_string(getSize()) + "/" + std::to_string(d_settings.d_maxEntries);
500
0
}
501
502
uint64_t DNSDistPacketCache::getEntriesCount()
503
0
{
504
0
  return getSize();
505
0
}
506
507
uint64_t DNSDistPacketCache::dump(int fileDesc, bool rawResponse)
508
0
{
509
0
  auto fileDescDuplicated = dup(fileDesc);
510
0
  if (fileDescDuplicated < 0) {
511
0
    return 0;
512
0
  }
513
0
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDescDuplicated, "w"));
514
0
  if (filePtr == nullptr) {
515
0
    return 0;
516
0
  }
517
518
0
  fprintf(filePtr.get(), "; dnsdist's packet cache dump follows\n;\n");
519
520
0
  uint64_t count = 0;
521
0
  time_t now = time(nullptr);
522
0
  for (auto& shard : d_shards) {
523
0
    auto map = shard.d_map.read_lock();
524
525
0
    for (const auto& entry : *map) {
526
0
      const CacheValue& value = entry.second;
527
0
      count++;
528
529
0
      try {
530
0
        uint8_t rcode = 0;
531
0
        if (value.len >= sizeof(dnsheader)) {
532
0
          dnsheader dnsHeader{};
533
0
          memcpy(&dnsHeader, value.value.data(), sizeof(dnsheader));
534
0
          rcode = dnsHeader.rcode;
535
0
        }
536
537
0
        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.value().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
0
        if (rawResponse) {
540
0
          std::string rawDataResponse = Base64Encode(value.value);
541
0
          fprintf(filePtr.get(), ", base64response %s", rawDataResponse.c_str());
542
0
        }
543
0
        fprintf(filePtr.get(), "\n");
544
0
      }
545
0
      catch (...) {
546
0
        fprintf(filePtr.get(), "; error printing '%s'\n", value.qname.empty() ? "EMPTY" : value.qname.toString().c_str());
547
0
      }
548
0
    }
549
0
  }
550
551
0
  return count;
552
0
}
553
554
std::set<DNSName> DNSDistPacketCache::getDomainsContainingRecords(const ComboAddress& addr)
555
0
{
556
0
  std::set<DNSName> domains;
557
558
0
  for (auto& shard : d_shards) {
559
0
    auto map = shard.d_map.read_lock();
560
561
0
    for (const auto& entry : *map) {
562
0
      const CacheValue& value = entry.second;
563
564
0
      try {
565
0
        if (value.len < sizeof(dnsheader)) {
566
0
          continue;
567
0
        }
568
569
0
        dnsheader_aligned dnsHeader(value.value.data());
570
0
        if (dnsHeader->rcode != RCode::NoError || (dnsHeader->ancount == 0 && dnsHeader->nscount == 0 && dnsHeader->arcount == 0)) {
571
0
          continue;
572
0
        }
573
574
0
        bool found = false;
575
0
        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) {
576
0
          if (qtype == QType::A && qclass == QClass::IN && addr.isIPv4() && rdatalength == 4 && rdata != nullptr) {
577
0
            ComboAddress parsed;
578
0
            parsed.sin4.sin_family = AF_INET;
579
0
            memcpy(&parsed.sin4.sin_addr.s_addr, rdata, rdatalength);
580
0
            if (parsed == addr) {
581
0
              found = true;
582
0
              return true;
583
0
            }
584
0
          }
585
0
          else if (qtype == QType::AAAA && qclass == QClass::IN && addr.isIPv6() && rdatalength == 16 && rdata != nullptr) {
586
0
            ComboAddress parsed;
587
0
            parsed.sin6.sin6_family = AF_INET6;
588
0
            memcpy(&parsed.sin6.sin6_addr.s6_addr, rdata, rdatalength);
589
0
            if (parsed == addr) {
590
0
              found = true;
591
0
              return true;
592
0
            }
593
0
          }
594
595
0
          return false;
596
0
        });
597
598
0
        if (valid && found) {
599
0
          domains.insert(value.qname);
600
0
        }
601
0
      }
602
0
      catch (...) {
603
0
        continue;
604
0
      }
605
0
    }
606
0
  }
607
608
0
  return domains;
609
0
}
610
611
std::set<ComboAddress> DNSDistPacketCache::getRecordsForDomain(const DNSName& domain)
612
0
{
613
0
  std::set<ComboAddress> addresses;
614
615
0
  for (auto& shard : d_shards) {
616
0
    auto map = shard.d_map.read_lock();
617
618
0
    for (const auto& entry : *map) {
619
0
      const CacheValue& value = entry.second;
620
621
0
      try {
622
0
        if (value.qname != domain) {
623
0
          continue;
624
0
        }
625
626
0
        if (value.len < sizeof(dnsheader)) {
627
0
          continue;
628
0
        }
629
630
0
        dnsheader_aligned dnsHeader(value.value.data());
631
0
        if (dnsHeader->rcode != RCode::NoError || (dnsHeader->ancount == 0 && dnsHeader->nscount == 0 && dnsHeader->arcount == 0)) {
632
0
          continue;
633
0
        }
634
635
0
        visitDNSPacket(value.value, [&addresses](uint8_t /* section */, uint16_t qclass, uint16_t qtype, uint32_t /* ttl */, uint16_t rdatalength, const char* rdata) {
636
0
          if (qtype == QType::A && qclass == QClass::IN && rdatalength == 4 && rdata != nullptr) {
637
0
            ComboAddress parsed;
638
0
            parsed.sin4.sin_family = AF_INET;
639
0
            memcpy(&parsed.sin4.sin_addr.s_addr, rdata, rdatalength);
640
0
            addresses.insert(parsed);
641
0
          }
642
0
          else if (qtype == QType::AAAA && qclass == QClass::IN && rdatalength == 16 && rdata != nullptr) {
643
0
            ComboAddress parsed;
644
0
            parsed.sin6.sin6_family = AF_INET6;
645
0
            memcpy(&parsed.sin6.sin6_addr.s6_addr, rdata, rdatalength);
646
0
            addresses.insert(parsed);
647
0
          }
648
649
0
          return false;
650
0
        });
651
0
      }
652
0
      catch (...) {
653
0
        continue;
654
0
      }
655
0
    }
656
0
  }
657
658
0
  return addresses;
659
0
}