]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursor_cache.cc
WARNING WARNING! This changes the Lua/PowerDNS interface in SVN, and restores the...
[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"
0ba0f794 7#include "syncres.hh"
34264879 8#include "recursor_cache.hh"
92294b60 9
eec1087c 10using namespace std;
61b26744 11#include "namespaces.hh"
eec1087c 12
90a5cfe2 13#include "config.h"
748eff9f 14
ea634573
BH
15DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const string& serial, uint32_t ttd)
16{
ea634573 17 DNSResourceRecord rr;
ea634573 18 rr.ttl=ttd;
9bd36a02
BH
19 rr.qtype=qt;
20 rr.qname=qname;
21
552788ce 22 if(rr.qtype.getCode()==QType::A && serial.size()==4) {
9bd36a02
BH
23 uint32_t ip;
24 memcpy((char*)&ip, serial.c_str(), 4);
25 rr.content=U32ToIP(ntohl(ip));
26 }
552788ce
BH
27 else if(rr.qtype.getCode()==QType::AAAA && serial.size()==16) {
28 ComboAddress tmp;
29 tmp.sin4.sin_family=AF_INET6;
30 memcpy(tmp.sin6.sin6_addr.s6_addr, serial.c_str(), 16);
31 rr.content=tmp.toString();
32 }
9bd36a02
BH
33 else if(rr.qtype.getCode()==QType::CNAME || rr.qtype.getCode()==QType::NS || rr.qtype.getCode()==QType::PTR) {
34 unsigned int frompos=0;
35 unsigned char labellen;
7738a23f 36
9bd36a02
BH
37 while((labellen=serial.at(frompos++))) {
38 if((labellen & 0xc0) == 0xc0) {
4957a608
BH
39 string encoded=simpleCompress(qname);
40 uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)serial.at(frompos++) - sizeof(dnsheader)-5;
9bd36a02 41
4957a608
BH
42 simpleExpandTo(encoded, offset, rr.content);
43 // cerr<<"Oops, fallback, content so far: '"<<rr.content<<"', offset: "<<offset<<", '"<<qname<<"', "<<qt.getName()<<"\n";
44 break;
9bd36a02
BH
45 }
46 rr.content.append(serial.c_str()+frompos, labellen);
47 frompos+=labellen;
48 rr.content.append(1,'.');
49 }
7b1469bb
BH
50 if(rr.content.empty())
51 rr.content=".";
9bd36a02
BH
52 }
53 else {
7b1469bb 54 shared_ptr<DNSRecordContent> regen=DNSRecordContent::unserialize(qname, qt.getCode(), serial);
9bd36a02
BH
55 rr.content=regen->getZoneRepresentation();
56 }
cd2de0e0
BH
57 rr.content.reserve(0);
58 rr.qname.reserve(0);
ea634573
BH
59 return rr;
60}
61
34264879 62// returns the RDATA for rr - might be compressed!
ea634573
BH
63string DNSRR2String(const DNSResourceRecord& rr)
64{
ea634573 65 uint16_t type=rr.qtype.getCode();
9bd36a02
BH
66
67 if(type==QType::A) {
68 uint32_t ip;
69 IpToU32(rr.content, &ip);
70 return string((char*)&ip, 4);
71 }
552788ce
BH
72 else if(type==QType::AAAA) {
73 ComboAddress ca(rr.content);
74 return string((char*)&ca.sin6.sin6_addr.s6_addr, 16);
75 }
7127879f
BH
76 else if(type==QType::NS || type==QType::CNAME)
77 return simpleCompress(rr.content, rr.qname);
9bd36a02
BH
78 else {
79 string ret;
80 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(type, 1, rr.content));
81 ret=drc->serialize(rr.qname);
a1754c6a 82 // cerr<<"stored '"<<rr.qname<<" '"<<rr.qtype.getName()<<"' '"<<rr.content<<"' as "<<ret.size()<<" bytes"<<endl;
9bd36a02
BH
83 return ret;
84 }
ea634573 85}
eec1087c
BH
86
87unsigned int MemRecursorCache::size()
88{
705f31ae 89 return (unsigned int)d_cache.size();
43a2b29c
BH
90}
91
92unsigned int MemRecursorCache::bytes()
93{
94 unsigned int ret=0;
95
96 for(cache_t::const_iterator i=d_cache.begin(); i!=d_cache.end(); ++i) {
705f31ae 97 ret+=(unsigned int)i->d_qname.length();
ca0b5def 98 for(vector<StoredRecord>::const_iterator j=i->d_records.begin(); j!= i->d_records.end(); ++j)
43a2b29c
BH
99 ret+=j->size();
100 }
101 return ret;
eec1087c
BH
102}
103
36c5ee42 104int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set<DNSResourceRecord>* res)
eec1087c 105{
8a63d3ce 106 unsigned int ttd=0;
b0c3b7d8
BH
107 // cerr<<"looking up "<< qname+"|"+qt.getName()<<"\n";
108
109 if(!d_cachecachevalid || !pdns_iequals(d_cachedqname, qname)) {
110 // cerr<<"had cache cache miss"<<endl;
111 d_cachedqname=qname;
112 d_cachecache=d_cache.equal_range(tie(qname));
113 d_cachecachevalid=true;
cf98aa40 114 }
b0c3b7d8
BH
115 else
116 // cerr<<"had cache cache hit!"<<endl;
117 ;
748eff9f 118
ea634573
BH
119 if(res)
120 res->clear();
121
cf98aa40 122 if(d_cachecache.first!=d_cachecache.second) {
e93c956b 123 for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i)
573051c7 124 if(i->d_qtype == qt.getCode() || qt.getCode()==QType::ANY ||
4957a608
BH
125 (qt.getCode()==QType::ADDR && (i->d_qtype == QType::A || i->d_qtype == QType::AAAA) )
126 ) {
127 typedef cache_t::nth_index<1>::type sequence_t;
b0c3b7d8 128 sequence_t& sidx=d_cache.get<1>();
4957a608
BH
129 sequence_t::iterator si=d_cache.project<1>(i);
130
131 for(vector<StoredRecord>::const_iterator k=i->d_records.begin(); k != i->d_records.end(); ++k) {
132 if(k->d_ttd < 1000000000 || k->d_ttd > (uint32_t) now) { // FIXME what does the 100000000 number mean?
133 ttd=k->d_ttd;
134 if(res) {
135 DNSResourceRecord rr=String2DNSRR(qname, QType(i->d_qtype), k->d_string, ttd);
136 res->insert(rr);
137 }
138 }
139 }
140 if(res) {
4957a608
BH
141 if(res->empty())
142 sidx.relocate(sidx.begin(), si);
143 else
144 sidx.relocate(sidx.end(), si);
4957a608
BH
145 }
146 if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
147 break;
e93c956b 148 }
b0d4fb45 149
ea634573 150 // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
a2b4f72f 151 return (int)ttd-now;
eec1087c 152 }
eec1087c
BH
153 return -1;
154}
34264879
BH
155
156
43a2b29c 157
f9f2ae96
BH
158bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const set<DNSResourceRecord>& content, const CacheEntry& stored)
159{
160 if(!stored.d_auth) {
161// cerr<<"feel free to scribble non-auth data!"<<endl;
162 return false;
163 }
164
165 if(qt.getCode()!=QType::NS) {
166 // cerr<<"Not NS record"<<endl;
167 return false;
168 }
169 if(content.size()!=stored.d_records.size()) {
170 // cerr<<"Not equal number of records"<<endl;
171 return false;
172 }
173 if(stored.d_records.empty())
174 return false;
175
176 if(stored.d_records.begin()->d_ttd > content.begin()->ttl) {
177 // cerr<<"attempt to LOWER TTL - fine by us"<<endl;
178 return false;
179 }
180
181
182 // cerr<<"Returning true - update attempt!\n";
183 return true;
184}
185
8a5602d4
BH
186/* the code below is rather tricky - it basically replaces the stuff cached for qname by content, but it is special
187 cased for when inserting identical records with only differing ttls, in which case the entry is not
188 touched, but only given a new ttd */
61973281 189void MemRecursorCache::replace(time_t now, const string &qname, const QType& qt, const set<DNSResourceRecord>& content, bool auth)
eec1087c 190{
cf98aa40 191 d_cachecachevalid=false;
7738a23f 192 tuple<string, uint16_t> key=make_tuple(qname, qt.getCode());
43a2b29c 193 cache_t::iterator stored=d_cache.find(key);
bec87d21 194
43a2b29c
BH
195 bool isNew=false;
196 if(stored == d_cache.end()) {
8db2cfd8 197 stored=d_cache.insert(CacheEntry(key,vector<StoredRecord>(), auth)).first;
43a2b29c
BH
198 isNew=true;
199 }
43a2b29c 200 pair<vector<StoredRecord>::iterator, vector<StoredRecord>::iterator> range;
4c789c50 201
8a5602d4 202 StoredRecord dr;
ca0b5def
BH
203 CacheEntry ce=*stored;
204
34264879 205 // cerr<<"storing "<< qname+"|"+qt.getName()<<" -> '"<<content.begin()->content<<"', isnew="<<isNew<<", auth="<<auth<<", ce.auth="<<ce.d_auth<<"\n";
6fa14d00 206
34264879
BH
207 if(qt.getCode()==QType::SOA || qt.getCode()==QType::CNAME) { // you can only have one (1) each of these
208 // cerr<<"\tCleaning out existing store because of SOA and CNAME\n";
209 ce.d_records.clear();
8db2cfd8 210 }
a85eb653
BH
211
212 if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh?
213 vector<StoredRecord>::iterator j;
214 for(j = ce.d_records.begin() ; j != ce.d_records.end(); ++j)
7305df82 215 if((time_t)j->d_ttd > now)
4957a608 216 break;
609a76b3 217 if(j != ce.d_records.end()) { // we still have valid data, ignore unauth data
34264879 218 // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
a85eb653 219 return;
609a76b3
BH
220 }
221 else {
222 ce.d_auth = false; // new data won't be auth
223 }
a85eb653 224 }
34264879
BH
225#if 0
226 if(auth && !attemptToRefreshNSTTL(qt, content, ce) ) {
227 cerr<<"\tGot auth data, and it was not refresh attempt of an NS record, nuking storage"<<endl;
228 ce.d_records.clear(); // clear non-auth data
229 ce.d_auth = true;
230 isNew=true; // data should be sorted again
231 }
232#endif
233 // cerr<<"\tHave "<<content.size()<<" records to store\n";
ea634573 234 for(set<DNSResourceRecord>::const_iterator i=content.begin(); i != content.end(); ++i) {
ea634573
BH
235 dr.d_ttd=i->ttl;
236 dr.d_string=DNSRR2String(*i);
43a2b29c
BH
237
238 if(isNew)
ca0b5def 239 ce.d_records.push_back(dr);
8a5602d4 240 else {
ca0b5def 241 range=equal_range(ce.d_records.begin(), ce.d_records.end(), dr);
34264879
BH
242
243 if(range.first != range.second && (range.first != ce.d_records.begin() || range.second != ce.d_records.end())) {
4957a608
BH
244 // cerr<<"\t\tIncomplete match! Must nuke"<<endl;
245 ce.d_records.clear();
246 range.first = range.second = ce.d_records.begin();
34264879
BH
247 }
248
43a2b29c 249 if(range.first != range.second) {
4957a608
BH
250 // cerr<<"\t\tMay need to modify TTL of stored record\n";
251 for(vector<StoredRecord>::iterator j=range.first ; j!=range.second; ++j) {
252 /* see http://mailman.powerdns.com/pipermail/pdns-users/2006-May/003413.html */
253 if(j->d_ttd > (unsigned int) now && i->ttl > j->d_ttd && qt.getCode()==QType::NS && auth) { // don't allow auth servers to *raise* TTL of an NS recor
254 // cerr<<"\t\tNot doing so, trying to raise TTL NS\n";
255 continue;
256 }
257 if(i->ttl > j->d_ttd || (auth && d_followRFC2181) ) { // authoritative packets can override the TTL to be lower
258 // cerr<<"\t\tUpdating the ttl, diff="<<j->d_ttd - i->ttl<<endl;;
259 j->d_ttd=i->ttl;
260 }
261 else {
262 // cerr<<"\t\tNOT updating the ttl, old= " <<j->d_ttd - now <<", new: "<<i->ttl - now <<endl;
263 }
264 }
43a2b29c
BH
265 }
266 else {
4957a608
BH
267 // cerr<<"\t\tThere was no exact copy of this record, so adding & sorting\n";
268 ce.d_records.push_back(dr);
269 sort(ce.d_records.begin(), ce.d_records.end());
8e843282 270 }
8a5602d4 271 }
ea634573 272 }
552788ce 273
43a2b29c 274 if(isNew) {
34264879 275 // cerr<<"\tSorting (because of isNew)\n";
ca0b5def 276 sort(ce.d_records.begin(), ce.d_records.end());
43a2b29c 277 }
552788ce 278
ca0b5def
BH
279 if(ce.d_records.capacity() != ce.d_records.size())
280 vector<StoredRecord>(ce.d_records).swap(ce.d_records);
552788ce 281
ca0b5def 282 d_cache.replace(stored, ce);
eec1087c 283}
92294b60 284
1ef00ba1 285int MemRecursorCache::doWipeCache(const string& name, uint16_t qtype)
748eff9f 286{
85c143a2 287 int count=0;
7e6139ba 288 d_cachecachevalid=false;
1ef00ba1
BH
289 pair<cache_t::iterator, cache_t::iterator> range;
290 if(qtype==0xffff)
291 range=d_cache.equal_range(tie(name));
292 else
293 range=d_cache.equal_range(tie(name, qtype));
294
85c143a2
BH
295 for(cache_t::const_iterator i=range.first; i != range.second; ) {
296 count++;
297 d_cache.erase(i++);
298 }
299 return count;
748eff9f 300}
92294b60 301
a2b4f72f
BH
302bool MemRecursorCache::doAgeCache(time_t now, const string& name, uint16_t qtype, int32_t newTTL)
303{
304 cache_t::iterator iter = d_cache.find(tie(name, qtype));
305 if(iter == d_cache.end())
306 return false;
307
308 int32_t ttl = iter->getTTD() - now;
309 if(ttl < 0)
310 return false; // would be dead anyhow
311
312 if(ttl > newTTL) {
313 d_cachecachevalid=false;
314
315 ttl = newTTL;
316 uint32_t newTTD = now + ttl;
317
318 CacheEntry ce = *iter;
319 for(vector<StoredRecord>::iterator j = ce.d_records.begin() ; j != ce.d_records.end(); ++j) {
320 j->d_ttd = newTTD;
321 }
322
323 d_cache.replace(iter, ce);
324 return true;
325 }
326 return false;
327}
328
329
748eff9f
BH
330void MemRecursorCache::doDumpAndClose(int fd)
331{
332 FILE* fp=fdopen(fd, "w");
333 if(!fp) {
334 close(fd);
335 return;
336 }
bec87d21
BH
337
338 typedef cache_t::nth_index<1>::type sequence_t;
339 sequence_t& sidx=d_cache.get<1>();
340
748eff9f 341 time_t now=time(0);
bec87d21 342 for(sequence_t::const_iterator i=sidx.begin(); i != sidx.end(); ++i) {
748eff9f 343 for(vector<StoredRecord>::const_iterator j=i->d_records.begin(); j != i->d_records.end(); ++j) {
4327eadf 344 try {
4957a608
BH
345 DNSResourceRecord rr=String2DNSRR(i->d_qname, QType(i->d_qtype), j->d_string, j->d_ttd - now);
346 fprintf(fp, "%s %d IN %s %s\n", rr.qname.c_str(), rr.ttl, rr.qtype.getName().c_str(), rr.content.c_str());
4327eadf
BH
347 }
348 catch(...) {
4957a608 349 fprintf(fp, "; error printing '%s'\n", i->d_qname.c_str());
4327eadf 350 }
748eff9f
BH
351 }
352 }
353 fclose(fp);
354}
43a2b29c 355
eec1087c
BH
356void MemRecursorCache::doPrune(void)
357{
ca0b5def 358 uint32_t now=(uint32_t)time(0);
cf98aa40 359 d_cachecachevalid=false;
43a2b29c 360
c3828c03 361 unsigned int maxCached=::arg().asNum("max-cache-entries") / g_numThreads;
bec87d21
BH
362 unsigned int toTrim=0;
363
364 unsigned int cacheSize=d_cache.size();
ca0b5def 365
255e0a07 366 if(maxCached && cacheSize > maxCached) {
bec87d21 367 toTrim = cacheSize - maxCached;
255e0a07 368 }
c3828c03 369
bec87d21
BH
370 // cout<<"Need to trim "<<toTrim<<" from cache to meet target!\n";
371
372 typedef cache_t::nth_index<1>::type sequence_t;
373 sequence_t& sidx=d_cache.get<1>();
374
bec87d21
BH
375 unsigned int tried=0, lookAt, erased=0;
376
c3828c03 377 // two modes - if toTrim is 0, just look through 0.1% of all records and nuke everything that is expired
bec87d21
BH
378 // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
379 if(toTrim)
380 lookAt=5*toTrim;
381 else
c3828c03 382 lookAt=cacheSize/1000;
bec87d21
BH
383
384 sequence_t::iterator iter=sidx.begin(), eiter;
385 for(; iter != sidx.end() && tried < lookAt ; ++tried) {
e93c956b
BH
386 unsigned int ttd=iter->getTTD();
387 if(ttd < now) {
bec87d21
BH
388 sidx.erase(iter++);
389 erased++;
eec1087c 390 }
bec87d21
BH
391 else
392 ++iter;
393
394 if(toTrim && erased > toTrim)
395 break;
eec1087c 396 }
bec87d21
BH
397
398 // cout<<"erased "<<erased<<" records based on ttd\n";
ca0b5def 399
bec87d21
BH
400 if(erased >= toTrim)
401 return;
402
403 // if(toTrim)
404 // cout<<"Still have "<<toTrim - erased<<" entries left to erase to meet target\n";
405
f2732984 406 toTrim -= erased;
bec87d21
BH
407
408 eiter=iter=sidx.begin();
7e6139ba 409 std::advance(eiter, toTrim);
bec87d21 410 sidx.erase(iter, eiter);
eec1087c
BH
411}
412