]>
Commit | Line | Data |
---|---|---|
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 | |
16 | RecursorPacketCache::RecursorPacketCache() | |
17 | { | |
16beeaa4 | 18 | d_hits = d_misses = 0; |
3ea54bf0 BH |
19 | } |
20 | ||
65a60c2c | 21 | int 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 | 46 | static 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 | ||
52 | uint32_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 | 95 | 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) |
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 |
136 | bool 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 | ||
142 | bool 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 | ||
148 | bool 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 | ||
163 | bool 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 | 182 | void 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 | 187 | void 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 |
232 | uint64_t RecursorPacketCache::size() |
233 | { | |
234 | return d_packetCache.size(); | |
235 | } | |
3f3459f0 | 236 | |
0bbf7d0a BH |
237 | uint64_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 |
246 | void RecursorPacketCache::doPruneTo(unsigned int maxCached) |
247 | { | |
38c9ceaa | 248 | pruneCollection(d_packetCache, maxCached); |
3f3459f0 BH |
249 | } |
250 | ||
09645ebb | 251 | uint64_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 | } |