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.
24 #include "negcache.hh"
26 #include "cachecleaner.hh"
29 NegCache::NegCache(size_t mapsCount
) :
34 size_t NegCache::size() const
37 for (const auto& map
: d_maps
) {
38 count
+= map
.d_entriesCount
;
44 * Set ne to the NegCacheEntry for the last label in qname and return true if there
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
52 bool NegCache::getRootNXTrust(const DNSName
& qname
, const struct timeval
& now
, NegCacheEntry
& ne
)
54 // Never deny the root.
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();
62 auto& map
= getMap(lastLabel
);
63 auto content
= map
.lock();
65 negcache_t::const_iterator ni
= content
->d_map
.find(std::tie(lastLabel
, qtnull
));
67 while (ni
!= content
->d_map
.end() && ni
->d_name
== lastLabel
&& ni
->d_auth
.isRoot() && ni
->d_qtype
== qtnull
) {
69 if (now
.tv_sec
< ni
->d_ttd
) {
71 moveCacheItemToBack
<SequenceTag
>(content
->d_map
, ni
);
74 moveCacheItemToFront
<SequenceTag
>(content
->d_map
, ni
);
81 * Set ne to the NegCacheEntry for the qname|qtype tuple and return true
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
89 bool NegCache::get(const DNSName
& qname
, const QType
& qtype
, const struct timeval
& now
, NegCacheEntry
& ne
, bool typeMustMatch
)
91 auto& map
= getMap(qname
);
92 auto content
= map
.lock();
94 const auto& idx
= content
->d_map
.get
<NegCacheEntry
>();
95 auto range
= idx
.equal_range(qname
);
96 auto ni
= range
.first
;
98 while (ni
!= range
.second
) {
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
);
104 if (now
.tv_sec
< ni
->d_ttd
) {
107 moveCacheItemToBack
<SequenceTag
>(content
->d_map
, firstIndexIterator
);
111 moveCacheItemToFront
<SequenceTag
>(content
->d_map
, firstIndexIterator
);
119 * Places ne into the negative cache, possibly overriding an existing entry.
121 * \param ne The NegCacheEntry to add to the cache
123 void NegCache::add(const NegCacheEntry
& ne
)
125 bool inserted
= false;
126 auto& map
= getMap(ne
.d_name
);
127 auto content
= map
.lock();
128 inserted
= lruReplacingInsert
<SequenceTag
>(content
->d_map
, ne
);
130 ++map
.d_entriesCount
;
135 * Update the validation state of an existing entry with the provided state.
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
141 void NegCache::updateValidationStatus(const DNSName
& qname
, const QType
& qtype
, const vState newState
, boost::optional
<time_t> capTTD
)
143 auto& mc
= getMap(qname
);
144 auto map
= mc
.lock();
145 auto range
= map
->d_map
.equal_range(std::tie(qname
, qtype
));
147 if (range
.first
!= range
.second
) {
148 range
.first
->d_validationState
= newState
;
150 range
.first
->d_ttd
= std::min(range
.first
->d_ttd
, *capTTD
);
156 * Returns the amount of entries in the cache
158 * \param qname The name of the entries to be counted
160 size_t NegCache::count(const DNSName
& qname
)
162 auto& map
= getMap(qname
);
163 auto content
= map
.lock();
164 return content
->d_map
.count(std::tie(qname
));
168 * Returns the amount of entries in the cache for qname+qtype
170 * \param qname The name of the entries to be counted
171 * \param qtype The type of the entries to be counted
173 size_t NegCache::count(const DNSName
& qname
, const QType qtype
)
175 auto& map
= getMap(qname
);
176 auto content
= map
.lock();
177 return content
->d_map
.count(std::tie(qname
, qtype
));
181 * Remove all entries for name from the cache. If subtree is true, wipe all names
184 * \param name The DNSName of the entries to wipe
185 * \param subtree Should all entries under name be removed?
187 size_t NegCache::wipe(const DNSName
& name
, bool subtree
)
191 for (auto& map
: d_maps
) {
193 for (auto i
= m
->d_map
.lower_bound(std::tie(name
)); i
!= m
->d_map
.end();) {
194 if (!i
->d_name
.isPartOf(name
))
196 i
= m
->d_map
.erase(i
);
198 --map
.d_entriesCount
;
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
);
211 --map
.d_entriesCount
;
217 * Clear the negative cache
219 void NegCache::clear()
221 for (auto& map
: d_maps
) {
224 map
.d_entriesCount
= 0;
229 * Perform some cleanup in the cache, removing stale entries
231 * \param maxEntries The maximum number of entries that may exist in the cache.
233 void NegCache::prune(size_t maxEntries
)
235 size_t cacheSize
= size();
236 pruneMutexCollectionsVector
<SequenceTag
>(*this, d_maps
, maxEntries
, cacheSize
);
240 * Writes the whole negative cache to fp
242 * \param fp A pointer to an open FILE object
244 size_t NegCache::dumpToFile(FILE* fp
, const struct timeval
& now
)
248 for (auto& mc
: d_maps
) {
250 auto& sidx
= m
->d_map
.get
<SequenceTag
>();
251 for (const NegCacheEntry
& ne
: sidx
) {
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());
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());
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());
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());