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

PowerDNS / pdns / 19741624072

27 Nov 2025 03:45PM UTC coverage: 73.086% (+0.02%) from 73.065%
19741624072

Pull #16570

github

web-flow
Merge 08a2cdb1d into f94a3f63f
Pull Request #16570: rec: rewrite all unwrap calls in web.rs

38523 of 63408 branches covered (60.75%)

Branch coverage included in aggregate %.

128044 of 164496 relevant lines covered (77.84%)

6531485.83 hits per line

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

96.08
/pdns/dnsdistdist/dnsdist-self-answers.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
#include <random>
23

24
#include "dnsdist-self-answers.hh"
25

26
#include "dnsdist-configuration.hh"
27
#include "dnsdist-ecs.hh"
28

29
namespace dnsdist::self_answers
30
{
31
static thread_local std::default_random_engine t_randomEngine;
32

33
static void addRecordHeader(PacketBuffer& packet, size_t& position, uint16_t qclass, uint32_t ttl, QType qtype, uint16_t rdataLen)
34
{
285✔
35
  std::array<unsigned char, 12> recordstart{
285✔
36
    0xc0, 0x0c, // compressed name
285✔
37
    0, 0, // QTYPE
285✔
38
    0, 0, // QCLASS
285✔
39
    0, 0, 0, 0, // TTL
285✔
40
    0, 0 // rdata length
285✔
41
  };
285✔
42
  ttl = htonl(ttl);
285✔
43
  qclass = htons(qclass);
285✔
44
  qtype = htons(qtype);
285✔
45
  rdataLen = htons(rdataLen);
285✔
46
  static_assert(recordstart.size() == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
285✔
47
  memcpy(&recordstart.at(2), &qtype, sizeof(qtype));
285✔
48
  memcpy(&recordstart.at(4), &qclass, sizeof(qclass));
285✔
49
  memcpy(&recordstart.at(6), &ttl, sizeof(ttl));
285✔
50
  memcpy(&recordstart.at(10), &rdataLen, sizeof(rdataLen));
285✔
51
  memcpy(&packet.at(position), recordstart.data(), recordstart.size());
285✔
52
  position += recordstart.size();
285✔
53
}
285✔
54

55
static std::pair<bool, bool> getEDNSStatusInQuery(DNSQuestion& dnsQuestion)
56
{
409✔
57
  if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses) {
409✔
58
    return {false, false};
6✔
59
  }
6✔
60

61
  if (dnsQuestion.ids.dnssecOK) {
403✔
62
    if (*dnsQuestion.ids.dnssecOK) {
170✔
63
      /* DNSSECOK was set, which means the query had EDNS */
64
      return {true, true};
7✔
65
    }
7✔
66
  }
170✔
67

68
  if (queryHasEDNS(dnsQuestion)) {
396✔
69
    bool dnssecOK = ((dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
53✔
70
    dnsQuestion.ids.dnssecOK = dnssecOK;
53✔
71
    return {true, dnssecOK};
53✔
72
  }
53✔
73

74
  dnsQuestion.ids.dnssecOK = false;
343✔
75
  return {false, false};
343✔
76
}
396✔
77

78
bool generateAnswerFromCNAME(DNSQuestion& dnsQuestion, const DNSName& cname, const dnsdist::ResponseConfig& responseConfig)
79
{
43✔
80
  QType qtype = QType::CNAME;
43✔
81
  unsigned int totrdatalen = cname.getStorage().size();
43✔
82
  size_t numberOfRecords = 1U;
43✔
83
  auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
43✔
84
  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
43!
85
    return false;
×
86
  }
×
87

88
  auto [hadEDNS, dnssecOK] = getEDNSStatusInQuery(dnsQuestion);
43✔
89
  auto& data = dnsQuestion.getMutableData();
43✔
90
  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
43✔
91
  size_t position = sizeof(dnsheader) + qnameWireLength + 4;
43✔
92

93
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [responseConfig](dnsheader& header) {
43✔
94
    header.qr = true; // for good measure
43✔
95
    setResponseHeadersFromConfig(header, responseConfig);
43✔
96
    header.ancount = 0;
43✔
97
    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
43✔
98
    return true;
43✔
99
  });
43✔
100

101
  const auto& wireData = cname.getStorage(); // Note! This doesn't do compression!
43✔
102
  addRecordHeader(data, position, dnsQuestion.ids.qclass, responseConfig.ttl, qtype, wireData.length());
43✔
103
  memcpy(&data.at(position), wireData.c_str(), wireData.length());
43✔
104

105
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [numberOfRecords](dnsheader& header) {
43✔
106
    header.ancount = htons(numberOfRecords);
43✔
107
    return true;
43✔
108
  });
43✔
109

110
  if (hadEDNS) {
43✔
111
    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
2✔
112
  }
2✔
113

114
  dnsQuestion.d_selfGeneratedHandledEDNS = true;
43✔
115
  return true;
43✔
116
}
43✔
117

