7 #include "recursor_cache.hh"
10 #include "dnsrecords.hh"
11 #include "arguments.hh"
13 #include "recursor_cache.hh"
14 #include "cachecleaner.hh"
15 #include "namespaces.hh"
17 unsigned int MemRecursorCache::size() const
19 return (unsigned int)d_cache
.size();
22 size_t MemRecursorCache::ecsIndexSize() const
24 return d_ecsIndex
.size();
27 // this function is too slow to poll!
28 unsigned int MemRecursorCache::bytes() const
32 for(const auto& i
: d_cache
) {
33 ret
+=sizeof(struct CacheEntry
);
34 ret
+=(unsigned int)i
.d_qname
.toString().length();
35 for(const auto& record
: i
.d_records
)
36 ret
+= sizeof(record
); // XXX WRONG we don't know the stored size!
41 int32_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
)
43 int32_t ttd
= entry
->d_ttd
;
45 if(variable
&& !entry
->d_netmask
.empty()) {
49 // cerr<<"Looking at "<<entry->d_records.size()<<" records for this name"<<endl;
51 res
->reserve(res
->size() + entry
->d_records
.size());
53 for(const auto& k
: entry
->d_records
) {
56 dr
.d_type
= entry
->d_qtype
;
57 dr
.d_class
= QClass::IN
;
59 dr
.d_ttl
= static_cast<uint32_t>(entry
->d_ttd
);
60 dr
.d_place
= DNSResourceRecord::ANSWER
;
61 res
->push_back(std::move(dr
));
65 if(signatures
) { // if you do an ANY lookup you are hosed XXXX
66 *signatures
= entry
->d_signatures
;
70 *authorityRecs
= entry
->d_authorityRecs
;
74 *state
= entry
->d_state
;
78 *wasAuth
= entry
->d_auth
;
81 moveCacheItemToBack(d_cache
, entry
);
86 MemRecursorCache::cache_t::const_iterator
MemRecursorCache::getEntryUsingECSIndex(time_t now
, const DNSName
&qname
, uint16_t qtype
, bool requireAuth
, const ComboAddress
& who
)
88 auto ecsIndexKey
= tie(qname
, qtype
);
89 auto ecsIndex
= d_ecsIndex
.find(ecsIndexKey
);
90 if (ecsIndex
!= d_ecsIndex
.end() && !ecsIndex
->isEmpty()) {
91 /* we have netmask-specific entries, let's see if we match one */
93 const Netmask best
= ecsIndex
->lookupBestMatch(who
);
95 /* we have nothing more specific for you */
98 auto key
= boost::make_tuple(qname
, qtype
, best
);
99 auto entry
= d_cache
.find(key
);
100 if (entry
== d_cache
.end()) {
101 /* ecsIndex is not up-to-date */
102 ecsIndex
->removeNetmask(best
);
103 if (ecsIndex
->isEmpty()) {
104 d_ecsIndex
.erase(ecsIndex
);
110 if (entry
->d_ttd
> now
) {
111 if (!requireAuth
|| entry
->d_auth
) {
114 /* we need auth data and the best match is not authoritative */
115 return d_cache
.end();
118 /* this netmask-specific entry has expired */
119 moveCacheItemToFront(d_cache
, entry
);
120 ecsIndex
->removeNetmask(best
);
121 if (ecsIndex
->isEmpty()) {
122 d_ecsIndex
.erase(ecsIndex
);
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
) {
139 moveCacheItemToFront(d_cache
, entry
);
143 /* nothing for you, sorry */
144 return d_cache
.end();
147 // returns -1 for no hits
148 std::pair
<MemRecursorCache::NameOnlyHashedTagIterator_t
, MemRecursorCache::NameOnlyHashedTagIterator_t
> MemRecursorCache::getEntries(const DNSName
&qname
, const QType
& qt
)
150 // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
151 if(!d_cachecachevalid
|| d_cachedqname
!= qname
) {
152 // cerr<<"had cache cache miss"<<endl;
153 d_cachedqname
= qname
;
154 const auto& idx
= d_cache
.get
<NameOnlyHashedTag
>();
155 d_cachecache
= idx
.equal_range(qname
);
156 d_cachecachevalid
= true;
158 // else cerr<<"had cache cache hit!"<<endl;
163 bool MemRecursorCache::entryMatches(MemRecursorCache::OrderedTagIterator_t
& entry
, uint16_t qt
, bool requireAuth
, const ComboAddress
& who
)
165 if (requireAuth
&& !entry
->d_auth
)
168 return ((entry
->d_qtype
== qt
|| qt
== QType::ANY
||
169 (qt
== QType::ADDR
&& (entry
->d_qtype
== QType::A
|| entry
->d_qtype
== QType::AAAA
)))
170 && (entry
->d_netmask
.empty() || entry
->d_netmask
.match(who
)));
173 // returns -1 for no hits
174 int32_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
)
177 // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
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. */
185 if (qtype
!= QType::ANY
&& !d_ecsIndex
.empty()) {
186 if (qtype
== QType::ADDR
) {
189 auto entryA
= getEntryUsingECSIndex(now
, qname
, QType::A
, requireAuth
, who
);
190 if (entryA
!= d_cache
.end()) {
191 ret
= handleHit(entryA
, qname
, who
, res
, signatures
, authorityRecs
, variable
, state
, wasAuth
);
193 auto entryAAAA
= getEntryUsingECSIndex(now
, qname
, QType::AAAA
, requireAuth
, who
);
194 if (entryAAAA
!= d_cache
.end()) {
195 int32_t ttdAAAA
= handleHit(entryAAAA
, qname
, who
, res
, signatures
, authorityRecs
, variable
, state
, wasAuth
);
197 ret
= std::min(ret
, ttdAAAA
);
202 return ret
> 0 ? static_cast<int32_t>(ret
-now
) : ret
;
205 auto entry
= getEntryUsingECSIndex(now
, qname
, qtype
, requireAuth
, who
);
206 if (entry
!= d_cache
.end()) {
207 return static_cast<int32_t>(handleHit(entry
, qname
, who
, res
, signatures
, authorityRecs
, variable
, state
, wasAuth
) - now
);
213 auto entries
= getEntries(qname
, qt
);
215 if(entries
.first
!=entries
.second
) {
216 for(auto i
=entries
.first
; i
!= entries
.second
; ++i
) {
218 auto firstIndexIterator
= d_cache
.project
<OrderedTag
>(i
);
219 if (i
->d_ttd
<= now
) {
220 moveCacheItemToFront(d_cache
, firstIndexIterator
);
224 if (!entryMatches(firstIndexIterator
, qtype
, requireAuth
, who
))
227 ttd
= handleHit(firstIndexIterator
, qname
, who
, res
, signatures
, authorityRecs
, variable
, state
, wasAuth
);
229 if(qt
.getCode()!=QType::ANY
&& qt
.getCode()!=QType::ADDR
) // normally if we have a hit, we are done
233 // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
234 return static_cast<int32_t>(ttd
-now
);
239 void 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
)
241 d_cachecachevalid
= false;
242 // cerr<<"Replacing "<<qname<<" for "<< (ednsmask ? ednsmask->toString() : "everyone") << endl;
243 auto key
= boost::make_tuple(qname
, qt
.getCode(), ednsmask
? *ednsmask
: Netmask());
245 cache_t::iterator stored
= d_cache
.find(key
);
246 if (stored
== d_cache
.end()) {
247 stored
= d_cache
.insert(CacheEntry(key
, auth
)).first
;
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.
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
;
264 ecsIndex
->addMask(*ednsmask
);
268 time_t maxTTD
=std::numeric_limits
<time_t>::max();
269 CacheEntry ce
=*stored
; // this is a COPY
270 ce
.d_qtype
=qt
.getCode();
271 ce
.d_signatures
=signatures
;
272 ce
.d_authorityRecs
=authorityRecs
;
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;
277 // cerr<<", ednsmask: " << (ednsmask ? ednsmask->toString() : "none") <<endl;
279 if(!auth
&& ce
.d_auth
) { // unauth data came in, we have some auth data, but is it fresh?
280 if(ce
.d_ttd
> now
) { // we still have valid data, ignore unauth data
281 // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
285 ce
.d_auth
= false; // new data won't be auth
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
292 // BUT make sure that we CAN refresh the root
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;
302 ce
.d_records
.clear();
303 ce
.d_records
.reserve(content
.size());
305 for(const auto i
: content
) {
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. */
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
);
314 moveCacheItemToBack(d_cache
, stored
);
316 d_cache
.replace(stored
, ce
);
319 int MemRecursorCache::doWipeCache(const DNSName
& name
, bool sub
, uint16_t qtype
)
322 d_cachecachevalid
=false;
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
) {
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
; ) {
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
; ) {
352 auto& idx
= d_cache
.get
<OrderedTag
>();
353 auto& ecsIdx
= d_ecsIndex
.get
<OrderedTag
>();
355 for(auto iter
= idx
.lower_bound(name
); iter
!= idx
.end(); ) {
356 if(!iter
->d_qname
.isPartOf(name
))
358 if(iter
->d_qtype
== qtype
|| qtype
== 0xffff) {
365 for(auto iter
= ecsIdx
.lower_bound(name
); iter
!= ecsIdx
.end(); ) {
366 if(!iter
->d_qname
.isPartOf(name
))
368 if(iter
->d_qtype
== qtype
|| qtype
== 0xffff) {
369 ecsIdx
.erase(iter
++);
379 bool MemRecursorCache::doAgeCache(time_t now
, const DNSName
& name
, uint16_t qtype
, uint32_t newTTL
)
381 cache_t::iterator iter
= d_cache
.find(tie(name
, qtype
));
382 if(iter
== d_cache
.end()) {
386 CacheEntry ce
= *iter
;
388 return false; // would be dead anyhow
390 uint32_t maxTTL
= static_cast<uint32_t>(ce
.d_ttd
- now
);
391 if(maxTTL
> newTTL
) {
392 d_cachecachevalid
=false;
394 time_t newTTD
= now
+ newTTL
;
397 if(ce
.d_ttd
> newTTD
) // do never renew expired or older TTLs
401 d_cache
.replace(iter
, ce
);
407 bool MemRecursorCache::updateValidationStatus(time_t now
, const DNSName
&qname
, const QType
& qt
, const ComboAddress
& who
, bool requireAuth
, vState newState
, boost::optional
<time_t> capTTD
)
409 bool updated
= false;
410 uint16_t qtype
= qt
.getCode();
411 if (qtype
!= QType::ANY
&& qtype
!= QType::ADDR
&& !d_ecsIndex
.empty()) {
412 auto entry
= getEntryUsingECSIndex(now
, qname
, qtype
, requireAuth
, who
);
413 if (entry
== d_cache
.end()) {
417 entry
->d_state
= newState
;
419 entry
->d_ttd
= std::min(entry
->d_ttd
, *capTTD
);
424 auto entries
= getEntries(qname
, qt
);
426 for(auto i
= entries
.first
; i
!= entries
.second
; ++i
) {
427 auto firstIndexIterator
= d_cache
.project
<OrderedTag
>(i
);
429 if (!entryMatches(firstIndexIterator
, qtype
, requireAuth
, who
))
432 i
->d_state
= newState
;
434 i
->d_ttd
= std::min(i
->d_ttd
, *capTTD
);
438 if(qtype
!= QType::ANY
&& qtype
!= QType::ADDR
) // normally if we have a hit, we are done
445 uint64_t MemRecursorCache::doDump(int fd
)
447 auto fp
= std::unique_ptr
<FILE, int(*)(FILE*)>(fdopen(dup(fd
), "w"), fclose
);
448 if(!fp
) { // dup probably failed
451 fprintf(fp
.get(), "; main record cache dump from thread follows\n;\n");
452 const auto& sidx
=d_cache
.get
<SequencedTag
>();
456 for(const auto i
: sidx
) {
457 for(const auto j
: i
.d_records
) {
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());
463 fprintf(fp
.get(), "; error printing '%s'\n", i
.d_qname
.empty() ? "EMPTY" : i
.d_qname
.toString().c_str());
466 for(const auto &sig
: i
.d_signatures
) {
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());
472 fprintf(fp
.get(), "; error printing '%s'\n", i
.d_qname
.empty() ? "EMPTY" : i
.d_qname
.toString().c_str());
479 void MemRecursorCache::doPrune(unsigned int keep
)
481 d_cachecachevalid
=false;
483 pruneCollection(*this, d_cache
, keep
);