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