]>
Commit | Line | Data |
---|---|---|
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 | |
15 | RecursorPacketCache::RecursorPacketCache() | |
16 | { | |
16beeaa4 | 17 | d_hits = d_misses = 0; |
3ea54bf0 BH |
18 | } |
19 | ||
65a60c2c | 20 | int 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 |
46 | static 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 | ||
55 | uint32_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 |
98 | bool 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 | ||
104 | bool 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 |
156 | void 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 | 161 | void 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 |
206 | uint64_t RecursorPacketCache::size() |
207 | { | |
208 | return d_packetCache.size(); | |
209 | } | |
3f3459f0 | 210 | |
0bbf7d0a BH |
211 | uint64_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 |
220 | void RecursorPacketCache::doPruneTo(unsigned int maxCached) |
221 | { | |
38c9ceaa | 222 | pruneCollection(d_packetCache, maxCached); |
3f3459f0 BH |
223 | } |
224 | ||
09645ebb | 225 | uint64_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 | } |