Coverage Report

Created: 2026-08-13 06:11

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