]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/recpacketcache.cc
Step 2: mv rec specific files to recursordist
[thirdparty/pdns.git] / pdns / recursordist / 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 #include "rec-taskqueue.hh"
12
13 unsigned int RecursorPacketCache::s_refresh_ttlperc{0};
14
15 int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
16 {
17 int count = 0;
18 auto& idx = d_packetCache.get<NameTag>();
19 for (auto iter = idx.lower_bound(name); iter != idx.end();) {
20 if (subtree) {
21 if (!iter->d_name.isPartOf(name)) { // this is case insensitive
22 break;
23 }
24 }
25 else {
26 if (iter->d_name != name)
27 break;
28 }
29
30 if (qtype == 0xffff || iter->d_type == qtype) {
31 iter = idx.erase(iter);
32 count++;
33 }
34 else
35 ++iter;
36 }
37 return count;
38 }
39
40 bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass)
41 {
42 // this ignores checking on the EDNS subnet flags!
43 if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
44 return false;
45 }
46
47 static const std::unordered_set<uint16_t> optionsToSkip{EDNSOptionCode::COOKIE, EDNSOptionCode::ECS};
48 return queryMatches(iter->d_query, queryPacket, qname, optionsToSkip);
49 }
50
51 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, OptPBData* pbdata)
52 {
53 for (auto iter = range.first; iter != range.second; ++iter) {
54 // the possibility is VERY real that we get hits that are not right - birthday paradox
55 if (!qrMatch(iter, queryPacket, qname, qtype, qclass)) {
56 continue;
57 }
58
59 if (now < iter->d_ttd) { // it is right, it is fresh!
60 *age = static_cast<uint32_t>(now - iter->d_creation);
61 // we know ttl is > 0
62 uint32_t ttl = static_cast<uint32_t>(iter->d_ttd - now);
63 if (s_refresh_ttlperc > 0 && !iter->d_submitted) {
64 const uint32_t deadline = iter->getOrigTTL() * s_refresh_ttlperc / 100;
65 const bool almostExpired = ttl <= deadline;
66 if (almostExpired) {
67 iter->d_submitted = true;
68 pushAlmostExpiredTask(qname, qtype, iter->d_ttd, Netmask());
69 }
70 }
71 *responsePacket = iter->d_packet;
72 responsePacket->replace(0, 2, queryPacket.c_str(), 2);
73 *valState = iter->d_vstate;
74
75 const size_t wirelength = qname.wirelength();
76 if (responsePacket->size() > (sizeof(dnsheader) + wirelength)) {
77 responsePacket->replace(sizeof(dnsheader), wirelength, queryPacket, sizeof(dnsheader), wirelength);
78 }
79
80 d_hits++;
81 moveCacheItemToBack<SequencedTag>(d_packetCache, iter);
82
83 if (pbdata != nullptr) {
84 if (iter->d_pbdata) {
85 *pbdata = iter->d_pbdata;
86 }
87 else {
88 *pbdata = boost::none;
89 }
90 }
91
92 return true;
93 }
94 else {
95 // We used to move the item to the front of "the to be deleted" sequence,
96 // but we very likely will update the entry very soon, so leave it
97 d_misses++;
98 break;
99 }
100 }
101
102 return false;
103 }
104
105 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
106 std::string* responsePacket, uint32_t* age, uint32_t* qhash)
107 {
108 DNSName qname;
109 uint16_t qtype, qclass;
110 vState valState;
111 return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, nullptr, false);
112 }
113
114 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
115 std::string* responsePacket, uint32_t* age, uint32_t* qhash)
116 {
117 vState valState;
118 return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, nullptr, false);
119 }
120
121 static const std::unordered_set<uint16_t> s_skipOptions = {EDNSOptionCode::ECS, EDNSOptionCode::COOKIE};
122
123 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
124 std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, OptPBData* pbdata, bool tcp)
125 {
126 *qhash = canHashPacket(queryPacket, s_skipOptions);
127 const auto& idx = d_packetCache.get<HashTag>();
128 auto range = idx.equal_range(std::tie(tag, *qhash, tcp));
129
130 if (range.first == range.second) {
131 d_misses++;
132 return false;
133 }
134
135 return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, pbdata);
136 }
137
138 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now,
139 std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, OptPBData* pbdata, bool tcp)
140 {
141 *qhash = canHashPacket(queryPacket, s_skipOptions);
142 const auto& idx = d_packetCache.get<HashTag>();
143 auto range = idx.equal_range(std::tie(tag, *qhash, tcp));
144
145 if (range.first == range.second) {
146 d_misses++;
147 return false;
148 }
149
150 qname = DNSName(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, qtype, qclass, 0);
151
152 return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, pbdata);
153 }
154
155 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, OptPBData&& pbdata, bool tcp)
156 {
157 auto& idx = d_packetCache.get<HashTag>();
158 auto range = idx.equal_range(std::tie(tag, qhash, tcp));
159 auto iter = range.first;
160
161 for (; iter != range.second; ++iter) {
162 if (iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname) {
163 continue;
164 }
165
166 moveCacheItemToBack<SequencedTag>(d_packetCache, iter);
167 iter->d_packet = std::move(responsePacket);
168 iter->d_query = std::move(query);
169 iter->d_ttd = now + ttl;
170 iter->d_creation = now;
171 iter->d_vstate = valState;
172 iter->d_submitted = false;
173 if (pbdata) {
174 iter->d_pbdata = std::move(*pbdata);
175 }
176
177 return;
178 }
179
180 struct Entry e(qname, qtype, qclass, std::move(responsePacket), std::move(query), tcp, qhash, now + ttl, now, tag, valState);
181 if (pbdata) {
182 e.d_pbdata = std::move(*pbdata);
183 }
184
185 d_packetCache.insert(e);
186
187 if (d_packetCache.size() > d_maxSize) {
188 auto& seq_idx = d_packetCache.get<SequencedTag>();
189 seq_idx.erase(seq_idx.begin());
190 }
191 }
192
193 uint64_t RecursorPacketCache::bytes() const
194 {
195 uint64_t sum = 0;
196 for (const auto& e : d_packetCache) {
197 sum += sizeof(e) + e.d_packet.length() + 4;
198 }
199 return sum;
200 }
201
202 void RecursorPacketCache::doPruneTo(size_t maxCached)
203 {
204 pruneCollection<SequencedTag>(*this, d_packetCache, maxCached);
205 }
206
207 uint64_t RecursorPacketCache::doDump(int fd)
208 {
209 auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(dup(fd), "w"), fclose);
210 if (!fp) { // dup probably failed
211 return 0;
212 }
213
214 fprintf(fp.get(), "; main packet cache dump from thread follows\n;\n");
215
216 const auto& sidx = d_packetCache.get<SequencedTag>();
217 uint64_t count = 0;
218 time_t now = time(nullptr);
219
220 for (const auto& i : sidx) {
221 count++;
222 try {
223 fprintf(fp.get(), "%s %" PRId64 " %s ; tag %d %s\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, i.d_tcp ? "tcp" : "udp");
224 }
225 catch (...) {
226 fprintf(fp.get(), "; error printing '%s'\n", i.d_name.empty() ? "EMPTY" : i.d_name.toString().c_str());
227 }
228 }
229 return count;
230 }