]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/auth-packetcache.cc
dnsdist: Add HTTPStatusAction to return a specific HTTP response
[thirdparty/pdns.git] / pdns / auth-packetcache.cc
CommitLineData
bf269e28
RG
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#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25#include "auth-packetcache.hh"
26#include "logger.hh"
27#include "statbag.hh"
28#include "cachecleaner.hh"
29extern StatBag S;
30
31const unsigned int AuthPacketCache::s_mincleaninterval, AuthPacketCache::s_maxcleaninterval;
32
33AuthPacketCache::AuthPacketCache(size_t mapsCount): d_lastclean(time(nullptr))
34{
35 d_maps.resize(mapsCount);
36 for(auto& mc : d_maps) {
37 pthread_rwlock_init(&mc.d_mut, 0);
38 }
39
40 S.declare("packetcache-hit", "Number of hits on the packet cache");
41 S.declare("packetcache-miss", "Number of misses on the packet cache");
42 S.declare("packetcache-size", "Number of entries in the packet cache");
43 S.declare("deferred-packetcache-inserts","Amount of packet cache inserts that were deferred because of maintenance");
44 S.declare("deferred-packetcache-lookup","Amount of packet cache lookups that were deferred because of maintenance");
45
46 d_statnumhit=S.getPointer("packetcache-hit");
47 d_statnummiss=S.getPointer("packetcache-miss");
48 d_statnumentries=S.getPointer("packetcache-size");
49}
50
51AuthPacketCache::~AuthPacketCache()
52{
53 try {
54 vector<WriteLock*> locks;
55 for(auto& mc : d_maps) {
56 locks.push_back(new WriteLock(&mc.d_mut));
57 }
58 for(auto wl : locks) {
59 delete wl;
60 }
61 }
62 catch(...) {
63 }
64}
65
66bool AuthPacketCache::get(DNSPacket *p, DNSPacket *cached)
67{
bf269e28 68 if(!d_ttl) {
bf269e28
RG
69 return false;
70 }
71
9a037bfa
KM
72 cleanupIfNeeded();
73
08b02366 74 uint32_t hash = canHashPacket(p->getString());
bf269e28
RG
75 p->setHash(hash);
76
77 string value;
78 bool haveSomething;
79 time_t now = time(nullptr);
80 auto& mc = getMap(p->qdomain);
81 {
82 TryReadLock rl(&mc.d_mut);
83 if(!rl.gotIt()) {
84 S.inc("deferred-packetcache-lookup");
85 return false;
86 }
87
08b02366 88 haveSomething = getEntryLocked(mc.d_map, p->getString(), hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
bf269e28
RG
89 }
90
91 if (!haveSomething) {
92 (*d_statnummiss)++;
93 return false;
94 }
95
96 if(cached->noparse(value.c_str(), value.size()) < 0) {
97 return false;
98 }
99
100 (*d_statnumhit)++;
101 cached->spoofQuestion(p); // for correct case
102 cached->qdomain = p->qdomain;
103 cached->qtype = p->qtype;
104
105 return true;
106}
107
08b02366
RG
108bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp)
109{
110 return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname);
111}
112
bf269e28
RG
113void AuthPacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxTTL)
114{
9a037bfa
KM
115 if(!d_ttl) {
116 return;
117 }
118
bf269e28
RG
119 cleanupIfNeeded();
120
121 if (ntohs(q->d.qdcount) != 1) {
122 return; // do not try to cache packets with multiple questions
123 }
124
125 if (q->qclass != QClass::IN) // we only cache the INternet
126 return;
127
128 uint32_t ourttl = std::min(d_ttl, maxTTL);
129 if (ourttl == 0) {
130 return;
131 }
132
133 uint32_t hash = q->getHash();
134 time_t now = time(nullptr);
135 CacheEntry entry;
136 entry.hash = hash;
137 entry.created = now;
138 entry.ttd = now + ourttl;
139 entry.qname = q->qdomain;
140 entry.qtype = q->qtype.getCode();
141 entry.value = r->getString();
142 entry.tcp = r->d_tcp;
08b02366 143 entry.query = q->getString();
bf269e28
RG
144
145 auto& mc = getMap(entry.qname);
146 {
147 TryWriteLock l(&mc.d_mut);
148 if (!l.gotIt()) {
149 S.inc("deferred-packetcache-inserts");
150 return;
151 }
152
153 auto& idx = mc.d_map.get<HashTag>();
154 auto range = idx.equal_range(hash);
155 auto iter = range.first;
156
157 for( ; iter != range.second ; ++iter) {
08b02366 158 if (!entryMatches(iter, entry.query, entry.qname, entry.qtype, entry.tcp)) {
bf269e28 159 continue;
08b02366 160 }
bf269e28
RG
161
162 iter->value = entry.value;
163 iter->ttd = now + ourttl;
164 iter->created = now;
165 return;
166 }
167
168 /* no existing entry found to refresh */
169 mc.d_map.insert(entry);
170 (*d_statnumentries)++;
171 }
172}
173
08b02366 174bool AuthPacketCache::getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
bf269e28
RG
175{
176 auto& idx = map.get<HashTag>();
177 auto range = idx.equal_range(hash);
178
179 for(auto iter = range.first; iter != range.second ; ++iter) {
08b02366 180 if (iter->ttd < now) {
bf269e28 181 continue;
08b02366 182 }
bf269e28 183
08b02366 184 if (!entryMatches(iter, query, qname, qtype, tcp)) {
bf269e28 185 continue;
08b02366 186 }
bf269e28
RG
187
188 value = iter->value;
189 return true;
190 }
191
192 return false;
193}
194
195/* clears the entire cache. */
196uint64_t AuthPacketCache::purge()
197{
9a037bfa
KM
198 if(!d_ttl) {
199 return 0;
200 }
201
bf269e28
RG
202 d_statnumentries->store(0);
203
204 return purgeLockedCollectionsVector(d_maps);
205}
206
207uint64_t AuthPacketCache::purgeExact(const DNSName& qname)
208{
209 auto& mc = getMap(qname);
210 uint64_t delcount = purgeExactLockedCollection(mc, qname);
211
212 *d_statnumentries -= delcount;
213
214 return delcount;
215}
216
217/* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
218uint64_t AuthPacketCache::purge(const string &match)
219{
9a037bfa
KM
220 if(!d_ttl) {
221 return 0;
222 }
223
bf269e28
RG
224 uint64_t delcount = 0;
225
226 if(ends_with(match, "$")) {
227 delcount = purgeLockedCollectionsVector(d_maps, match);
228 *d_statnumentries -= delcount;
229 }
230 else {
231 delcount = purgeExact(DNSName(match));
232 }
233
234 return delcount;
235}
236
237void AuthPacketCache::cleanup()
238{
239 uint64_t maxCached = d_maxEntries;
240 uint64_t cacheSize = *d_statnumentries;
241 uint64_t totErased = 0;
242
243 totErased = pruneLockedCollectionsVector(d_maps, maxCached, cacheSize);
244 *d_statnumentries -= totErased;
245
e6a9dde5 246 DLOG(g_log<<"Done with cache clean, cacheSize: "<<(*d_statnumentries)<<", totErased"<<totErased<<endl);
bf269e28
RG
247}
248
249/* the logic:
250 after d_nextclean operations, we clean. We also adjust the cleaninterval
251 a bit so we slowly move it to a value where we clean roughly every 30 seconds.
252
253 If d_nextclean has reached its maximum value, we also test if we were called
254 within 30 seconds, and if so, we skip cleaning. This means that under high load,
255 we will not clean more often than every 30 seconds anyhow.
256*/
257
258void AuthPacketCache::cleanupIfNeeded()
259{
260 if (d_ops++ == d_nextclean) {
261 time_t now = time(nullptr);
262 int timediff = max((int)(now - d_lastclean), 1);
263
e6a9dde5 264 DLOG(g_log<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
bf269e28
RG
265
266 if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
267 d_cleanskipped = true;
268 d_nextclean += d_cleaninterval;
269
e6a9dde5 270 DLOG(g_log<<"cleaning skipped, timediff: "<<timediff<<endl);
bf269e28
RG
271
272 return;
273 }
274
275 if(!d_cleanskipped) {
276 d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
277 d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
278 d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
279
e6a9dde5 280 DLOG(g_log<<"new cleaninterval: "<<d_cleaninterval<<endl);
bf269e28
RG
281 } else {
282 d_cleanskipped = false;
283 }
284
285 d_nextclean += d_cleaninterval;
286 d_lastclean=now;
287 cleanup();
288 }
289}