]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursor_cache.cc
rec: Dump RRSIGs from record cache
[thirdparty/pdns.git] / pdns / recursor_cache.cc
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include "recursor_cache.hh"
5 #include "misc.hh"
6 #include <iostream>
7 #include "dnsrecords.hh"
8 #include "arguments.hh"
9 #include "syncres.hh"
10 #include "recursor_cache.hh"
11 #include "cachecleaner.hh"
12 #include "namespaces.hh"
13
14 unsigned int MemRecursorCache::size()
15 {
16 return (unsigned int)d_cache.size();
17 }
18
19 // this function is too slow to poll!
20 unsigned int MemRecursorCache::bytes()
21 {
22 unsigned int ret=0;
23
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();
29 }
30 return ret;
31 }
32
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)
35 {
36 unsigned int ttd=0;
37 // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
38
39 if(!d_cachecachevalid || d_cachedqname!= qname) {
40 // cerr<<"had cache cache miss"<<endl;
41 d_cachedqname=qname;
42 d_cachecache=d_cache.equal_range(tie(qname));
43 d_cachecachevalid=true;
44 }
45 // else cerr<<"had cache cache hit!"<<endl;
46
47 if(res)
48 res->clear();
49
50 if(d_cachecache.first!=d_cachecache.second) {
51 for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i) {
52 if(i->d_ttd > now) {
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))
56 ) {
57 if(variable && !i->d_netmask.empty()) {
58 *variable=true;
59 }
60
61 ttd = i->d_ttd;
62
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) {
65 if(res) {
66 DNSRecord dr;
67 dr.d_name = qname;
68 dr.d_type = i->d_qtype;
69 dr.d_class = 1;
70 dr.d_content = *k;
71 dr.d_ttl = static_cast<uint32_t>(i->d_ttd);
72 dr.d_place = DNSResourceRecord::ANSWER;
73 res->push_back(dr);
74 }
75 }
76
77 if(signatures) // if you do an ANY lookup you are hosed XXXX
78 *signatures=i->d_signatures;
79
80 if(res && !res->empty()) {
81 // cache hit
82 moveCacheItemToBack(d_cache, i);
83 }
84
85 if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
86 break;
87 }
88 } else {
89 // expired entry
90 moveCacheItemToFront(d_cache, i);
91 }
92 }
93 // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
94 return (int)ttd-now;
95 }
96 return -1;
97 }
98
99
100
101 bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const vector<DNSRecord>& content, const CacheEntry& stored)
102 {
103 if(!stored.d_auth) {
104 //~ cerr<<"feel free to scribble non-auth data!"<<endl;
105 return false;
106 }
107
108 if(qt.getCode()!=QType::NS) {
109 //~ cerr<<"Not NS record"<<endl;
110 return false;
111 }
112 if(content.size()!=stored.d_records.size()) {
113 //~ cerr<<"Not equal number of records"<<endl;
114 return false;
115 }
116 if(stored.d_records.empty())
117 return false;
118
119 if(stored.d_ttd > content.begin()->d_ttl) {
120 //~ cerr<<"attempt to LOWER TTL - fine by us"<<endl;
121 return false;
122 }
123
124
125 // cerr<<"Returning true - update attempt!\n";
126 return true;
127 }
128
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)
130 {
131 d_cachecachevalid=false;
132 cache_t::iterator stored;
133 bool isNew = false;
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;
138 isNew = true;
139 }
140
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;
145
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;
149
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";
153 return;
154 }
155 else {
156 ce.d_auth = false; // new data won't be auth
157 }
158 }
159 ce.d_records.clear();
160
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;
164 maxTTD = ce.d_ttd;
165 }
166
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
171 ce.d_auth = true;
172 }
173 // else cerr<<"\tNot nuking"<<endl;
174
175
176 for(auto i=content.cbegin(); i != content.cend(); ++i) {
177
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
182 }
183
184 if (!isNew) {
185 moveCacheItemToBack(d_cache, stored);
186 }
187 d_cache.replace(stored, ce);
188 }
189
190 int MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qtype)
191 {
192 int count=0;
193 d_cachecachevalid=false;
194 pair<cache_t::iterator, cache_t::iterator> range;
195
196 if(!sub) {
197 if(qtype==0xffff)
198 range=d_cache.equal_range(tie(name));
199 else
200 range=d_cache.equal_range(tie(name, qtype));
201 for(cache_t::const_iterator i=range.first; i != range.second; ) {
202 count++;
203 d_cache.erase(i++);
204 }
205 }
206 else {
207 for(auto iter = d_cache.lower_bound(tie(name)); iter != d_cache.end(); ) {
208 if(!iter->d_qname.isPartOf(name))
209 break;
210 if(iter->d_qtype == qtype || qtype == 0xffff) {
211 count++;
212 d_cache.erase(iter++);
213 }
214 else
215 iter++;
216 }
217 }
218 return count;
219 }
220
221 bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtype, int32_t newTTL)
222 {
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()) {
226 return false;
227 }
228
229 CacheEntry ce = *iter;
230
231
232 maxTTD=ce.d_ttd;
233 int32_t maxTTL = maxTTD - now;
234
235 if(maxTTL < 0)
236 return false; // would be dead anyhow
237
238 if(maxTTL > newTTL) {
239 d_cachecachevalid=false;
240
241 uint32_t newTTD = now + newTTL;
242
243
244 if(ce.d_ttd > newTTD) // do never renew expired or older TTLs
245 ce.d_ttd = newTTD;
246
247
248 d_cache.replace(iter, ce);
249 return true;
250 }
251 return false;
252 }
253
254 uint64_t MemRecursorCache::doDumpNSSpeeds(int fd)
255 {
256 FILE* fp=fdopen(dup(fd), "w");
257 if(!fp)
258 return 0;
259 fprintf(fp, "; nsspeed dump from thread follows\n;\n");
260 uint64_t count=0;
261
262 for(SyncRes::nsspeeds_t::iterator i = t_sstorage->nsSpeeds.begin() ; i!= t_sstorage->nsSpeeds.end(); ++i)
263 {
264 count++;
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)
267 {
268 // typedef vector<pair<ComboAddress, DecayingEwma> > collection_t;
269 fprintf(fp, "%s/%f ", j->first.toString().c_str(), j->second.peek());
270 }
271 fprintf(fp, "\n");
272 }
273 fclose(fp);
274 return count;
275 }
276
277 uint64_t MemRecursorCache::doDump(int fd)
278 {
279 FILE* fp=fdopen(dup(fd), "w");
280 if(!fp) { // dup probably failed
281 return 0;
282 }
283 fprintf(fp, "; main record cache dump from thread follows\n;\n");
284 const auto& sidx=d_cache.get<1>();
285
286 uint64_t count=0;
287 time_t now=time(0);
288 for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) {
289 for(auto j=i->d_records.cbegin(); j != i->d_records.cend(); ++j) {
290 count++;
291 try {
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());
293 }
294 catch(...) {
295 fprintf(fp, "; error printing '%s'\n", i->d_qname.empty() ? "EMPTY" : i->d_qname.toString().c_str());
296 }
297 }
298 for(const auto &sig : i->d_signatures) {
299 count++;
300 try {
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());
302 }
303 catch(...) {
304 fprintf(fp, "; error printing '%s'\n", i->d_qname.empty() ? "EMPTY" : i->d_qname.toString().c_str());
305 }
306 }
307 }
308 fclose(fp);
309 return count;
310 }
311
312 void MemRecursorCache::doPrune(void)
313 {
314 d_cachecachevalid=false;
315
316 unsigned int maxCached=::arg().asNum("max-cache-entries") / g_numThreads;
317 pruneCollection(d_cache, maxCached);
318 }