Coverage Report

Created: 2026-07-16 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsparser.cc
Line
Count
Source
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 "dnsparser.hh"
23
#include "dnswriter.hh"
24
#include <boost/algorithm/string.hpp>
25
#include <boost/format.hpp>
26
#include <cstdint>
27
#include <stdexcept>
28
#include <string>
29
30
#include "dns_random.hh"
31
#include "namespaces.hh"
32
#include "noinitvector.hh"
33
34
std::atomic<bool> DNSRecordContent::d_locked{false};
35
36
UnknownRecordContent::UnknownRecordContent(const string& zone)
37
323
{
38
  // parse the input
39
323
  vector<string> parts;
40
323
  stringtok(parts, zone);
41
  // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
42
323
  if (parts.size() != 3 && !(parts.size() == 2 && boost::equals(parts.at(1), "0"))) {
43
149
    throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
44
149
  }
45
46
174
  if (parts.at(0) != "\\#") {
47
75
    throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
48
75
  }
49
50
99
  const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
51
99
  auto total = pdns::checked_stoi<unsigned int>(parts.at(1));
52
99
  if (relevant.size() % 2 || (relevant.size() / 2) != total) {
53
55
    throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
54
55
  }
55
56
44
  string out;
57
44
  out.reserve(total + 1);
58
59
322
  for (unsigned int n = 0; n < total; ++n) {
60
282
    int c;
61
282
    if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
62
4
      throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
63
4
    }
64
278
    out.append(1, (char)c);
65
278
  }
66
67
40
  d_record.insert(d_record.end(), out.begin(), out.end());
68
40
}
69
70
string UnknownRecordContent::getZoneRepresentation(bool /* noDot */) const
71
365
{
72
365
  ostringstream str;
73
365
  str<<"\\# "<<(unsigned int)d_record.size();
74
365
  if (!d_record.empty()) {
75
362
    std::array<char,4> hex{};
76
362
    str << " ";
77
879k
    for (auto byte : d_record) {
78
879k
      snprintf(hex.data(), hex.size(), "%02x", byte);
79
879k
      str << hex.data();
80
879k
    }
81
362
  }
82
365
  return str.str();
83
365
}
84
85
void UnknownRecordContent::toPacket(DNSPacketWriter& pw) const
86
22
{
87
22
  pw.xfrBlob(string(d_record.begin(),d_record.end()));
88
22
}
89
90
shared_ptr<DNSRecordContent> DNSRecordContent::deserialize(const DNSName& qname, uint16_t qtype, const string& serialized, uint16_t qclass, bool internalRepresentation)
91
12.8k
{
92
12.8k
  dnsheader dnsheader;
93
12.8k
  memset(&dnsheader, 0, sizeof(dnsheader));
94
12.8k
  dnsheader.qdcount=htons(1);
95
12.8k
  dnsheader.ancount=htons(1);
96
97
12.8k
  PacketBuffer packet; // build pseudo packet
98
  /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
99
12.8k
  const auto& encoded = qname.getStorage();
100
12.8k
  packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
101
102
12.8k
  uint16_t pos=0;
103
12.8k
  memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
104
105
12.8k
  constexpr std::array<uint8_t, 5> tmp= {'\x0', '\x0', '\x1', '\x0', '\x1' }; // root question for ns_t_a
106
12.8k
  memcpy(&packet[pos], tmp.data(), tmp.size()); pos += tmp.size();
107
108
12.8k
  memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
109
110
12.8k
  struct dnsrecordheader drh;
111
12.8k
  drh.d_type=htons(qtype);
112
12.8k
  drh.d_class=htons(qclass);
113
12.8k
  drh.d_ttl=0;
114
12.8k
  drh.d_clen=htons(serialized.size());
115
116
12.8k
  memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
117
12.8k
  if (!serialized.empty()) {
118
12.7k
    memcpy(&packet[pos], serialized.c_str(), serialized.size());
119
12.7k
    pos += (uint16_t) serialized.size();
120
12.7k
    (void) pos;
121
12.7k
  }
122
123
12.8k
  DNSRecord dr;
124
12.8k
  dr.d_class = qclass;
125
12.8k
  dr.d_type = qtype;
126
12.8k
  dr.d_name = qname;
127
12.8k
  dr.d_clen = serialized.size();
128
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): packet.data() is uint8_t *
129
12.8k
  PacketReader reader(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()), packet.size() - serialized.size() - sizeof(dnsrecordheader), internalRepresentation);
130
  /* needed to get the record boundaries right */
131
12.8k
  reader.getDnsrecordheader(drh);
132
12.8k
  auto content = DNSRecordContent::make(dr, reader, Opcode::Query);
133
12.8k
  return content;
134
12.8k
}
135
136
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr,
137
                                                         PacketReader& pr)
138
0
{
139
0
  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
140
141
0
  auto i = getTypemap().find(pair(searchclass, dr.d_type));
142
0
  if(i==getTypemap().end() || !i->second) {
143
0
    return std::make_shared<UnknownRecordContent>(dr, pr);
144
0
  }
145
146
0
  return i->second(dr, pr);
147
0
}
148
149
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(uint16_t qtype, uint16_t qclass,
150
                                                         const string& content)
