2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
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"
36 #include "validate.hh"
38 using namespace ::boost::multi_index;
40 /* FIXME should become part of the normal cache (I think) and should become more like
42 * vector<DNSRecord> records;
43 * vector<DNSRecord> signatures;
46 * typedef vector<recsig_t> recordsAndSignatures;
50 vector<DNSRecord> records;
51 vector<DNSRecord> signatures;
52 } recordsAndSignatures;
54 class NegCache : public boost::noncopyable
57 NegCache(size_t mapsCount = 128);
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;
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
73 mutable uint16_t d_servedStale{0};
74 mutable vState d_validationState{vState::Indeterminate};
75 QType d_qtype; // The denied type
77 bool isStale(time_t now) const
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;
88 bool isEntryUsable(time_t now, bool serveStale) const
90 // When serving stale, we consider expired records
91 return d_ttd > now || serveStale || d_servedStale != 0;
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);
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);
115 typedef boost::multi_index_container<
118 ordered_unique<tag<CompositeKey>,
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>>>>
130 void updateStaleEntry(time_t now, negcache_t::iterator& entry, QType qtype);
135 MapCombo(const MapCombo&) = delete;
136 MapCombo& operator=(const MapCombo&) = delete;
140 uint64_t d_contended_count{0};
141 uint64_t d_acquired_count{0};
143 void preRemoval(const NegCacheEntry& /* entry */) {}
146 LockGuardedTryHolder<MapCombo::LockedContent> lock()
148 auto locked = d_content.try_lock();
149 if (!locked.owns_lock()) {
151 ++locked->d_contended_count;
153 ++locked->d_acquired_count;
157 [[nodiscard]] auto getEntriesCount() const
159 return d_entriesCount.load();
162 void incEntriesCount()
167 void decEntriesCount()
172 void clearEntriesCount()
178 LockGuarded<LockedContent> d_content;
179 pdns::stat_t d_entriesCount{0};
182 vector<MapCombo> d_maps;
184 MapCombo& getMap(const DNSName& qname)
186 return d_maps.at(qname.hash() % d_maps.size());
188 const MapCombo& getMap(const DNSName& qname) const
190 return d_maps.at(qname.hash() % d_maps.size());