]>
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" |
92294b60 | 7 | |
eec1087c | 8 | using namespace std; |
ea634573 | 9 | using 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 |
16 | namespace __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 |
36 | string 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 | ||
51 | void 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 |
61 | DNSResourceRecord 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 | ||
102 | string 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 | |
128 | unsigned int MemRecursorCache::size() | |
129 | { | |
705f31ae | 130 | return (unsigned int)d_cache.size(); |
43a2b29c BH |
131 | } |
132 | ||
133 | unsigned 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 | 146 | int 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 |
201 | void 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 |
255 | void 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 |
261 | void 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 |
282 | void MemRecursorCache::doSlash(int perc) |
283 | { | |
284 | doPrune(); | |
285 | } | |
286 | ||
eec1087c BH |
287 | void 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 |