]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/packetcache.cc
Standardize license text in all PDNS files
[thirdparty/pdns.git] / pdns / packetcache.cc
CommitLineData
12c86877 1/*
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
8e50cd4c 25#include "utility.hh"
12c86877
BH
26#include "packetcache.hh"
27#include "logger.hh"
28#include "arguments.hh"
29#include "statbag.hh"
30#include <map>
9e134196 31#include <boost/algorithm/string.hpp>
12c86877
BH
32
33extern StatBag S;
34
35PacketCache::PacketCache()
36{
c3064e57 37 d_ops=0;
908c1491 38 d_maps.resize(1024);
46e4b77c 39 for(auto& mc : d_maps) {
908c1491 40 pthread_rwlock_init(&mc.d_mut, 0);
41 }
12c86877
BH
42
43 d_ttl=-1;
44 d_recursivettl=-1;
45
46 S.declare("packetcache-hit");
47 S.declare("packetcache-miss");
48 S.declare("packetcache-size");
49
dee7ba5a
BH
50 d_statnumhit=S.getPointer("packetcache-hit");
51 d_statnummiss=S.getPointer("packetcache-miss");
52 d_statnumentries=S.getPointer("packetcache-size");
dfd3b427 53 d_doRecursion=false;
12c86877
BH
54}
55
3dbfa51e
BH
56PacketCache::~PacketCache()
57{
908c1491 58 // WriteLock l(&d_mut);
59 vector<WriteLock*> locks;
46e4b77c 60 for(auto& mc : d_maps) {
908c1491 61 locks.push_back(new WriteLock(&mc.d_mut));
62 }
46e4b77c 63 for(auto wl : locks) {
908c1491 64 delete wl;
65 }
3dbfa51e
BH
66}
67
908c1491 68
69
75fde355 70int PacketCache::get(DNSPacket *p, DNSPacket *cached, bool recursive)
12c86877 71{
2667dc9a 72 extern StatBag S;
12c86877 73
2667dc9a
BH
74 if(d_ttl<0)
75 getTTLS();
12c86877 76
ac0995bb 77 cleanupIfNeeded();
b2ac0df8 78
2667dc9a
BH
79 if(d_doRecursion && p->d.rd) { // wants recursion
80 if(!d_recursivettl) {
dee7ba5a 81 (*d_statnummiss)++;
2667dc9a
BH
82 return 0;
83 }
84 }
85 else { // does not
86 if(!d_ttl) {
dee7ba5a 87 (*d_statnummiss)++;
2667dc9a
BH
88 return 0;
89 }
90 }
91
2667dc9a
BH
92 if(ntohs(p->d.qdcount)!=1) // we get confused by packets with more than one question
93 return 0;
94
75fde355 95 unsigned int age=0;
2f24bcd2
BH
96 string value;
97 bool haveSomething;
2667dc9a 98 {
46e4b77c 99 auto& mc=getMap(p->qdomain);
908c1491 100 TryReadLock l(&mc.d_mut); // take a readlock here
2667dc9a
BH
101 if(!l.gotIt()) {
102 S.inc("deferred-cache-lookup");
103 return 0;
104 }
105
9a8a1c8b 106 uint16_t maxReplyLen = p->d_tcp ? 0xffff : p->getMaxReplyLen();
8487d349 107 haveSomething=getEntryLocked(p->qdomain, p->qtype, PacketCache::PACKETCACHE, value, -1, recursive, maxReplyLen, p->d_dnssecOk, p->hasEDNS(), &age);
2f24bcd2
BH
108 }
109 if(haveSomething) {
110 (*d_statnumhit)++;
75fde355
KM
111 if (recursive)
112 ageDNSPacket(value, age);
113 if(cached->noparse(value.c_str(), value.size()) < 0)
2f24bcd2 114 return 0;
63e365db 115 cached->spoofQuestion(p); // for correct case
9951e2d0
KM
116 cached->qdomain=p->qdomain;
117 cached->qtype=p->qtype;
2f24bcd2 118 return 1;
2667dc9a 119 }
2f24bcd2 120
15d02f9d 121 // cerr<<"Packet cache miss for '"<<p->qdomain<<"', merits: "<<packetMeritsRecursion<<endl;
dee7ba5a 122 (*d_statnummiss)++;
2667dc9a 123 return 0; // bummer
12c86877
BH
124}
125
126void PacketCache::getTTLS()
127{
ba45c866
BH
128 d_ttl=::arg().asNum("cache-ttl");
129 d_recursivettl=::arg().asNum("recursive-cache-ttl");
12c86877 130
ba45c866 131 d_doRecursion=::arg().mustDo("recursor");
12c86877
BH
132}
133
2667dc9a 134
75fde355 135void PacketCache::insert(DNSPacket *q, DNSPacket *r, bool recursive, unsigned int maxttl)
12c86877 136{
2667dc9a 137 if(d_ttl < 0)
12c86877 138 getTTLS();
2667dc9a
BH
139
140 if(ntohs(q->d.qdcount)!=1) {
2667dc9a
BH
141 return; // do not try to cache packets with multiple questions
142 }
12c86877 143
adf13442
BH
144 if(q->qclass != QClass::IN) // we only cache the INternet
145 return;
146
9a8a1c8b 147 uint16_t maxReplyLen = q->d_tcp ? 0xffff : q->getMaxReplyLen();
75fde355
KM
148 unsigned int ourttl = recursive ? d_recursivettl : d_ttl;
149 if(!recursive) {
150 if(maxttl<ourttl)
151 ourttl=maxttl;
152 } else {
153 unsigned int minttl = r->getMinTTL();
154 if(minttl<ourttl)
155 ourttl=minttl;
156 }
8487d349 157 insert(q->qdomain, q->qtype, PacketCache::PACKETCACHE, r->getString(), ourttl, -1, recursive,
17d0b1e6 158 maxReplyLen, q->d_dnssecOk, q->hasEDNS());
12c86877
BH
159}
160
ba45c866 161// universal key appears to be: qname, qtype, kind (packet, query cache), optionally zoneid, meritsRecursion
675fa24c 162void PacketCache::insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const string& value, unsigned int ttl, int zoneID,
17d0b1e6 163 bool meritsRecursion, unsigned int maxReplyLen, bool dnssecOk, bool EDNS)
12c86877 164{
ac0995bb 165 cleanupIfNeeded();
b2ac0df8 166
12c86877
BH
167 if(!ttl)
168 return;
ba45c866 169
17d0b1e6 170 //cerr<<"Inserting qname '"<<qname<<"', cet: "<<(int)cet<<", qtype: "<<qtype.getName()<<", ttl: "<<ttl<<", maxreplylen: "<<maxReplyLen<<", hasEDNS: "<<EDNS<<endl;
ba45c866 171 CacheEntry val;
75fde355
KM
172 val.created=time(0);
173 val.ttd=val.created+ttl;
478748c3 174 val.qname=qname;
ba45c866
BH
175 val.qtype=qtype.getCode();
176 val.value=value;
177 val.ctype=cet;
178 val.meritsRecursion=meritsRecursion;
cf00d8d3 179 val.maxReplyLen = maxReplyLen;
a637d0a5 180 val.dnssecOk = dnssecOk;
771af01a 181 val.zoneID = zoneID;
17d0b1e6 182 val.hasEDNS = EDNS;
a637d0a5 183
46e4b77c 184 auto& mc = getMap(val.qname);
185
908c1491 186 TryWriteLock l(&mc.d_mut);
ba45c866
BH
187 if(l.gotIt()) {
188 bool success;
189 cmap_t::iterator place;
908c1491 190 tie(place, success)=mc.d_map.insert(val);
191
aee8cae2 192 if(!success)
908c1491 193 mc.d_map.replace(place, val);
ba45c866 194 }
12c86877 195 else
eefd15f9 196 S.inc("deferred-cache-inserts");
12c86877
BH
197}
198
a3b6f8d0 199void PacketCache::insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const vector<DNSResourceRecord>& value, unsigned int ttl, int zoneID)
200{
ac0995bb 201 cleanupIfNeeded();
a3b6f8d0 202
203 if(!ttl)
204 return;
205
206 //cerr<<"Inserting qname '"<<qname<<"', cet: "<<(int)cet<<", qtype: "<<qtype.getName()<<", ttl: "<<ttl<<", maxreplylen: "<<maxReplyLen<<", hasEDNS: "<<EDNS<<endl;
207 CacheEntry val;
208 val.created=time(0);
209 val.ttd=val.created+ttl;
210 val.qname=qname;
211 val.qtype=qtype.getCode();
212 val.drs=value;
213 val.ctype=cet;
214 val.meritsRecursion=false;
215 val.maxReplyLen = 0;
216 val.dnssecOk = false;
217 val.zoneID = zoneID;
218 val.hasEDNS = false;
219
220 auto& mc = getMap(val.qname);
221
222 TryWriteLock l(&mc.d_mut);
223 if(l.gotIt()) {
224 bool success;
225 cmap_t::iterator place;
226 tie(place, success)=mc.d_map.insert(val);
227
228 if(!success)
229 mc.d_map.replace(place, val);
230 }
231 else
232 S.inc("deferred-cache-inserts");
233}
234
235
27fdc3fc
BH
236/* clears the entire packetcache. */
237int PacketCache::purge()
238{
908c1491 239 int delcount=0;
46e4b77c 240 for(auto& mc : d_maps) {
908c1491 241 WriteLock l(&mc.d_mut);
242 delcount+=mc.d_map.size();
243 mc.d_map.clear();
244 }
ac0995bb 245 d_statnumentries->store(0);
27fdc3fc
BH
246 return delcount;
247}
248
be9d7339
CH
249int PacketCache::purgeExact(const DNSName& qname)
250{
251 int delcount=0;
252 auto& mc = getMap(qname);
253
254 WriteLock l(&mc.d_mut);
255 auto range = mc.d_map.equal_range(tie(qname));
256 if(range.first != range.second) {
257 delcount+=distance(range.first, range.second);
258 mc.d_map.erase(range.first, range.second);
259 }
260 *d_statnumentries-=delcount; // XXX FIXME NEEDS TO BE ADJUSTED (for packetcache shards)
261 return delcount;
262}
263
27fdc3fc
BH
264/* purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
265int PacketCache::purge(const string &match)
12c86877 266{
478748c3 267 if(ends_with(match, "$")) {
be9d7339 268 int delcount=0;
478748c3 269 string prefix(match);
270 prefix.resize(prefix.size()-1);
271 DNSName dprefix(prefix);
46e4b77c 272 for(auto& mc : d_maps) {
478748c3 273 WriteLock l(&mc.d_mut);
274 cmap_t::const_iterator iter = mc.d_map.lower_bound(tie(dprefix));
275 auto start=iter;
27fdc3fc 276
908c1491 277 for(; iter != mc.d_map.end(); ++iter) {
478748c3 278 if(!iter->qname.isPartOf(dprefix)) {
8487d349
KM
279 break;
280 }
281 delcount++;
9e134196 282 }
908c1491 283 mc.d_map.erase(start, iter);
284 }
be9d7339
CH
285 *d_statnumentries-=delcount; // XXX FIXME NEEDS TO BE ADJUSTED (for packetcache shards)
286 return delcount;
478748c3 287 }
288 else {
be9d7339 289 return purgeExact(DNSName(match));
9e134196 290 }
12c86877 291}
a637d0a5 292// called from ueberbackend
a3b6f8d0 293bool PacketCache::getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSResourceRecord>& value, int zoneID)
12c86877 294{
b2ac0df8
BH
295 if(d_ttl<0)
296 getTTLS();
297
ac0995bb 298 cleanupIfNeeded();
b2ac0df8 299
46e4b77c 300 auto& mc=getMap(qname);
908c1491 301
302 TryReadLock l(&mc.d_mut); // take a readlock here
12c86877 303 if(!l.gotIt()) {
eefd15f9 304 S.inc( "deferred-cache-lookup");
12c86877
BH
305 return false;
306 }
a637d0a5 307
a3b6f8d0 308 return getEntryLocked(qname, qtype, cet, value, zoneID);
b2ac0df8
BH
309}
310
a637d0a5 311
675fa24c 312bool PacketCache::getEntryLocked(const DNSName &qname, const QType& qtype, CacheEntryType cet, string& value, int zoneID, bool meritsRecursion,
75fde355 313 unsigned int maxReplyLen, bool dnssecOK, bool hasEDNS, unsigned int *age)
b2ac0df8 314{
ba45c866 315 uint16_t qt = qtype.getCode();
9a8a1c8b 316 //cerr<<"Lookup for maxReplyLen: "<<maxReplyLen<<endl;
46e4b77c 317 auto& mc=getMap(qname);
478748c3 318 cmap_t::const_iterator i=mc.d_map.find(tie(qname, qt, cet, zoneID, meritsRecursion, maxReplyLen, dnssecOK, hasEDNS, *age));
12c86877 319 time_t now=time(0);
908c1491 320 bool ret=(i!=mc.d_map.end() && i->ttd > now);
75fde355
KM
321 if(ret) {
322 if (age)
323 *age = now - i->created;
ba45c866 324 value = i->value;
75fde355 325 }
a3b6f8d0 326
327 return ret;
328}
329
330bool PacketCache::getEntryLocked(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSResourceRecord>& value, int zoneID)
331{
332 uint16_t qt = qtype.getCode();
333 //cerr<<"Lookup for maxReplyLen: "<<maxReplyLen<<endl;
334 auto& mc=getMap(qname);
335 cmap_t::const_iterator i=mc.d_map.find(tie(qname, qt, cet, zoneID));
336 time_t now=time(0);
337 bool ret=(i!=mc.d_map.end() && i->ttd > now);
338 if(ret) {
339 value = i->drs;
340 }
12c86877
BH
341 return ret;
342}
343
801812e6 344
12c86877
BH
345map<char,int> PacketCache::getCounts()
346{
4b298c7f
BH
347 int recursivePackets=0, nonRecursivePackets=0, queryCacheEntries=0, negQueryCacheEntries=0;
348
46e4b77c 349 for(auto& mc : d_maps) {
908c1491 350 ReadLock l(&mc.d_mut);
351
352 for(cmap_t::const_iterator iter = mc.d_map.begin() ; iter != mc.d_map.end(); ++iter) {
353 if(iter->ctype == PACKETCACHE)
354 if(iter->meritsRecursion)
355 recursivePackets++;
356 else
357 nonRecursivePackets++;
358 else if(iter->ctype == QUERYCACHE) {
359 if(iter->value.empty())
360 negQueryCacheEntries++;
361 else
362 queryCacheEntries++;
363 }
2f24bcd2 364 }
4b298c7f 365 }
908c1491 366 map<char,int> ret;
367
4b298c7f
BH
368 ret['!']=negQueryCacheEntries;
369 ret['Q']=queryCacheEntries;
370 ret['n']=nonRecursivePackets;
371 ret['r']=recursivePackets;
12c86877 372 return ret;
12c86877
BH
373}
374
12c86877
BH
375int PacketCache::size()
376{
908c1491 377 uint64_t ret=0;
46e4b77c 378 for(auto& mc : d_maps) {
908c1491 379 ReadLock l(&mc.d_mut);
380 ret+=mc.d_map.size();
381 }
382 return ret;
12c86877
BH
383}
384
385/** readlock for figuring out which iterators to delete, upgrade to writelock when actually cleaning */
386void PacketCache::cleanup()
387{
ac0995bb 388 d_statnumentries->store(0);
46e4b77c 389 for(auto& mc : d_maps) {
908c1491 390 ReadLock l(&mc.d_mut);
12c86877 391
908c1491 392 *d_statnumentries+=mc.d_map.size();
393 }
ba45c866
BH
394 unsigned int maxCached=::arg().asNum("max-cache-entries");
395 unsigned int toTrim=0;
396
ac0995bb 397 unsigned long cacheSize=*d_statnumentries;
908c1491 398
ba45c866
BH
399 if(maxCached && cacheSize > maxCached) {
400 toTrim = cacheSize - maxCached;
401 }
402
403 unsigned int lookAt=0;
b2ac0df8 404 // two modes - if toTrim is 0, just look through 10% of the cache and nuke everything that is expired
ba45c866
BH
405 // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
406 if(toTrim)
407 lookAt=5*toTrim;
408 else
409 lookAt=cacheSize/10;
410
411 // cerr<<"cacheSize: "<<cacheSize<<", lookAt: "<<lookAt<<", toTrim: "<<toTrim<<endl;
12c86877 412 time_t now=time(0);
12c86877 413 DLOG(L<<"Starting cache clean"<<endl);
20ac38f9 414 //unsigned int totErased=0;
46e4b77c 415 for(auto& mc : d_maps) {
20ac38f9 416 WriteLock wl(&mc.d_mut);
908c1491 417 typedef cmap_t::nth_index<1>::type sequence_t;
418 sequence_t& sidx=mc.d_map.get<1>();
419 unsigned int erased=0, lookedAt=0;
420 for(sequence_t::iterator i=sidx.begin(); i != sidx.end(); lookedAt++) {
421 if(i->ttd < now) {
422 sidx.erase(i++);
423 erased++;
424 }
20ac38f9 425 else {
908c1491 426 ++i;
20ac38f9 427 }
12c86877 428
908c1491 429 if(toTrim && erased > toTrim / d_maps.size())
430 break;
431
432 if(lookedAt > lookAt / d_maps.size())
433 break;
434 }
20ac38f9 435 //totErased += erased;
ba45c866 436 }
20ac38f9 437 // if(totErased)
438 // cerr<<"erased: "<<totErased<<endl;
439
ac0995bb 440 d_statnumentries->store(0);
46e4b77c 441 for(auto& mc : d_maps) {
908c1491 442 ReadLock l(&mc.d_mut);
443 *d_statnumentries+=mc.d_map.size();
444 }
445
12c86877
BH
446 DLOG(L<<"Done with cache clean"<<endl);
447}