118
bool generateAnswerFromIPAddresses(DNSQuestion& dnsQuestion, const std::vector<ComboAddress>& addresses, const dnsdist::ResponseConfig& responseConfig)
119
{
170✔
120
  uint16_t qtype = dnsQuestion.ids.qtype;
170✔
121
  std::vector<ComboAddress> addrs = {};
170✔
122
  unsigned int totrdatalen = 0;
170✔
123
  size_t numberOfRecords = 0;
170✔
124
  for (const auto& addr : addresses) {
223✔
125
    if (qtype != QType::ANY && ((addr.sin4.sin_family == AF_INET && qtype != QType::A) || (addr.sin4.sin_family == AF_INET6 && qtype != QType::AAAA))) {
223✔
126
      continue;
27✔
127
    }
27✔
128
    totrdatalen += addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
196✔
129
    addrs.push_back(addr);
196✔
130
    ++numberOfRecords;
196✔
131
  }
196✔
132

133
  if (addrs.size() > 1) {
170✔
134
    shuffle(addrs.begin(), addrs.end(), t_randomEngine);
22✔
135
  }
22✔
136

137
  unsigned int qnameWireLength = dnsQuestion.ids.qname.wirelength();
170✔
138
  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
170!
139
    return false;
×
140
  }
×
141

142
  auto [hadEDNS, dnssecOK] = getEDNSStatusInQuery(dnsQuestion);
170✔
143
  auto& data = dnsQuestion.getMutableData();
170✔
144
  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
170✔
145
  size_t position = sizeof(dnsheader) + qnameWireLength + 4;
170✔
146

147
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [responseConfig](dnsheader& header) {
170✔
148
    header.qr = true; // for good measure
170✔
149
    setResponseHeadersFromConfig(header, responseConfig);
170✔
150
    header.ancount = 0;
170✔
151
    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
170✔
152
    return true;
170✔
153
  });
170✔
154

155
  for (const auto& addr : addrs) {
196✔
156
    uint16_t rdataLen = addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
196✔
157
    qtype = addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA;
196✔
158

159
    addRecordHeader(data, position, dnsQuestion.ids.qclass, responseConfig.ttl, qtype, rdataLen);
196✔
160

161
    memcpy(&data.at(position),
196✔
162
           // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
163
           addr.sin4.sin_family == AF_INET ? reinterpret_cast<const void*>(&addr.sin4.sin_addr.s_addr) : reinterpret_cast<const void*>(&addr.sin6.sin6_addr.s6_addr),
196✔
164
           addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
196✔
165

166
    position += (addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
196✔
167
  }
196✔
168

169
  auto finalANCount = addrs.size();
170✔
170
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [finalANCount](dnsheader& header) {
170✔
171
    header.ancount = htons(finalANCount);
170✔
172
    return true;
170✔
173
  });
170✔
174

175
  if (hadEDNS) {
170✔
176
    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
30✔
177
  }
30✔
178

179
  dnsQuestion.d_selfGeneratedHandledEDNS = true;
170✔
180
  return true;
170✔
181
}
170✔
182

