Coverage Report

Created: 2023-09-25 06:58

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