]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursor_cache.cc
Merge pull request #8096 from mind04/pdns-notify-db-queries
[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
1ffea92c 17unsigned int MemRecursorCache::size() const
eec1087c 18{
705f31ae 19 return (unsigned int)d_cache.size();
43a2b29c
BH
20}
21
1ffea92c
RG
22size_t MemRecursorCache::ecsIndexSize() const
23{
24 return d_ecsIndex.size();
25}
26
8e42d27d 27// this function is too slow to poll!
1ffea92c 28unsigned int MemRecursorCache::bytes() const
43a2b29c
BH
29{
30 unsigned int ret=0;
31
4c8d2585 32 for(const auto& i : d_cache) {
9e5ed2e4 33 ret+=sizeof(struct CacheEntry);
4c8d2585
RG
34 ret+=(unsigned int)i.d_qname.toString().length();
35 for(const auto& record : i.d_records)
9b750357 36 ret+= sizeof(record); // XXX WRONG we don't know the stored size!
43a2b29c
BH
37 }
38 return ret;
eec1087c
BH
39}
40
4c8d2585 41int32_t MemRecursorCache::handleHit(MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
e74f866a
RG
42{
43 int32_t ttd = entry->d_ttd;
44
45 if(variable && !entry->d_netmask.empty()) {
46 *variable = true;
47 }
48
49 // cerr<<"Looking at "<<entry->d_records.size()<<" records for this name"<<endl;
50 if (res) {
4c8d2585
RG
51 res->reserve(res->size() + entry->d_records.size());
52
e74f866a
RG
53 for(const auto& k : entry->d_records) {
54 DNSRecord dr;
55 dr.d_name = qname;
56 dr.d_type = entry->d_qtype;
57 dr.d_class = QClass::IN;
58 dr.d_content = k;
59 dr.d_ttl = static_cast<uint32_t>(entry->d_ttd);
60 dr.d_place = DNSResourceRecord::ANSWER;
4c8d2585 61 res->push_back(std::move(dr));
e74f866a
RG
62 }
63 }
64
65 if(signatures) { // if you do an ANY lookup you are hosed XXXX
66 *signatures = entry->d_signatures;
67 }
68
69 if(authorityRecs) {
70 *authorityRecs = entry->d_authorityRecs;
71 }
72
73 if (state) {
74 *state = entry->d_state;
75 }
76
77 if (wasAuth) {
78 *wasAuth = entry->d_auth;
79 }
80
94306029 81 moveCacheItemToBack<SequencedTag>(d_cache, entry);
e74f866a
RG
82
83 return ttd;
84}
85
b0274132 86MemRecursorCache::cache_t::const_iterator MemRecursorCache::getEntryUsingECSIndex(time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who)
e74f866a 87{
b0274132
RG
88 auto ecsIndexKey = tie(qname, qtype);
89 auto ecsIndex = d_ecsIndex.find(ecsIndexKey);
90 if (ecsIndex != d_ecsIndex.end() && !ecsIndex->isEmpty()) {
e74f866a
RG
91 /* we have netmask-specific entries, let's see if we match one */
92 while (true) {
b0274132 93 const Netmask best = ecsIndex->lookupBestMatch(who);
e74f866a
RG
94 if (best.empty()) {
95 /* we have nothing more specific for you */
96 break;
97 }
e74f866a
RG
98 auto key = boost::make_tuple(qname, qtype, best);
99 auto entry = d_cache.find(key);
100 if (entry == d_cache.end()) {
b0274132
RG
101 /* ecsIndex is not up-to-date */
102 ecsIndex->removeNetmask(best);
103 if (ecsIndex->isEmpty()) {
104 d_ecsIndex.erase(ecsIndex);
e74f866a
RG
105 break;
106 }
107 continue;
108 }
109
110 if (entry->d_ttd > now) {
111 if (!requireAuth || entry->d_auth) {
112 return entry;
113 }
c8d4f498
RG
114 /* we need auth data and the best match is not authoritative */
115 return d_cache.end();
e74f866a
RG
116 }
117 else {
118 /* this netmask-specific entry has expired */
94306029 119 moveCacheItemToFront<SequencedTag>(d_cache, entry);
b0274132
RG
120 ecsIndex->removeNetmask(best);
121 if (ecsIndex->isEmpty()) {
122 d_ecsIndex.erase(ecsIndex);
e74f866a
RG
123 break;
124 }
125 }
126 }
127 }
128
129 /* we have nothing specific, let's see if we have a generic one */
130 auto key = boost::make_tuple(qname, qtype, Netmask());
131 auto entry = d_cache.find(key);
132 if (entry != d_cache.end()) {
133 if (entry->d_ttd > now) {
134 if (!requireAuth || entry->d_auth) {
135 return entry;
136 }
137 }
138 else {
94306029 139 moveCacheItemToFront<SequencedTag>(d_cache, entry);
e74f866a
RG
140 }
141 }
142
143 /* nothing for you, sorry */
144 return d_cache.end();
145}
146
d0cd0376 147// returns -1 for no hits
4c8d2585 148std::pair<MemRecursorCache::NameOnlyHashedTagIterator_t, MemRecursorCache::NameOnlyHashedTagIterator_t> MemRecursorCache::getEntries(const DNSName &qname, const QType& qt)
eec1087c 149{
e325f20c 150 // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
e325f20c 151 if(!d_cachecachevalid || d_cachedqname!= qname) {
b0c3b7d8 152 // cerr<<"had cache cache miss"<<endl;
4c8d2585
RG
153 d_cachedqname = qname;
154 const auto& idx = d_cache.get<NameOnlyHashedTag>();
155 d_cachecache = idx.equal_range(qname);
156 d_cachecachevalid = true;
cf98aa40 157 }
e325f20c 158 // else cerr<<"had cache cache hit!"<<endl;
748eff9f 159
787737ae
RG
160 return d_cachecache;
161}
162
4c8d2585 163bool MemRecursorCache::entryMatches(MemRecursorCache::OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who)
787737ae
RG
164{
165 if (requireAuth && !entry->d_auth)
166 return false;
167
e74f866a
RG
168 return ((entry->d_qtype == qt || qt == QType::ANY ||
169 (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
787737ae
RG
170 && (entry->d_netmask.empty() || entry->d_netmask.match(who)));
171}
172
173// returns -1 for no hits
174int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
175{
176 time_t ttd=0;
177 // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
e74f866a
RG
178 if(res) {
179 res->clear();
180 }
787737ae 181
e74f866a
RG
182 const uint16_t qtype = qt.getCode();
183 /* If we don't have any netmask-specific entries at all, let's just skip this
184 to be able to use the nice d_cachecache hack. */
b0274132 185 if (qtype != QType::ANY && !d_ecsIndex.empty()) {
e74f866a
RG
186 if (qtype == QType::ADDR) {
187 int32_t ret = -1;
787737ae 188
b0274132 189 auto entryA = getEntryUsingECSIndex(now, qname, QType::A, requireAuth, who);
e74f866a
RG
190 if (entryA != d_cache.end()) {
191 ret = handleHit(entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
192 }
b0274132 193 auto entryAAAA = getEntryUsingECSIndex(now, qname, QType::AAAA, requireAuth, who);
e74f866a 194 if (entryAAAA != d_cache.end()) {
4474afe1 195 int32_t ttdAAAA = handleHit(entryAAAA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
e74f866a
RG
196 if (ret > 0) {
197 ret = std::min(ret, ttdAAAA);
198 } else {
199 ret = ttdAAAA;
200 }
201 }
202 return ret > 0 ? static_cast<int32_t>(ret-now) : ret;
203 }
204 else {
b0274132 205 auto entry = getEntryUsingECSIndex(now, qname, qtype, requireAuth, who);
e74f866a
RG
206 if (entry != d_cache.end()) {
207 return static_cast<int32_t>(handleHit(entry, qname, who, res, signatures, authorityRecs, variable, state, wasAuth) - now);
208 }
209 return -1;
210 }
211 }
212
213 auto entries = getEntries(qname, qt);
ea634573 214
787737ae 215 if(entries.first!=entries.second) {
4c8d2585 216 for(auto i=entries.first; i != entries.second; ++i) {
787737ae 217
4c8d2585 218 auto firstIndexIterator = d_cache.project<OrderedTag>(i);
787737ae 219 if (i->d_ttd <= now) {
94306029 220 moveCacheItemToFront<SequencedTag>(d_cache, firstIndexIterator);
24bb9b58 221 continue;
787737ae 222 }
24bb9b58 223
4c8d2585 224 if (!entryMatches(firstIndexIterator, qtype, requireAuth, who))
787737ae
RG
225 continue;
226
4c8d2585 227 ttd = handleHit(firstIndexIterator, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
787737ae
RG
228
229 if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
230 break;
24bb9b58 231 }
b0d4fb45 232
4d2be65d 233 // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
6b68a4e3 234 return static_cast<int32_t>(ttd-now);
eec1087c 235 }
eec1087c
BH
236 return -1;
237}
34264879 238
e74f866a 239void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
eec1087c 240{
e74f866a 241 d_cachecachevalid = false;
f9c681bf 242 // cerr<<"Replacing "<<qname<<" for "<< (ednsmask ? ednsmask->toString() : "everyone") << endl;
80462253
SB
243 if (ednsmask) {
244 ednsmask = ednsmask->getNormalized();
245 }
e74f866a 246 auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
a072ce44 247 bool isNew = false;
e74f866a
RG
248 cache_t::iterator stored = d_cache.find(key);
249 if (stored == d_cache.end()) {
48f19abe 250 stored = d_cache.insert(CacheEntry(key, auth)).first;
a072ce44 251 isNew = true;
f9c681bf 252 }
e74f866a 253
d0fcc32b
RG
254 /* if we are inserting a new entry or updating an expired one (in which case the
255 ECS index might have been removed but the entry still exists because it has not
256 been garbage collected yet) we might need to update the ECS index.
257 Otherwise it should already be indexed and we don't need to update it.
258 */
259 if (isNew || stored->d_ttd <= now) {
260 /* don't bother building an ecsIndex if we don't have any netmask-specific entries */
261 if (ednsmask && !ednsmask->empty()) {
262 auto ecsIndexKey = boost::make_tuple(qname, qt.getCode());
263 auto ecsIndex = d_ecsIndex.find(ecsIndexKey);
264 if (ecsIndex == d_ecsIndex.end()) {
265 ecsIndex = d_ecsIndex.insert(ECSIndexEntry(qname, qt.getCode())).first;
266 }
267 ecsIndex->addMask(*ednsmask);
e74f866a 268 }
6c674e9a 269 }
270
6b68a4e3 271 time_t maxTTD=std::numeric_limits<time_t>::max();
d0cd0376 272 CacheEntry ce=*stored; // this is a COPY
e325f20c 273 ce.d_qtype=qt.getCode();
57769f13 274 ce.d_signatures=signatures;
2b984251 275 ce.d_authorityRecs=authorityRecs;
4d2be65d 276 ce.d_state=state;
fbb356b6 277
d0cd0376 278 // cerr<<"asked to store "<< (qname.empty() ? "EMPTY" : qname.toString()) <<"|"+qt.getName()<<" -> '";
279 // cerr<<(content.empty() ? string("EMPTY CONTENT") : content.begin()->d_content->getZoneRepresentation())<<"', auth="<<auth<<", ce.auth="<<ce.d_auth;
4d2be65d 280 // cerr<<", ednsmask: " << (ednsmask ? ednsmask->toString() : "none") <<endl;
a85eb653
BH
281
282 if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh?
e325f20c 283 if(ce.d_ttd > now) { // we still have valid data, ignore unauth data
bf4ab707 284 // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
a85eb653 285 return;
609a76b3
BH
286 }
287 else {
288 ce.d_auth = false; // new data won't be auth
289 }
a85eb653 290 }
d0cd0376 291
48f19abe
RG
292 // refuse any attempt to *raise* the TTL of auth NS records, as it would make it possible
293 // for an auth to keep a "ghost" zone alive forever, even after the delegation is gone from
294 // the parent
295 // BUT make sure that we CAN refresh the root
fbb356b6 296 if(ce.d_auth && auth && qt.getCode()==QType::NS && !isNew && !qname.isRoot()) {
297 // cerr<<"\tLimiting TTL of auth->auth NS set replace to "<<ce.d_ttd<<endl;
e325f20c 298 maxTTD = ce.d_ttd;
fc202159
PD
299 }
300
48f19abe 301 if(auth) {
34264879 302 ce.d_auth = true;
34264879 303 }
09a6f097 304
48f19abe
RG
305 ce.d_records.clear();
306 ce.d_records.reserve(content.size());
6196f908 307
e74f866a 308 for(const auto i : content) {
6b68a4e3
RG
309 /* Yes, we have altered the d_ttl value by adding time(nullptr) to it
310 prior to calling this function, so the TTL actually holds a TTD. */
e74f866a
RG
311 ce.d_ttd=min(maxTTD, static_cast<time_t>(i.d_ttl)); // XXX this does weird things if TTLs differ in the set
312 // cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
313 ce.d_records.push_back(i.d_content);
ea634573 314 }
bf4ab707 315
a072ce44 316 if (!isNew) {
94306029 317 moveCacheItemToBack<SequencedTag>(d_cache, stored);
a072ce44 318 }
ca0b5def 319 d_cache.replace(stored, ce);
eec1087c 320}
92294b60 321
86f3ca51 322int MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qtype)
748eff9f 323{
85c143a2 324 int count=0;
7e6139ba 325 d_cachecachevalid=false;
1ef00ba1 326
86f3ca51 327 if(!sub) {
4c8d2585
RG
328 auto& idx = d_cache.get<NameOnlyHashedTag>();
329 auto range = idx.equal_range(name);
330 for(auto& i=range.first; i != range.second; ) {
331 if (qtype == 0xffff || i->d_qtype == qtype) {
332 count++;
333 idx.erase(i++);
334 }
335 else {
336 ++i;
337 }
e74f866a 338 }
4c8d2585
RG
339 if (qtype == 0xffff) {
340 auto& ecsIdx = d_ecsIndex.get<OrderedTag>();
341 auto ecsIndexRange = ecsIdx.equal_range(name);
342 for(auto i = ecsIndexRange.first; i != ecsIndexRange.second; ) {
343 ecsIdx.erase(i++);
344 }
86f3ca51 345 }
4c8d2585
RG
346 else {
347 auto& ecsIdx = d_ecsIndex.get<HashedTag>();
348 auto ecsIndexRange = ecsIdx.equal_range(tie(name, qtype));
349 for(auto i = ecsIndexRange.first; i != ecsIndexRange.second; ) {
350 ecsIdx.erase(i++);
351 }
e74f866a 352 }
86f3ca51 353 }
354 else {
4c8d2585
RG
355 auto& idx = d_cache.get<OrderedTag>();
356 auto& ecsIdx = d_ecsIndex.get<OrderedTag>();
357
358 for(auto iter = idx.lower_bound(name); iter != idx.end(); ) {
86f3ca51 359 if(!iter->d_qname.isPartOf(name))
360 break;
361 if(iter->d_qtype == qtype || qtype == 0xffff) {
362 count++;
4c8d2585 363 idx.erase(iter++);
86f3ca51 364 }
365 else
366 iter++;
367 }
4c8d2585 368 for(auto iter = ecsIdx.lower_bound(name); iter != ecsIdx.end(); ) {
e74f866a
RG
369 if(!iter->d_qname.isPartOf(name))
370 break;
371 if(iter->d_qtype == qtype || qtype == 0xffff) {
4c8d2585 372 ecsIdx.erase(iter++);
e74f866a
RG
373 }
374 else {
375 iter++;
376 }
377 }
85c143a2
BH
378 }
379 return count;
748eff9f 380}
92294b60 381
6b68a4e3 382bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL)
a2b4f72f
BH
383{
384 cache_t::iterator iter = d_cache.find(tie(name, qtype));
e06f9f15 385 if(iter == d_cache.end()) {
a2b4f72f 386 return false;
e06f9f15
PD
387 }
388
389 CacheEntry ce = *iter;
6b68a4e3 390 if(ce.d_ttd < now)
a2b4f72f
BH
391 return false; // would be dead anyhow
392
6b68a4e3 393 uint32_t maxTTL = static_cast<uint32_t>(ce.d_ttd - now);
e06f9f15 394 if(maxTTL > newTTL) {
a2b4f72f
BH
395 d_cachecachevalid=false;
396
6b68a4e3 397 time_t newTTD = now + newTTL;
3ddb9247 398
e325f20c 399
400 if(ce.d_ttd > newTTD) // do never renew expired or older TTLs
401 ce.d_ttd = newTTD;
402
3ddb9247 403
a2b4f72f
BH
404 d_cache.replace(iter, ce);
405 return true;
406 }
407 return false;
408}
409
b9473937 410bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState, boost::optional<time_t> capTTD)
787737ae
RG
411{
412 bool updated = false;
e74f866a 413 uint16_t qtype = qt.getCode();
b0274132
RG
414 if (qtype != QType::ANY && qtype != QType::ADDR && !d_ecsIndex.empty()) {
415 auto entry = getEntryUsingECSIndex(now, qname, qtype, requireAuth, who);
e74f866a
RG
416 if (entry == d_cache.end()) {
417 return false;
418 }
419
420 entry->d_state = newState;
b9473937
RG
421 if (capTTD) {
422 entry->d_ttd = std::min(entry->d_ttd, *capTTD);
423 }
e74f866a
RG
424 return true;
425 }
426
787737ae
RG
427 auto entries = getEntries(qname, qt);
428
429 for(auto i = entries.first; i != entries.second; ++i) {
4c8d2585
RG
430 auto firstIndexIterator = d_cache.project<OrderedTag>(i);
431
432 if (!entryMatches(firstIndexIterator, qtype, requireAuth, who))
787737ae
RG
433 continue;
434
435 i->d_state = newState;
b9473937
RG
436 if (capTTD) {
437 i->d_ttd = std::min(i->d_ttd, *capTTD);
438 }
787737ae
RG
439 updated = true;
440
e74f866a 441 if(qtype != QType::ANY && qtype != QType::ADDR) // normally if we have a hit, we are done
787737ae 442 break;
787737ae
RG
443 }
444
445 return updated;
446}
447
d7948528 448uint64_t MemRecursorCache::doDump(int fd)
748eff9f 449{
5e1f23ca 450 auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(dup(fd), "w"), fclose);
d7948528
BH
451 if(!fp) { // dup probably failed
452 return 0;
748eff9f 453 }
5e1f23ca 454 fprintf(fp.get(), "; main record cache dump from thread follows\n;\n");
4c8d2585 455 const auto& sidx=d_cache.get<SequencedTag>();
bec87d21 456
d7948528 457 uint64_t count=0;
748eff9f 458 time_t now=time(0);
e74f866a
RG
459 for(const auto i : sidx) {
460 for(const auto j : i.d_records) {
d7948528 461 count++;
4327eadf 462 try {
5e1f23ca 463 fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i %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(), vStates[i.d_state], i.d_auth, i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str());
4327eadf
BH
464 }
465 catch(...) {
5e1f23ca 466 fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
4327eadf 467 }
748eff9f 468 }
d50e9bc8
PL
469 for(const auto &sig : i.d_signatures) {
470 count++;
471 try {
5e1f23ca 472 fprintf(fp.get(), "%s %" PRId64 " IN RRSIG %s ; %s\n", i.d_qname.toString().c_str(), static_cast<int64_t>(i.d_ttd - now), sig->getZoneRepresentation().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str());
d50e9bc8
PL
473 }
474 catch(...) {
5e1f23ca 475 fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
d50e9bc8
PL
476 }
477 }
748eff9f 478 }
d7948528 479 return count;
748eff9f 480}
43a2b29c 481
c68ae9fa 482void MemRecursorCache::doPrune(unsigned int keep)
eec1087c 483{
cf98aa40 484 d_cachecachevalid=false;
43a2b29c 485
94306029 486 pruneCollection<SequencedTag>(*this, d_cache, keep);
c68ae9fa
RG
487}
488