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

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

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

67.72
/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
{
25✔
17
  for (auto& shard : d_maps) {
25,600✔
18
    auto lock = shard.lock();
25,600✔
19
    lock->d_shardSize = shardSize;
25,600✔
20
  }
25,600✔
21
}
25✔
22

23
uint64_t RecursorPacketCache::size() const
24
{
221✔
25
  uint64_t count = 0;
221✔
26
  for (const auto& map : d_maps) {
226,304✔
27
    count += map.getEntriesCount();
226,304✔
28
  }
226,304✔
29
  return count;
221✔
30
}
221✔
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
{
105✔
46
  uint64_t sum = 0;
105✔
47
  for (auto& shard : d_maps) {
107,520✔
48
    auto lock = shard.lock();
107,520✔
49
    sum += lock->d_hits;
107,520✔
50
  }
107,520✔
51
  return sum;
105✔
52
}
105✔
53

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

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

76
uint64_t RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
77
{
12✔
78
  uint64_t count = 0;
12✔
79
  for (auto& map : d_maps) {
12,288✔
80
    auto shard = map.lock();
12,288✔
81
    auto& idx = shard->d_map.get<NameTag>();
12,288✔
82
    for (auto iter = idx.lower_bound(name); iter != idx.end();) {
12,304✔
83
      if (subtree) {
16✔
84
        if (!iter->d_name.isPartOf(name)) { // this is case insensitive
4!
85
          break;
×
86
        }
×
87
      }
4✔
88
      else {
12✔
89
        if (iter->d_name != name) {
12!
90
          break;
×
91
        }
×
92
      }
12✔
93
      if (qtype == 0xffff || iter->d_type == qtype) {
16!
94
        iter = idx.erase(iter);
16✔
95
        map.decEntriesCount();
16✔
96
        count++;
16✔
97
      }
16✔
98
      else {
×
99
        ++iter;
×
100
      }
×
101
    }
16✔
102
  }
12,288✔
103
  return count;
12✔
104
}
12✔
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
{
2,922✔
108
  // this ignores checking on the EDNS subnet flags!
109
  if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
2,934!
110
    return false;
×
111
  }
×
112

113
  static const std::unordered_set<uint16_t> optionsToSkip{EDNSOptionCode::COOKIE, EDNSOptionCode::ECS};
2,922✔
114
  return queryMatches(iter->d_query, queryPacket, qname, optionsToSkip);
2,922✔
115
}
2,922✔
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
{
2,930✔
119
  for (auto iter = range.first; iter != range.second; ++iter) {
2,930✔
120
    // the possibility is VERY real that we get hits that are not right - birthday paradox
121
    if (!qrMatch(iter, queryPacket, qname, qtype, qclass)) {
2,910!
122
      continue;
×
123
    }
×
124

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

146
      const size_t wirelength = qname.wirelength();
2,747✔
147
      if (responsePacket->size() > (sizeof(dnsheader) + wirelength)) {
2,751✔
148
        responsePacket->replace(sizeof(dnsheader), wirelength, queryPacket, sizeof(dnsheader), wirelength);
2,748✔
149
      }
2,748✔
150

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

154
      if (pbdata != nullptr) {
2,750✔
155
        if (iter->d_pbdata) {
2,712!
UNCOV
156
          *pbdata = iter->d_pbdata;
×
UNCOV
157
        }
×
158
        else {
2,712✔
159
          *pbdata = boost::none;
2,712✔
160
        }
2,712✔
161
      }
2,712✔
162

163
      return true;
2,747✔
164
    }
2,747✔
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++;
2,147,483,651✔
168
    break;
2,147,483,651✔
169
  }
2,910✔
170

171
  return false;
183✔
172
}
2,930✔
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
{
46✔
179
  *qhash = canHashPacket(queryPacket, s_skipOptions);
46✔
180
  auto& map = getMap(tag, *qhash, tcp);
46✔
181
  auto shard = map.lock();
46✔
182
  const auto& idx = shard->d_map.get<HashTag>();
46✔
183
  auto range = idx.equal_range(std::tie(tag, *qhash, tcp));
46✔
184

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

190
  return checkResponseMatches(*shard, range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, pbdata);
24✔
191
}
46✔
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
{
4,453✔
196
  *qhash = canHashPacket(queryPacket, s_skipOptions);
4,453✔
197
  auto& map = getMap(tag, *qhash, tcp);
4,453✔
198
  auto shard = map.lock();
4,453✔
199
  const auto& idx = shard->d_map.get<HashTag>();
4,453✔
200
  auto range = idx.equal_range(std::tie(tag, *qhash, tcp));
4,453✔
201

202
  if (range.first == range.second) {
4,453✔
203
    shard->d_misses++;
1,546✔
204
    return false;
1,546✔
205
  }
1,546✔
206

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

209
  return checkResponseMatches(*shard, range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, pbdata);
2,907✔
210
}
4,453✔
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
{
1,747✔
214
  auto& map = getMap(tag, qhash, tcp);
1,747✔
215
  auto shard = map.lock();
1,747✔
216
  auto& idx = shard->d_map.get<HashTag>();
1,747✔
217
  auto range = idx.equal_range(std::tie(tag, qhash, tcp));
1,747✔
218
  auto iter = range.first;
1,747✔
219

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

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

236
    return;
179✔
237
  }
179✔
238

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

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

247
  if (shard->d_map.size() > shard->d_shardSize) {
1,568!
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
1,568✔
253
}
1,568✔
254

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

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

UNCOV
273
  uint64_t count = 0;
×
UNCOV
274
  time_t now = time(nullptr);
×
275

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

UNCOV
281
  for (auto& shard : d_maps) {
×
UNCOV
282
    auto lock = shard.lock();
×
UNCOV
283
    const auto& sidx = lock->d_map.get<SequencedTag>();
×
UNCOV
284
    const auto shardSize = lock->d_map.size();
×
UNCOV
285
    fprintf(filePtr.get(), "; packetcache shard %zu; size %zu/%zu\n", shardNum, shardSize, lock->d_shardSize);
×
UNCOV
286
    min = std::min(min, shardSize);
×
UNCOV
287
    max = std::max(max, shardSize);
×
UNCOV
288
    maxSize += lock->d_shardSize;
×
UNCOV
289
    shardNum++;
×
UNCOV
290
    for (const auto& entry : sidx) {
×
UNCOV
291
      count++;
×
UNCOV
292
      try {
×
UNCOV
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");
×
UNCOV
294
      }
×
UNCOV
295
      catch (...) {
×
296
        fprintf(filePtr.get(), "; error printing '%s'\n", entry.d_name.empty() ? "EMPTY" : entry.d_name.toString().c_str());
×
297
      }
×
UNCOV
298
    }
×
UNCOV
299
  }
×
UNCOV
300
  fprintf(filePtr.get(), "; packetcache size: %" PRIu64 "/%" PRIu64 " shards: %zu min/max shard size: %zu/%zu\n", size(), maxSize, d_maps.size(), min, max);
×
UNCOV
301
  return count;
×
UNCOV
302
}
×
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