]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursor_cache.cc
edns subnet for the cache. All hooked up, untested.
[thirdparty/pdns.git] / pdns / recursor_cache.cc
CommitLineData
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
14unsigned 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
20unsigned 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 33int 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 98bool 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 126void 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 204int 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 235bool 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
268uint64_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 291uint64_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
318void 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}