]>
Commit | Line | Data |
---|---|---|
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 | 10 | using namespace std; |
61b26744 | 11 | #include "namespaces.hh" |
eec1087c | 12 | |
90a5cfe2 | 13 | #include "config.h" |
748eff9f | 14 | |
ea634573 BH |
15 | DNSResourceRecord 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 |
63 | string 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 | |
87 | unsigned int MemRecursorCache::size() | |
88 | { | |
705f31ae | 89 | return (unsigned int)d_cache.size(); |
43a2b29c BH |
90 | } |
91 | ||
92 | unsigned 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 | 104 | int 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 |
158 | bool 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 | 189 | void 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 | 285 | int 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 |
302 | bool 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 |
330 | void 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 |
356 | void 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 |