151
9.33k
{
152
9.33k
  auto i = getZmakermap().find(pair(qclass, qtype));
153
9.33k
  if(i==getZmakermap().end()) {
154
323
    return std::make_shared<UnknownRecordContent>(content);
155
323
  }
156
157
9.00k
  return i->second(content);
158
9.33k
}
159
160
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr, PacketReader& pr, uint16_t oc)
161
188k
{
162
  // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is
163
  // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent.
164
  // For section 3.2.3, we do need content so we need to get it properly. But only for the correct QClasses.
165
188k
  if (oc == Opcode::Update && dr.d_place == DNSResourceRecord::ANSWER && dr.d_class != 1)
166
3.03k
    return std::make_shared<UnknownRecordContent>(dr, pr);
167
168
185k
  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
169
170
185k
  auto i = getTypemap().find(pair(searchclass, dr.d_type));
171
185k
  if(i==getTypemap().end() || !i->second) {
172
57.6k
    return std::make_shared<UnknownRecordContent>(dr, pr);
173
57.6k
  }
174
175
127k
  return i->second(dr, pr);
176
185k
}
177
178
0
string DNSRecordContent::upgradeContent(const DNSName& qname, const QType& qtype, const string& content) {
179
  // seamless upgrade for previously unsupported but now implemented types.
180
0
  UnknownRecordContent unknown_content(content);
181
0
  shared_ptr<DNSRecordContent> rc = DNSRecordContent::deserialize(qname, qtype.getCode(), unknown_content.serialize(qname));
182
0
  return rc->getZoneRepresentation();
183
0
}
184
185
DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
186
371k
{
187
371k
  static DNSRecordContent::typemap_t typemap;
188
371k
  return typemap;
189
371k
}
190
191
DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
192
351k
{
193
351k
  static DNSRecordContent::n2typemap_t n2typemap;
194
351k
  return n2typemap;
195
351k
}
196
197
DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
198
38.0k
{
199
38.0k
  static DNSRecordContent::t2namemap_t t2namemap;
200
38.0k
  return t2namemap;
201
38.0k
}
202
203
DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
204
19.1k
{
205
19.1k
  static DNSRecordContent::zmakermap_t zmakermap;
206
19.1k
  return zmakermap;
207
19.1k
}
208
209
bool DNSRecordContent::isRegisteredType(uint16_t rtype, uint16_t rclass)
210
0
{
211
0
  return getTypemap().count(pair(rclass, rtype)) != 0;
212
0
}
213
214
0
DNSRecord::DNSRecord(const DNSResourceRecord& rr): d_name(rr.qname)
215
0
{
216
0
  d_type = rr.qtype.getCode();
217
0
  d_ttl = rr.ttl;
218
0
  d_class = rr.qclass;
219
0
  d_place = DNSResourceRecord::ANSWER;
220
0
  d_clen = 0;
221
0
  d_content = DNSRecordContent::make(d_type, rr.qclass, rr.content);
222
0
}
223
224
// If you call this and you are not parsing a packet coming from a socket, you are doing it wrong.
225
DNSResourceRecord DNSResourceRecord::fromWire(const DNSRecord& wire)
226
0
{
227
0
  DNSResourceRecord resourceRecord;
228
0
  resourceRecord.qname = wire.d_name;
229
0
  resourceRecord.qtype = QType(wire.d_type);
230
0
  resourceRecord.ttl = wire.d_ttl;
231
0
  resourceRecord.content = wire.getContent()->getZoneRepresentation(true);
232
0
  resourceRecord.auth = false;
233
0
  resourceRecord.qclass = wire.d_class;
234
0
  return resourceRecord;
235
0
}
236
237
void MOADNSParser::init(bool query, const std::string_view& packet)
238
15.1k
{
239
15.1k
  if (packet.size() < sizeof(dnsheader))
240
16
    throw MOADNSException("Packet shorter than minimal header");
241
242
15.1k
  memcpy(&d_header, packet.data(), sizeof(dnsheader));
243
244
15.1k
  if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
245
8
    throw MOADNSException("Can't parse non-query packet with opcode="+ std::to_string(d_header.opcode));
246
247
15.1k
  d_header.qdcount=ntohs(d_header.qdcount);
248
15.1k
  d_header.ancount=ntohs(d_header.ancount);
249
15.1k
  d_header.nscount=ntohs(d_header.nscount);
250
15.1k
  d_header.arcount=ntohs(d_header.arcount);
251
252
15.1k
  if (query && (d_header.qdcount > 1))
253
281
    throw MOADNSException("Query with QD > 1 ("+std::to_string(d_header.qdcount)+")");
254
255
14.8k
  unsigned int n=0;
256
257
14.8k
  PacketReader pr(packet);
258
14.8k
  bool validPacket=false;
259
14.8k
  try {
260
14.8k
    d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
261
262
59.1k
    for(n=0;n < d_header.qdcount; ++n) {
263
44.3k
      d_qname=pr.getName();
264
44.3k
      d_qtype=pr.get16BitInt();
265
44.3k
      d_qclass=pr.get16BitInt();
266
44.3k
    }
267
268
14.8k
    struct dnsrecordheader ah;
269
14.8k
    vector<unsigned char> record;
270
14.8k
    bool seenTSIG = false;
271
14.8k
    validPacket=true;
272
14.8k
    unsigned int supposedRecordCount = d_header.ancount + d_header.nscount + d_header.arcount;
273
    // No need to reserve more memory for more records than the request can
274
    // contain. We could try to be smarter and actually count the records
275
    // by doing getDnsrecordheader and skip the payload in a loop, but all
276
    // we really want here is to avoid reserving too much memory in case of
277
    // maliciously high record counts.
278
14.8k
    auto reserveRecordCount = std::min(1 + (packet.size() - sizeof(dnsheader)) / sizeof(dnsrecordheader), static_cast<size_t>(supposedRecordCount));
279
14.8k
    d_answers.reserve(reserveRecordCount);
280
250k
    for (n = 0; n < supposedRecordCount; ++n) {
281
236k
      DNSRecord dr;
282
283
236k
      if(n < d_header.ancount)
284
131k
        dr.d_place=DNSResourceRecord::ANSWER;
285
105k
      else if(n < d_header.ancount + d_header.nscount)
286
69.5k
        dr.d_place=DNSResourceRecord::AUTHORITY;
287
35.6k
      else
288
35.6k
        dr.d_place=DNSResourceRecord::ADDITIONAL;
289
290
236k
      unsigned int recordStartPos=pr.getPosition();
291
292
236k
      DNSName name=pr.getName();
293
294
236k
      pr.getDnsrecordheader(ah);
295
236k
      dr.d_ttl=ah.d_ttl;
296
236k
      dr.d_type=ah.d_type;
297
236k
      dr.d_class=ah.d_class;
298
299
236k
      dr.d_name = std::move(name);
300
236k
      dr.d_clen = ah.d_clen;
301
302
236k
      if (query &&
303
55.6k
          !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section
304
54.6k
          (dr.d_place == DNSResourceRecord::ANSWER || dr.d_place == DNSResourceRecord::AUTHORITY || (dr.d_type != QType::OPT && dr.d_type != QType::TSIG && dr.d_type != QType::SIG && dr.d_type != QType::TKEY) || ((dr.d_type == QType::TSIG || dr.d_type == QType::SIG || dr.d_type == QType::TKEY) && dr.d_class != QClass::ANY))) {
305
//        cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
306
52.9k
        dr.setContent(std::make_shared<UnknownRecordContent>(dr, pr));
307
52.9k
      }
308
183k
      else {
309
//        cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
310
183k
        dr.setContent(DNSRecordContent::make(dr, pr, d_header.opcode));
311
183k
      }
312
313
236k
      if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
314
180
        throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has an unexpected record ("+std::to_string(dr.d_type)+") after a TSIG one.");
315
180
      }
316
317
236k
      if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
318
465
        if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
319
235
          throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
320
235
        }
321
230
        seenTSIG = true;
322
230
        d_tsigPos = recordStartPos;
323
230
      }
324
325
235k
      d_answers.emplace_back(std::move(dr));
326
235k
    }
327
328
#if 0
329
    if(pr.getPosition()!=packet.size()) {
330
      throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.getPosition()) + " < " +
331
                            std::to_string(packet.size()) + ")");
332
    }
333
#endif
334
14.8k
  }
