]>
Commit | Line | Data |
---|---|---|
870a0fe4 AT |
1 | #ifdef HAVE_CONFIG_H |
2 | #include "config.h" | |
3 | #endif | |
eec1087c BH |
4 | #include "recursor_cache.hh" |
5 | #include "misc.hh" | |
6 | #include <iostream> | |
ea634573 | 7 | #include "dnsrecords.hh" |
bec87d21 | 8 | #include "arguments.hh" |
0ba0f794 | 9 | #include "syncres.hh" |
34264879 | 10 | #include "recursor_cache.hh" |
38c9ceaa | 11 | #include "cachecleaner.hh" |
61b26744 | 12 | #include "namespaces.hh" |
eec1087c | 13 | |
eec1087c BH |
14 | unsigned int MemRecursorCache::size() |
15 | { | |
705f31ae | 16 | return (unsigned int)d_cache.size(); |
43a2b29c BH |
17 | } |
18 | ||
8e42d27d | 19 | // this function is too slow to poll! |
43a2b29c BH |
20 | unsigned int MemRecursorCache::bytes() |
21 | { | |
22 | unsigned int ret=0; | |
23 | ||
24 | for(cache_t::const_iterator i=d_cache.begin(); i!=d_cache.end(); ++i) { | |
9e5ed2e4 | 25 | ret+=sizeof(struct CacheEntry); |
c5c066bf | 26 | ret+=(unsigned int)i->d_qname.toString().length(); |
e325f20c | 27 | for(auto j=i->d_records.begin(); j!= i->d_records.end(); ++j) |
28 | ret+= sizeof(*j); // XXX WRONG we don't know the stored size! j->size(); | |
43a2b29c BH |
29 | } |
30 | return ret; | |
eec1087c BH |
31 | } |
32 | ||
376effcf | 33 | int MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures) |
eec1087c | 34 | { |
8a63d3ce | 35 | unsigned int ttd=0; |
e325f20c | 36 | // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n"; |
b0c3b7d8 | 37 | |
e325f20c | 38 | if(!d_cachecachevalid || d_cachedqname!= qname) { |
b0c3b7d8 BH |
39 | // cerr<<"had cache cache miss"<<endl; |
40 | d_cachedqname=qname; | |
41 | d_cachecache=d_cache.equal_range(tie(qname)); | |
42 | d_cachecachevalid=true; | |
cf98aa40 | 43 | } |
e325f20c | 44 | // else cerr<<"had cache cache hit!"<<endl; |
748eff9f | 45 | |
ea634573 BH |
46 | if(res) |
47 | res->clear(); | |
48 | ||
3ddb9247 PD |
49 | if(d_cachecache.first!=d_cachecache.second) { |
50 | for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i) | |
e325f20c | 51 | if(i->d_ttd > now && (i->d_qtype == qt.getCode() || qt.getCode()==QType::ANY || |
52 | (qt.getCode()==QType::ADDR && (i->d_qtype == QType::A || i->d_qtype == QType::AAAA) ) ) | |
3ddb9247 | 53 | ) { |
e325f20c | 54 | |
55 | ttd = i->d_ttd; | |
376effcf | 56 | auto records = &i->d_records; |
57 | if(!i->d_subnetspecific.empty()) { | |
58 | for(const auto& p : i->d_subnetspecific) { | |
59 | if(p.first.match(who)) { | |
60 | records = &p.second; | |
61 | break; | |
62 | } | |
63 | } | |
64 | } | |
65 | for(auto k=records->begin(); k != records->end(); ++k) { | |
e325f20c | 66 | if(res) { |
67 | DNSRecord dr; | |
68 | dr.d_name = qname; | |
69 | dr.d_type = i->d_qtype; | |
70 | dr.d_class = 1; | |
71 | dr.d_content = *k; | |
72 | dr.d_ttl = i->d_ttd; | |
e693ff5a | 73 | dr.d_place = DNSResourceRecord::ANSWER; |
e325f20c | 74 | res->push_back(dr); |
75 | } | |
76 | } | |
77 | ||
57769f13 | 78 | if(signatures) // if you do an ANY lookup you are hosed XXXX |
79 | *signatures=i->d_signatures; | |
4957a608 | 80 | if(res) { |
4957a608 | 81 | if(res->empty()) |
c2567ad1 | 82 | moveCacheItemToFront(d_cache, i); |
4957a608 | 83 | else |
c2567ad1 | 84 | moveCacheItemToBack(d_cache, i); |
4957a608 BH |
85 | } |
86 | if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done | |
87 | break; | |
e93c956b | 88 | } |
b0d4fb45 | 89 | |
ea634573 | 90 | // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n"; |
a2b4f72f | 91 | return (int)ttd-now; |
eec1087c | 92 | } |
eec1087c BH |
93 | return -1; |
94 | } | |
34264879 BH |
95 | |
96 | ||
3ddb9247 | 97 | |
e325f20c | 98 | bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const vector<DNSRecord>& content, const CacheEntry& stored) |
f9f2ae96 BH |
99 | { |
100 | if(!stored.d_auth) { | |
09a6f097 | 101 | //~ cerr<<"feel free to scribble non-auth data!"<<endl; |
f9f2ae96 BH |
102 | return false; |
103 | } | |
104 | ||
105 | if(qt.getCode()!=QType::NS) { | |
09a6f097 | 106 | //~ cerr<<"Not NS record"<<endl; |
f9f2ae96 BH |
107 | return false; |
108 | } | |
109 | if(content.size()!=stored.d_records.size()) { | |
09a6f097 | 110 | //~ cerr<<"Not equal number of records"<<endl; |
f9f2ae96 BH |
111 | return false; |
112 | } | |
113 | if(stored.d_records.empty()) | |
114 | return false; | |
115 | ||
e325f20c | 116 | if(stored.d_ttd > content.begin()->d_ttl) { |
09a6f097 | 117 | //~ cerr<<"attempt to LOWER TTL - fine by us"<<endl; |
f9f2ae96 BH |
118 | return false; |
119 | } | |
120 | ||
121 | ||
74191fb7 | 122 | // cerr<<"Returning true - update attempt!\n"; |
f9f2ae96 BH |
123 | return true; |
124 | } | |
125 | ||
376effcf | 126 | void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, bool auth, boost::optional<Netmask> ednsmask) |
eec1087c | 127 | { |
376effcf | 128 | if(ednsmask) { |
129 | cerr<<"This data is actually subnet mask specific!!"<<endl; | |
130 | } | |
cf98aa40 | 131 | d_cachecachevalid=false; |
c5c066bf | 132 | boost::tuple<DNSName, uint16_t> key=boost::make_tuple(qname, qt.getCode()); |
43a2b29c | 133 | cache_t::iterator stored=d_cache.find(key); |
fc202159 | 134 | uint32_t maxTTD=UINT_MAX; |
bec87d21 | 135 | |
43a2b29c | 136 | if(stored == d_cache.end()) { |
e325f20c | 137 | stored=d_cache.insert(CacheEntry(key,CacheEntry::records_t(), auth)).first; |
43a2b29c | 138 | } |
e325f20c | 139 | |
ca0b5def | 140 | CacheEntry ce=*stored; |
e325f20c | 141 | ce.d_qtype=qt.getCode(); |
57769f13 | 142 | ce.d_signatures=signatures; |
6fa14d00 | 143 | |
e325f20c | 144 | // cerr<<"asked to store "<< qname<<"|"+qt.getName()<<" -> '"<<content.begin()->d_content->getZoneRepresentation()<<"', auth="<<auth<<", ce.auth="<<ce.d_auth<<"\n"; |
145 | ||
376effcf | 146 | auto records = &ce.d_records; |
147 | if(ednsmask) { | |
148 | records=nullptr; // there, we use nullptr! | |
149 | ||
150 | /* so the logic here is.. if you update a specific mask, we'll update too. | |
151 | If you supply anything more generic, we'll store that next to your more | |
152 | specific answers. | |
153 | If you supply more specific answers, we'll store those too. */ | |
154 | for(auto &p : ce.d_subnetspecific) { | |
155 | if(p.first == *ednsmask) { | |
156 | records = &p.second; | |
157 | break; | |
158 | } | |
159 | } | |
160 | if(records==nullptr) { | |
161 | ce.d_subnetspecific.push_back({*ednsmask, CacheEntry::records_t()}); | |
162 | records = &ce.d_subnetspecific.rbegin()->second; | |
163 | } | |
164 | ||
165 | } | |
166 | records->clear(); | |
a85eb653 BH |
167 | |
168 | if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh? | |
e325f20c | 169 | if(ce.d_ttd > now) { // we still have valid data, ignore unauth data |
34264879 | 170 | // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n"; |
a85eb653 | 171 | return; |
609a76b3 BH |
172 | } |
173 | else { | |
174 | ce.d_auth = false; // new data won't be auth | |
175 | } | |
a85eb653 | 176 | } |
3ddb9247 | 177 | |
fc202159 | 178 | // limit TTL of auth->auth NSset update if needed, except for root |
e325f20c | 179 | if(ce.d_auth && auth && qt.getCode()==QType::NS && !(qname == DNSName())) { |
fc202159 | 180 | // cerr<<"\tLimiting TTL of auth->auth NS set replace"<<endl; |
e325f20c | 181 | maxTTD = ce.d_ttd; |
fc202159 PD |
182 | } |
183 | ||
74191fb7 | 184 | // make sure that we CAN refresh the root |
e325f20c | 185 | if(auth && ((qname == DNSName()) || !attemptToRefreshNSTTL(qt, content, ce) ) ) { |
fc202159 | 186 | // cerr<<"\tGot auth data, and it was not refresh attempt of an unchanged NS set, nuking storage"<<endl; |
376effcf | 187 | records->clear(); // clear non-auth data |
34264879 | 188 | ce.d_auth = true; |
34264879 | 189 | } |
2c242eb3 | 190 | // else cerr<<"\tNot nuking"<<endl; |
09a6f097 | 191 | |
6196f908 | 192 | |
09a6f097 | 193 | // cerr<<"\tHave "<<content.size()<<" records to store\n"; |
e325f20c | 194 | for(auto i=content.cbegin(); i != content.cend(); ++i) { |
fc202159 | 195 | // cerr<<"To store: "<<i->content<<" with ttl/ttd "<<i->ttl<<endl; |
e325f20c | 196 | ce.d_ttd=min(maxTTD, i->d_ttl); // XXX this does weird things if TTLs differ in the set |
376effcf | 197 | records->push_back(i->d_content); |
198 | // there was code here that did things with TTL and auth. Unsure if it was good. XXX | |
ea634573 | 199 | } |
552788ce | 200 | |
ca0b5def | 201 | d_cache.replace(stored, ce); |
eec1087c | 202 | } |
92294b60 | 203 | |
86f3ca51 | 204 | int MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qtype) |
748eff9f | 205 | { |
85c143a2 | 206 | int count=0; |
7e6139ba | 207 | d_cachecachevalid=false; |
1ef00ba1 | 208 | pair<cache_t::iterator, cache_t::iterator> range; |
1ef00ba1 | 209 | |
86f3ca51 | 210 | if(!sub) { |
211 | if(qtype==0xffff) | |
212 | range=d_cache.equal_range(tie(name)); | |
213 | else | |
214 | range=d_cache.equal_range(tie(name, qtype)); | |
215 | for(cache_t::const_iterator i=range.first; i != range.second; ) { | |
216 | count++; | |
217 | d_cache.erase(i++); | |
218 | } | |
219 | } | |
220 | else { | |
221 | for(auto iter = d_cache.lower_bound(tie(name)); iter != d_cache.end(); ) { | |
222 | if(!iter->d_qname.isPartOf(name)) | |
223 | break; | |
224 | if(iter->d_qtype == qtype || qtype == 0xffff) { | |
225 | count++; | |
226 | d_cache.erase(iter++); | |
227 | } | |
228 | else | |
229 | iter++; | |
230 | } | |
85c143a2 BH |
231 | } |
232 | return count; | |
748eff9f | 233 | } |
92294b60 | 234 | |
0ad83dab | 235 | bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtype, int32_t newTTL) |
a2b4f72f BH |
236 | { |
237 | cache_t::iterator iter = d_cache.find(tie(name, qtype)); | |
e06f9f15 PD |
238 | uint32_t maxTTD=std::numeric_limits<uint32_t>::min(); |
239 | if(iter == d_cache.end()) { | |
a2b4f72f | 240 | return false; |
e06f9f15 PD |
241 | } |
242 | ||
243 | CacheEntry ce = *iter; | |
244 | ||
e06f9f15 | 245 | |
e325f20c | 246 | maxTTD=ce.d_ttd; |
e06f9f15 | 247 | int32_t maxTTL = maxTTD - now; |
a2b4f72f | 248 | |
e06f9f15 | 249 | if(maxTTL < 0) |
a2b4f72f BH |
250 | return false; // would be dead anyhow |
251 | ||
e06f9f15 | 252 | if(maxTTL > newTTL) { |
a2b4f72f BH |
253 | d_cachecachevalid=false; |
254 | ||
e06f9f15 | 255 | uint32_t newTTD = now + newTTL; |
3ddb9247 | 256 | |
e325f20c | 257 | |
258 | if(ce.d_ttd > newTTD) // do never renew expired or older TTLs | |
259 | ce.d_ttd = newTTD; | |
260 | ||
3ddb9247 | 261 | |
a2b4f72f BH |
262 | d_cache.replace(iter, ce); |
263 | return true; | |
264 | } | |
265 | return false; | |
266 | } | |
267 | ||
a82ce718 PD |
268 | uint64_t MemRecursorCache::doDumpNSSpeeds(int fd) |
269 | { | |
270 | FILE* fp=fdopen(dup(fd), "w"); | |
271 | if(!fp) | |
272 | return 0; | |
273 | fprintf(fp, "; nsspeed dump from thread follows\n;\n"); | |
274 | uint64_t count=0; | |
275 | ||
276 | for(SyncRes::nsspeeds_t::iterator i = t_sstorage->nsSpeeds.begin() ; i!= t_sstorage->nsSpeeds.end(); ++i) | |
277 | { | |
278 | count++; | |
c5c066bf | 279 | fprintf(fp, "%s -> ", i->first.toString().c_str()); |
a82ce718 PD |
280 | for(SyncRes::DecayingEwmaCollection::collection_t::iterator j = i->second.d_collection.begin(); j!= i->second.d_collection.end(); ++j) |
281 | { | |
282 | // typedef vector<pair<ComboAddress, DecayingEwma> > collection_t; | |
283 | fprintf(fp, "%s/%f ", j->first.toString().c_str(), j->second.peek()); | |
284 | } | |
285 | fprintf(fp, "\n"); | |
286 | } | |
287 | fclose(fp); | |
288 | return count; | |
289 | } | |
a2b4f72f | 290 | |
d7948528 | 291 | uint64_t MemRecursorCache::doDump(int fd) |
748eff9f | 292 | { |
d7948528 BH |
293 | FILE* fp=fdopen(dup(fd), "w"); |
294 | if(!fp) { // dup probably failed | |
295 | return 0; | |
748eff9f | 296 | } |
fd2d69bb | 297 | fprintf(fp, "; main record cache dump from thread follows\n;\n"); |
86f3ca51 | 298 | |
299 | auto& sidx=d_cache.get<0>(); | |
bec87d21 | 300 | |
d7948528 | 301 | uint64_t count=0; |
748eff9f | 302 | time_t now=time(0); |
86f3ca51 | 303 | for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) { |
e325f20c | 304 | for(auto j=i->d_records.cbegin(); j != i->d_records.cend(); ++j) { |
d7948528 | 305 | count++; |
4327eadf | 306 | try { |
e325f20c | 307 | fprintf(fp, "%s %d IN %s %s\n", i->d_qname.toString().c_str(), (int32_t)(i->d_ttd - now), DNSRecordContent::NumberToType(i->d_qtype).c_str(), (*j)->getZoneRepresentation().c_str()); |
4327eadf BH |
308 | } |
309 | catch(...) { | |
c5c066bf | 310 | fprintf(fp, "; error printing '%s'\n", i->d_qname.toString().c_str()); |
4327eadf | 311 | } |
748eff9f BH |
312 | } |
313 | } | |
314 | fclose(fp); | |
d7948528 | 315 | return count; |
748eff9f | 316 | } |
43a2b29c | 317 | |
eec1087c BH |
318 | void MemRecursorCache::doPrune(void) |
319 | { | |
cf98aa40 | 320 | d_cachecachevalid=false; |
43a2b29c | 321 | |
c3828c03 | 322 | unsigned int maxCached=::arg().asNum("max-cache-entries") / g_numThreads; |
38c9ceaa | 323 | pruneCollection(d_cache, maxCached); |
eec1087c | 324 | } |