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

PowerDNS / pdns / 18843183846

27 Oct 2025 01:46PM UTC coverage: 73.01% (+0.006%) from 73.004%
18843183846

Pull #16376

github

web-flow
Merge 56e38aee1 into 82ea647b4
Pull Request #16376: Bump actions

38260 of 63120 branches covered (60.61%)

Branch coverage included in aggregate %.

127459 of 163861 relevant lines covered (77.78%)

5830359.46 hits per line

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

53.93
/pdns/shuffle.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25

26
#include <string>
27

28
#include "shuffle.hh"
29
#include "dns_random.hh"
30
#include "dnsparser.hh"
31

32
// shuffle, maintaining some semblance of order
33
void pdns::shuffle(std::vector<DNSZoneRecord>& rrs)
34
{
×
35
  std::vector<DNSZoneRecord>::iterator first;
×
36
  std::vector<DNSZoneRecord>::iterator second;
×
37

38
  // We assume the CNAMES are listed first in the ANSWER section and the the other records
39
  // and we want to shuffle the other records only
40

41
  // First we scan for the first non-CNAME ANSWER record
42
  for (first = rrs.begin(); first != rrs.end(); ++first) {
×
43
    if (first->dr.d_place == DNSResourceRecord::ANSWER && first->dr.d_type != QType::CNAME) {
×
44
      break;
×
45
    }
×
46
  }
×
47
  // And then for one past the last ANSWER record
48
  for (second = first; second != rrs.end(); ++second) {
×
49
    if (second->dr.d_place != DNSResourceRecord::ANSWER) {
×
50
      break;
×
51
    }
×
52
  }
×
53

54
  // Now shuffle the non-CNAME ANSWER records
55
  dns_random_engine randomEngine;
×
56
  if (second - first > 1) {
×
57
    shuffle(first, second, randomEngine);
×
58
  }
×
59

60
  // now shuffle the ADDITIONAL records in the same manner as the ANSWER records
61
  for (first = second; first != rrs.end(); ++first) {
×
62
    if (first->dr.d_place == DNSResourceRecord::ADDITIONAL && first->dr.d_type != QType::CNAME) {
×
63
      break;
×
64
    }
×
65
  }
×
66
  for (second = first; second != rrs.end(); ++second) {
×
67
    if (second->dr.d_place != DNSResourceRecord::ADDITIONAL) {
×
68
      break;
×
69
    }
×
70
  }
×
71

72
  if (second - first > 1) {
×
73
    shuffle(first, second, randomEngine);
×
74
  }
×
75
  // we don't shuffle the rest
76
}
×
77

78
// shuffle, maintaining some semblance of order
79
static void shuffle(std::vector<DNSRecord>& rrs, bool includingAdditionals)
80
{
3,092✔
81
  // This shuffles in the same style as the above method, keeping CNAME in the front and RRSIGs at the end
82
  std::vector<DNSRecord>::iterator first;
3,092✔
83
  std::vector<DNSRecord>::iterator second;
3,092✔
84

85
  for (first = rrs.begin(); first != rrs.end(); ++first) {
5,212✔
86
    if (first->d_place == DNSResourceRecord::ANSWER && first->d_type != QType::CNAME) {
4,841✔
87
      break;
2,721✔
88
    }
2,721✔
89
  }
4,841✔
90
  for (second = first; second != rrs.end(); ++second) {
8,753✔
91
    if (second->d_place != DNSResourceRecord::ANSWER || second->d_type == QType::RRSIG) {
6,536✔
92
      break;
875✔
93
    }
875✔
94
  }
6,536✔
95

96
  pdns::dns_random_engine randomEngine;
3,092✔
97
  if (second - first > 1) {
3,092✔
98
    shuffle(first, second, randomEngine);
838✔
99
  }
838✔
100

101
  if (!includingAdditionals) {
3,092!
102
    return;
3,092✔
103
  }
3,092✔
104

105
  // now shuffle the additional records
106
  for (first = second; first != rrs.end(); ++first) {
×
107
    if (first->d_place == DNSResourceRecord::ADDITIONAL && first->d_type != QType::CNAME) {
×
108
      break;
×
109
    }
×
110
  }
×
111
  for (second = first; second != rrs.end(); ++second) {
×
112
    if (second->d_place != DNSResourceRecord::ADDITIONAL) {
×
113
      break;
×
114
    }
×
115
  }
×
116

117
  if (second - first > 1) {
×
118
    shuffle(first, second, randomEngine);
×
119
  }
×
120
  // we don't shuffle the rest
121
}
×
122

123
static uint16_t mapTypesToOrder(uint16_t type)
124
{
27,380✔
125
  if (type == QType::CNAME) {
27,380✔
126
    return 0;
5,374✔
127
  }
5,374✔
128
  if (type == QType::RRSIG) {
22,006✔
129
    return 65535;
3,563✔
130
  }
3,563✔
131
  return 1;
18,443✔
132
}
22,006✔
133

134
// make sure rrs is sorted in d_place order to avoid surprises later
135
// then shuffle the parts that desire shuffling
136
void pdns::orderAndShuffle(vector<DNSRecord>& rrs, bool includingAdditionals)
137
{
3,092✔
138
  std::stable_sort(rrs.begin(), rrs.end(), [](const DNSRecord& lhs, const DNSRecord& rhs) {
13,690✔
139
    return std::tuple(lhs.d_place, mapTypesToOrder(lhs.d_type)) < std::tuple(rhs.d_place, mapTypesToOrder(rhs.d_type));
13,690✔
140
  });
13,690✔
141
  shuffle(rrs, includingAdditionals);
3,092✔
142
}
3,092✔
143

144
unsigned int pdns::dedupRecords(vector<DNSRecord>& rrs)
145
{
5,223✔
146
  // This function tries to avoid unnecessary work
147
  // First a vector with zero or one element does not need dedupping
148
  if (rrs.size() <= 1) {
5,223✔
149
    return 0;
1,371✔
150
  }
1,371✔
151

152
  // If we have a larger vector, first check if we actually have duplicates.
153
  // We assume the most common case is: no
154
  std::unordered_set<std::string> seen;
3,852✔
155
  std::vector<bool> dups(rrs.size(), false);
3,852✔
156

157
  unsigned int counter = 0;
3,852✔
158
  unsigned int numDups = 0;
3,852✔
159

160
  seen.reserve(rrs.size());
3,852✔
161
  for (const auto& rec : rrs) {
13,428✔
162
    auto key = rec.getContent()->serialize(rec.d_name, true, true, true);
13,428✔
163
    // This ignores class, ttl and place by using constants for those
164
    if (!seen.emplace(std::move(key)).second) {
13,428✔
165
      dups[counter] = true;
10✔
166
      numDups++;
10✔
167
    }
10✔
168
    ++counter;
13,428✔
169
  }
13,428✔
170

171
  if (numDups == 0) {
3,852✔
172
    // Original is fine as-is.
173
    return 0;
3,844✔
174
  }
3,844✔
175

176
  // We avoid calling erase, as it calls a lot of move constructors. This can hurt, especially if
177
  // you call it on a large vector multiple times.
178
  // So we just take the elements that are unique
179
  std::vector<DNSRecord> ret;
8✔
180
  ret.reserve(rrs.size() - numDups);
8✔
181
  for (counter = 0; counter < rrs.size(); ++counter) {
32✔
182
    if (!dups[counter]) {
24✔
183
      ret.emplace_back(std::move(rrs[counter]));
14✔
184
    }
14✔
185
  }
24✔
186
  rrs = std::move(ret);
8✔
187
  return numDups;
8✔
188
}
3,852✔
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