]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursor_cache.cc
Merge pull request #5236 from rgacogne/rec-doresolveat-refactor
[thirdparty/pdns.git] / pdns / recursor_cache.cc
CommitLineData
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
17unsigned 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
23unsigned 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 37int32_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 95bool 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 123void 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 186int 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 217bool 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 245uint64_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
271void 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}