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