]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/recpacketcache.cc
7 #include "recpacketcache.hh"
8 #include "cachecleaner.hh"
10 #include "namespaces.hh"
11 #include "rec-taskqueue.hh"
13 unsigned int RecursorPacketCache::s_refresh_ttlperc
{0};
15 int RecursorPacketCache::doWipePacketCache(const DNSName
& name
, uint16_t qtype
, bool subtree
)
18 auto& idx
= d_packetCache
.get
<NameTag
>();
19 for (auto iter
= idx
.lower_bound(name
); iter
!= idx
.end();) {
21 if (!iter
->d_name
.isPartOf(name
)) { // this is case insensitive
26 if (iter
->d_name
!= name
)
30 if (qtype
== 0xffff || iter
->d_type
== qtype
) {
31 iter
= idx
.erase(iter
);
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
)
42 // this ignores checking on the EDNS subnet flags!
43 if (qname
!= iter
->d_name
|| iter
->d_type
!= qtype
|| iter
->d_class
!= qclass
) {
47 static const std::unordered_set
<uint16_t> optionsToSkip
{EDNSOptionCode::COOKIE
, EDNSOptionCode::ECS
};
48 return queryMatches(iter
->d_query
, queryPacket
, qname
, optionsToSkip
);
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
)
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
)) {
59 if (now
< iter
->d_ttd
) { // it is right, it is fresh!
60 *age
= static_cast<uint32_t>(now
- iter
->d_creation
);
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
;
67 iter
->d_submitted
= true;
68 pushAlmostExpiredTask(qname
, qtype
, iter
->d_ttd
, Netmask());
71 *responsePacket
= iter
->d_packet
;
72 responsePacket
->replace(0, 2, queryPacket
.c_str(), 2);
73 *valState
= iter
->d_vstate
;
75 const size_t wirelength
= qname
.wirelength();
76 if (responsePacket
->size() > (sizeof(dnsheader
) + wirelength
)) {
77 responsePacket
->replace(sizeof(dnsheader
), wirelength
, queryPacket
, sizeof(dnsheader
), wirelength
);
81 moveCacheItemToBack
<SequencedTag
>(d_packetCache
, iter
);
83 if (pbdata
!= nullptr) {
85 *pbdata
= iter
->d_pbdata
;
88 *pbdata
= boost::none
;
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
105 bool RecursorPacketCache::getResponsePacket(unsigned int tag
, const std::string
& queryPacket
, time_t now
,
106 std::string
* responsePacket
, uint32_t* age
, uint32_t* qhash
)
109 uint16_t qtype
, qclass
;
111 return getResponsePacket(tag
, queryPacket
, qname
, &qtype
, &qclass
, now
, responsePacket
, age
, &valState
, qhash
, nullptr, false);
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
)
118 return getResponsePacket(tag
, queryPacket
, qname
, qtype
, qclass
, now
, responsePacket
, age
, &valState
, qhash
, nullptr, false);
121 static const std::unordered_set
<uint16_t> s_skipOptions
= {EDNSOptionCode::ECS
, EDNSOptionCode::COOKIE
};
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
)
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
));
130 if (range
.first
== range
.second
) {
135 return checkResponseMatches(range
, queryPacket
, qname
, qtype
, qclass
, now
, responsePacket
, age
, valState
, pbdata
);
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
)
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
));
145 if (range
.first
== range
.second
) {
150 qname
= DNSName(queryPacket
.c_str(), queryPacket
.length(), sizeof(dnsheader
), false, qtype
, qclass
, 0);
152 return checkResponseMatches(range
, queryPacket
, qname
, *qtype
, *qclass
, now
, responsePacket
, age
, valState
, pbdata
);
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
)
157 auto& idx
= d_packetCache
.get
<HashTag
>();
158 auto range
= idx
.equal_range(std::tie(tag
, qhash
, tcp
));
159 auto iter
= range
.first
;
161 for (; iter
!= range
.second
; ++iter
) {
162 if (iter
->d_type
!= qtype
|| iter
->d_class
!= qclass
|| iter
->d_name
!= qname
) {
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;
174 iter
->d_pbdata
= std::move(*pbdata
);
180 struct Entry
e(qname
, qtype
, qclass
, std::move(responsePacket
), std::move(query
), tcp
, qhash
, now
+ ttl
, now
, tag
, valState
);
182 e
.d_pbdata
= std::move(*pbdata
);
185 d_packetCache
.insert(e
);
187 if (d_packetCache
.size() > d_maxSize
) {
188 auto& seq_idx
= d_packetCache
.get
<SequencedTag
>();
189 seq_idx
.erase(seq_idx
.begin());
193 uint64_t RecursorPacketCache::bytes() const
196 for (const auto& e
: d_packetCache
) {
197 sum
+= sizeof(e
) + e
.d_packet
.length() + 4;
202 void RecursorPacketCache::doPruneTo(size_t maxCached
)
204 pruneCollection
<SequencedTag
>(*this, d_packetCache
, maxCached
);
207 uint64_t RecursorPacketCache::doDump(int fd
)
209 auto fp
= std::unique_ptr
<FILE, int (*)(FILE*)>(fdopen(dup(fd
), "w"), fclose
);
210 if (!fp
) { // dup probably failed
214 fprintf(fp
.get(), "; main packet cache dump from thread follows\n;\n");
216 const auto& sidx
= d_packetCache
.get
<SequencedTag
>();
218 time_t now
= time(nullptr);
220 for (const auto& i
: sidx
) {
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");
226 fprintf(fp
.get(), "; error printing '%s'\n", i
.d_name
.empty() ? "EMPTY" : i
.d_name
.toString().c_str());