335
14.8k
  catch(const std::out_of_range &re) {
336
10.6k
    if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
337
2.30k
      if(n < d_header.ancount) {
338
1.36k
        d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
339
1.36k
      }
340
940
      else if(n < d_header.ancount + d_header.nscount) {
341
503
        d_header.nscount = n - d_header.ancount; d_header.arcount=0;
342
503
      }
343
437
      else {
344
437
        d_header.arcount = n - d_header.ancount - d_header.nscount;
345
437
      }
346
2.30k
    }
347
8.35k
    else {
348
8.35k
      throw MOADNSException("Error parsing packet of "+std::to_string(packet.size())+" bytes (rd="+
349
8.35k
                            std::to_string(d_header.rd)+
350
8.35k
                            "), out of bounds: "+string(re.what()));
351
8.35k
    }
352
10.6k
  }
353
14.8k
}
354
355
bool MOADNSParser::hasEDNS() const
356
0
{
357
0
  if (d_header.arcount == 0 || d_answers.empty()) {
358
0
    return false;
359
0
  }
360
361
0
  for (const auto& record : d_answers) {
362
0
    if (record.d_place == DNSResourceRecord::ADDITIONAL && record.d_type == QType::OPT) {
363
0
      return true;
364
0
    }
365
0
  }
366
367
0
  return false;
368
0
}
369
370
void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
371
243k
{
372
243k
  unsigned char *p = reinterpret_cast<unsigned char*>(&ah);
373
374
2.67M
  for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
375
2.43M
    p[n] = d_content.at(d_pos++);
376
2.43M
  }
377
378
243k
  ah.d_type = ntohs(ah.d_type);
379
243k
  ah.d_class = ntohs(ah.d_class);
380
243k
  ah.d_clen = ntohs(ah.d_clen);
381
243k
  ah.d_ttl = ntohl(ah.d_ttl);
382
383
243k
  d_startrecordpos = d_pos; // needed for getBlob later on
384
243k
  d_recordlen = ah.d_clen;
385
243k
  if (d_pos > d_content.size() || (d_content.size() - d_pos) < (d_recordlen)) {
386
1.23k
    throw std::out_of_range("DNS record length (" + std::to_string(d_recordlen) + " starting at " + std::to_string(d_pos) + ") goes beyond the packet's content (" + std::to_string(d_content.size()) + ")");
387
1.23k
  }
388
243k
}
389
390
391
void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
392
113k
{
393
113k
  if (len == 0) {
394
69.2k
    return;
395
69.2k
  }
396
44.3k
  if ((d_pos + len) > d_content.size()) {
397
0
    throw std::out_of_range("Attempt to copy outside of packet");
398
0
  }
399
400
44.3k
  dest.resize(len);
401
402
12.2M
  for (uint16_t n = 0; n < len; ++n) {
403
12.2M
    dest.at(n) = d_content.at(d_pos++);
404
12.2M
  }
405
44.3k
}
406
407
void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
408
1.78k
{
409
1.78k
  if (d_pos + len > d_content.size()) {
410
0
    throw std::out_of_range("Attempt to copy outside of packet");
411
0
  }
412
413
1.78k
  memcpy(dest, &d_content.at(d_pos), len);
414
1.78k
  d_pos += len;
415
1.78k
}
416
417
void PacketReader::xfrNodeOrLocatorID(NodeOrLocatorID& ret)
418
2.09k
{
419
2.09k
  if (d_pos + sizeof(ret) > d_content.size()) {
420
49
    throw std::out_of_range("Attempt to read 64 bit value outside of packet");
421
49
  }
422
2.04k
  memcpy(&ret.content, &d_content.at(d_pos), sizeof(ret.content));
423
2.04k
  d_pos += sizeof(ret);
424
2.04k
}
425
426
void PacketReader::xfr48BitInt(uint64_t& ret)
427
611
{
428
611
  ret=0;
429
611
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
430
611
  ret<<=8;
431
611
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
432
611
  ret<<=8;
433
611
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
434
611
  ret<<=8;
435
611
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
436
611
  ret<<=8;
437
611
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
438
611
  ret<<=8;
439
611
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
440
611
}
441
442
uint32_t PacketReader::get32BitInt()
443
37.4k
{
444
37.4k
  uint32_t ret=0;
445
37.4k
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
446
37.4k
  ret<<=8;
447
37.4k
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
448
37.4k
  ret<<=8;
449
37.4k
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
450
37.4k
  ret<<=8;
451
37.4k
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
452
453
37.4k
  return ret;
454
37.4k
}
455
456
457
uint16_t PacketReader::get16BitInt()
458
1.06M
{
459
1.06M
  uint16_t ret=0;
460
1.06M
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
461
1.06M
  ret<<=8;
462
1.06M
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
463
464
1.06M
  return ret;
465
1.06M
}
466
467
uint8_t PacketReader::get8BitInt()
468
505k
{
469
505k
  return d_content.at(d_pos++);
470
505k
}
471
472
DNSName PacketReader::getName()
473
347k
{
474
347k
  unsigned int consumed;
475
347k
  try {
476
347k
    DNSName dn((const char*) d_content.data(), d_content.size(), d_pos, true /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, &consumed, sizeof(dnsheader));
477
478
347k
    d_pos+=consumed;
479
347k
    return dn;
480
347k
  }
481
347k
  catch(const std::range_error& re) {
482
8.89k
    throw std::out_of_range(string("dnsname issue: ")+re.what());
483
8.89k
  }
484
347k
  catch(...) {
485
0
    throw std::out_of_range("dnsname issue");
486
0
  }
487
0
  throw PDNSException("PacketReader::getName(): name is empty");
488
347k
}
489
490
// FIXME see #6010 and #3503 if you want a proper solution
491
string txtEscape(const string &name)
492
635k
{
493
635k
  string ret;
494
635k
  std::array<char, 5> ebuf{};
495
496
8.81M
  for (char letter : name) {
497
8.81M
    const unsigned uch = static_cast<unsigned char>(letter);
498
8.81M
    if (uch >= 127 || uch < 32) {
499
2.09M
      snprintf(ebuf.data(), ebuf.size(), "\\%03u", uch);
500
2.09M
      ret += ebuf.data();
501
2.09M
    }
502
6.72M
    else if (letter == '"' || letter == '\\'){
503
681k
      ret += '\\';
504
681k
      ret += letter;
505
681k
    }
506
6.04M
    else {
507
6.04M
      ret += letter;
508
6.04M
    }
509
8.81M
  }
510
635k
  return ret;
511
635k
}
512
513
// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
514
string PacketReader::getText(bool multi, bool lenField)
515
19.1k
{
516
19.1k
  string ret;
517
19.1k
  ret.reserve(40);
518
418k
  while (d_pos < d_startrecordpos + d_recordlen ) {
519
401k
    if (!ret.empty()) {
520
393k
      ret.append(1,' ');
521
393k
    }
522
401k
    uint16_t labellen;
523
401k
    if (lenField) {
524
398k
      labellen = static_cast<uint8_t>(d_content.at(d_pos++));
525
398k
    }
526
3.65k
    else {
527
3.65k
      labellen = d_recordlen - (d_pos - d_startrecordpos);
528
3.65k
    }
529
530
401k
    const uint16_t remaining = (d_startrecordpos + d_recordlen) - d_pos;
531
401k
    if (labellen > remaining) {
532
621
      throw std::out_of_range("label length in text record exceeds record boundary");
533
621
    }
534
535
401k
    ret.append(1, '"');
536
401k
    if (labellen) { // no need to do anything for an empty string
537
71.7k
      string val(&d_content.at(d_pos), &d_content.at(d_pos + labellen - 1) + 1);
538
71.7k
      ret.append(txtEscape(val)); // the end is one beyond the packet
539
71.7k
    }
540
401k
    ret.append(1, '"');
541
401k
    d_pos += labellen;
542
401k
    if (!multi) {
543
1.84k
      break;
544
1.84k
    }
545
401k
  }
546
547
18.5k
  if (ret.empty() && !lenField) {
548
    // all lenField == false cases (CAA and URI at the time of this writing) want that emptiness to be explicit
549
1.64k
    return "\"\"";
550
1.64k
  }
551
16.8k
  return ret;
552
18.5k
}
553
554
string PacketReader::getUnquotedText(bool lenField)
555
3.85k
{
556
3.85k
  uint16_t stop_at{};
557
3.85k
  if (lenField) {
558
3.85k
    stop_at = static_cast<uint8_t>(d_content.at(d_pos)) + d_pos + 1;
559
3.85k
  }
560
0
  else {
561
0
    stop_at = d_recordlen;
562
0
  }
563
564
  /* think unsigned overflow */
565
3.85k
  if (stop_at < d_pos) {
566
1
    throw std::out_of_range("getUnquotedText out of record range");
567
1
  }
568
569
  /* Validate against record boundary */
570
3.85k
  const uint16_t recordEnd = d_startrecordpos + d_recordlen;
571
3.85k
  if (stop_at > recordEnd) {
572
131
    throw std::out_of_range("getUnquotedText: length exceeds record boundary");
573
131
  }
574
575
3.72k
  if (stop_at == d_pos) {
576
0
    return "";
577
0
  }
578
579
3.72k
  d_pos++;
580
3.72k
  string ret(d_content.substr(d_pos, stop_at-d_pos));
581
3.72k
  d_pos = stop_at;
582
3.72k
  return ret;
583
3.72k
}
584
585
void PacketReader::xfrBlob(string& blob)
586
45.9k
{
587
45.9k
  try {
588
45.9k
    if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
589
15.0k
      if (d_pos > (d_startrecordpos + d_recordlen)) {
590
29
        throw std::out_of_range("xfrBlob out of record range");
591
29
      }
592
14.9k
      blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
593
14.9k
    }
594
30.9k
    else {
595
30.9k
      blob.clear();
596
30.9k
    }
597
598
45.9k
    d_pos = d_startrecordpos + d_recordlen;
599
45.9k
  }
