• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 18743945403

23 Oct 2025 09:29AM UTC coverage: 65.845% (+0.02%) from 65.829%
18743945403

Pull #16356

github

web-flow
Merge 8a2027ef1 into efa3637e8
Pull Request #16356: auth 5.0: backport "pdnsutil: fix b2b-migrate to from sql to non-sql"

42073 of 92452 branches covered (45.51%)

Branch coverage included in aggregate %.

128008 of 165855 relevant lines covered (77.18%)

6379935.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.28
/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
void RecursorPacketCache::setShardSizes(size_t shardSize)
16
{
178✔
17
  for (auto& shard : d_maps) {
182,272✔
18
    auto lock = shard.lock();
182,272✔
19
    lock->d_shardSize = shardSize;
182,272✔
20
  }
182,272✔
21
}
178✔
22

23
uint64_t RecursorPacketCache::size() const
24
{
407✔
25
  uint64_t count = 0;
407✔
26
  for (const auto& map : d_maps) {
416,768✔
27
    count += map.getEntriesCount();
416,768✔
28
  }
416,768✔
29
  return count;
407✔
30
}
407✔
31

32
uint64_t RecursorPacketCache::bytes()
33
{
×
34
  uint64_t sum = 0;
×
35
  for (auto& shard : d_maps) {
×
36
    auto lock = shard.lock();
×
37
    for (const auto& entry : lock->d_map) {
×
38
      sum += sizeof(entry) + entry.d_packet.length() + 4;
×
39
    }
×
40
  }
×
41
  return sum;
×
42
}
×
43

44
uint64_t RecursorPacketCache::getHits()
45
{
193✔
46
  uint64_t sum = 0;
193✔
47
  for (auto& shard : d_maps) {
197,632✔
48
    auto lock = shard.lock();
197,632✔
49
    sum += lock->d_hits;
197,632✔
50
  }
197,632✔
51
  return sum;
193✔
52
}
193✔
53

54
uint64_t RecursorPacketCache::getMisses()
55
{
193✔
56
  uint64_t sum = 0;
193✔
57
  for (auto& shard : d_maps) {
197,632✔
58
    auto lock = shard.lock();
197,632✔
59
    sum += lock->d_misses;
197,632✔
60
  }
197,632✔
61
  return sum;
193✔
62
}
193✔
63

64
pair<uint64_t, uint64_t> RecursorPacketCache::stats()
65
{
326✔
66
  uint64_t contended = 0;
326✔
67
  uint64_t acquired = 0;
326✔
68
  for (auto& shard : d_maps) {
333,824✔
69
    auto content = shard.lock();
333,824✔
70
    contended += content->d_contended_count;
333,824✔
71
    acquired += content->d_acquired_count;
333,824✔
72
  }
333,824✔
73
  return {contended, acquired};
326✔
74
}
326✔
75

76
uint64_t RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
77
{
417✔
78
  uint64_t count = 0;
417✔
79
  for (auto& map : d_maps) {
427,008✔
80
    auto shard = map.lock();
427,008✔
81
    auto& idx = shard->d_map.get<NameTag>();
427,008✔
82
    for (auto iter = idx.lower_bound(name); iter != idx.end();) {
427,186✔
83
      if (subtree) {
178✔
84
        if (!iter->d_name.isPartOf(name)) { // this is case insensitive
166!
85
          break;
×
86
        }
×
87
      }
166✔
88
      else {
12✔
89
        if (iter->d_name != name) {
12!
90
          break;
×
91
        }
×
92
      }
12✔
93
      if (qtype == 0xffff || iter->d_type == qtype) {
178!
94
        iter = idx.erase(iter);
178✔
95
        map.decEntriesCount();
178✔
96
        count++;
178✔
97
      }
178✔
98
      else {
×
99
        ++iter;
×
100
      }
×
101
    }
178✔
102
  }
427,008✔
103
  return count;
417✔
104
}
417✔
105

106
bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass)
107
{
3,137✔
108
  // this ignores checking on the EDNS subnet flags!
109
  if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
3,150!
110
    return false;
×
111
  }
×
112

113
  static const std::unordered_set<uint16_t> optionsToSkip{EDNSOptionCode::COOKIE, EDNSOptionCode::ECS};
3,137✔
114
  return queryMatches(iter->d_query, queryPacket, qname, optionsToSkip);
3,137✔
115
}
3,137✔
116

