]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursor_cache.cc
the eventlogger!
[thirdparty/pdns.git] / pdns / recursor_cache.cc
CommitLineData
eec1087c
BH
1#include "recursor_cache.hh"
2#include "misc.hh"
3#include <iostream>
ea634573
BH
4#include <boost/shared_ptr.hpp>
5#include "dnsrecords.hh"
bec87d21 6#include "arguments.hh"
92294b60 7
eec1087c 8using namespace std;
ea634573 9using namespace boost;
eec1087c 10
90a5cfe2 11#include "config.h"
748eff9f 12
90a5cfe2
BH
13#ifdef GCC_SKIP_LOCKING
14#include <bits/atomicity.h>
43a2b29c
BH
15// This code is ugly but does speedup the recursor tremendously on multi-processor systems, and even has a large effect (20, 30%) on uniprocessor
16namespace __gnu_cxx
17{
18 _Atomic_word
19 __attribute__ ((__unused__))
20 __exchange_and_add(volatile _Atomic_word* __mem, int __val)
21 {
90a5cfe2
BH
22 register _Atomic_word __result=*__mem;
23 *__mem+=__val;
24 return __result;
43a2b29c
BH
25 }
26
27 void
28 __attribute__ ((__unused__))
29 __atomic_add(volatile _Atomic_word* __mem, int __val)
30 {
90a5cfe2 31 *__mem+=__val;
43a2b29c
BH
32 }
33}
34#endif
35
9bd36a02
BH
36string simpleCompress(const string& label)
37{
38 typedef vector<pair<unsigned int, unsigned int> > parts_t;
39 parts_t parts;
40 vstringtok(parts, label, ".");
41 string ret;
42 ret.reserve(label.size()+4);
43 for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
44 ret.append(1, (char)(i->second - i->first));
45 ret.append(label.c_str() + i->first, i->second - i->first);
46 }
ffb584a9 47 ret.append(1, (char)0);
9bd36a02
BH
48 return ret;
49}
50
51void simpleExpandTo(const string& label, unsigned int frompos, string& ret)
52{
53 unsigned int labellen=0;
54 while((labellen=label.at(frompos++))) {
55 ret.append(label.c_str()+frompos, labellen);
56 ret.append(1,'.');
57 frompos+=labellen;
58 }
59}
60
ea634573
BH
61DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const string& serial, uint32_t ttd)
62{
ea634573 63 DNSResourceRecord rr;
ea634573 64 rr.ttl=ttd;
9bd36a02
BH
65 rr.qtype=qt;
66 rr.qname=qname;
67
68 if(rr.qtype.getCode()==QType::A) {
69 uint32_t ip;
70 memcpy((char*)&ip, serial.c_str(), 4);
71 rr.content=U32ToIP(ntohl(ip));
72 }
73 else if(rr.qtype.getCode()==QType::CNAME || rr.qtype.getCode()==QType::NS || rr.qtype.getCode()==QType::PTR) {
74 unsigned int frompos=0;
75 unsigned char labellen;
7738a23f 76
9bd36a02
BH
77 while((labellen=serial.at(frompos++))) {
78 if((labellen & 0xc0) == 0xc0) {
79 string encoded=simpleCompress(qname);
80 uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)serial.at(frompos++) - sizeof(dnsheader)-5;
81
82 simpleExpandTo(encoded, offset, rr.content);
83 // cerr<<"Oops, fallback, content so far: '"<<rr.content<<"', offset: "<<offset<<", '"<<qname<<"', "<<qt.getName()<<"\n";
84 break;
85 }
86 rr.content.append(serial.c_str()+frompos, labellen);
87 frompos+=labellen;
88 rr.content.append(1,'.');
89 }
7b1469bb
BH
90 if(rr.content.empty())
91 rr.content=".";
9bd36a02
BH
92 }
93 else {
7b1469bb 94 shared_ptr<DNSRecordContent> regen=DNSRecordContent::unserialize(qname, qt.getCode(), serial);
9bd36a02
BH
95 rr.content=regen->getZoneRepresentation();
96 }
cd2de0e0
BH
97 rr.content.reserve(0);
98 rr.qname.reserve(0);
ea634573
BH
99 return rr;
100}
101
102string DNSRR2String(const DNSResourceRecord& rr)
103{
ea634573 104 uint16_t type=rr.qtype.getCode();
9bd36a02
BH
105
106 if(type==QType::A) {
107 uint32_t ip;
108 IpToU32(rr.content, &ip);
109 return string((char*)&ip, 4);
110 }
111 else if(type==QType::NS) {
112 NSRecordContent ar(rr.content);
113 return ar.serialize(rr.qname);
114 }
115 else if(type==QType::CNAME) {
116 CNAMERecordContent ar(rr.content);
117 return ar.serialize(rr.qname);
118 }
119 else {
120 string ret;
121 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(type, 1, rr.content));
122 ret=drc->serialize(rr.qname);
a1754c6a 123 // cerr<<"stored '"<<rr.qname<<" '"<<rr.qtype.getName()<<"' '"<<rr.content<<"' as "<<ret.size()<<" bytes"<<endl;
9bd36a02
BH
124 return ret;
125 }
ea634573 126}
eec1087c
BH
127
128unsigned int MemRecursorCache::size()
129{
705f31ae 130 return (unsigned int)d_cache.size();
43a2b29c
BH
131}
132
133unsigned int MemRecursorCache::bytes()
134{
135 unsigned int ret=0;
136
137 for(cache_t::const_iterator i=d_cache.begin(); i!=d_cache.end(); ++i) {
705f31ae 138 ret+=(unsigned int)i->d_qname.length();
ca0b5def 139 for(vector<StoredRecord>::const_iterator j=i->d_records.begin(); j!= i->d_records.end(); ++j)
43a2b29c
BH
140 ret+=j->size();
141 }
142 return ret;
eec1087c
BH
143}
144
43a2b29c 145
36c5ee42 146int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set<DNSResourceRecord>* res)
eec1087c 147{
8a63d3ce 148 unsigned int ttd=0;
cf98aa40 149
7738a23f 150 // cerr<<"looking up "<< qname+"|"+qt.getName()<<"\n";
cf98aa40 151
705f31ae 152 if(!d_cachecachevalid || Utility::strcasecmp(d_cachedqname.c_str(), qname.c_str())) {
cf98aa40 153 // cerr<<"had cache cache miss"<<endl;
7738a23f
BH
154 d_cachedqname=qname;
155 d_cachecache=d_cache.equal_range(tie(qname));
cf98aa40
BH
156 d_cachecachevalid=true;
157 }
158 else
159 ;
160 // cerr<<"had cache cache hit!"<<endl;
748eff9f 161
748eff9f 162
ea634573
BH
163 if(res)
164 res->clear();
165
cf98aa40 166 if(d_cachecache.first!=d_cachecache.second) {
e93c956b 167 for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i)
9bc8c14c 168 if(i->d_qtype == qt.getCode() || qt.getCode()==QType::ANY) {
e93c956b
BH
169 typedef cache_t::nth_index<1>::type sequence_t;
170 sequence_t& sidx=d_cache.get<1>();
171 sequence_t::iterator si=d_cache.project<1>(i);
172
173 for(vector<StoredRecord>::const_iterator k=i->d_records.begin(); k != i->d_records.end(); ++k) {
174 if(k->d_ttd < 1000000000 || k->d_ttd > (uint32_t) now) {
175 ttd=k->d_ttd;
176 if(res) {
9bc8c14c 177 DNSResourceRecord rr=String2DNSRR(qname, QType(i->d_qtype), k->d_string, ttd);
cf98aa40
BH
178 res->insert(rr);
179 }
180 }
e93c956b
BH
181 }
182 if(res) {
bec87d21
BH
183 if(res->empty())
184 sidx.relocate(sidx.begin(), si);
185 else
186 sidx.relocate(sidx.end(), si);
bec87d21 187 }
9bc8c14c
BH
188 if(qt.getCode()!=QType::ANY) // normally if we have a hit, we are done
189 break;
e93c956b 190 }
b0d4fb45 191
ea634573
BH
192 // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
193 return (unsigned int)ttd-now;
eec1087c 194 }
eec1087c
BH
195 return -1;
196}
43a2b29c 197
8a5602d4
BH
198/* the code below is rather tricky - it basically replaces the stuff cached for qname by content, but it is special
199 cased for when inserting identical records with only differing ttls, in which case the entry is not
200 touched, but only given a new ttd */
eec1087c
BH
201void MemRecursorCache::replace(const string &qname, const QType& qt, const set<DNSResourceRecord>& content)
202{
cf98aa40 203 d_cachecachevalid=false;
7738a23f 204 tuple<string, uint16_t> key=make_tuple(qname, qt.getCode());
43a2b29c 205 cache_t::iterator stored=d_cache.find(key);
bec87d21 206
7738a23f 207 // cerr<<"storing "<< qname+"|"+qt.getName()<<" -> '"<<content.begin()->content<<"'\n";
6fa14d00 208
43a2b29c
BH
209 bool isNew=false;
210 if(stored == d_cache.end()) {
ca0b5def 211 stored=d_cache.insert(CacheEntry(key,vector<StoredRecord>())).first;
43a2b29c
BH
212 isNew=true;
213 }
6fa14d00 214
43a2b29c 215 pair<vector<StoredRecord>::iterator, vector<StoredRecord>::iterator> range;
4c789c50 216
8a5602d4 217 StoredRecord dr;
ca0b5def
BH
218 CacheEntry ce=*stored;
219
6fa14d00
BH
220 if(qt.getCode()==QType::SOA || qt.getCode()==QType::CNAME) // you can only have one (1) each of these
221 ce.d_records.clear();
222
223
ea634573 224 for(set<DNSResourceRecord>::const_iterator i=content.begin(); i != content.end(); ++i) {
ea634573
BH
225 dr.d_ttd=i->ttl;
226 dr.d_string=DNSRR2String(*i);
43a2b29c
BH
227
228 if(isNew)
ca0b5def 229 ce.d_records.push_back(dr);
8a5602d4 230 else {
ca0b5def 231 range=equal_range(ce.d_records.begin(), ce.d_records.end(), dr);
43a2b29c
BH
232
233 if(range.first != range.second) {
7004b0d5
BH
234 for(vector<StoredRecord>::iterator j=range.first ; j!=range.second; ++j) {
235 if(i->ttl > j->d_ttd)
236 j->d_ttd=i->ttl;
237 }
43a2b29c
BH
238 }
239 else {
ca0b5def
BH
240 ce.d_records.push_back(dr);
241 sort(ce.d_records.begin(), ce.d_records.end());
8e843282 242 }
8a5602d4 243 }
ea634573 244 }
43a2b29c 245 if(isNew) {
ca0b5def 246 sort(ce.d_records.begin(), ce.d_records.end());
43a2b29c 247 }
ca0b5def
BH
248
249 if(ce.d_records.capacity() != ce.d_records.size())
250 vector<StoredRecord>(ce.d_records).swap(ce.d_records);
251
252 d_cache.replace(stored, ce);
eec1087c 253}
92294b60 254
748eff9f
BH
255void MemRecursorCache::doWipeCache(const string& name)
256{
257 pair<cache_t::iterator, cache_t::iterator> range=d_cache.equal_range(tie(name));
258 d_cache.erase(range.first, range.second);
259}
92294b60 260
748eff9f
BH
261void MemRecursorCache::doDumpAndClose(int fd)
262{
263 FILE* fp=fdopen(fd, "w");
264 if(!fp) {
265 close(fd);
266 return;
267 }
bec87d21
BH
268
269 typedef cache_t::nth_index<1>::type sequence_t;
270 sequence_t& sidx=d_cache.get<1>();
271
748eff9f 272 time_t now=time(0);
bec87d21 273 for(sequence_t::const_iterator i=sidx.begin(); i != sidx.end(); ++i) {
748eff9f
BH
274 for(vector<StoredRecord>::const_iterator j=i->d_records.begin(); j != i->d_records.end(); ++j) {
275 DNSResourceRecord rr=String2DNSRR(i->d_qname, QType(i->d_qtype), j->d_string, j->d_ttd - now);
7738a23f 276 fprintf(fp, "%s %d IN %s %s\n", rr.qname.c_str(), rr.ttl, rr.qtype.getName().c_str(), rr.content.c_str());
748eff9f
BH
277 }
278 }
279 fclose(fp);
280}
43a2b29c 281
bec87d21
BH
282void MemRecursorCache::doSlash(int perc)
283{
284 doPrune();
285}
286
eec1087c
BH
287void MemRecursorCache::doPrune(void)
288{
ca0b5def 289 uint32_t now=(uint32_t)time(0);
cf98aa40 290 d_cachecachevalid=false;
43a2b29c 291
bec87d21
BH
292 unsigned int maxCached=::arg().asNum("max-cache-entries");
293 unsigned int toTrim=0;
294
295 unsigned int cacheSize=d_cache.size();
ca0b5def 296
255e0a07 297 if(maxCached && cacheSize > maxCached) {
bec87d21 298 toTrim = cacheSize - maxCached;
255e0a07 299 }
bec87d21
BH
300
301 // cout<<"Need to trim "<<toTrim<<" from cache to meet target!\n";
302
303 typedef cache_t::nth_index<1>::type sequence_t;
304 sequence_t& sidx=d_cache.get<1>();
305
bec87d21
BH
306 unsigned int tried=0, lookAt, erased=0;
307
308 // two modes - if toTrim is 0, just look through 10000 records and nuke everything that is expired
309 // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
310 if(toTrim)
311 lookAt=5*toTrim;
312 else
255e0a07 313 lookAt=cacheSize/10;
bec87d21
BH
314
315
316 sequence_t::iterator iter=sidx.begin(), eiter;
317 for(; iter != sidx.end() && tried < lookAt ; ++tried) {
e93c956b
BH
318 unsigned int ttd=iter->getTTD();
319 if(ttd < now) {
bec87d21
BH
320 sidx.erase(iter++);
321 erased++;
eec1087c 322 }
bec87d21
BH
323 else
324 ++iter;
325
326 if(toTrim && erased > toTrim)
327 break;
eec1087c 328 }
bec87d21
BH
329
330 // cout<<"erased "<<erased<<" records based on ttd\n";
ca0b5def 331
bec87d21
BH
332 if(erased >= toTrim)
333 return;
334
335 // if(toTrim)
336 // cout<<"Still have "<<toTrim - erased<<" entries left to erase to meet target\n";
337
338
339 eiter=iter=sidx.begin();
340 advance(eiter, toTrim);
341 sidx.erase(iter, eiter);
eec1087c
BH
342}
343