600
45.9k
  catch(...)
601
45.9k
  {
602
29
    throw std::out_of_range("xfrBlob out of range");
603
29
  }
604
45.9k
}
605
606
2.16k
void PacketReader::xfrBlobNoSpaces(string& blob, int length) {
607
2.16k
  xfrBlob(blob, length);
608
2.16k
}
609
610
void PacketReader::xfrBlob(string& blob, int length)
611
612k
{
612
612k
  if(length) {
613
606k
    if (length < 0) {
614
0
      throw std::out_of_range("xfrBlob out of range (negative length)");
615
0
    }
616
606k
    auto available = (d_startrecordpos + d_recordlen) - d_pos;
617
606k
    if (available < length) {
618
529
      throw std::out_of_range("xfrBlob out of range (excessive length)");
619
529
    }
620
621
605k
    blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
622
623
605k
    d_pos += length;
624
605k
  }
625
6.16k
  else {
626
6.16k
    blob.clear();
627
6.16k
  }
628
612k
}
629
630
12.1k
void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
631
12.1k
  int32_t lastKey{-1}; // Keep track of the last key, as ordering should be strict
632
633
27.4k
  while (d_pos < (d_startrecordpos + d_recordlen)) {
634
16.4k
    if (d_pos + 2 > (d_startrecordpos + d_recordlen)) {
635
82
      throw std::out_of_range("incomplete key");
636
82
    }
637
16.3k
    uint16_t keyInt;
638
16.3k
    xfr16BitInt(keyInt);
639
640
16.3k
    if (keyInt <= lastKey) {
641
294
      throw std::out_of_range("Found SVCParamKey " + std::to_string(keyInt) + " after SVCParamKey " + std::to_string(lastKey));
642
294
    }
643
16.0k
    lastKey = keyInt;
644
645
16.0k
    auto key = static_cast<SvcParam::SvcParamKey>(keyInt);
646
647
16.0k
    uint16_t len;
648
16.0k
    xfr16BitInt(len);
649
650
16.0k
    if (d_pos + len > (d_startrecordpos + d_recordlen)) {
651
448
      throw std::out_of_range("record is shorter than SVCB lengthfield implies");
652
448
    }
653
654
15.6k
    switch (key)
655
15.6k
    {
656
1.58k
    case SvcParam::mandatory: {
657
1.58k
      if (len % 2 != 0) {
658
8
        throw std::out_of_range("mandatory SvcParam has invalid length");
659
8
      }
660
1.57k
      if (len == 0) {
661
32
        throw std::out_of_range("empty 'mandatory' values");
662
32
      }
663
1.54k
      std::set<SvcParam::SvcParamKey> paramKeys;
664
1.54k
      size_t stop = d_pos + len;
665
632k
      while (d_pos < stop) {
666
630k
        uint16_t keyval;
667
630k
        xfr16BitInt(keyval);
668
630k
        paramKeys.insert(static_cast<SvcParam::SvcParamKey>(keyval));
669
630k
      }
670
1.54k
      kvs.insert(SvcParam(key, std::move(paramKeys)));
671
1.54k
      break;
672
1.57k
    }
673
2.33k
    case SvcParam::alpn: {
674
2.33k
      size_t stop = d_pos + len;
675
2.33k
      std::vector<string> alpns;
676
297k
      while (d_pos < stop) {
677
295k
        string alpn;
678
295k
        uint8_t alpnLen = 0;
679
295k
        xfr8BitInt(alpnLen);
680
295k
        if (alpnLen == 0) {
681
65
          throw std::out_of_range("alpn length of 0");
682
65
        }
683
295k
        if (d_pos + alpnLen > stop) {
684
120
          throw std::out_of_range("alpn length is larger than rest of alpn SVC Param");
685
120
        }
686
295k
        xfrBlob(alpn, alpnLen);
687
295k
        alpns.push_back(std::move(alpn));
688
295k
      }
689
2.15k
      kvs.insert(SvcParam(key, std::move(alpns)));
690
2.15k
      break;
691
2.33k
    }
692
458
    case SvcParam::ohttp:
693
755
    case SvcParam::no_default_alpn: {
694
755
      if (len != 0) {
695
7
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
696
7
      }
697
748
      kvs.insert(SvcParam(key));
698
748
      break;
699
755
    }
700
888
    case SvcParam::port: {
701
888
      if (len != 2) {
702
24
        throw std::out_of_range("invalid length for port");
703
24
      }
704
864
      uint16_t port;
705
864
      xfr16BitInt(port);
706
864
      kvs.insert(SvcParam(key, port));
707
864
      break;
708
888
    }
709
859
    case SvcParam::ipv4hint:
710
1.53k
    case SvcParam::ipv6hint: {
711
1.53k
      size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16);
712
1.53k
      if (len % addrLen != 0) {
713
17
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
714
17
      }
715
1.51k
      vector<ComboAddress> addresses;
716
1.51k
      auto stop = d_pos + len;
717
305k
      while (d_pos < stop)
718
303k
      {
719
303k
        ComboAddress addr;
720
303k
        xfrCAWithoutPort(key, addr);
721
303k
        addresses.push_back(addr);
722
303k
      }
723
      // If there were no addresses, and the input comes from internal
724
      // representation, we can reasonably assume this is the serialization
725
      // of "auto".
726
1.51k
      bool doAuto{d_internal && len == 0};
727
1.51k
      auto param = SvcParam(key, std::move(addresses));
728
1.51k
      param.setAutoHint(doAuto);
729
1.51k
      kvs.insert(std::move(param));
730
1.51k
      break;
731
1.53k
    }
732
1.03k
    case SvcParam::ech: {
733
1.03k
      std::string blob;
734
1.03k
      blob.reserve(len);
735
1.03k
      xfrBlobNoSpaces(blob, len);
736
1.03k
      kvs.insert(SvcParam(key, blob));
737
1.03k
      break;
738
1.53k
    }
739
1.28k
    case SvcParam::tls_supported_groups: {
740
1.28k
      if (len % 2 != 0) {
741
3
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
742
3
      }
743
1.27k
      vector<uint16_t> groups;
744
1.27k
      groups.reserve(len / 2);
745
1.27k
      auto stop = d_pos + len;
746
207k
      while (d_pos < stop)
747
206k
      {
748
206k
        uint16_t group = 0;
749
206k
        xfr16BitInt(group);
750
206k
        groups.push_back(group);
751
206k
      }
752
1.27k
      auto param = SvcParam(key, std::move(groups));
753
1.27k
      kvs.insert(std::move(param));
754
1.27k
      break;
755
1.28k
    }
756
6.12k
    default: {
757
6.12k
      std::string blob;
758
6.12k
      blob.reserve(len);
759
6.12k
      xfrBlob(blob, len);
760
6.12k
      kvs.insert(SvcParam(key, blob));
761
6.12k
      break;
762
1.28k
    }
763
15.6k
    }
