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