Coverage Report

Created: 2025-09-05 06:36

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