117
bool RecursorPacketCache::checkResponseMatches(MapCombo::LockedContent& shard, 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)
118
{
3,149✔
119
  for (auto iter = range.first; iter != range.second; ++iter) {
3,149✔
120
    // the possibility is VERY real that we get hits that are not right - birthday paradox
121
    if (!qrMatch(iter, queryPacket, qname, qtype, qclass)) {
3,133!
122
      continue;
×
123
    }
×
124

125
    if (now < iter->d_ttd) { // it is right, it is fresh!
3,133✔
126
      // coverity[store_truncates_time_t]
127
      *age = static_cast<uint32_t>(now - iter->d_creation);
3,012✔
128
      // we know ttl is > 0
129
      auto ttl = static_cast<uint32_t>(iter->d_ttd - now);
3,012✔
130
      if (s_refresh_ttlperc > 0 && !iter->d_submitted && taskQTypeIsSupported(qtype)) {
3,015✔
131
        const dnsheader_aligned header(iter->d_packet.data());
2,808✔
132
        const auto* headerPtr = header.get();
2,808✔
133
        if (headerPtr->rcode == RCode::NoError) {
2,820✔
134
          const uint32_t deadline = iter->getOrigTTL() * s_refresh_ttlperc / 100;
2,820✔
135
          const bool almostExpired = ttl <= deadline;
2,820✔
136
          if (almostExpired) {
2,820✔
137
            iter->d_submitted = true;
2✔
138
            pushAlmostExpiredTask(qname, qtype, iter->d_ttd, Netmask());
2✔
139
          }
2✔
140
        }
2,820✔
141
      }
2,808✔
142
      *responsePacket = iter->d_packet;
3,012✔
143
      responsePacket->replace(0, 2, queryPacket.c_str(), 2);
3,012✔
144
      *valState = iter->d_vstate;
3,012✔
145

146
      const size_t wirelength = qname.wirelength();
3,012✔
147
      if (responsePacket->size() > (sizeof(dnsheader) + wirelength)) {
3,015✔
148
        responsePacket->replace(sizeof(dnsheader), wirelength, queryPacket, sizeof(dnsheader), wirelength);
3,009✔
149
      }
3,009✔
150

151
      shard.d_hits++;
3,012✔
152
      moveCacheItemToBack<SequencedTag>(shard.d_map, iter);
3,012✔
153

154
      if (pbdata != nullptr) {
3,014✔
155
        if (iter->d_pbdata) {
2,977✔
156
          *pbdata = iter->d_pbdata;
9✔
157
        }
9✔
158
        else {
2,968✔
159
          *pbdata = boost::none;
2,968✔
160
        }
2,968✔
161
      }
2,977✔
162

163
      return true;
3,012✔
164
    }
3,012✔
165
    // We used to move the item to the front of "the to be deleted" sequence,
166
    // but we very likely will update the entry very soon, so leave it
167
    shard.d_misses++;
121✔
168
    break;
121✔
169
  }
3,133✔
170

171
  return false;
137✔
172
}
3,149✔
173

174
static const std::unordered_set<uint16_t> s_skipOptions = {EDNSOptionCode::ECS, EDNSOptionCode::COOKIE};
175

176
bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
177
                                            std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, OptPBData* pbdata, bool tcp)
178
{
179✔
179
  *qhash = canHashPacket(queryPacket, s_skipOptions);
179✔
180
  auto& map = getMap(tag, *qhash, tcp);
179✔
181
  auto shard = map.lock();
179✔
182
  const auto& idx = shard->d_map.get<HashTag>();
179✔
183
  auto range = idx.equal_range(std::tie(tag, *qhash, tcp));
179✔
184

185
  if (range.first == range.second) {
179✔
186
    shard->d_misses++;
136✔
187
    return false;
136✔
188
  }
136✔
189

190
  return checkResponseMatches(*shard, range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, pbdata);
43✔
191
}
179✔
192

193
bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now,
194
                                            std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, OptPBData* pbdata, bool tcp)
195
{
5,878✔
196
  *qhash = canHashPacket(queryPacket, s_skipOptions);
5,878✔
197
  auto& map = getMap(tag, *qhash, tcp);
5,878✔
198
  auto shard = map.lock();
5,878✔
199
  const auto& idx = shard->d_map.get<HashTag>();
5,878✔
200
  auto range = idx.equal_range(std::tie(tag, *qhash, tcp));
5,878✔
201

202
  if (range.first == range.second) {
5,878✔
203
    shard->d_misses++;
2,766✔
204
    return false;
2,766✔
205
  }
2,766✔
206

207
  qname = DNSName(queryPacket.c_str(), static_cast<int>(queryPacket.length()), sizeof(dnsheader), false, qtype, qclass);
3,112✔
208

209
  return checkResponseMatches(*shard, range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, pbdata);
3,112✔
210
}
5,878✔
211

