• 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

87.93
/pdns/dnsdistdist/dnsdist-dnsparser.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 "dnsdist-dnsparser.hh"
23
#include "dnsparser.hh"
24
#include "iputils.hh"
25

26
namespace dnsdist
27
{
28
DNSPacketOverlay::DNSPacketOverlay(const std::string_view& packet)
29
{
27✔
30
  if (packet.size() < sizeof(dnsheader)) {
27✔
31
    throw std::runtime_error("Packet is too small for a DNS packet");
3✔
32
  }
3✔
33

34
  memcpy(&d_header, packet.data(), sizeof(dnsheader));
24✔
35
  uint64_t numRecords = ntohs(d_header.ancount) + ntohs(d_header.nscount) + ntohs(d_header.arcount);
24✔
36
  d_records.reserve(numRecords);
24✔
37

38
  try {
24✔
39
    PacketReader reader(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()));
24✔
40

41
    for (uint16_t n = 0; n < ntohs(d_header.qdcount); ++n) {
48✔
42
      reader.xfrName(d_qname);
24✔
43
      reader.xfrType(d_qtype);
24✔
44
      reader.xfrType(d_qclass);
24✔
45
    }
24✔
46

47
    for (uint64_t n = 0; n < numRecords; ++n) {
113✔
48
      Record rec;
89✔
49
      reader.xfrName(rec.d_name);
89✔
50
      rec.d_place = n < ntohs(d_header.ancount) ? DNSResourceRecord::ANSWER : (n < (ntohs(d_header.ancount) + ntohs(d_header.nscount)) ? DNSResourceRecord::AUTHORITY : DNSResourceRecord::ADDITIONAL);
89!
51
      reader.xfrType(rec.d_type);
89✔
52
      reader.xfrType(rec.d_class);
89✔
53
      reader.xfr32BitInt(rec.d_ttl);
89✔
54
      reader.xfr16BitInt(rec.d_contentLength);
89✔
55
      rec.d_contentOffset = reader.getPosition();
89✔
56
      reader.skip(rec.d_contentLength);
89✔
57
      d_records.push_back(std::move(rec));
89✔
58
    }
89✔
59
  }
24✔
60
  catch (const std::exception& e) {
24✔
61
    throw std::runtime_error("Unable to parse DNS packet: " + std::string(e.what()));
6✔
62
  }
6✔
63
  catch (...) {
24✔
64
    throw std::runtime_error("Unable to parse DNS packet");
×
65
  }
×
66
}
24✔
67

68
bool changeNameInDNSPacket(PacketBuffer& initialPacket, const DNSName& from, const DNSName& to)
69
{
47✔
70
  if (initialPacket.size() < sizeof(dnsheader)) {
47✔
71
    return false;
3✔
72
  }
3✔
73

74
  PacketReader pr(std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size()));
44✔
75

76
  dnsheader dh;
44✔
77
  memcpy(&dh, initialPacket.data(), sizeof(dh));
44✔
78
  size_t idx = 0;
44✔
79
  DNSName rrname;
44✔
80
  uint16_t qdcount = ntohs(dh.qdcount);
44✔
81
  uint16_t ancount = ntohs(dh.ancount);
44✔
82
  uint16_t nscount = ntohs(dh.nscount);
44✔
83
  uint16_t arcount = ntohs(dh.arcount);
44✔
84
  uint16_t rrtype;
44✔
85
  uint16_t rrclass;
44✔
86
  string blob;
44✔
87

88
  size_t recordsCount = ancount + nscount + arcount;
44✔
89
  struct dnsrecordheader ah;
44✔
90

91
  rrname = pr.getName();
44✔
92
  if (rrname == from) {
44✔
93
    rrname = to;
41✔
94
  }
41✔
95

96
  rrtype = pr.get16BitInt();
44✔
97
  rrclass = pr.get16BitInt();
44✔
98

99
  PacketBuffer newContent;
44✔
100
  newContent.reserve(initialPacket.size());
44✔
101
  GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh.opcode);
44✔
102
  /* we want to copy the flags and ID but not the counts since we recreate the records below */
103
  pw.getHeader()->id = dh.id;
44✔
104
  pw.getHeader()->qr = dh.qr;
44✔
105
  pw.getHeader()->aa = dh.aa;
44✔
106
  pw.getHeader()->tc = dh.tc;
44✔
107
  pw.getHeader()->rd = dh.rd;
44✔
108
  pw.getHeader()->ra = dh.ra;
44✔
109
  pw.getHeader()->ad = dh.ad;
44✔
110
  pw.getHeader()->cd = dh.cd;
44✔
111
  pw.getHeader()->rcode = dh.rcode;
44✔
112

113
  /* consume remaining qd if any, but do not copy it */