764
15.6k
  }
765
12.1k
}
766
767
768
void PacketReader::xfrHexBlob(string& blob, bool /* keepReading */)
769
15.2k
{
770
15.2k
  xfrBlob(blob);
771
15.2k
}
772
773
//FIXME400 remove this method completely
774
string simpleCompress(const string& elabel, const string& root)
775
0
{
776
0
  string label=elabel;
777
  // FIXME400: this relies on the semi-canonical escaped output from getName
778
0
  if(strchr(label.c_str(), '\\')) {
779
0
    boost::replace_all(label, "\\.", ".");
780
0
    boost::replace_all(label, "\\032", " ");
781
0
    boost::replace_all(label, "\\\\", "\\");
782
0
  }
783
0
  typedef vector<pair<unsigned int, unsigned int> > parts_t;
784
0
  parts_t parts;
785
0
  vstringtok(parts, label, ".");
786
0
  string ret;
787
0
  ret.reserve(label.size()+4);
788
0
  for(const auto & part : parts) {
789
0
    if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + part.first, 1 + label.length() - part.first)) { // also match trailing 0, hence '1 +'
790
0
      const unsigned char rootptr[2]={0xc0,0x11};
791
0
      ret.append((const char *) rootptr, 2);
792
0
      return ret;
793
0
    }
794
0
    ret.append(1, (char)(part.second - part.first));
795
0
    ret.append(label.c_str() + part.first, part.second - part.first);
796
0
  }
797
0
  ret.append(1, (char)0);
798
0
  return ret;
799
0
}
800
801
// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
802
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor)
803
0
{
804
0
  if(length < sizeof(dnsheader))
805
0
    return;
806
0
  try
807
0
  {
808
0
    dnsheader dh;
809
0
    memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh));
810
0
    uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
811
0
    DNSPacketMangler dpm(packet, length);
812
813
0
    uint64_t n;
814
0
    for(n=0; n < ntohs(dh.qdcount) ; ++n) {
815
0
      dpm.skipDomainName();
816
      /* type and class */
817
0
      dpm.skipBytes(4);
818
0
    }
819
820
0
    for(n=0; n < numrecords; ++n) {
821
0
      dpm.skipDomainName();
822
823
0
      uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
824
0
      uint16_t dnstype = dpm.get16BitInt();
825
0
      uint16_t dnsclass = dpm.get16BitInt();
826
827
0
      if(dnstype == QType::OPT) // not getting near that one with a stick
828
0
        break;
829
830
0
      uint32_t dnsttl = dpm.get32BitInt();
831
0
      uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
832
0
      if (newttl) {
833
0
        dpm.rewindBytes(sizeof(newttl));
834
0
        dpm.setAndSkip32BitInt(newttl);
835
0
      }
836
0
      dpm.skipRData();
837
0
    }
838
0
  }
839
0
  catch(...)
840
0
  {
841
0
    return;
842
0
  }