212
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)
213
{
2,868✔
214
  auto& map = getMap(tag, qhash, tcp);
2,868✔
215
  auto shard = map.lock();
2,868✔
216
  auto& idx = shard->d_map.get<HashTag>();
2,868✔
217
  auto range = idx.equal_range(std::tie(tag, qhash, tcp));
2,868✔
218
  auto iter = range.first;
2,868✔
219

220
  for (; iter != range.second; ++iter) {
2,868✔
221
    if (iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname) {
701!
222
      continue;
×
223
    }
×
224

225
    moveCacheItemToBack<SequencedTag>(shard->d_map, iter);
701✔
226
    iter->d_packet = std::move(responsePacket);
701✔
227
    iter->d_query = std::move(query);
701✔
228
    iter->d_ttd = now + ttl;
701✔
229
    iter->d_creation = now;
701✔
230
    iter->d_vstate = valState;
701✔
231
    iter->d_submitted = false;
701✔
232
    if (pbdata) {
701!
233
      iter->d_pbdata = std::move(*pbdata);
×
234
    }
×
235

236
    return;
701✔
237
  }
701✔
238

239
  struct Entry entry(DNSName(qname), qtype, qclass, std::move(responsePacket), std::move(query), tcp, qhash, now + ttl, now, tag, valState);
2,167✔
240
  if (pbdata) {
2,167✔
241
    entry.d_pbdata = std::move(*pbdata);
28✔
242
  }
28✔
243

244
  shard->d_map.insert(entry);
2,167✔
245
  map.incEntriesCount();
2,167✔
246

247
  if (shard->d_map.size() > shard->d_shardSize) {
2,167!
248
    auto& seq_idx = shard->d_map.get<SequencedTag>();
×
249
    seq_idx.erase(seq_idx.begin());
×
250
    map.decEntriesCount();
×
251
  }
×
252
  assert(map.getEntriesCount() == shard->d_map.size()); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay): clib implementation
2,167✔
253
}
2,167✔
254

255
void RecursorPacketCache::doPruneTo(time_t now, size_t maxSize)
256
{
141✔
257
  size_t cacheSize = size();
141✔
258
  pruneMutexCollectionsVector<SequencedTag>(now, d_maps, maxSize, cacheSize);
141✔
259
}
141✔
260

261
uint64_t RecursorPacketCache::doDump(int file)
262
{
5✔
263
  int fdupped = dup(file);
5✔
264
  if (fdupped == -1) {
5!
265
    return 0;
×
266
  }
×
267
  auto filePtr = pdns::UniqueFilePtr(fdopen(fdupped, "w"));
5✔
268
  if (!filePtr) {
5!
269
    close(fdupped);
×
270
    return 0;
×
271
  }
×
272

273
  uint64_t count = 0;
5✔
274
  time_t now = time(nullptr);
5✔
275

276
  size_t shardNum = 0;
5✔
277
  size_t min = std::numeric_limits<size_t>::max();
5✔
278
  size_t max = 0;
5✔
279
  uint64_t maxSize = 0;
5✔
280

281
  for (auto& shard : d_maps) {
5,120✔
282
    auto lock = shard.lock();
5,120✔
283
    const auto& sidx = lock->d_map.get<SequencedTag>();
5,120✔
284
    const auto shardSize = lock->d_map.size();
5,120✔
285
    fprintf(filePtr.get(), "; packetcache shard %zu; size %zu/%zu\n", shardNum, shardSize, lock->d_shardSize);
5,120✔
286
    min = std::min(min, shardSize);
5,120✔
287
    max = std::max(max, shardSize);
5,120✔
288
    maxSize += lock->d_shardSize;
5,120✔
289
    shardNum++;
5,120✔
290
    for (const auto& entry : sidx) {
5,120✔
291
      count++;
13✔
292
      try {
13✔
293
        fprintf(filePtr.get(), "%s %" PRId64 " %s  ; tag %d %s\n", entry.d_name.toString().c_str(), static_cast<int64_t>(entry.d_ttd - now), DNSRecordContent::NumberToType(entry.d_type).c_str(), entry.d_tag, entry.d_tcp ? "tcp" : "udp");
13✔
294
      }
13✔
295
      catch (...) {
13✔
296
        fprintf(filePtr.get(), "; error printing '%s'\n", entry.d_name.empty() ? "EMPTY" : entry.d_name.toString().c_str());
×
297
      }
×
298
    }
13✔
299
  }
5,120✔
300
  fprintf(filePtr.get(), "; packetcache size: %" PRIu64 "/%" PRIu64 " shards: %zu min/max shard size: %zu/%zu\n", size(), maxSize, d_maps.size(), min, max);
5✔
301
  return count;
5✔
302
}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc