]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursordist/negcache.cc
Fix clang-tidy warnings
[thirdparty/pdns.git] / pdns / recursordist / negcache.cc
CommitLineData
39ce10b2
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 */
b0d06ef2
RG
22#include <cinttypes>
23
39ce10b2
PL
24#include "negcache.hh"
25#include "misc.hh"
26#include "cachecleaner.hh"
606accb0 27#include "utility.hh"
4bab9d74
OM
28#include "rec-taskqueue.hh"
29
8c08b4ec 30// For a description on how ServeStale works, see recursor_cache.cc, the general structure is the same.
4bab9d74 31uint16_t NegCache::s_maxServedStaleExtensions;
39ce10b2 32
8f1b8e79 33NegCache::NegCache(size_t mapsCount) :
5dfa679e 34 d_maps(mapsCount == 0 ? 1 : mapsCount)
a0c1d047
OM
35{
36}
37
7a83afe1 38size_t NegCache::size() const
a0c1d047
OM
39{
40 size_t count = 0;
7a83afe1 41 for (const auto& map : d_maps) {
662dda3e 42 count += map.getEntriesCount();
a0c1d047
OM
43 }
44 return count;
45}
46
39ce10b2 47/*!
2509f359
PL
48 * Set ne to the NegCacheEntry for the last label in qname and return true if there
49 * was one.
39ce10b2
PL
50 *
51 * \param qname The name to look up (only the last label is used)
52 * \param now A timeval with the current time, to check if an entry is expired
53 * \param ne A NegCacheEntry that is filled when there is a cache entry
54 * \return true if ne was filled out, false otherwise
55 */
e2bfa146 56bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& negEntry, bool serveStale, bool refresh)
42dcf516 57{
2509f359 58 // Never deny the root.
e2bfa146 59 if (qname.isRoot()) {
2509f359 60 return false;
e2bfa146 61 }
2509f359 62
39ce10b2 63 DNSName lastLabel = qname.getLastLabel();
e2bfa146
OM
64 NegCacheEntry found;
65 // An 'ENT' QType entry, used as "whole name" in the neg-cache context.
66 auto exists = get(lastLabel, QType::ENT, now, found, true, serveStale, refresh);
67 if (exists && found.d_auth.isRoot()) {
4c5a50dc 68 negEntry = std::move(found);
e2bfa146 69 return true;
39ce10b2
PL
70 }
71 return false;
72}
73
2541e0f5 74void NegCache::updateStaleEntry(time_t now, negcache_t::iterator& entry, QType qtype)
4bab9d74
OM
75{
76 // We need to take care a infrequently access stale item cannot be extended past
77 // s_maxServedStaleExtension * s_serveStaleExtensionPeriod
78 // We we look how old the entry is, and increase d_servedStale accordingly, taking care not to overflow
79 const time_t howlong = std::max(static_cast<time_t>(1), now - entry->d_ttd);
80 const uint32_t extension = std::max(1U, std::min(entry->d_orig_ttl, s_serveStaleExtensionPeriod));
81 entry->d_servedStale = std::min(entry->d_servedStale + 1 + howlong / extension, static_cast<time_t>(s_maxServedStaleExtensions));
82 entry->d_ttd = now + std::min(entry->d_orig_ttl, s_serveStaleExtensionPeriod);
83
2541e0f5
OM
84 if (qtype == QType::ENT) {
85 qtype = QType::A;
86 }
87
88 pushAlmostExpiredTask(entry->d_name, qtype, entry->d_ttd, Netmask());
4bab9d74
OM
89}
90
39ce10b2
PL
91/*!
92 * Set ne to the NegCacheEntry for the qname|qtype tuple and return true
93 *
94 * \param qname The name to look up
95 * \param qtype The qtype to look up
96 * \param now A timeval with the current time, to check if an entry is expired
97 * \param ne A NegCacheEntry that is filled when there is a cache entry
98 * \return true if ne was filled out, false otherwise
99 */
2541e0f5 100bool NegCache::get(const DNSName& qname, QType qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch, bool serveStale, bool refresh)
42dcf516 101{
7a83afe1 102 auto& map = getMap(qname);
bbad1c01 103 auto content = map.lock();
a0c1d047 104
7a83afe1 105 const auto& idx = content->d_map.get<NegCacheEntry>();
c86e0c73 106 auto range = idx.equal_range(qname);
39ce10b2 107
16804be7 108 for (auto ni = range.first; ni != range.second; ++ni) {
39ce10b2 109 // We have an entry
2541e0f5 110 if ((!typeMustMatch && ni->d_qtype == QType::ENT) || ni->d_qtype == qtype) {
39ce10b2 111 // We match the QType or the whole name is denied
7a83afe1 112 auto firstIndexIterator = content->d_map.project<CompositeKey>(ni);
c86e0c73 113
16804be7
OM
114 // this checks ttd, but also takes into account serve-stale
115 if (!ni->isEntryUsable(now.tv_sec, serveStale)) {
116 // Outdated
117 moveCacheItemToFront<SequenceTag>(content->d_map, firstIndexIterator);
118 continue;
119 }
120 // If we are serving this record stale (or *should*) and the ttd has passed increase ttd to
121 // the future and remember that we did. Also push a refresh task.
122 if ((serveStale || ni->d_servedStale > 0) && ni->d_ttd <= now.tv_sec && ni->d_servedStale < s_maxServedStaleExtensions) {
cce57cb0 123 updateStaleEntry(now.tv_sec, firstIndexIterator, qtype);
4bab9d74 124 }
16804be7 125 if (now.tv_sec < ni->d_ttd) {
39ce10b2 126 // Not expired
33433a8a 127 ne = *ni;
7a83afe1 128 moveCacheItemToBack<SequenceTag>(content->d_map, firstIndexIterator);
c5b31227
OM
129 // when refreshing, we consider served-stale entries outdated
130 return !(refresh && ni->d_servedStale > 0);
39ce10b2 131 }
39ce10b2 132 }
39ce10b2
PL
133 }
134 return false;
135}
136
137/*!
138 * Places ne into the negative cache, possibly overriding an existing entry.
139 *
140 * \param ne The NegCacheEntry to add to the cache
141 */
42dcf516
OM
142void NegCache::add(const NegCacheEntry& ne)
143{
7a83afe1
RG
144 bool inserted = false;
145 auto& map = getMap(ne.d_name);
bbad1c01 146 auto content = map.lock();
7a83afe1 147 inserted = lruReplacingInsert<SequenceTag>(content->d_map, ne);
a0c1d047 148 if (inserted) {
662dda3e 149 map.incEntriesCount();
a0c1d047 150 }
39ce10b2
PL
151}
152
142ab370
RG
153/*!
154 * Update the validation state of an existing entry with the provided state.
155 *
156 * \param qname The name of the entry to replace
157 * \param qtype The type of the entry to replace
158 * \param newState The new validation state
159 */
2541e0f5 160void NegCache::updateValidationStatus(const DNSName& qname, const QType qtype, const vState newState, boost::optional<time_t> capTTD)
42dcf516 161{
7a83afe1 162 auto& mc = getMap(qname);
bbad1c01 163 auto map = mc.lock();
905dae56 164 auto range = map->d_map.equal_range(std::tie(qname, qtype));
142ab370
RG
165
166 if (range.first != range.second) {
167 range.first->d_validationState = newState;
b9473937
RG
168 if (capTTD) {
169 range.first->d_ttd = std::min(range.first->d_ttd, *capTTD);
170 }
142ab370
RG
171 }
172}
173
39ce10b2
PL
174/*!
175 * Returns the amount of entries in the cache
17f7162d
PL
176 *
177 * \param qname The name of the entries to be counted
39ce10b2 178 */
41fcded6 179size_t NegCache::count(const DNSName& qname)
42dcf516 180{
7a83afe1 181 auto& map = getMap(qname);
bbad1c01 182 auto content = map.lock();
905dae56 183 return content->d_map.count(std::tie(qname));
39ce10b2
PL
184}
185
17f7162d
PL
186/*!
187 * Returns the amount of entries in the cache for qname+qtype
188 *
189 * \param qname The name of the entries to be counted
190 * \param qtype The type of the entries to be counted
191 */
41fcded6 192size_t NegCache::count(const DNSName& qname, const QType qtype)
42dcf516 193{
7a83afe1 194 auto& map = getMap(qname);
bbad1c01 195 auto content = map.lock();
905dae56 196 return content->d_map.count(std::tie(qname, qtype));
17f7162d
PL
197}
198
39ce10b2
PL
199/*!
200 * Remove all entries for name from the cache. If subtree is true, wipe all names
201 * underneath it.
202 *
203 * \param name The DNSName of the entries to wipe
204 * \param subtree Should all entries under name be removed?
205 */
a0c1d047 206size_t NegCache::wipe(const DNSName& name, bool subtree)
42dcf516 207{
a0c1d047 208 size_t ret = 0;
39ce10b2 209 if (subtree) {
41fcded6 210 for (auto& map : d_maps) {
bbad1c01 211 auto m = map.lock();
905dae56 212 for (auto i = m->d_map.lower_bound(std::tie(name)); i != m->d_map.end();) {
a0c1d047
OM
213 if (!i->d_name.isPartOf(name))
214 break;
41fcded6 215 i = m->d_map.erase(i);
a0c1d047 216 ret++;
662dda3e 217 map.decEntriesCount();
a0c1d047 218 }
39ce10b2
PL
219 }
220 return ret;
221 }
222
7a83afe1 223 auto& map = getMap(name);
bbad1c01 224 auto content = map.lock();
905dae56 225 auto range = content->d_map.equal_range(std::tie(name));
a0c1d047
OM
226 auto i = range.first;
227 while (i != range.second) {
7a83afe1 228 i = content->d_map.erase(i);
a0c1d047 229 ret++;
662dda3e 230 map.decEntriesCount();
a0c1d047 231 }
39ce10b2
PL
232 return ret;
233}
234
6075405b 235size_t NegCache::wipeTyped(const DNSName& qname, QType qtype)
9597df1a
OM
236{
237 size_t ret = 0;
238 auto& map = getMap(qname);
239 auto content = map.lock();
240 auto range = content->d_map.equal_range(std::tie(qname));
241 auto i = range.first;
242 while (i != range.second) {
243 if (i->d_qtype == QType::ENT || i->d_qtype == qtype) {
244 i = content->d_map.erase(i);
245 ++ret;
662dda3e 246 map.decEntriesCount();
9597df1a
OM
247 }
248 else {
249 ++i;
250 }
251 }
252 return ret;
253}
254
39ce10b2
PL
255/*!
256 * Clear the negative cache
257 */
42dcf516
OM
258void NegCache::clear()
259{
41fcded6 260 for (auto& map : d_maps) {
bbad1c01 261 auto m = map.lock();
41fcded6 262 m->d_map.clear();
662dda3e 263 map.clearEntriesCount();
a0c1d047 264 }
39ce10b2
PL
265}
266
267/*!
268 * Perform some cleanup in the cache, removing stale entries
269 *
270 * \param maxEntries The maximum number of entries that may exist in the cache.
271 */
48d02436 272void NegCache::prune(time_t now, size_t maxEntries)
42dcf516 273{
a0c1d047 274 size_t cacheSize = size();
a6465c88 275 pruneMutexCollectionsVector<SequenceTag>(now, d_maps, maxEntries, cacheSize);
39ce10b2
PL
276}
277
278/*!
114b8796 279 * Writes the whole negative cache to fd
39ce10b2 280 *
114b8796 281 * \param fd A pointer to an open FILE object
39ce10b2 282 */
99934d02 283size_t NegCache::doDump(int fd, size_t maxCacheEntries, time_t now)
42dcf516 284{
911738a7
OM
285 int newfd = dup(fd);
286 if (newfd == -1) {
287 return 0;
288 }
114b8796
RG
289 auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
290 if (!filePtr) {
07ef790c 291 close(newfd);
911738a7
OM
292 return 0;
293 }
114b8796 294 fprintf(filePtr.get(), "; negcache dump follows\n;\n");
911738a7 295
5246a6dc 296 size_t ret = 0;
606accb0 297
911738a7
OM
298 size_t shard = 0;
299 size_t min = std::numeric_limits<size_t>::max();
300 size_t max = 0;
7a83afe1 301 for (auto& mc : d_maps) {
bbad1c01 302 auto m = mc.lock();
911738a7 303 const auto shardSize = m->d_map.size();
114b8796 304 fprintf(filePtr.get(), "; negcache shard %zu; size %zu\n", shard, shardSize);
911738a7
OM
305 min = std::min(min, shardSize);
306 max = std::max(max, shardSize);
307 shard++;
41fcded6 308 auto& sidx = m->d_map.get<SequenceTag>();
a0c1d047
OM
309 for (const NegCacheEntry& ne : sidx) {
310 ret++;
99934d02 311 int64_t ttl = ne.d_ttd - now;
114b8796 312 fprintf(filePtr.get(), "%s %" PRId64 " IN %s VIA %s ; (%s) origttl=%" PRIu32 " ss=%hu\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(), ne.d_orig_ttl, ne.d_servedStale);
a0c1d047 313 for (const auto& rec : ne.authoritySOA.records) {
114b8796 314 fprintf(filePtr.get(), "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), ttl, DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.getContent()->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
a0c1d047
OM
315 }
316 for (const auto& sig : ne.authoritySOA.signatures) {
114b8796 317 fprintf(filePtr.get(), "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), ttl, sig.getContent()->getZoneRepresentation().c_str());
a0c1d047
OM
318 }
319 for (const auto& rec : ne.DNSSECRecords.records) {
114b8796 320 fprintf(filePtr.get(), "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), ttl, DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.getContent()->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
a0c1d047
OM
321 }
322 for (const auto& sig : ne.DNSSECRecords.signatures) {
114b8796 323 fprintf(filePtr.get(), "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), ttl, sig.getContent()->getZoneRepresentation().c_str());
a0c1d047 324 }
629073f6 325 }
39ce10b2 326 }
114b8796 327 fprintf(filePtr.get(), "; negcache size: %zu/%zu shards: %zu min/max shard size: %zu/%zu\n", size(), maxCacheEntries, d_maps.size(), min, max);
39ce10b2
PL
328 return ret;
329}