114
  for (idx = 1; idx < qdcount; idx++) {
44!
115
    rrname = pr.getName();
×
116
    (void)pr.get16BitInt();
×
117
    (void)pr.get16BitInt();
×
118
  }
×
119

120
  static const std::unordered_set<QType> nameOnlyTypes{QType::NS, QType::PTR, QType::CNAME, QType::DNAME};
44✔
121
  static const std::unordered_set<QType> noNameTypes{QType::A, QType::AAAA, QType::DHCID, QType::TXT, QType::OPT, QType::HINFO, QType::DNSKEY, QType::CDNSKEY, QType::DS, QType::CDS, QType::DLV, QType::SSHFP, QType::KEY, QType::CERT, QType::TLSA, QType::SMIMEA, QType::OPENPGPKEY, QType::NSEC, QType::NSEC3, QType::CSYNC, QType::NSEC3PARAM, QType::LOC, QType::NID, QType::L32, QType::L64, QType::EUI48, QType::EUI64, QType::URI, QType::CAA};
44✔
122

123
  /* copy AN, NS and AR */
124
  for (idx = 0; idx < recordsCount; idx++) {
122✔
125
    rrname = pr.getName();
81✔
126
    if (rrname == from) {
81✔
127
      rrname = to;
58✔
128
    }
58✔
129
    pr.getDnsrecordheader(ah);
81✔
130

131
    auto place = idx < ancount ? DNSResourceRecord::ANSWER : (idx < (ancount + nscount) ? DNSResourceRecord::AUTHORITY : DNSResourceRecord::ADDITIONAL);
81!
132
    pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, place, true);
81✔
133
    if (nameOnlyTypes.count(ah.d_type)) {
81✔
134
      rrname = pr.getName();
9✔
135
      pw.xfrName(rrname);
9✔
136
    }
9✔
137
    else if (noNameTypes.count(ah.d_type)) {
72✔
138
      pr.xfrBlob(blob);
54✔
139
      pw.xfrBlob(blob);
54✔
140
    }
54✔
141
    else if (ah.d_type == QType::RRSIG) {
18✔
142
      /* good luck */
143
      pr.xfrBlob(blob);
3✔
144
      pw.xfrBlob(blob);
3✔
145
    }
3✔
146
    else if (ah.d_type == QType::MX) {
15✔
147
      auto prio = pr.get16BitInt();
6✔
148
      rrname = pr.getName();
6✔
149
      pw.xfr16BitInt(prio);
6✔
150
      pw.xfrName(rrname);
6✔
151
    }
6✔
152
    else if (ah.d_type == QType::SOA) {
9✔
153
      auto mname = pr.getName();
3✔
154
      pw.xfrName(mname);
3✔
155
      auto rname = pr.getName();
3✔
156
      pw.xfrName(rname);
3✔
157
      /* serial */
158
      pw.xfr32BitInt(pr.get32BitInt());
3✔
159
      /* refresh */
160
      pw.xfr32BitInt(pr.get32BitInt());
3✔
161
      /* retry */
162
      pw.xfr32BitInt(pr.get32BitInt());
3✔
163
      /* expire */
164
      pw.xfr32BitInt(pr.get32BitInt());
3✔
165
      /* minimal */
166
      pw.xfr32BitInt(pr.get32BitInt());
3✔
167
    }
3✔
168
    else if (ah.d_type == QType::SRV) {
6✔
169
      /* preference */
170
      pw.xfr16BitInt(pr.get16BitInt());
3✔
171
      /* weight */
172
      pw.xfr16BitInt(pr.get16BitInt());
3✔
173
      /* port */
174
      pw.xfr16BitInt(pr.get16BitInt());
3✔
175
      auto target = pr.getName();
3✔
176
      pw.xfrName(target);
3✔
177
    }
3✔
178
    else {
3✔
179
      /* sorry, unsafe type */
180
      return false;
3✔
181
    }
3✔
182
  }
81✔
183

184
  pw.commit();
41✔
185
  initialPacket = std::move(newContent);
41✔
186

187
  return true;
41✔
188
}
44✔
189

