]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/negcache.hh
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / pdns / recursordist / negcache.hh
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 #pragma once
23
24 #include <vector>
25 #include <boost/multi_index_container.hpp>
26 #include <boost/multi_index/ordered_index.hpp>
27 #include <boost/multi_index/hashed_index.hpp>
28 #include <boost/multi_index/sequenced_index.hpp>
29 #include <boost/multi_index/key_extractors.hpp>
30 #include <boost/optional.hpp>
31 #include "dnsparser.hh"
32 #include "dnsname.hh"
33 #include "dns.hh"
34 #include "lock.hh"
35 #include "stat_t.hh"
36 #include "validate.hh"
37
38 using namespace ::boost::multi_index;
39
40 /* FIXME should become part of the normal cache (I think) and should become more like
41 * struct {
42 * vector<DNSRecord> records;
43 * vector<DNSRecord> signatures;
44 * } recsig_t;
45 *
46 * typedef vector<recsig_t> recordsAndSignatures;
47 */
48 typedef struct
49 {
50 vector<DNSRecord> records;
51 vector<DNSRecord> signatures;
52 } recordsAndSignatures;
53
54 class NegCache : public boost::noncopyable
55 {
56 public:
57 NegCache(size_t mapsCount = 128);
58
59 // For a description on how ServeStale works, see recursor_cache.cc, the general structure is the same.
60 // The number of times a stale cache entry is extended
61 static uint16_t s_maxServedStaleExtensions;
62 // The time a stale cache entry is extended
63 static constexpr uint32_t s_serveStaleExtensionPeriod = 30;
64
65 struct NegCacheEntry
66 {
67 recordsAndSignatures authoritySOA; // The upstream SOA record and RRSIGs
68 recordsAndSignatures DNSSECRecords; // The upstream NSEC(3) and RRSIGs
69 DNSName d_name; // The denied name
70 DNSName d_auth; // The denying name (aka auth)
71 mutable time_t d_ttd; // Timestamp when this entry should die
72 uint32_t d_orig_ttl;
73 mutable uint16_t d_servedStale{0};
74 mutable vState d_validationState{vState::Indeterminate};
75 QType d_qtype; // The denied type
76
77 bool isStale(time_t now) const
78 {
79 // We like to keep things in cache when we (potentially) should serve stale
80 if (s_maxServedStaleExtensions > 0) {
81 return d_ttd + static_cast<time_t>(s_maxServedStaleExtensions) * std::min(s_serveStaleExtensionPeriod, d_orig_ttl) < now;
82 }
83 else {
84 return d_ttd < now;
85 }
86 };
87
88 bool isEntryUsable(time_t now, bool serveStale) const
89 {
90 // When serving stale, we consider expired records
91 return d_ttd > now || serveStale || d_servedStale != 0;
92 }
93 };
94
95 void add(const NegCacheEntry& ne);
96 void updateValidationStatus(const DNSName& qname, QType qtype, vState newState, boost::optional<time_t> capTTD);
97 bool get(const DNSName& qname, QType qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch = false, bool serverStale = false, bool refresh = false);
98 bool getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& negEntry, bool serveStale, bool refresh);
99 size_t count(const DNSName& qname);
100 size_t count(const DNSName& qname, QType qtype);
101 void prune(time_t now, size_t maxEntries);
102 void clear();
103 size_t doDump(int fd, size_t maxCacheEntries, time_t now = time(nullptr));
104 size_t wipe(const DNSName& name, bool subtree = false);
105 size_t wipeTyped(const DNSName& name, QType qtype);
106 size_t size() const;
107
108 private:
109 struct CompositeKey
110 {
111 };
112 struct SequenceTag
113 {
114 };
115 typedef boost::multi_index_container<
116 NegCacheEntry,
117 indexed_by<
118 ordered_unique<tag<CompositeKey>,
119 composite_key<
120 NegCacheEntry,
121 member<NegCacheEntry, DNSName, &NegCacheEntry::d_name>,
122 member<NegCacheEntry, QType, &NegCacheEntry::d_qtype>>,
123 composite_key_compare<
124 CanonDNSNameCompare, std::less<QType>>>,
125 sequenced<tag<SequenceTag>>,
126 hashed_non_unique<tag<NegCacheEntry>,
127 member<NegCacheEntry, DNSName, &NegCacheEntry::d_name>>>>
128 negcache_t;
129
130 void updateStaleEntry(time_t now, negcache_t::iterator& entry, QType qtype);
131
132 struct MapCombo
133 {
134 MapCombo() {}
135 MapCombo(const MapCombo&) = delete;
136 MapCombo& operator=(const MapCombo&) = delete;
137 struct LockedContent
138 {
139 negcache_t d_map;
140 uint64_t d_contended_count{0};
141 uint64_t d_acquired_count{0};
142 void invalidate() {}
143 void preRemoval(const NegCacheEntry& /* entry */) {}
144 };
145
146 LockGuardedTryHolder<MapCombo::LockedContent> lock()
147 {
148 auto locked = d_content.try_lock();
149 if (!locked.owns_lock()) {
150 locked.lock();
151 ++locked->d_contended_count;
152 }
153 ++locked->d_acquired_count;
154 return locked;
155 }
156
157 [[nodiscard]] auto getEntriesCount() const
158 {
159 return d_entriesCount.load();
160 }
161
162 void incEntriesCount()
163 {
164 ++d_entriesCount;
165 }
166
167 void decEntriesCount()
168 {
169 --d_entriesCount;
170 }
171
172 void clearEntriesCount()
173 {
174 d_entriesCount = 0;
175 }
176
177 private:
178 LockGuarded<LockedContent> d_content;
179 pdns::stat_t d_entriesCount{0};
180 };
181
182 vector<MapCombo> d_maps;
183
184 MapCombo& getMap(const DNSName& qname)
185 {
186 return d_maps.at(qname.hash() % d_maps.size());
187 }
188 const MapCombo& getMap(const DNSName& qname) const
189 {
190 return d_maps.at(qname.hash() % d_maps.size());
191 }
192 };