]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recpacketcache.cc
rec: Set ecs-ipv4-cache-bits and ecs-ipv6-cache-bits in the tests
[thirdparty/pdns.git] / pdns / recpacketcache.cc
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <iostream>
5 #include <cinttypes>
6
7 #include "recpacketcache.hh"
8 #include "cachecleaner.hh"
9 #include "dns.hh"
10 #include "dnsparser.hh"
11 #include "namespaces.hh"
12 #include "lock.hh"
13 #include "dnswriter.hh"
14 #include "ednsoptions.hh"
15
16 RecursorPacketCache::RecursorPacketCache()
17 {
18 d_hits = d_misses = 0;
19 }
20
21 int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
22 {
23 int count=0;
24 auto& idx = d_packetCache.get<NameTag>();
25 for(auto iter = idx.lower_bound(name); iter != idx.end(); ) {
26 if(subtree) {
27 if(!iter->d_name.isPartOf(name)) { // this is case insensitive
28 break;
29 }
30 }
31 else {
32 if(iter->d_name != name)
33 break;
34 }
35
36 if(qtype==0xffff || iter->d_type == qtype) {
37 iter=idx.erase(iter);
38 count++;
39 }
40 else
41 ++iter;
42 }
43 return count;
44 }
45
46 bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t ecsBegin, uint16_t ecsEnd)
47 {
48 // this ignores checking on the EDNS subnet flags!
49 if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
50 return false;
51 }
52
53 if (iter->d_ecsBegin != ecsBegin || iter->d_ecsEnd != ecsEnd) {
54 return false;
55 }
56
57 return queryMatches(iter->d_query, queryPacket, qname, ecsBegin, ecsEnd);
58 }
59
60 bool 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, uint16_t ecsBegin, uint16_t ecsEnd)
61 {
62 for(auto iter = range.first ; iter != range.second ; ++iter) {
63 // the possibility is VERY real that we get hits that are not right - birthday paradox
64 if (!qrMatch(iter, queryPacket, qname, qtype, qclass, ecsBegin, ecsEnd)) {
65 continue;
66 }
67
68 if (now < iter->d_ttd) { // it is right, it is fresh!
69 *age = static_cast<uint32_t>(now - iter->d_creation);
70 *responsePacket = iter->d_packet;
71 responsePacket->replace(0, 2, queryPacket.c_str(), 2);
72
73 string::size_type i=sizeof(dnsheader);
74
75 for(;;) {
76 unsigned int labellen = (unsigned char)queryPacket[i];
77 if(!labellen || i + labellen > responsePacket->size()) break;
78 i++;
79 responsePacket->replace(i, labellen, queryPacket, i, labellen);
80 i = i + labellen;
81 }
82
83 d_hits++;
84 moveCacheItemToBack(d_packetCache, iter);
85 #ifdef HAVE_PROTOBUF
86 if (protobufMessage) {
87 *protobufMessage = iter->d_protobufMessage;
88 }
89 #endif
90
91 return true;
92 }
93 else {
94 moveCacheItemToFront(d_packetCache, iter);
95 d_misses++;
96 break;
97 }
98 }
99
100 return false;
101 }
102
103 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
104 std::string* responsePacket, uint32_t* age, uint32_t* qhash)
105 {
106 DNSName qname;
107 uint16_t qtype, qclass;
108 uint16_t ecsBegin;
109 uint16_t ecsEnd;
110 return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, qhash, &ecsBegin, &ecsEnd, nullptr);
111 }
112
113 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
114 std::string* responsePacket, uint32_t* age, uint32_t* qhash)
115 {
116 uint16_t ecsBegin;
117 uint16_t ecsEnd;
118 return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, qhash, &ecsBegin, &ecsEnd, nullptr);
119 }
120
121 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
122 std::string* responsePacket, uint32_t* age, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage)
123 {
124 *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
125 const auto& idx = d_packetCache.get<HashTag>();
126 auto range = idx.equal_range(tie(tag,*qhash));
127
128 if(range.first == range.second) {
129 d_misses++;
130 return false;
131 }
132
133 return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, protobufMessage, *ecsBegin, *ecsEnd);
134 }
135
136 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now,
137 std::string* responsePacket, uint32_t* age, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage)
138 {
139 *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
140 const auto& idx = d_packetCache.get<HashTag>();
141 auto range = idx.equal_range(tie(tag,*qhash));
142
143 if(range.first == range.second) {
144 d_misses++;
145 return false;
146 }
147
148 qname = DNSName(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, qtype, qclass, 0);
149
150 return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, protobufMessage, *ecsBegin, *ecsEnd);
151 }
152
153
154 void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const std::string& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, uint16_t ecsBegin, uint16_t ecsEnd)
155 {
156 insertResponsePacket(tag, qhash, query, qname, qtype, qclass, responsePacket, now, ttl, ecsBegin, ecsEnd, nullptr);
157 }
158
159 void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const std::string& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, uint16_t ecsBegin, uint16_t ecsEnd, const RecProtoBufMessage* protobufMessage)
160 {
161 auto& idx = d_packetCache.get<HashTag>();
162 auto range = idx.equal_range(tie(tag,qhash));
163 auto iter = range.first;
164
165 for( ; iter != range.second ; ++iter) {
166 if (iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname) {
167 continue;
168 }
169
170 moveCacheItemToBack(d_packetCache, iter);
171 iter->d_packet = responsePacket;
172 iter->d_query = query;
173 iter->d_ecsBegin = ecsBegin;
174 iter->d_ecsEnd = ecsEnd;
175 iter->d_ttd = now + ttl;
176 iter->d_creation = now;
177 #ifdef HAVE_PROTOBUF
178 if (protobufMessage) {
179 iter->d_protobufMessage = *protobufMessage;
180 }
181 #endif
182
183 break;
184 }
185
186 if(iter == range.second) { // nothing to refresh
187 struct Entry e(qname, responsePacket, query);
188 e.d_qhash = qhash;
189 e.d_ecsBegin = ecsBegin;
190 e.d_ecsEnd = ecsEnd;
191 e.d_type = qtype;
192 e.d_class = qclass;
193 e.d_ttd = now+ttl;
194 e.d_creation = now;
195 e.d_tag = tag;
196 #ifdef HAVE_PROTOBUF
197 if (protobufMessage) {
198 e.d_protobufMessage = *protobufMessage;
199 }
200 #endif
201 d_packetCache.insert(e);
202 }
203 }
204
205 uint64_t RecursorPacketCache::size()
206 {
207 return d_packetCache.size();
208 }
209
210 uint64_t RecursorPacketCache::bytes()
211 {
212 uint64_t sum=0;
213 for(const auto& e : d_packetCache) {
214 sum += sizeof(e) + e.d_packet.length() + 4;
215 }
216 return sum;
217 }
218
219 void RecursorPacketCache::doPruneTo(unsigned int maxCached)
220 {
221 pruneCollection(*this, d_packetCache, maxCached);
222 }
223
224 uint64_t RecursorPacketCache::doDump(int fd)
225 {
226 FILE* fp=fdopen(dup(fd), "w");
227 if(!fp) { // dup probably failed
228 return 0;
229 }
230 fprintf(fp, "; main packet cache dump from thread follows\n;\n");
231 const auto& sidx=d_packetCache.get<1>();
232
233 uint64_t count=0;
234 time_t now=time(0);
235 for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) {
236 count++;
237 try {
238 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);
239 }
240 catch(...) {
241 fprintf(fp, "; error printing '%s'\n", i->d_name.empty() ? "EMPTY" : i->d_name.toString().c_str());
242 }
243 }
244 fclose(fp);
245 return count;
246
247 }