843
0
}
844
845
static bool checkIfPacketContainsRecords(const PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
846
0
{
847
0
  auto length = packet.size();
848
0
  if (length < sizeof(dnsheader)) {
849
0
    return false;
850
0
  }
851
852
0
  try {
853
0
    const dnsheader_aligned dh(packet.data());
854
0
    DNSPacketMangler dpm(const_cast<char*>(reinterpret_cast<const char*>(packet.data())), length);
855
856
0
    const uint16_t qdcount = ntohs(dh->qdcount);
857
0
    for (size_t n = 0; n < qdcount; ++n) {
858
0
      dpm.skipDomainName();
859
      /* type and class */
860
0
      dpm.skipBytes(4);
861
0
    }
862
0
    const size_t recordsCount = static_cast<size_t>(ntohs(dh->ancount)) + ntohs(dh->nscount) + ntohs(dh->arcount);
863
0
    for (size_t n = 0; n < recordsCount; ++n) {
864
0
      dpm.skipDomainName();
865
0
      uint16_t dnstype = dpm.get16BitInt();
866
0
      uint16_t dnsclass = dpm.get16BitInt();
867
0
      if (dnsclass == QClass::IN && qtypes.count(dnstype) > 0) {
868
0
        return true;
869
0
      }
870
      /* ttl */
871
0
      dpm.skipBytes(4);
872
0
      dpm.skipRData();
873
0
    }
874
0
  }
875
0
  catch (...) {
876
0
  }
877
878
0
  return false;
879
0
}
880
881
static int rewritePacketWithoutRecordTypes(const PacketBuffer& initialPacket, PacketBuffer& newContent, const std::unordered_set<QType>& qtypes)
882
0
{
883
0
  static const std::unordered_set<QType>& safeTypes{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::SVCB, QType::HTTPS, QType::NSEC3, QType::CSYNC, QType::NSEC3PARAM, QType::LOC, QType::NID, QType::L32, QType::L64, QType::EUI48, QType::EUI64, QType::URI, QType::CAA};
884
885
0
  if (initialPacket.size() < sizeof(dnsheader)) {
886
0
    return EINVAL;
887
0
  }
888
0
  try {
889
0
    const dnsheader_aligned dh(initialPacket.data());
890
891
0
    if (ntohs(dh->qdcount) == 0)
892
0
      return ENOENT;
893
0
    auto packetView = std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size());
894
895
0
    PacketReader pr(packetView);
896
897
0
    size_t idx = 0;
898
0
    DNSName rrname;
899
0
    uint16_t qdcount = ntohs(dh->qdcount);
900
0
    uint16_t ancount = ntohs(dh->ancount);
901
0
    uint16_t nscount = ntohs(dh->nscount);
902
0
    uint16_t arcount = ntohs(dh->arcount);
903
0
    uint16_t rrtype;
904
0
    uint16_t rrclass;
905
0
    string blob;
906
0
    struct dnsrecordheader ah;
907
908
0
    rrname = pr.getName();
909
0
    rrtype = pr.get16BitInt();
910
0
    rrclass = pr.get16BitInt();
911
912
0
    GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh->opcode);
913
0
    pw.getHeader()->id=dh->id;
914
0
    pw.getHeader()->qr=dh->qr;
915
0
    pw.getHeader()->aa=dh->aa;
916
0
    pw.getHeader()->tc=dh->tc;
917
0
    pw.getHeader()->rd=dh->rd;
918
0
    pw.getHeader()->ra=dh->ra;
919
0
    pw.getHeader()->ad=dh->ad;
920
0
    pw.getHeader()->cd=dh->cd;
921
0
    pw.getHeader()->rcode=dh->rcode;
922
923
    /* consume remaining qd if any */
924
0
    if (qdcount > 1) {
925
0
      for(idx = 1; idx < qdcount; idx++) {
926
0
        rrname = pr.getName();
927
0
        rrtype = pr.get16BitInt();
928
0
        rrclass = pr.get16BitInt();
929
0
        (void) rrtype;
930
0
        (void) rrclass;
931
0
      }
932
0
    }
933
934
    /* copy AN */
935
0
    for (idx = 0; idx < ancount; idx++) {
936
0
      rrname = pr.getName();
937
0
      pr.getDnsrecordheader(ah);
938
0
      pr.xfrBlob(blob);
939
940
0
      if (qtypes.find(ah.d_type) == qtypes.end()) {
941
        // if this is not a safe type
942
0
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
943
          // "unsafe" types might contain compressed data, so cancel rewrite
944
0
          newContent.clear();
945
0
          return EIO;
946
0
        }
947
0
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
948
0
        pw.xfrBlob(blob);
949
0
      }
950
0
    }
951
952
    /* copy NS */
953
0
    for (idx = 0; idx < nscount; idx++) {
954
0
      rrname = pr.getName();
955
0
      pr.getDnsrecordheader(ah);
956
0
      pr.xfrBlob(blob);
957
958
0
      if (qtypes.find(ah.d_type) == qtypes.end()) {
959
0
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
960
          // "unsafe" types might contain compressed data, so cancel rewrite
961
0
          newContent.clear();
962
0
          return EIO;
963
0
        }
964
0
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
965
0
        pw.xfrBlob(blob);
966
0
      }
967
0
    }
968
    /* copy AR */
969
0
    for (idx = 0; idx < arcount; idx++) {
970
0
      rrname = pr.getName();
971
0
      pr.getDnsrecordheader(ah);
972
0
      pr.xfrBlob(blob);
973
974
0
      if (qtypes.find(ah.d_type) == qtypes.end()) {
975
0
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
976
          // "unsafe" types might contain compressed data, so cancel rewrite
977
0
          newContent.clear();
978
0
          return EIO;
979
0
        }
980
0
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
981
0
        pw.xfrBlob(blob);
982
0
      }
983
0
    }
984
0
    pw.commit();
985
986
0
  }
987
0
  catch (...)
988
0
  {
989
0
    newContent.clear();
990
0
    return EIO;
991
0
  }
992
0
  return 0;
