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