]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recpacketcache.cc
Merge pull request #5113 from RobinGeuze/addPerPoolPolicy
[thirdparty/pdns.git] / pdns / recpacketcache.cc
CommitLineData
870a0fe4
AT
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
3ea54bf0 4#include <iostream>
6b68a4e3 5#include <cinttypes>
fa8fd4d2 6
3ea54bf0 7#include "recpacketcache.hh"
38c9ceaa 8#include "cachecleaner.hh"
3ea54bf0 9#include "dns.hh"
09645ebb 10#include "dnsparser.hh"
3ea54bf0
BH
11#include "namespaces.hh"
12#include "lock.hh"
7313fe62 13#include "dnswriter.hh"
5c3b5e7f 14#include "ednsoptions.hh"
3ea54bf0
BH
15
16RecursorPacketCache::RecursorPacketCache()
17{
16beeaa4 18 d_hits = d_misses = 0;
3ea54bf0
BH
19}
20
65a60c2c 21int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
effe43d0
BH
22{
23 int count=0;
49a3500d 24 auto& idx = d_packetCache.get<NameTag>();
25 for(auto iter = idx.lower_bound(name); iter != idx.end(); ) {
65a60c2c 26 if(subtree) {
49a3500d 27 if(!iter->d_name.isPartOf(name)) { // this is case insensitive
65a60c2c 28 break;
29 }
30 }
31 else {
49a3500d 32 if(iter->d_name != name)
65a60c2c 33 break;
7313fe62 34 }
e9f63d47 35
49a3500d 36 if(qtype==0xffff || iter->d_type == qtype) {
37 iter=idx.erase(iter);
7313fe62 38 count++;
effe43d0 39 }
7313fe62 40 else
41 ++iter;
effe43d0
BH
42 }
43 return count;
44}
45
c15ff3df 46static bool qrMatch(const DNSName& qname, uint16_t qtype, uint16_t qclass, const DNSName& rname, uint16_t rtype, uint16_t rclass)
3ea54bf0 47{
a774e78b 48 // this ignores checking on the EDNS subnet flags!
e9f63d47 49 return qname==rname && rtype == qtype && rclass == qclass;
49a3500d 50}
51
52uint32_t RecursorPacketCache::canHashPacket(const std::string& origPacket)
53{
54 // return 42; // should still work if you do this!
55 uint32_t ret=0;
56 ret=burtle((const unsigned char*)origPacket.c_str() + 2, 10, ret); // rest of dnsheader, skip id
57 const char* end = origPacket.c_str() + origPacket.size();
58 const char* p = origPacket.c_str() + 12;
59
60 for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
6b68a4e3
RG
61 const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
62 ret=burtle(&l, 1, ret);
b3cb172a 63 } // XXX the embedded 0 in the qname will break the subnet stripping
c6fcaf44 64
b3cb172a 65 struct dnsheader* dh = (struct dnsheader*)origPacket.c_str();
c6fcaf44
RG
66 const char* skipBegin = p;
67 const char* skipEnd = p;
68 /* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
69 + OPT root label (1), type (2), class (2) and ttl (4)
70 + the OPT RR rdlen (2)
71 = 16
72 */
73 if(ntohs(dh->arcount)==1 && (p+16) < end) {
5c3b5e7f
RG
74 char* optionBegin = nullptr;
75 size_t optionLen = 0;
c6fcaf44
RG
76 /* skip the final empty label (1), the qtype (2), qclass (2) */
77 /* root label (1), type (2), class (2) and ttl (4) */
5c3b5e7f
RG
78 int res = getEDNSOption((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
79 if (res == 0) {
80 skipBegin = optionBegin;
81 skipEnd = optionBegin + optionLen;
b3cb172a 82 }
49a3500d 83 }
c6fcaf44
RG
84 if (skipBegin > p) {
85 //cout << "Hashing from " << (p-origPacket.c_str()) << " for " << skipBegin-p << "bytes, end is at "<< end-origPacket.c_str() << endl;
86 ret = burtle((const unsigned char*)p, skipBegin-p, ret);
87 }
88 if (skipEnd < end) {
89 //cout << "Hashing from " << (skipEnd-origPacket.c_str()) << " for " << end-skipEnd << "bytes, end is at " << end-origPacket.c_str() << endl;
90 ret = burtle((const unsigned char*) skipEnd, end-skipEnd, ret);
91 }
92 return ret;
49a3500d 93}
94
c15ff3df 95bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, RecProtoBufMessage* protobufMessage)
49a3500d 96{
49a3500d 97 for(auto iter = range.first ; iter != range.second ; ++ iter) {
98 // the possibility is VERY real that we get hits that are not right - birthday paradox
c15ff3df 99 if(!qrMatch(qname, qtype, qclass, iter->d_name, iter->d_type, iter->d_class))
49a3500d 100 continue;
6b68a4e3
RG
101 if(now < iter->d_ttd) { // it is right, it is fresh!
102 *age = static_cast<uint32_t>(now - iter->d_creation);
49a3500d 103 *responsePacket = iter->d_packet;
104 responsePacket->replace(0, 2, queryPacket.c_str(), 2);
90974597 105
49a3500d 106 string::size_type i=sizeof(dnsheader);
107
108 for(;;) {
c15ff3df 109 unsigned int labellen = (unsigned char)queryPacket[i];
49a3500d 110 if(!labellen || i + labellen > responsePacket->size()) break;
111 i++;
112 responsePacket->replace(i, labellen, queryPacket, i, labellen);
113 i = i + labellen;
114 }
3f3459f0 115
49a3500d 116 d_hits++;
117 moveCacheItemToBack(d_packetCache, iter);
02b47f43
RG
118#ifdef HAVE_PROTOBUF
119 if (protobufMessage) {
120 *protobufMessage = iter->d_protobufMessage;
121 }
122#endif
49a3500d 123
124 return true;
125 }
126 else {
127 moveCacheItemToFront(d_packetCache, iter);
128 d_misses++;
129 break;
130 }
3ea54bf0 131 }
49a3500d 132
3ea54bf0
BH
133 return false;
134}
135
c15ff3df
RG
136bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
137 std::string* responsePacket, uint32_t* age, uint32_t* qhash)
138{
139 return getResponsePacket(tag, queryPacket, now, responsePacket, age, qhash, nullptr);
140}
141
142bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
143 std::string* responsePacket, uint32_t* age, uint32_t* qhash)
144{
145 return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, qhash, nullptr);
146}
147
148bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
149 std::string* responsePacket, uint32_t* age, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
150{
151 *qhash = canHashPacket(queryPacket);
152 const auto& idx = d_packetCache.get<HashTag>();
153 auto range = idx.equal_range(tie(tag,*qhash));
154
155 if(range.first == range.second) {
156 d_misses++;
157 return false;
158 }
159
160 return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, protobufMessage);
161}
162
163bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
164 std::string* responsePacket, uint32_t* age, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
165{
166 *qhash = canHashPacket(queryPacket);
167 const auto& idx = d_packetCache.get<HashTag>();
168 auto range = idx.equal_range(tie(tag,*qhash));
169
170 if(range.first == range.second) {
171 d_misses++;
172 return false;
173 }
174
175 uint16_t qtype, qclass;
176 DNSName qname(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, &qtype, &qclass, 0);
177
178 return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, protobufMessage);
179}
180
49a3500d 181
e9f63d47 182void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl)
02b47f43 183{
e9f63d47 184 insertResponsePacket(tag, qhash, qname, qtype, qclass, responsePacket, now, ttl, nullptr);
02b47f43
RG
185}
186
e9f63d47 187void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, const RecProtoBufMessage* protobufMessage)
3ea54bf0 188{
49a3500d 189 auto& idx = d_packetCache.get<HashTag>();
190 auto range = idx.equal_range(tie(tag,qhash));
191 auto iter = range.first;
192
193 for( ; iter != range.second ; ++iter) {
e9f63d47 194 if(iter->d_type != qtype || iter->d_class != qclass)
49a3500d 195 continue;
196 // this only happens on insert which is relatively rare and does not need to be super fast
197 DNSName respname(iter->d_packet.c_str(), iter->d_packet.length(), sizeof(dnsheader), false, 0, 0, 0);
198 if(qname != respname)
199 continue;
a072ce44 200 moveCacheItemToBack(d_packetCache, iter);
3ea54bf0 201 iter->d_packet = responsePacket;
3f3459f0 202 iter->d_ttd = now + ttl;
2c73e580 203 iter->d_creation = now;
02b47f43
RG
204#ifdef HAVE_PROTOBUF
205 if (protobufMessage) {
206 iter->d_protobufMessage = *protobufMessage;
207 }
208#endif
209
49a3500d 210 break;
3ea54bf0 211 }
49a3500d 212
213 if(iter == range.second) { // nothing to refresh
214 struct Entry e;
215 e.d_packet = responsePacket;
216 e.d_name = qname;
217 e.d_qhash = qhash;
218 e.d_type = qtype;
e9f63d47 219 e.d_class = qclass;
49a3500d 220 e.d_ttd = now+ttl;
221 e.d_creation = now;
222 e.d_tag = tag;
02b47f43
RG
223#ifdef HAVE_PROTOBUF
224 if (protobufMessage) {
225 e.d_protobufMessage = *protobufMessage;
226 }
227#endif
3ea54bf0 228 d_packetCache.insert(e);
49a3500d 229 }
3ea54bf0
BH
230}
231
16beeaa4
BH
232uint64_t RecursorPacketCache::size()
233{
234 return d_packetCache.size();
235}
3f3459f0 236
0bbf7d0a
BH
237uint64_t RecursorPacketCache::bytes()
238{
239 uint64_t sum=0;
f226db2f 240 for(const auto& e : d_packetCache) {
0bbf7d0a
BH
241 sum += sizeof(e) + e.d_packet.length() + 4;
242 }
243 return sum;
244}
245
3f3459f0
BH
246void RecursorPacketCache::doPruneTo(unsigned int maxCached)
247{
38c9ceaa 248 pruneCollection(d_packetCache, maxCached);
3f3459f0
BH
249}
250
09645ebb 251uint64_t RecursorPacketCache::doDump(int fd)
252{
253 FILE* fp=fdopen(dup(fd), "w");
254 if(!fp) { // dup probably failed
255 return 0;
256 }
257 fprintf(fp, "; main packet cache dump from thread follows\n;\n");
258 const auto& sidx=d_packetCache.get<1>();
259
260 uint64_t count=0;
261 time_t now=time(0);
262 for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) {
263 count++;
264 try {
6b68a4e3 265 fprintf(fp, "%s %" PRId64 " %s ; tag %d\n", i->d_name.toString().c_str(), static_cast<int64_t>(i->d_ttd - now), DNSRecordContent::NumberToType(i->d_type).c_str(), i->d_tag);
09645ebb 266 }
267 catch(...) {
268 fprintf(fp, "; error printing '%s'\n", i->d_name.empty() ? "EMPTY" : i->d_name.toString().c_str());
269 }
270 }
271 fclose(fp);
272 return count;
273
274}