993
0
}
994
995
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes)
996
0
{
997
0
  return clearDNSPacketRecordTypes(reinterpret_cast<PacketBuffer&>(packet), qtypes);
998
0
}
999
1000
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
1001
0
{
1002
0
  if (!checkIfPacketContainsRecords(packet, qtypes)) {
1003
0
    return;
1004
0
  }
1005
1006
0
  PacketBuffer newContent;
1007
1008
0
  auto result = rewritePacketWithoutRecordTypes(packet, newContent, qtypes);
1009
0
  if (!result) {
1010
0
    packet = std::move(newContent);
1011
0
  }
1012
0
}
1013
1014
// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
1015
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned& aligned_dh)
1016
0
{
1017
0
  if (length < sizeof(dnsheader)) {
1018
0
    return;
1019
0
  }
1020
0
  try {
1021
0
    const dnsheader* dhp = aligned_dh.get();
1022
0
    const uint64_t dqcount = ntohs(dhp->qdcount);
1023
0
    const uint64_t numrecords = ntohs(dhp->ancount) + ntohs(dhp->nscount) + ntohs(dhp->arcount);
1024
0
    DNSPacketMangler dpm(packet, length);
1025
1026
0
    for (uint64_t rec = 0; rec < dqcount; ++rec) {
1027
0
      dpm.skipDomainName();
1028
      /* type and class */
1029
0
      dpm.skipBytes(4);
1030
0
    }
1031
1032
0
    for(uint64_t rec = 0; rec < numrecords; ++rec) {
1033
0
      dpm.skipDomainName();
1034
1035
0
      uint16_t dnstype = dpm.get16BitInt();
1036
      /* class */
1037
0
      dpm.skipBytes(2);
1038
1039
0
      if (dnstype != QType::OPT) { // not aging that one with a stick
1040
0
        dpm.decreaseAndSkip32BitInt(seconds);
1041
0
      } else {
1042
0
        dpm.skipBytes(4);
1043
0
      }
1044
0
      dpm.skipRData();
1045
0
    }
1046
0
  }
1047
0
  catch(...) {
1048
0
  }
1049
0
}
1050
1051
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned& aligned_dh)
1052
0
{
1053
0
  ageDNSPacket(packet.data(), packet.length(), seconds, aligned_dh);
1054
0
}
1055
1056
void shuffleDNSPacket(char* packet, size_t length, const dnsheader_aligned& aligned_dh)
1057
0
{
1058
0
  if (length < sizeof(dnsheader)) {
1059
0
    return;
1060
0
  }
1061
0
  try {
1062
0
    const dnsheader* dhp = aligned_dh.get();
1063
0
    const uint16_t ancount = ntohs(dhp->ancount);
1064
0
    if (ancount == 1) {
1065
      // quick exit, nothing to shuffle
1066
0
      return;
1067
0
    }
1068
1069
0
    DNSPacketMangler dpm(packet, length);
1070
1071
0
    const uint16_t qdcount = ntohs(dhp->qdcount);
1072
1073
0
    for(size_t iter = 0; iter < qdcount; ++iter) {
1074
0
      dpm.skipDomainName();
1075
      /* type and class */
1076
0
      dpm.skipBytes(4);
1077
0
    }
1078
1079
    // for now shuffle only first rrset, only As and AAAAs
1080
0
    uint16_t rrset_type = 0;
1081
0
    DNSName rrset_dnsname{};
1082
0
    std::vector<std::pair<uint32_t, uint32_t>> rrdata_indexes;
1083
0
    rrdata_indexes.reserve(ancount);
1084
1085
0
    for(size_t iter = 0; iter < ancount; ++iter) {
1086
0
      auto domain_start = dpm.getOffset();
1087
0
      dpm.skipDomainName();
1088
0
      const uint16_t dnstype = dpm.get16BitInt();
1089
0
      if (dnstype == QType::A || dnstype == QType::AAAA) {
1090
0
        if (rrdata_indexes.empty()) {
1091
0
          rrset_type = dnstype;
1092
0
          rrset_dnsname = DNSName(packet, length, domain_start, true);
1093
0
        } else {
1094
0
          if (dnstype != rrset_type) {
1095
0
            break;
1096
0
          }
1097
0
          if (DNSName(packet, length, domain_start, true) != rrset_dnsname) {
1098
0
            break;
1099
0
          }
1100
0
        }
1101
        /* class */
1102
0
        dpm.skipBytes(2);
1103
1104
        /* ttl */
1105
0
        dpm.skipBytes(4);
1106
0
        rrdata_indexes.push_back(dpm.skipRDataAndReturnOffsets());
1107
0
      } else {
1108
0
        if (!rrdata_indexes.empty()) {
1109
0
          break;
1110
0
        }
1111
        /* class */
1112
0
        dpm.skipBytes(2);
1113
1114
        /* ttl */
1115
0
        dpm.skipBytes(4);
1116
0
        dpm.skipRData();
1117
0
      }
1118
0
    }
1119
1120
0
    if (rrdata_indexes.size() >= 2) {
1121
0
      using uid = std::uniform_int_distribution<std::vector<std::pair<uint32_t, uint32_t>>::size_type>;
1122
0
      uid dist;
1123
1124
0
      pdns::dns_random_engine randomEngine;
1125
0
      for (auto swapped = rrdata_indexes.size() - 1; swapped > 0; --swapped) {
1126
0
        auto swapped_with = dist(randomEngine, uid::param_type(0, swapped));
1127
0
        if (swapped != swapped_with) {
1128
0
          dpm.swapInPlace(rrdata_indexes.at(swapped), rrdata_indexes.at(swapped_with));
1129
0
        }
1130
0
      }
1131
0
    }
1132
0
  }
1133
0
  catch(...) {
1134
0
  }
1135
0
}
1136
1137
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
1138
0
{
1139
0
  uint32_t result = std::numeric_limits<uint32_t>::max();
1140
0
  if(length < sizeof(dnsheader)) {
1141
0
    return result;
1142
0
  }
1143
0
  try
1144
0
  {
1145
0
    const dnsheader_aligned dh(packet);
1146
0
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1147
1148
0
    const uint16_t qdcount = ntohs(dh->qdcount);
1149
0
    for(size_t n = 0; n < qdcount; ++n) {
1150
0
      dpm.skipDomainName();
1151
      /* type and class */
1152
0
      dpm.skipBytes(4);
1153
0
    }
1154
0
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1155
0
    for(size_t n = 0; n < numrecords; ++n) {
1156
0
      dpm.skipDomainName();
1157
0
      const uint16_t dnstype = dpm.get16BitInt();
1158
      /* class */
1159
0
      const uint16_t dnsclass = dpm.get16BitInt();
1160
1161
0
      if(dnstype == QType::OPT) {
1162
0
        break;
1163
0
      }
1164
1165
      /* report it if we see a SOA record in the AUTHORITY section */
1166
0
      if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
1167
0
        *seenAuthSOA = true;
1168
0
      }
1169
1170
0
      const uint32_t ttl = dpm.get32BitInt();
1171
0
      result = std::min(result, ttl);
1172
1173
0
      dpm.skipRData();
1174
0
    }
1175
0
  }
1176
0
  catch(...)
1177
0
  {
1178
0
  }
1179
0
  return result;
