]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursor_cache.cc
rec: ensure correct service user on debian
[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
81 moveCacheItemToBack(d_cache, entry);
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 */
119 moveCacheItemToFront(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 {
139 moveCacheItemToFront(d_cache, entry);
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) {
4c8d2585 220 moveCacheItemToFront(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;
e74f866a 243 auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
a072ce44 244 bool isNew = false;
e74f866a
RG
245 cache_t::iterator stored = d_cache.find(key);
246 if (stored == d_cache.end()) {
48f19abe 247 stored = d_cache.insert(CacheEntry(key, auth)).first;
a072ce44 248 isNew = true;
f9c681bf 249 }
e74f866a 250
d0fcc32b
RG
251 /* if we are inserting a new entry or updating an expired one (in which case the
252 ECS index might have been removed but the entry still exists because it has not
253 been garbage collected yet) we might need to update the ECS index.
254 Otherwise it should already be indexed and we don't need to update it.
255 */
256 if (isNew || stored->d_ttd <= now) {
257 /* don't bother building an ecsIndex if we don't have any netmask-specific entries */
258 if (ednsmask && !ednsmask->empty()) {
259 auto ecsIndexKey = boost::make_tuple(qname, qt.getCode());
260 auto ecsIndex = d_ecsIndex.find(ecsIndexKey);
261 if (ecsIndex == d_ecsIndex.end()) {
262 ecsIndex = d_ecsIndex.insert(ECSIndexEntry(qname, qt.getCode())).first;
263 }
264 ecsIndex->addMask(*ednsmask);
e74f866a 265 }
6c674e9a 266 }
267
6b68a4e3 268 time_t maxTTD=std::numeric_limits<time_t>::max();
d0cd0376 269 CacheEntry ce=*stored; // this is a COPY
e325f20c 270 ce.d_qtype=qt.getCode();
57769f13 271 ce.d_signatures=signatures;
2b984251 272 ce.d_authorityRecs=authorityRecs;
4d2be65d 273 ce.d_state=state;
fbb356b6 274
d0cd0376 275 // cerr<<"asked to store "<< (qname.empty() ? "EMPTY" : qname.toString()) <<"|"+qt.getName()<<" -> '";
276 // cerr<<(content.empty() ? string("EMPTY CONTENT") : content.begin()->d_content->getZoneRepresentation())<<"', auth="<<auth<<", ce.auth="<<ce.d_auth;
4d2be65d 277 // cerr<<", ednsmask: " << (ednsmask ? ednsmask->toString() : "none") <<endl;
a85eb653
BH
278
279 if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh?
e325f20c 280 if(ce.d_ttd > now) { // we still have valid data, ignore unauth data
bf4ab707 281 // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
a85eb653 282 return;
609a76b3
BH
283 }
284 else {
285 ce.d_auth = false; // new data won't be auth
286 }
a85eb653 287 }
d0cd0376 288
48f19abe
RG
289 // refuse any attempt to *raise* the TTL of auth NS records, as it would make it possible
290 // for an auth to keep a "ghost" zone alive forever, even after the delegation is gone from
291 // the parent
292 // BUT make sure that we CAN refresh the root
fbb356b6 293 if(ce.d_auth && auth && qt.getCode()==QType::NS && !isNew && !qname.isRoot()) {
294 // cerr<<"\tLimiting TTL of auth->auth NS set replace to "<<ce.d_ttd<<endl;
e325f20c 295 maxTTD = ce.d_ttd;
fc202159
PD
296 }
297
48f19abe 298 if(auth) {
34264879 299 ce.d_auth = true;
34264879 300 }
09a6f097 301
48f19abe
RG
302 ce.d_records.clear();
303 ce.d_records.reserve(content.size());
6196f908 304
e74f866a 305 for(const auto i : content) {
6b68a4e3
RG
306 /* Yes, we have altered the d_ttl value by adding time(nullptr) to it
307 prior to calling this function, so the TTL actually holds a TTD. */
e74f866a
RG
308 ce.d_ttd=min(maxTTD, static_cast<time_t>(i.d_ttl)); // XXX this does weird things if TTLs differ in the set
309 // cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
310 ce.d_records.push_back(i.d_content);
ea634573 311 }
bf4ab707 312
a072ce44
RG
313 if (!isNew) {
314 moveCacheItemToBack(d_cache, stored);
315 }
ca0b5def 316 d_cache.replace(stored, ce);
eec1087c 317}
92294b60 318
86f3ca51 319int MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qtype)
748eff9f 320{
85c143a2 321 int count=0;
7e6139ba 322 d_cachecachevalid=false;
1ef00ba1 323
86f3ca51 324 if(!sub) {
4c8d2585
RG
325 auto& idx = d_cache.get<NameOnlyHashedTag>();
326 auto range = idx.equal_range(name);
327 for(auto& i=range.first; i != range.second; ) {
328 if (qtype == 0xffff || i->d_qtype == qtype) {
329 count++;
330 idx.erase(i++);
331 }
332 else {
333 ++i;
334 }
e74f866a 335 }
4c8d2585
RG
336 if (qtype == 0xffff) {
337 auto& ecsIdx = d_ecsIndex.get<OrderedTag>();
338 auto ecsIndexRange = ecsIdx.equal_range(name);
339 for(auto i = ecsIndexRange.first; i != ecsIndexRange.second; ) {
340 ecsIdx.erase(i++);
341 }
86f3ca51 342 }
4c8d2585
RG
343 else {
344 auto& ecsIdx = d_ecsIndex.get<HashedTag>();
345 auto ecsIndexRange = ecsIdx.equal_range(tie(name, qtype));
346 for(auto i = ecsIndexRange.first; i != ecsIndexRange.second; ) {
347 ecsIdx.erase(i++);
348 }
e74f866a 349 }
86f3ca51 350 }
351 else {
4c8d2585
RG
352 auto& idx = d_cache.get<OrderedTag>();
353 auto& ecsIdx = d_ecsIndex.get<OrderedTag>();
354
355 for(auto iter = idx.lower_bound(name); iter != idx.end(); ) {
86f3ca51 356 if(!iter->d_qname.isPartOf(name))
357 break;
358 if(iter->d_qtype == qtype || qtype == 0xffff) {
359 count++;
4c8d2585 360 idx.erase(iter++);
86f3ca51 361 }
362 else
363 iter++;
364 }
4c8d2585 365 for(auto iter = ecsIdx.lower_bound(name); iter != ecsIdx.end(); ) {
e74f866a
RG
366 if(!iter->d_qname.isPartOf(name))
367 break;
368 if(iter->d_qtype == qtype || qtype == 0xffff) {
4c8d2585 369 ecsIdx.erase(iter++);
e74f866a
RG
370 }
371 else {
372 iter++;
373 }
374 }
85c143a2
BH
375 }
376 return count;
748eff9f 377}
92294b60 378
6b68a4e3 379bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL)
a2b4f72f
BH
380{
381 cache_t::iterator iter = d_cache.find(tie(name, qtype));
e06f9f15 382 if(iter == d_cache.end()) {
a2b4f72f 383 return false;
e06f9f15
PD
384 }
385
386 CacheEntry ce = *iter;
6b68a4e3 387 if(ce.d_ttd < now)
a2b4f72f
BH
388 return false; // would be dead anyhow
389
6b68a4e3 390 uint32_t maxTTL = static_cast<uint32_t>(ce.d_ttd - now);
e06f9f15 391 if(maxTTL > newTTL) {
a2b4f72f
BH
392 d_cachecachevalid=false;
393
6b68a4e3 394 time_t newTTD = now + newTTL;
3ddb9247 395
e325f20c 396
397 if(ce.d_ttd > newTTD) // do never renew expired or older TTLs
398 ce.d_ttd = newTTD;
399
3ddb9247 400
a2b4f72f
BH
401 d_cache.replace(iter, ce);
402 return true;
403 }
404 return false;
405}
406
b9473937 407bool 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
408{
409 bool updated = false;
e74f866a 410 uint16_t qtype = qt.getCode();
b0274132
RG
411 if (qtype != QType::ANY && qtype != QType::ADDR && !d_ecsIndex.empty()) {
412 auto entry = getEntryUsingECSIndex(now, qname, qtype, requireAuth, who);
e74f866a
RG
413 if (entry == d_cache.end()) {
414 return false;
415 }
416
417 entry->d_state = newState;
b9473937
RG
418 if (capTTD) {
419 entry->d_ttd = std::min(entry->d_ttd, *capTTD);
420 }
e74f866a
RG
421 return true;
422 }
423
787737ae
RG
424 auto entries = getEntries(qname, qt);
425
426 for(auto i = entries.first; i != entries.second; ++i) {
4c8d2585
RG
427 auto firstIndexIterator = d_cache.project<OrderedTag>(i);
428
429 if (!entryMatches(firstIndexIterator, qtype, requireAuth, who))
787737ae
RG
430 continue;
431
432 i->d_state = newState;
b9473937
RG
433 if (capTTD) {
434 i->d_ttd = std::min(i->d_ttd, *capTTD);
435 }
787737ae
RG
436 updated = true;
437
e74f866a 438 if(qtype != QType::ANY && qtype != QType::ADDR) // normally if we have a hit, we are done
787737ae 439 break;
787737ae
RG
440 }
441
442 return updated;
443}
444
d7948528 445uint64_t MemRecursorCache::doDump(int fd)
748eff9f 446{
5e1f23ca 447 auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(dup(fd), "w"), fclose);
d7948528
BH
448 if(!fp) { // dup probably failed
449 return 0;
748eff9f 450 }
5e1f23ca 451 fprintf(fp.get(), "; main record cache dump from thread follows\n;\n");
4c8d2585 452 const auto& sidx=d_cache.get<SequencedTag>();
bec87d21 453
d7948528 454 uint64_t count=0;
748eff9f 455 time_t now=time(0);
e74f866a
RG
456 for(const auto i : sidx) {
457 for(const auto j : i.d_records) {
d7948528 458 count++;
4327eadf 459 try {
5e1f23ca 460 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
461 }
462 catch(...) {
5e1f23ca 463 fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
4327eadf 464 }
748eff9f 465 }
d50e9bc8
PL
466 for(const auto &sig : i.d_signatures) {
467 count++;
468 try {
5e1f23ca 469 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
470 }
471 catch(...) {
5e1f23ca 472 fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
d50e9bc8
PL
473 }
474 }
748eff9f 475 }
d7948528 476 return count;
748eff9f 477}
43a2b29c 478
c68ae9fa 479void MemRecursorCache::doPrune(unsigned int keep)
eec1087c 480{
cf98aa40 481 d_cachecachevalid=false;
43a2b29c 482
c68ae9fa
RG
483 pruneCollection(*this, d_cache, keep);
484}
485