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