183
bool generateAnswerFromRDataEntries(DNSQuestion& dnsQuestion, const std::vector<std::string>& entries, std::optional<uint16_t> typeForAny, const dnsdist::ResponseConfig& responseConfig)
184
{
32✔
185
  unsigned int totrdatalen = 0;
32✔
186
  size_t numberOfRecords = 0;
32✔
187
  auto shuffledEntries = entries;
32✔
188
  for (const auto& entry : shuffledEntries) {
46✔
189
    totrdatalen += entry.size();
46✔
190
    ++numberOfRecords;
46✔
191
  }
46✔
192
  if (shuffledEntries.size() > 1) {
32✔
193
    shuffle(shuffledEntries.begin(), shuffledEntries.end(), t_randomEngine);
14✔
194
  }
14✔
195

196
  auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
32✔
197
  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
32!
198
    return false;
×
199
  }
×
200

201
  auto [hadEDNS, dnssecOK] = getEDNSStatusInQuery(dnsQuestion);
32✔
202
  auto& data = dnsQuestion.getMutableData();
32✔
203
  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
32✔
204
  size_t position = sizeof(dnsheader) + qnameWireLength + 4;
32✔
205

206
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [&responseConfig](dnsheader& header) {
32✔
207
    header.qr = true; // for good measure
32✔
208
    setResponseHeadersFromConfig(header, responseConfig);
32✔
209
    header.ancount = 0;
32✔
210
    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
32✔
211
    return true;
32✔
212
  });
32✔
213

214
  QType qtype = dnsQuestion.ids.qtype;
32✔
215
  if (qtype == QType::ANY && typeForAny) {
32!
216
    qtype = *typeForAny;
2✔
217
  }
2✔
218

219
  for (const auto& entry : shuffledEntries) {
46✔
220
    uint16_t rdataLen = entry.size();
46✔
221
    addRecordHeader(data, position, dnsQuestion.ids.qclass, responseConfig.ttl, qtype, rdataLen);
46✔
222
    memcpy(&data.at(position), entry.c_str(), entry.size());
46✔
223
    position += entry.size();
46✔
224
  }
46✔
225

226
  auto finalANCount = shuffledEntries.size();
32✔
227
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [finalANCount](dnsheader& header) {
32✔
228
    header.ancount = htons(finalANCount);
32✔
229
    return true;
32✔
230
  });
32✔
231

232
  if (hadEDNS) {
32✔
233
    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
2✔
234
  }
2✔
235

236
  dnsQuestion.d_selfGeneratedHandledEDNS = true;
32✔
237
  return true;
32✔
238
}
32✔
239

240
bool generateAnswerFromRawPacket(DNSQuestion& dnsQuestion, const PacketBuffer& packet)
241
{
6✔
242
  auto questionId = dnsQuestion.getHeader()->id;
6✔
243
  dnsQuestion.getMutableData() = packet;
6✔
244
  dnsQuestion.d_selfGeneratedHandledEDNS = true;
6✔
245
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [questionId](dnsheader& header) {
6✔
246
    header.id = questionId;
6✔
247
    return true;
6✔
248
  });
6✔
249
  return true;
6✔
250
}
6✔
251

252
bool removeRecordsAndSetRCode(DNSQuestion& dnsQuestion, uint8_t rcode)
253
{
164✔
254
  auto [hadEDNS, dnssecOK] = getEDNSStatusInQuery(dnsQuestion);
164✔
255
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [rcode](dnsheader& header) {
164✔
256
    header.rcode = rcode;
164✔
257
    header.qr = true;
164✔
258
    header.qdcount = htons(1);
164✔
259
    header.ancount = 0;
164✔
260
    header.nscount = 0;
164✔
261
    header.arcount = 0;
164✔
262
    return true;
164✔
263
  });
164✔
264
  auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
164✔
265
  dnsQuestion.getMutableData().resize(sizeof(dnsheader) + qnameWireLength + 4);
164✔
266

267
  if (hadEDNS) {
164✔
268
    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
26✔
269
  }
26✔
270

271
  return true;
164✔
272
}
164✔
273

274
}
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