1180
0
}
1181
1182
uint32_t getDNSPacketLength(const char* packet, size_t length)
1183
0
{
1184
0
  uint32_t result = length;
1185
0
  if(length < sizeof(dnsheader)) {
1186
0
    return result;
1187
0
  }
1188
0
  try
1189
0
  {
1190
0
    const dnsheader_aligned dh(packet);
1191
0
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1192
1193
0
    const uint16_t qdcount = ntohs(dh->qdcount);
1194
0
    for(size_t n = 0; n < qdcount; ++n) {
1195
0
      dpm.skipDomainName();
1196
      /* type and class */
1197
0
      dpm.skipBytes(4);
1198
0
    }
1199
0
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1200
0
    for(size_t n = 0; n < numrecords; ++n) {
1201
0
      dpm.skipDomainName();
1202
      /* type (2), class (2) and ttl (4) */
1203
0
      dpm.skipBytes(8);
1204
0
      dpm.skipRData();
1205
0
    }
1206
0
    result = dpm.getOffset();
1207
0
  }
1208
0
  catch(...)
1209
0
  {
1210
0
  }
1211
0
  return result;
1212
0
}
1213
1214
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1215
0
{
1216
0
  uint16_t result = 0;
1217
0
  if(length < sizeof(dnsheader)) {
1218
0
    return result;
1219
0
  }
1220
0
  try
1221
0
  {
1222
0
    const dnsheader_aligned dh(packet);
1223
0
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1224
1225
0
    const uint16_t qdcount = ntohs(dh->qdcount);
1226
0
    for(size_t n = 0; n < qdcount; ++n) {
1227
0
      dpm.skipDomainName();
1228
0
      if (section == 0) {
1229
0
        uint16_t dnstype = dpm.get16BitInt();
1230
0
        if (dnstype == type) {
1231
0
          result++;
1232
0
        }
1233
        /* class */
1234
0
        dpm.skipBytes(2);
1235
0
      } else {
1236
        /* type and class */
1237
0
        dpm.skipBytes(4);
1238
0
      }
1239
0
    }
1240
0
    const uint16_t ancount = ntohs(dh->ancount);
1241
0
    for(size_t n = 0; n < ancount; ++n) {
1242
0
      dpm.skipDomainName();
1243
0
      if (section == 1) {
1244
0
        uint16_t dnstype = dpm.get16BitInt();
1245
0
        if (dnstype == type) {
1246
0
          result++;
1247
0
        }
1248
        /* class */
1249
0
        dpm.skipBytes(2);
1250
0
      } else {
1251
        /* type and class */
1252
0
        dpm.skipBytes(4);
1253
0
      }
1254
      /* ttl */
1255
0
      dpm.skipBytes(4);
1256
0
      dpm.skipRData();
1257
0
    }
1258
0
    const uint16_t nscount = ntohs(dh->nscount);
1259
0
    for(size_t n = 0; n < nscount; ++n) {
1260
0
      dpm.skipDomainName();
1261
0
      if (section == 2) {
1262
0
        uint16_t dnstype = dpm.get16BitInt();
1263
0
        if (dnstype == type) {
1264
0
          result++;
1265
0
        }
1266
        /* class */
1267
0
        dpm.skipBytes(2);
1268
0
      } else {
1269
        /* type and class */
1270
0
        dpm.skipBytes(4);
1271
0
      }
1272
      /* ttl */
1273
0
      dpm.skipBytes(4);
1274
0
      dpm.skipRData();
1275
0
    }
1276
0
    const uint16_t arcount = ntohs(dh->arcount);
1277
0
    for(size_t n = 0; n < arcount; ++n) {
1278
0
      dpm.skipDomainName();
1279
0
      if (section == 3) {
1280
0
        uint16_t dnstype = dpm.get16BitInt();
1281
0
        if (dnstype == type) {
1282
0
          result++;
1283
0
        }
1284
        /* class */
1285
0
        dpm.skipBytes(2);
1286
0
      } else {
1287
        /* type and class */
1288
0
        dpm.skipBytes(4);
1289
0
      }
1290
      /* ttl */
1291
0
      dpm.skipBytes(4);
1292
0
      dpm.skipRData();
1293
0
    }
1294
0
  }
1295
0
  catch(...)
1296
0
  {
1297
0
  }
1298
0
  return result;
1299
0
}
1300
1301
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
1302
0
{
1303
0
  if (length < sizeof(dnsheader)) {
1304
0
    return false;
1305
0
  }
1306
1307
0
  *payloadSize = 0;
1308
0
  *z = 0;
1309
1310
0
  try
1311
0
  {
1312
0
    const dnsheader_aligned dh(packet);
1313
0
    if (dh->arcount == 0) {
1314
      // The OPT pseudo-RR, if present, has to be in the additional section (https://datatracker.ietf.org/doc/html/rfc6891#section-6.1.1)
1315
0
      return false;
1316
0
    }
1317
1318
0
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1319
1320
0
    const uint16_t qdcount = ntohs(dh->qdcount);
1321
0
    for(size_t n = 0; n < qdcount; ++n) {
1322
0
      dpm.skipDomainName();
1323
      /* type and class */
1324
0
      dpm.skipBytes(4);
1325
0
    }
1326
0
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1327
0
    for(size_t n = 0; n < numrecords; ++n) {
1328
0
      dpm.skipDomainName();
1329
0
      const auto dnstype = dpm.get16BitInt();
1330
1331
0
      if (dnstype == QType::OPT) {
1332
0
        const auto dnsclass = dpm.get16BitInt();
1333
        /* skip extended rcode and version */
1334
0
        dpm.skipBytes(2);
1335
0
        *z = dpm.get16BitInt();
1336
0
        *payloadSize = dnsclass;
1337
0
        return true;
1338
0
      }
1339
      /* skip class */
1340
0
      dpm.skipBytes(2);
1341
      /* TTL */
1342
0
      dpm.skipBytes(4);
1343
0
      dpm.skipRData();
1344
0
    }
1345
0
  }
1346
0
  catch(...)
1347
0
  {
1348
0
  }
1349
1350
0
  return false;
1351
0
}
1352
1353
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
1354
0
{
1355
0
  if (packet.size() < sizeof(dnsheader)) {
1356
0
    return false;
1357
0
  }
1358
1359
0
  try
1360
0
  {
1361
0
    const dnsheader_aligned dh(packet.data());
1362
0
    uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1363
0
    PacketReader reader(packet);
1364
1365
0
    uint64_t n;
1366
0
    for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
1367
0
      (void) reader.getName();
1368
      /* type and class */
1369
0
      reader.skip(4);
1370
0
    }
1371
1372
0
    for (n = 0; n < numrecords; ++n) {
1373
0
      (void) reader.getName();
1374
1375
0
      uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
1376
0
      uint16_t dnstype = reader.get16BitInt();
1377
0
      uint16_t dnsclass = reader.get16BitInt();
1378
1379
0
      if (dnstype == QType::OPT) {
1380
        // not getting near that one with a stick
1381
0
        break;
1382
0
      }
1383
1384
0
      uint32_t dnsttl = reader.get32BitInt();
1385
0
      uint16_t contentLength = reader.get16BitInt();
1386
0
      uint16_t pos = reader.getPosition();
1387
0
      reader.skip(contentLength);
1388
1389
0
      bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
1390
0
      if (done) {
1391
0
        return true;
1392
0
      }
1393
0
    }
1394
0
  }
1395
0
  catch (...) {
1396
0
    return false;
1397
0
  }
1398
1399
0
  return true;
1400
0
}