]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursor_cache.cc
4 #include "recursor_cache.hh"
7 #include "dnsrecords.hh"
8 #include "arguments.hh"
10 #include "recursor_cache.hh"
11 #include "cachecleaner.hh"
12 #include "namespaces.hh"
14 unsigned int MemRecursorCache::size()
16 return (unsigned int)d_cache
.size();
19 // this function is too slow to poll!
20 unsigned int MemRecursorCache::bytes()
24 for(cache_t::const_iterator i
=d_cache
.begin(); i
!=d_cache
.end(); ++i
) {
25 ret
+=sizeof(struct CacheEntry
);
26 ret
+=(unsigned int)i
->d_qname
.toString().length();
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();
33 // returns -1 for no hits
34 int MemRecursorCache::get(time_t now
, const DNSName
&qname
, const QType
& qt
, vector
<DNSRecord
>* res
, const ComboAddress
& who
, vector
<std::shared_ptr
<RRSIGRecordContent
>>* signatures
, bool* variable
)
37 // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
39 if(!d_cachecachevalid
|| d_cachedqname
!= qname
) {
40 // cerr<<"had cache cache miss"<<endl;
42 d_cachecache
=d_cache
.equal_range(tie(qname
));
43 d_cachecachevalid
=true;
45 // else cerr<<"had cache cache hit!"<<endl;
50 if(d_cachecache
.first
!=d_cachecache
.second
) {
51 for(cache_t::const_iterator i
=d_cachecache
.first
; i
!= d_cachecache
.second
; ++i
) {
53 if ((i
->d_qtype
== qt
.getCode() || qt
.getCode()==QType::ANY
||
54 (qt
.getCode()==QType::ADDR
&& (i
->d_qtype
== QType::A
|| i
->d_qtype
== QType::AAAA
) ))
55 && (i
->d_netmask
.empty() || i
->d_netmask
.match(who
))
57 if(variable
&& !i
->d_netmask
.empty()) {
63 // cerr<<"Looking at "<<i->d_records.size()<<" records for this name"<<endl;
64 for(auto k
=i
->d_records
.begin(); k
!= i
->d_records
.end(); ++k
) {
68 dr
.d_type
= i
->d_qtype
;
71 dr
.d_ttl
= static_cast<uint32_t>(i
->d_ttd
);
72 dr
.d_place
= DNSResourceRecord::ANSWER
;
77 if(signatures
) // if you do an ANY lookup you are hosed XXXX
78 *signatures
=i
->d_signatures
;
80 if(res
&& !res
->empty()) {
82 moveCacheItemToBack(d_cache
, i
);
85 if(qt
.getCode()!=QType::ANY
&& qt
.getCode()!=QType::ADDR
) // normally if we have a hit, we are done
90 moveCacheItemToFront(d_cache
, i
);
93 // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
101 bool MemRecursorCache::attemptToRefreshNSTTL(const QType
& qt
, const vector
<DNSRecord
>& content
, const CacheEntry
& stored
)
104 //~ cerr<<"feel free to scribble non-auth data!"<<endl;
108 if(qt
.getCode()!=QType::NS
) {
109 //~ cerr<<"Not NS record"<<endl;
112 if(content
.size()!=stored
.d_records
.size()) {
113 //~ cerr<<"Not equal number of records"<<endl;
116 if(stored
.d_records
.empty())
119 if(stored
.d_ttd
> content
.begin()->d_ttl
) {
120 //~ cerr<<"attempt to LOWER TTL - fine by us"<<endl;
125 // cerr<<"Returning true - update attempt!\n";
129 void 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
)
131 d_cachecachevalid
=false;
132 cache_t::iterator stored
;
134 auto key
=boost::make_tuple(qname
, qt
.getCode(), ednsmask
? *ednsmask
: Netmask());
135 stored
=d_cache
.find(key
);
136 if(stored
== d_cache
.end()) {
137 stored
=d_cache
.insert(CacheEntry(key
,CacheEntry::records_t(), auth
)).first
;
141 uint32_t maxTTD
=UINT_MAX
;
142 CacheEntry ce
=*stored
; // this is a COPY
143 ce
.d_qtype
=qt
.getCode();
144 ce
.d_signatures
=signatures
;
146 // cerr<<"asked to store "<< (qname.empty() ? "EMPTY" : qname.toString()) <<"|"+qt.getName()<<" -> '";
147 // cerr<<(content.empty() ? string("EMPTY CONTENT") : content.begin()->d_content->getZoneRepresentation())<<"', auth="<<auth<<", ce.auth="<<ce.d_auth;
148 // cerr<<", ednsmask: " << (ednsmask ? ednsmask->toString() : "none") <<endl;
150 if(!auth
&& ce
.d_auth
) { // unauth data came in, we have some auth data, but is it fresh?
151 if(ce
.d_ttd
> now
) { // we still have valid data, ignore unauth data
152 // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
156 ce
.d_auth
= false; // new data won't be auth
159 ce
.d_records
.clear();
161 // limit TTL of auth->auth NSset update if needed, except for root
162 if(ce
.d_auth
&& auth
&& qt
.getCode()==QType::NS
&& !isNew
&& !qname
.isRoot()) {
163 // cerr<<"\tLimiting TTL of auth->auth NS set replace to "<<ce.d_ttd<<endl;
167 // make sure that we CAN refresh the root
168 if(auth
&& (qname
.isRoot() || !attemptToRefreshNSTTL(qt
, content
, ce
) ) ) {
169 // cerr<<"\tGot auth data, and it was not refresh attempt of an unchanged NS set, nuking storage"<<endl;
170 ce
.d_records
.clear(); // clear non-auth data
173 // else cerr<<"\tNot nuking"<<endl;
176 for(auto i
=content
.cbegin(); i
!= content
.cend(); ++i
) {
178 ce
.d_ttd
=min(maxTTD
, i
->d_ttl
); // XXX this does weird things if TTLs differ in the set
179 // cerr<<"To store: "<<i->d_content->getZoneRepresentation()<<" with ttl/ttd "<<i->d_ttl<<", capped at: "<<maxTTD<<endl;
180 ce
.d_records
.push_back(i
->d_content
);
181 // there was code here that did things with TTL and auth. Unsure if it was good. XXX
185 moveCacheItemToBack(d_cache
, stored
);
187 d_cache
.replace(stored
, ce
);
190 int MemRecursorCache::doWipeCache(const DNSName
& name
, bool sub
, uint16_t qtype
)
193 d_cachecachevalid
=false;
194 pair
<cache_t::iterator
, cache_t::iterator
> range
;
198 range
=d_cache
.equal_range(tie(name
));
200 range
=d_cache
.equal_range(tie(name
, qtype
));
201 for(cache_t::const_iterator i
=range
.first
; i
!= range
.second
; ) {
207 for(auto iter
= d_cache
.lower_bound(tie(name
)); iter
!= d_cache
.end(); ) {
208 if(!iter
->d_qname
.isPartOf(name
))
210 if(iter
->d_qtype
== qtype
|| qtype
== 0xffff) {
212 d_cache
.erase(iter
++);
221 bool MemRecursorCache::doAgeCache(time_t now
, const DNSName
& name
, uint16_t qtype
, int32_t newTTL
)
223 cache_t::iterator iter
= d_cache
.find(tie(name
, qtype
));
224 uint32_t maxTTD
=std::numeric_limits
<uint32_t>::min();
225 if(iter
== d_cache
.end()) {
229 CacheEntry ce
= *iter
;
233 int32_t maxTTL
= maxTTD
- now
;
236 return false; // would be dead anyhow
238 if(maxTTL
> newTTL
) {
239 d_cachecachevalid
=false;
241 uint32_t newTTD
= now
+ newTTL
;
244 if(ce
.d_ttd
> newTTD
) // do never renew expired or older TTLs
248 d_cache
.replace(iter
, ce
);
254 uint64_t MemRecursorCache::doDumpNSSpeeds(int fd
)
256 FILE* fp
=fdopen(dup(fd
), "w");
259 fprintf(fp
, "; nsspeed dump from thread follows\n;\n");
262 for(SyncRes::nsspeeds_t::iterator i
= t_sstorage
->nsSpeeds
.begin() ; i
!= t_sstorage
->nsSpeeds
.end(); ++i
)
265 fprintf(fp
, "%s -> ", i
->first
.toString().c_str());
266 for(SyncRes::DecayingEwmaCollection::collection_t::iterator j
= i
->second
.d_collection
.begin(); j
!= i
->second
.d_collection
.end(); ++j
)
268 // typedef vector<pair<ComboAddress, DecayingEwma> > collection_t;
269 fprintf(fp
, "%s/%f ", j
->first
.toString().c_str(), j
->second
.peek());
277 uint64_t MemRecursorCache::doDump(int fd
)
279 FILE* fp
=fdopen(dup(fd
), "w");
280 if(!fp
) { // dup probably failed
283 fprintf(fp
, "; main record cache dump from thread follows\n;\n");
284 const auto& sidx
=d_cache
.get
<1>();
288 for(auto i
=sidx
.cbegin(); i
!= sidx
.cend(); ++i
) {
289 for(auto j
=i
->d_records
.cbegin(); j
!= i
->d_records
.cend(); ++j
) {
292 fprintf(fp
, "%s %d IN %s %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(), i
->d_netmask
.empty() ? "" : i
->d_netmask
.toString().c_str());
295 fprintf(fp
, "; error printing '%s'\n", i
->d_qname
.empty() ? "EMPTY" : i
->d_qname
.toString().c_str());
298 for(const auto &sig
: i
->d_signatures
) {
301 fprintf(fp
, "%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());
304 fprintf(fp
, "; error printing '%s'\n", i
->d_qname
.empty() ? "EMPTY" : i
->d_qname
.toString().c_str());
312 void MemRecursorCache::doPrune(void)
314 d_cachecachevalid
=false;
316 unsigned int maxCached
=::arg().asNum("max-cache-entries") / g_numThreads
;
317 pruneCollection(d_cache
, maxCached
);