]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/negcache.cc
updated KSK and ZSK Rollover procedures, small fixes in Algorithm Rollover procedure
[thirdparty/pdns.git] / pdns / recursordist / negcache.cc
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 "negcache.hh"
25 #include "misc.hh"
26 #include "cachecleaner.hh"
27 #include "utility.hh"
28
29 NegCache::NegCache(size_t mapsCount) :
30 d_maps(mapsCount)
31 {
32 }
33
34 size_t NegCache::size() const
35 {
36 size_t count = 0;
37 for (const auto& map : d_maps) {
38 count += map.d_entriesCount;
39 }
40 return count;
41 }
42
43 /*!
44 * Set ne to the NegCacheEntry for the last label in qname and return true if there
45 * was one.
46 *
47 * \param qname The name to look up (only the last label is used)
48 * \param now A timeval with the current time, to check if an entry is expired
49 * \param ne A NegCacheEntry that is filled when there is a cache entry
50 * \return true if ne was filled out, false otherwise
51 */
52 bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& ne)
53 {
54 // Never deny the root.
55 if (qname.isRoot())
56 return false;
57
58 // An 'ENT' QType entry, used as "whole name" in the neg-cache context.
59 static const QType qtnull(0);
60 DNSName lastLabel = qname.getLastLabel();
61
62 auto& map = getMap(lastLabel);
63 auto content = map.lock();
64
65 negcache_t::const_iterator ni = content->d_map.find(std::tie(lastLabel, qtnull));
66
67 while (ni != content->d_map.end() && ni->d_name == lastLabel && ni->d_auth.isRoot() && ni->d_qtype == qtnull) {
68 // We have something
69 if (now.tv_sec < ni->d_ttd) {
70 ne = *ni;
71 moveCacheItemToBack<SequenceTag>(content->d_map, ni);
72 return true;
73 }
74 moveCacheItemToFront<SequenceTag>(content->d_map, ni);
75 ++ni;
76 }
77 return false;
78 }
79
80 /*!
81 * Set ne to the NegCacheEntry for the qname|qtype tuple and return true
82 *
83 * \param qname The name to look up
84 * \param qtype The qtype to look up
85 * \param now A timeval with the current time, to check if an entry is expired
86 * \param ne A NegCacheEntry that is filled when there is a cache entry
87 * \return true if ne was filled out, false otherwise
88 */
89 bool NegCache::get(const DNSName& qname, const QType& qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch)
90 {
91 auto& map = getMap(qname);
92 auto content = map.lock();
93
94 const auto& idx = content->d_map.get<NegCacheEntry>();
95 auto range = idx.equal_range(qname);
96 auto ni = range.first;
97
98 while (ni != range.second) {
99 // We have an entry
100 if ((!typeMustMatch && ni->d_qtype.getCode() == 0) || ni->d_qtype == qtype) {
101 // We match the QType or the whole name is denied
102 auto firstIndexIterator = content->d_map.project<CompositeKey>(ni);
103
104 if (now.tv_sec < ni->d_ttd) {
105 // Not expired
106 ne = *ni;
107 moveCacheItemToBack<SequenceTag>(content->d_map, firstIndexIterator);
108 return true;
109 }
110 // expired
111 moveCacheItemToFront<SequenceTag>(content->d_map, firstIndexIterator);
112 }
113 ++ni;
114 }
115 return false;
116 }
117
118 /*!
119 * Places ne into the negative cache, possibly overriding an existing entry.
120 *
121 * \param ne The NegCacheEntry to add to the cache
122 */
123 void NegCache::add(const NegCacheEntry& ne)
124 {
125 bool inserted = false;
126 auto& map = getMap(ne.d_name);
127 auto content = map.lock();
128 inserted = lruReplacingInsert<SequenceTag>(content->d_map, ne);
129 if (inserted) {
130 ++map.d_entriesCount;
131 }
132 }
133
134 /*!
135 * Update the validation state of an existing entry with the provided state.
136 *
137 * \param qname The name of the entry to replace
138 * \param qtype The type of the entry to replace
139 * \param newState The new validation state
140 */
141 void NegCache::updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState, boost::optional<time_t> capTTD)
142 {
143 auto& mc = getMap(qname);
144 auto map = mc.lock();
145 auto range = map->d_map.equal_range(std::tie(qname, qtype));
146
147 if (range.first != range.second) {
148 range.first->d_validationState = newState;
149 if (capTTD) {
150 range.first->d_ttd = std::min(range.first->d_ttd, *capTTD);
151 }
152 }
153 }
154
155 /*!
156 * Returns the amount of entries in the cache
157 *
158 * \param qname The name of the entries to be counted
159 */
160 size_t NegCache::count(const DNSName& qname)
161 {
162 auto& map = getMap(qname);
163 auto content = map.lock();
164 return content->d_map.count(std::tie(qname));
165 }
166
167 /*!
168 * Returns the amount of entries in the cache for qname+qtype
169 *
170 * \param qname The name of the entries to be counted
171 * \param qtype The type of the entries to be counted
172 */
173 size_t NegCache::count(const DNSName& qname, const QType qtype)
174 {
175 auto& map = getMap(qname);
176 auto content = map.lock();
177 return content->d_map.count(std::tie(qname, qtype));
178 }
179
180 /*!
181 * Remove all entries for name from the cache. If subtree is true, wipe all names
182 * underneath it.
183 *
184 * \param name The DNSName of the entries to wipe
185 * \param subtree Should all entries under name be removed?
186 */
187 size_t NegCache::wipe(const DNSName& name, bool subtree)
188 {
189 size_t ret = 0;
190 if (subtree) {
191 for (auto& map : d_maps) {
192 auto m = map.lock();
193 for (auto i = m->d_map.lower_bound(std::tie(name)); i != m->d_map.end();) {
194 if (!i->d_name.isPartOf(name))
195 break;
196 i = m->d_map.erase(i);
197 ret++;
198 --map.d_entriesCount;
199 }
200 }
201 return ret;
202 }
203
204 auto& map = getMap(name);
205 auto content = map.lock();
206 auto range = content->d_map.equal_range(std::tie(name));
207 auto i = range.first;
208 while (i != range.second) {
209 i = content->d_map.erase(i);
210 ret++;
211 --map.d_entriesCount;
212 }
213 return ret;
214 }
215
216 /*!
217 * Clear the negative cache
218 */
219 void NegCache::clear()
220 {
221 for (auto& map : d_maps) {
222 auto m = map.lock();
223 m->d_map.clear();
224 map.d_entriesCount = 0;
225 }
226 }
227
228 /*!
229 * Perform some cleanup in the cache, removing stale entries
230 *
231 * \param maxEntries The maximum number of entries that may exist in the cache.
232 */
233 void NegCache::prune(size_t maxEntries)
234 {
235 size_t cacheSize = size();
236 pruneMutexCollectionsVector<SequenceTag>(*this, d_maps, maxEntries, cacheSize);
237 }
238
239 /*!
240 * Writes the whole negative cache to fp
241 *
242 * \param fp A pointer to an open FILE object
243 */
244 size_t NegCache::dumpToFile(FILE* fp, const struct timeval& now)
245 {
246 size_t ret = 0;
247
248 for (auto& mc : d_maps) {
249 auto m = mc.lock();
250 auto& sidx = m->d_map.get<SequenceTag>();
251 for (const NegCacheEntry& ne : sidx) {
252 ret++;
253 int64_t ttl = ne.d_ttd - now.tv_sec;
254 fprintf(fp, "%s %" PRId64 " IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), ttl, ne.d_qtype.toString().c_str(), ne.d_auth.toString().c_str(), vStateToString(ne.d_validationState).c_str());
255 for (const auto& rec : ne.authoritySOA.records) {
256 fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), ttl, DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
257 }
258 for (const auto& sig : ne.authoritySOA.signatures) {
259 fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), ttl, sig.d_content->getZoneRepresentation().c_str());
260 }
261 for (const auto& rec : ne.DNSSECRecords.records) {
262 fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), ttl, DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
263 }
264 for (const auto& sig : ne.DNSSECRecords.signatures) {
265 fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), ttl, sig.d_content->getZoneRepresentation().c_str());
266 }
267 }
268 }
269 return ret;
270 }