190
namespace PacketMangling
191
{
192
  bool editDNSHeaderFromPacket(PacketBuffer& packet, const std::function<bool(dnsheader& header)>& editFunction)
193
  {
74,033✔
194
    if (packet.size() < sizeof(dnsheader)) {
74,033!
195
      throw std::runtime_error("Trying to edit the DNS header of a too small packet");
×
196
    }
×
197

198
    return editDNSHeaderFromRawPacket(packet.data(), editFunction);
74,033✔
199
  }
74,033✔
200

201
  bool editDNSHeaderFromRawPacket(void* packet, const std::function<bool(dnsheader& header)>& editFunction)
202
  {
74,041✔
203
    if (dnsheader_aligned::isMemoryAligned(packet)) {
74,041!
204
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
205
      auto* header = reinterpret_cast<dnsheader*>(packet);
74,041✔
206
      return editFunction(*header);
74,041✔
207
    }
74,041✔
208

209
    dnsheader header{};
×
210
    memcpy(&header, packet, sizeof(header));
×
211
    if (!editFunction(header)) {
×
212
      return false;
×
213
    }
×
214
    memcpy(packet, &header, sizeof(header));
×
215
    return true;
×
216
  }
×
217

218
  void restrictDNSPacketTTLs(PacketBuffer& packet, uint32_t minimumValue, uint32_t maximumValue, const std::unordered_set<QType>& types)
219
  {
20✔
220
    auto visitor = [minimumValue, maximumValue, types](uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl) {
20✔
221
      (void)section;
11✔
222
      if (!types.empty() && qclass == QClass::IN && types.count(qtype) == 0) {
11!
223
        return ttl;
×
224
      }
×
225

226
      if (minimumValue > 0) {
11✔
227
        if (ttl < minimumValue) {
7✔
228
          ttl = minimumValue;
6✔
229
        }
6✔
230
      }
7✔
231
      if (ttl > maximumValue) {
11✔
232
        ttl = maximumValue;
7✔
233
      }
7✔
234
      return ttl;
11✔
235
    };
11✔
236
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
237
    editDNSPacketTTL(reinterpret_cast<char*>(packet.data()), packet.size(), visitor);
20✔
238
  }
20✔
239

240
}
241

242
namespace RecordParsers
243
{
244
  std::optional<ComboAddress> parseARecord(const std::string_view& packet, const DNSPacketOverlay::Record& record)
245
  {
9✔
246
    if (record.d_type != QType::A || record.d_contentLength != 4) {
9!
247
      return {};
4✔
248
    }
4✔
249

250
    // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage): length is passed in and used to read data
251
    return makeComboAddressFromRaw(4, packet.substr(record.d_contentOffset, record.d_contentOffset + 4).data(), record.d_contentLength);
5✔
252
  }
9✔
253

254
  std::optional<ComboAddress> parseAAAARecord(const std::string_view& packet, const DNSPacketOverlay::Record& record)
255
  {
9✔
256
    if (record.d_type != QType::AAAA || record.d_contentLength != 16) {
9!
257
      return {};
4✔
258
    }
4✔
259

260
    // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage): length is passed in and used to read data
261
    return makeComboAddressFromRaw(6, packet.substr(record.d_contentOffset, record.d_contentOffset + 16).data(), record.d_contentLength);
5✔
262
  }
9✔
263

264
  std::optional<ComboAddress> parseAddressRecord(const std::string_view& packet, const DNSPacketOverlay::Record& record)
265
  {
12✔
266
    if (record.d_type == QType::A && record.d_contentLength == 4) {
12!
267
      // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage): length is passed in and used to read data
268
      return makeComboAddressFromRaw(4, packet.substr(record.d_contentOffset, record.d_contentOffset + 4).data(), record.d_contentLength);
5✔
269
    }
5✔
270

271
    if (record.d_type == QType::AAAA && record.d_contentLength == 16) {
7!
272
      // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage): length is passed in and used to read data
273
      return makeComboAddressFromRaw(6, packet.substr(record.d_contentOffset, record.d_contentOffset + 16).data(), record.d_contentLength);
5✔
274
    }
5✔
275

276
    return {};
2✔
277
  }
7✔
278

279
  std::optional<DNSName> parseCNAMERecord(const std::string_view& packet, const DNSPacketOverlay::Record& record)
280
  {
9✔
281
    if (record.d_type != QType::CNAME) {
9✔
282
      return {};
4✔
283
    }
4✔
284

285
    // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage): length is passed in and used to read data
286
    return DNSName(packet.data(), record.d_contentOffset + record.d_contentLength, record.d_contentOffset, true);
5✔
287
  }
9✔
288
}
289

290
void setResponseHeadersFromConfig(dnsheader& dnsheader, const ResponseConfig& config)
291
{
399✔
292
  if (config.setAA) {
399✔
293
    dnsheader.aa = *config.setAA;
63✔
294
  }
63✔
295
  if (config.setAD) {
399✔
296
    dnsheader.ad = *config.setAD;
44✔
297
  }
44✔
298
  else {
355✔
299
    dnsheader.ad = false;
355✔
300
  }
355✔
301
  if (config.setRA) {
399✔
302
    dnsheader.ra = *config.setRA;
46✔
303
  }
46✔
304
  else {
353✔
305
    dnsheader.ra = dnsheader.rd; // for good measure
353✔
306
  }
353✔
307
}
399✔
308
}
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