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