• 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

85.8
/pdns/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 "dnsparser.hh"
23
#include "dnswriter.hh"
24
#include <boost/algorithm/string.hpp>
25
#include <boost/format.hpp>
26

27
#include "dns_random.hh"
28
#include "namespaces.hh"
29
#include "noinitvector.hh"
30

31
std::atomic<bool> DNSRecordContent::d_locked{false};
32

33
UnknownRecordContent::UnknownRecordContent(const string& zone)
34
{
417✔
35
  // parse the input
36
  vector<string> parts;
417✔
37
  stringtok(parts, zone);
417✔
38
  // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
39
  if (parts.size() != 3 && !(parts.size() == 2 && boost::equals(parts.at(1), "0"))) {
417✔
40
    throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
6✔
41
  }
6✔
42

43
  if (parts.at(0) != "\\#") {
411✔
44
    throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
3✔
45
  }
3✔
46

47
  const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
408✔
48
  auto total = pdns::checked_stoi<unsigned int>(parts.at(1));
408✔
49
  if (relevant.size() % 2 || (relevant.size() / 2) != total) {
408✔
50
    throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
6✔
51
  }
6✔
52

53
  string out;
402✔
54
  out.reserve(total + 1);
402✔
55

56
  for (unsigned int n = 0; n < total; ++n) {
2,244✔
57
    int c;
1,845✔
58
    if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
1,845✔
59
      throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
3✔
60
    }
3✔
61
    out.append(1, (char)c);
1,842✔
62
  }
1,842✔
63

64
  d_record.insert(d_record.end(), out.begin(), out.end());
399✔
65
}
399✔
66

67
string UnknownRecordContent::getZoneRepresentation(bool /* noDot */) const
68
{
317✔
69
  ostringstream str;
317✔
70
  str<<"\\# "<<(unsigned int)d_record.size()<<" ";
317✔
71
  char hex[4];
317✔
72
  for (unsigned char n : d_record) {
1,063✔
73
    snprintf(hex, sizeof(hex), "%02x", n);
1,063✔
74
    str << hex;
1,063✔
75
  }
1,063✔
76
  return str.str();
317✔
77
}
317✔
78

79
void UnknownRecordContent::toPacket(DNSPacketWriter& pw) const
80
{
1,309✔
81
  pw.xfrBlob(string(d_record.begin(),d_record.end()));
1,309✔
82
}
1,309✔
83

84
shared_ptr<DNSRecordContent> DNSRecordContent::deserialize(const DNSName& qname, uint16_t qtype, const string& serialized, uint16_t qclass, bool internalRepresentation)
85
{
158,540✔
86
  dnsheader dnsheader;
158,540✔
87
  memset(&dnsheader, 0, sizeof(dnsheader));
158,540✔
88
  dnsheader.qdcount=htons(1);
158,540✔
89
  dnsheader.ancount=htons(1);
158,540✔
90

91
  PacketBuffer packet; // build pseudo packet
158,540✔
92
  /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
93
  const auto& encoded = qname.getStorage();
158,540✔
94
  packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
158,540✔
95

96
  uint16_t pos=0;
158,540✔
97
  memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
158,540✔
98

99
  constexpr std::array<uint8_t, 5> tmp= {'\x0', '\x0', '\x1', '\x0', '\x1' }; // root question for ns_t_a
158,540✔
100
  memcpy(&packet[pos], tmp.data(), tmp.size()); pos += tmp.size();
158,540✔
101

102
  memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
158,540✔
103

104
  struct dnsrecordheader drh;
158,540✔
105
  drh.d_type=htons(qtype);
158,540✔
106
  drh.d_class=htons(qclass);
158,540✔
107
  drh.d_ttl=0;
158,540✔
108
  drh.d_clen=htons(serialized.size());
158,540✔
109

110
  memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
158,540✔
111
  if (!serialized.empty()) {
158,540✔
112
    memcpy(&packet[pos], serialized.c_str(), serialized.size());
154,384✔
113
    pos += (uint16_t) serialized.size();
154,384✔
114
    (void) pos;
154,384✔
115
  }
154,384✔
116

117
  DNSRecord dr;
158,540✔
118
  dr.d_class = qclass;
158,540✔
119
  dr.d_type = qtype;
158,540✔
120
  dr.d_name = qname;
158,540✔
121
  dr.d_clen = serialized.size();
158,540✔
122
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): packet.data() is uint8_t *
123
  PacketReader reader(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()), packet.size() - serialized.size() - sizeof(dnsrecordheader), internalRepresentation);
158,540✔
124
  /* needed to get the record boundaries right */
125
  reader.getDnsrecordheader(drh);
158,540✔
126
  auto content = DNSRecordContent::make(dr, reader, Opcode::Query);
158,540✔
127
  return content;
158,540✔
128
}
158,540✔
129

130
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr,
131
                                                         PacketReader& pr)
132
{
276✔
133
  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
276!
134

135
  auto i = getTypemap().find(pair(searchclass, dr.d_type));
276✔
136
  if(i==getTypemap().end() || !i->second) {
276!
137
    return std::make_shared<UnknownRecordContent>(dr, pr);
1✔
138
  }
1✔
139

140
  return i->second(dr, pr);
275✔
141
}
276✔
142

143
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(uint16_t qtype, uint16_t qclass,
144
                                                         const string& content)
145
{
1,682,648✔
146
  auto i = getZmakermap().find(pair(qclass, qtype));
1,682,648✔
147
  if(i==getZmakermap().end()) {
1,682,648✔
148
    return std::make_shared<UnknownRecordContent>(content);
405✔
149
  }
405✔
150

151
  return i->second(content);
1,682,243✔
152
}
1,682,648✔
153

154
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr, PacketReader& pr, uint16_t oc)
155
{
2,459,597✔
156
  // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is
157
  // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent.
158
  // For section 3.2.3, we do need content so we need to get it properly. But only for the correct QClasses.
159
  if (oc == Opcode::Update && dr.d_place == DNSResourceRecord::ANSWER && dr.d_class != 1)
2,459,597✔
160
    return std::make_shared<UnknownRecordContent>(dr, pr);
240✔
161

162
  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
2,459,357✔
163

164
  auto i = getTypemap().find(pair(searchclass, dr.d_type));
2,459,357✔
165
  if(i==getTypemap().end() || !i->second) {
2,459,359✔
166
    return std::make_shared<UnknownRecordContent>(dr, pr);
1,658✔
167
  }
1,658✔
168

169
  return i->second(dr, pr);
2,457,699✔
170
}
2,459,357✔
171

172
string DNSRecordContent::upgradeContent(const DNSName& qname, const QType& qtype, const string& content) {
12✔
173
  // seamless upgrade for previously unsupported but now implemented types.
174
  UnknownRecordContent unknown_content(content);
12✔
175
  shared_ptr<DNSRecordContent> rc = DNSRecordContent::deserialize(qname, qtype.getCode(), unknown_content.serialize(qname));
12✔
176
  return rc->getZoneRepresentation();
12✔
177
}
12✔
178

179
DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
180
{
9,249,523✔
181
  static DNSRecordContent::typemap_t typemap;
9,249,523✔
182
  return typemap;
9,249,523✔
183
}
9,249,523✔
184

185
DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
186
{
14,954,489✔
187
  static DNSRecordContent::n2typemap_t n2typemap;
14,954,489✔
188
  return n2typemap;
14,954,489✔
189
}
14,954,489✔
190

191
DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
192
{
9,245,972✔
193
  static DNSRecordContent::t2namemap_t t2namemap;
9,245,972✔
194
  return t2namemap;
9,245,972✔
195
}
9,245,972✔
196

197
DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
198
{
7,695,596✔
199
  static DNSRecordContent::zmakermap_t zmakermap;
7,695,596✔
200
  return zmakermap;
7,695,596✔
201
}
7,695,596✔
202

203
bool DNSRecordContent::isRegisteredType(uint16_t rtype, uint16_t rclass)
204
{
×
205
  return getTypemap().count(pair(rclass, rtype)) != 0;
×
206
}
×
207

208
DNSRecord::DNSRecord(const DNSResourceRecord& rr): d_name(rr.qname)
209
{
512,092✔
210
  d_type = rr.qtype.getCode();
512,092✔
211
  d_ttl = rr.ttl;
512,092✔
212
  d_class = rr.qclass;
512,092✔
213
  d_place = DNSResourceRecord::ANSWER;
512,092✔
214
  d_clen = 0;
512,092✔
215
  d_content = DNSRecordContent::make(d_type, rr.qclass, rr.content);
512,092✔
216
}
512,092✔
217

218
// If you call this and you are not parsing a packet coming from a socket, you are doing it wrong.
219
DNSResourceRecord DNSResourceRecord::fromWire(const DNSRecord& wire)
220
{
3,855✔
221
  DNSResourceRecord resourceRecord;
3,855✔
222
  resourceRecord.qname = wire.d_name;
3,855✔
223
  resourceRecord.qtype = QType(wire.d_type);
3,855✔
224
  resourceRecord.ttl = wire.d_ttl;
3,855✔
225
  resourceRecord.content = wire.getContent()->getZoneRepresentation(true);
3,855✔
226
  resourceRecord.auth = false;
3,855✔
227
  resourceRecord.qclass = wire.d_class;
3,855✔
228
  return resourceRecord;
3,855✔
229
}
3,855✔
230

231
void MOADNSParser::init(bool query, const std::string_view& packet)
232
{
1,418,080✔
233
  if (packet.size() < sizeof(dnsheader))
1,418,080!
234
    throw MOADNSException("Packet shorter than minimal header");
×
235

236
  memcpy(&d_header, packet.data(), sizeof(dnsheader));
1,418,080✔
237

238
  if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
1,418,080!
239
    throw MOADNSException("Can't parse non-query packet with opcode="+ std::to_string(d_header.opcode));
×
240

241
  d_header.qdcount=ntohs(d_header.qdcount);
1,418,080✔
242
  d_header.ancount=ntohs(d_header.ancount);
1,418,080✔
243
  d_header.nscount=ntohs(d_header.nscount);
1,418,080✔
244
  d_header.arcount=ntohs(d_header.arcount);
1,418,080✔
245

246
  if (query && (d_header.qdcount > 1))
1,418,080!
247
    throw MOADNSException("Query with QD > 1 ("+std::to_string(d_header.qdcount)+")");
×
248

249
  unsigned int n=0;
1,418,080✔
250

251
  PacketReader pr(packet);
1,418,080✔
252
  bool validPacket=false;
1,418,080✔
253
  try {
1,418,080✔
254
    d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
1,418,080✔
255

256
    for(n=0;n < d_header.qdcount; ++n) {
2,831,594✔
257
      d_qname=pr.getName();
1,413,514✔
258
      d_qtype=pr.get16BitInt();
1,413,514✔
259
      d_qclass=pr.get16BitInt();
1,413,514✔
260
    }
1,413,514✔
261

262
    struct dnsrecordheader ah;
1,418,080✔
263
    vector<unsigned char> record;
1,418,080✔
264
    bool seenTSIG = false;
1,418,080✔
265
    validPacket=true;
1,418,080✔
266
    d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
1,418,080✔
267
    for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
3,725,783✔
268
      DNSRecord dr;
2,307,706✔
269

270
      if(n < d_header.ancount)
2,307,706✔
271
        dr.d_place=DNSResourceRecord::ANSWER;
2,002,985✔
272
      else if(n < d_header.ancount + d_header.nscount)
304,721✔
273
        dr.d_place=DNSResourceRecord::AUTHORITY;
96,232✔
274
      else
208,489✔
275
        dr.d_place=DNSResourceRecord::ADDITIONAL;
208,489✔
276

277
      unsigned int recordStartPos=pr.getPosition();
2,307,706✔
278

279
      DNSName name=pr.getName();
2,307,706✔
280

281
      pr.getDnsrecordheader(ah);
2,307,706✔
282
      dr.d_ttl=ah.d_ttl;
2,307,706✔
283
      dr.d_type=ah.d_type;
2,307,706✔
284
      dr.d_class=ah.d_class;
2,307,706✔
285

286
      dr.d_name = std::move(name);
2,307,706✔
287
      dr.d_clen = ah.d_clen;
2,307,706✔
288

289
      if (query &&
2,307,706✔
290
          !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section
2,307,706!
291
          (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))) {
2,307,706!
292
//        cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
293
        dr.setContent(std::make_shared<UnknownRecordContent>(dr, pr));
6,693✔
294
      }
6,693✔
295
      else {
2,301,013✔
296
//        cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
297
        dr.setContent(DNSRecordContent::make(dr, pr, d_header.opcode));
2,301,013✔
298
      }
2,301,013✔
299

300
      if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
2,307,706✔
301
        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.");
3✔
302
      }
3✔
303

304
      if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
2,307,703!
305
        if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
1,157!
306
          throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
×
307
        }
×
308
        seenTSIG = true;
1,157✔
309
        d_tsigPos = recordStartPos;
1,157✔
310
      }
1,157✔
311

312
      d_answers.emplace_back(std::move(dr));
2,307,703✔
313
    }
2,307,703✔
314

315
#if 0
316
    if(pr.getPosition()!=packet.size()) {
317
      throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.getPosition()) + " < " +
318
                            std::to_string(packet.size()) + ")");
319
    }
320
#endif
321
  }
1,418,080✔
322
  catch(const std::out_of_range &re) {
1,418,080✔
323
    if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
12!
324
      if(n < d_header.ancount) {
×
325
        d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
×
326
      }
×
327
      else if(n < d_header.ancount + d_header.nscount) {
×
328
        d_header.nscount = n - d_header.ancount; d_header.arcount=0;
×
329
      }
×
330
      else {
×
331
        d_header.arcount = n - d_header.ancount - d_header.nscount;
×
332
      }
×
333
    }
×
334
    else {
12✔
335
      throw MOADNSException("Error parsing packet of "+std::to_string(packet.size())+" bytes (rd="+
12✔
336
                            std::to_string(d_header.rd)+
12✔
337
                            "), out of bounds: "+string(re.what()));
12✔
338
    }
12✔
339
  }
12✔
340
}
1,418,080✔
341

342
bool MOADNSParser::hasEDNS() const
343
{
×
344
  if (d_header.arcount == 0 || d_answers.empty()) {
×
345
    return false;
×
346
  }
×
347

348
  for (const auto& record : d_answers) {
×
349
    if (record.d_place == DNSResourceRecord::ADDITIONAL && record.d_type == QType::OPT) {
×
350
      return true;
×
351
    }
×
352
  }
×
353

354
  return false;
×
355
}
×
356

357
void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
358
{
2,466,992✔
359
  unsigned char *p = reinterpret_cast<unsigned char*>(&ah);
2,466,992✔
360

361
  for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
27,134,950✔
362
    p[n] = d_content.at(d_pos++);
24,667,958✔
363
  }
24,667,958✔
364

365
  ah.d_type = ntohs(ah.d_type);
2,466,992✔
366
  ah.d_class = ntohs(ah.d_class);
2,466,992✔
367
  ah.d_clen = ntohs(ah.d_clen);
2,466,992✔
368
  ah.d_ttl = ntohl(ah.d_ttl);
2,466,992✔
369

370
  d_startrecordpos = d_pos; // needed for getBlob later on
2,466,992✔
371
  d_recordlen = ah.d_clen;
2,466,992✔
372
}
2,466,992✔
373

374

375
void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
376
{
8,592✔
377
  if (len == 0) {
8,592✔
378
    return;
2,494✔
379
  }
2,494✔
380
  if ((d_pos + len) > d_content.size()) {
6,098!
381
    throw std::out_of_range("Attempt to copy outside of packet");
×
382
  }
×
383

384
  dest.resize(len);
6,098✔
385

386
  for (uint16_t n = 0; n < len; ++n) {
10,300,076✔
387
    dest.at(n) = d_content.at(d_pos++);
10,293,978✔
388
  }
10,293,978✔
389
}
6,098✔
390

391
void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
392
{
436✔
393
  if (d_pos + len > d_content.size()) {
436!
394
    throw std::out_of_range("Attempt to copy outside of packet");
×
395
  }
×
396

397
  memcpy(dest, &d_content.at(d_pos), len);
436✔
398
  d_pos += len;
436✔
399
}
436✔
400

401
void PacketReader::xfrNodeOrLocatorID(NodeOrLocatorID& ret)
402
{
9✔
403
  if (d_pos + sizeof(ret) > d_content.size()) {
9!
404
    throw std::out_of_range("Attempt to read 64 bit value outside of packet");
×
405
  }
×
406
  memcpy(&ret.content, &d_content.at(d_pos), sizeof(ret.content));
9✔
407
  d_pos += sizeof(ret);
9✔
408
}
9✔
409

410
void PacketReader::xfr48BitInt(uint64_t& ret)
411
{
1,139✔
412
  ret=0;
1,139✔
413
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
1,139✔
414
  ret<<=8;
1,139✔
415
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
1,139✔
416
  ret<<=8;
1,139✔
417
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
1,139✔
418
  ret<<=8;
1,139✔
419
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
1,139✔
420
  ret<<=8;
1,139✔
421
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
1,139✔
422
  ret<<=8;
1,139✔
423
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
1,139✔
424
}
1,139✔
425

426
uint32_t PacketReader::get32BitInt()
427
{
3,273,435✔
428
  uint32_t ret=0;
3,273,435✔
429
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,273,435✔
430
  ret<<=8;
3,273,435✔
431
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,273,435✔
432
  ret<<=8;
3,273,435✔
433
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,273,435✔
434
  ret<<=8;
3,273,435✔
435
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,273,435✔
436

437
  return ret;
3,273,435✔
438
}
3,273,435✔
439

440

441
uint16_t PacketReader::get16BitInt()
442
{
4,641,756✔
443
  uint16_t ret=0;
4,641,756✔
444
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
4,641,756✔
445
  ret<<=8;
4,641,756✔
446
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
4,641,756✔
447

448
  return ret;
4,641,756✔
449
}
4,641,756✔
450

451
uint8_t PacketReader::get8BitInt()
452
{
2,560,518✔
453
  return d_content.at(d_pos++);
2,560,518✔
454
}
2,560,518✔
455

456
DNSName PacketReader::getName()
457
{
4,675,842✔
458
  unsigned int consumed;
4,675,842✔
459
  try {
4,675,842✔
460
    DNSName dn((const char*) d_content.data(), d_content.size(), d_pos, true /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, &consumed, sizeof(dnsheader));
4,675,842✔
461

462
    d_pos+=consumed;
4,675,842✔
463
    return dn;
4,675,842✔
464
  }
4,675,842✔
465
  catch(const std::range_error& re) {
4,675,842✔
466
    throw std::out_of_range(string("dnsname issue: ")+re.what());
4✔
467
  }
4✔
468
  catch(...) {
4,675,842✔
469
    throw std::out_of_range("dnsname issue");
×
470
  }
×
471
  throw PDNSException("PacketReader::getName(): name is empty");
×
472
}
4,675,842✔
473

474
// FIXME see #6010 and #3503 if you want a proper solution
475
string txtEscape(const string &name)
476
{
140,157✔
477
  string ret;
140,157✔
478
  std::array<char, 5> ebuf{};
140,157✔
479

480
  for (char letter : name) {
14,461,237✔
481
    const unsigned uch = static_cast<unsigned char>(letter);
14,461,237✔
482
    if (uch >= 127 || uch < 32) {
14,461,237✔
483
      snprintf(ebuf.data(), ebuf.size(), "\\%03u", uch);
2,525✔
484
      ret += ebuf.data();
2,525✔
485
    }
2,525✔
486
    else if (letter == '"' || letter == '\\'){
14,458,712✔
487
      ret += '\\';
1,102✔
488
      ret += letter;
1,102✔
489
    }
1,102✔
490
    else {
14,457,610✔
491
      ret += letter;
14,457,610✔
492
    }
14,457,610✔
493
  }
14,461,237✔
494
  return ret;
140,157✔
495
}
140,157✔
496

497
// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
498
string PacketReader::getText(bool multi, bool lenField)
499
{
136,261✔
500
  string ret;
136,261✔
501
  ret.reserve(40);
136,261✔
502
  while(d_pos < d_startrecordpos + d_recordlen ) {
269,471✔
503
    if(!ret.empty()) {
139,208✔
504
      ret.append(1,' ');
2,950✔
505
    }
2,950✔
506
    uint16_t labellen;
139,208✔
507
    if(lenField)
139,208✔
508
      labellen=static_cast<uint8_t>(d_content.at(d_pos++));
139,184✔
509
    else
24✔
510
      labellen=d_recordlen - (d_pos - d_startrecordpos);
24✔
511

512
    ret.append(1,'"');
139,208✔
513
    if(labellen) { // no need to do anything for an empty string
139,208✔
514
      string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
137,360✔
515
      ret.append(txtEscape(val)); // the end is one beyond the packet
137,360✔
516
    }
137,360✔
517
    ret.append(1,'"');
139,208✔
518
    d_pos+=labellen;
139,208✔
519
    if(!multi)
139,208✔
520
      break;
5,998✔
521
  }
139,208✔
522

523
  if (ret.empty() && !lenField) {
136,261!
524
    // all lenField == false cases (CAA and URI at the time of this writing) want that emptiness to be explicit
525
    return "\"\"";
3✔
526
  }
3✔
527
  return ret;
136,258✔
528
}
136,261✔
529

530
string PacketReader::getUnquotedText(bool lenField)
531
{
21✔
532
  uint16_t stop_at;
21✔
533
  if(lenField)
21!
534
    stop_at = static_cast<uint8_t>(d_content.at(d_pos)) + d_pos + 1;
21✔
535
  else
×
536
    stop_at = d_recordlen;
×
537

538
  /* think unsigned overflow */
539
  if (stop_at < d_pos) {
21!
540
    throw std::out_of_range("getUnquotedText out of record range");
×
541
  }
×
542

543
  if(stop_at == d_pos)
21!
544
    return "";
×
545

546
  d_pos++;
21✔
547
  string ret(d_content.substr(d_pos, stop_at-d_pos));
21✔
548
  d_pos = stop_at;
21✔
549
  return ret;
21✔
550
}
21✔
551

552
void PacketReader::xfrBlob(string& blob)
553
{
1,297,409✔
554
  try {
1,297,409✔
555
    if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
1,297,409✔
556
      if (d_pos > (d_startrecordpos + d_recordlen)) {
1,124,633!
557
        throw std::out_of_range("xfrBlob out of record range");
×
558
      }
×
559
      blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
1,124,633✔
560
    }
1,124,633✔
561
    else {
172,776✔
562
      blob.clear();
172,776✔
563
    }
172,776✔
564

565
    d_pos = d_startrecordpos + d_recordlen;
1,297,409✔
566
  }
1,297,409✔
567
  catch(...)
1,297,409✔
568
  {
1,297,409✔
569
    throw std::out_of_range("xfrBlob out of range");
9✔
570
  }
9✔
571
}
1,297,409✔
572

573
void PacketReader::xfrBlobNoSpaces(string& blob, int length) {
1,311✔
574
  xfrBlob(blob, length);
1,311✔
575
}
1,311✔
576

577
void PacketReader::xfrBlob(string& blob, int length)
578
{
538,441✔
579
  if(length) {
538,441✔
580
    if (length < 0) {
535,681!
581
      throw std::out_of_range("xfrBlob out of range (negative length)");
×
582
    }
×
583

584
    blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
535,681✔
585

586
    d_pos += length;
535,681✔
587
  }
535,681✔
588
  else {
2,760✔
589
    blob.clear();
2,760✔
590
  }
2,760✔
591
}
538,441✔
592

593
void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
1,392✔
594
  while (d_pos < (d_startrecordpos + d_recordlen)) {
4,568✔
595
    if (d_pos + 2 > (d_startrecordpos + d_recordlen)) {
3,176!
596
      throw std::out_of_range("incomplete key");
×
597
    }
×
598
    uint16_t keyInt;
3,176✔
599
    xfr16BitInt(keyInt);
3,176✔
600
    auto key = static_cast<SvcParam::SvcParamKey>(keyInt);
3,176✔
601
    uint16_t len;
3,176✔
602
    xfr16BitInt(len);
3,176✔
603

604
    if (d_pos + len > (d_startrecordpos + d_recordlen)) {
3,176!
605
      throw std::out_of_range("record is shorter than SVCB lengthfield implies");
×
606
    }
×
607

608
    switch (key)
3,176✔
609
    {
3,176✔
610
    case SvcParam::mandatory: {
27✔
611
      if (len % 2 != 0) {
27!
612
        throw std::out_of_range("mandatory SvcParam has invalid length");
×
613
      }
×
614
      if (len == 0) {
27!
615
        throw std::out_of_range("empty 'mandatory' values");
×
616
      }
×
617
      std::set<SvcParam::SvcParamKey> paramKeys;
27✔
618
      size_t stop = d_pos + len;
27✔
619
      while (d_pos < stop) {
57✔
620
        uint16_t keyval;
30✔
621
        xfr16BitInt(keyval);
30✔
622
        paramKeys.insert(static_cast<SvcParam::SvcParamKey>(keyval));
30✔
623
      }
30✔
624
      kvs.insert(SvcParam(key, std::move(paramKeys)));
27✔
625
      break;
27✔
626
    }
27✔
627
    case SvcParam::alpn: {
899✔
628
      size_t stop = d_pos + len;
899✔
629
      std::vector<string> alpns;
899✔
630
      while (d_pos < stop) {
2,046✔
631
        string alpn;
1,147✔
632
        uint8_t alpnLen = 0;
1,147✔
633
        xfr8BitInt(alpnLen);
1,147✔
634
        if (alpnLen == 0) {
1,147!
635
          throw std::out_of_range("alpn length of 0");
×
636
        }
×
637
        xfrBlob(alpn, alpnLen);
1,147✔
638
        alpns.push_back(std::move(alpn));
1,147✔
639
      }
1,147✔
640
      kvs.insert(SvcParam(key, std::move(alpns)));
899✔
641
      break;
899✔
642
    }
899✔
643
    case SvcParam::ohttp:
215✔
644
    case SvcParam::no_default_alpn: {
433✔
645
      if (len != 0) {
433!
646
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
×
647
      }
×
648
      kvs.insert(SvcParam(key));
433✔
649
      break;
433✔
650
    }
433✔
651
    case SvcParam::port: {
657✔
652
      if (len != 2) {
657!
653
        throw std::out_of_range("invalid length for port");
×
654
      }
×
655
      uint16_t port;
657✔
656
      xfr16BitInt(port);
657✔
657
      kvs.insert(SvcParam(key, port));
657✔
658
      break;
657✔
659
    }
657✔
660
    case SvcParam::ipv4hint:
35✔
661
    case SvcParam::ipv6hint: {
50✔
662
      size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16);
50✔
663
      if (len % addrLen != 0) {
50!
664
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
×
665
      }
×
666
      vector<ComboAddress> addresses;
50✔
667
      auto stop = d_pos + len;
50✔
668
      while (d_pos < stop)
115✔
669
      {
65✔
670
        ComboAddress addr;
65✔
671
        xfrCAWithoutPort(key, addr);
65✔
672
        addresses.push_back(addr);
65✔
673
      }
65✔
674
      // If there were no addresses, and the input comes from internal
675
      // representation, we can reasonably assume this is the serialization
676
      // of "auto".
677
      bool doAuto{d_internal && len == 0};
50!
678
      auto param = SvcParam(key, std::move(addresses));
50✔
679
      param.setAutoHint(doAuto);
50✔
680
      kvs.insert(std::move(param));
50✔
681
      break;
50✔
682
    }
50✔
683
    case SvcParam::ech: {
15✔
684
      std::string blob;
15✔
685
      blob.reserve(len);
15✔
686
      xfrBlobNoSpaces(blob, len);
15✔
687
      kvs.insert(SvcParam(key, blob));
15✔
688
      break;
15✔
689
    }
50✔
690
    case SvcParam::tls_supported_groups: {
218✔
691
      if (len % 2 != 0) {
218!
692
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
×
693
      }
×
694
      vector<uint16_t> groups;
218✔
695
      groups.reserve(len / 2);
218✔
696
      auto stop = d_pos + len;
218✔
697
      while (d_pos < stop)
654✔
698
      {
436✔
699
        uint16_t group = 0;
436✔
700
        xfr16BitInt(group);
436✔
701
        groups.push_back(group);
436✔
702
      }
436✔
703
      auto param = SvcParam(key, std::move(groups));
218✔
704
      kvs.insert(std::move(param));
218✔
705
      break;
218✔
706
    }
218✔
707
    default: {
877✔
708
      std::string blob;
877✔
709
      blob.reserve(len);
877✔
710
      xfrBlob(blob, len);
877✔
711
      kvs.insert(SvcParam(key, blob));
877✔
712
      break;
877✔
713
    }
218✔
714
    }
3,176✔
715
  }
3,176✔
716
}
1,392✔
717

718

719
void PacketReader::xfrHexBlob(string& blob, bool /* keepReading */)
720
{
6,048✔
721
  xfrBlob(blob);
6,048✔
722
}
6,048✔
723

724
//FIXME400 remove this method completely
725
string simpleCompress(const string& elabel, const string& root)
726
{
218✔
727
  string label=elabel;
218✔
728
  // FIXME400: this relies on the semi-canonical escaped output from getName
729
  if(strchr(label.c_str(), '\\')) {
218!
730
    boost::replace_all(label, "\\.", ".");
×
731
    boost::replace_all(label, "\\032", " ");
×
732
    boost::replace_all(label, "\\\\", "\\");
×
733
  }
×
734
  typedef vector<pair<unsigned int, unsigned int> > parts_t;
218✔
735
  parts_t parts;
218✔
736
  vstringtok(parts, label, ".");
218✔
737
  string ret;
218✔
738
  ret.reserve(label.size()+4);
218✔
739
  for(const auto & part : parts) {
1,310✔
740
    if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + part.first, 1 + label.length() - part.first)) { // also match trailing 0, hence '1 +'
1,310!
741
      const unsigned char rootptr[2]={0xc0,0x11};
×
742
      ret.append((const char *) rootptr, 2);
×
743
      return ret;
×
744
    }
×
745
    ret.append(1, (char)(part.second - part.first));
1,310✔
746
    ret.append(label.c_str() + part.first, part.second - part.first);
1,310✔
747
  }
1,310✔
748
  ret.append(1, (char)0);
218✔
749
  return ret;
218✔
750
}
218✔
751

752
// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
753
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor)
754
{
36✔
755
  if(length < sizeof(dnsheader))
36!
756
    return;
×
757
  try
36✔
758
  {
36✔
759
    dnsheader dh;
36✔
760
    memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh));
36✔
761
    uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
36✔
762
    DNSPacketMangler dpm(packet, length);
36✔
763

764
    uint64_t n;
36✔
765
    for(n=0; n < ntohs(dh.qdcount) ; ++n) {
72✔
766
      dpm.skipDomainName();
36✔
767
      /* type and class */
768
      dpm.skipBytes(4);
36✔
769
    }
36✔
770

771
    for(n=0; n < numrecords; ++n) {
95✔
772
      dpm.skipDomainName();
59✔
773

774
      uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
59!
775
      uint16_t dnstype = dpm.get16BitInt();
59✔
776
      uint16_t dnsclass = dpm.get16BitInt();
59✔
777

778
      if(dnstype == QType::OPT) // not getting near that one with a stick
59!
779
        break;
×
780

781
      uint32_t dnsttl = dpm.get32BitInt();
59✔
782
      uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
59✔
783
      if (newttl) {
59✔
784
        dpm.rewindBytes(sizeof(newttl));
23✔
785
        dpm.setAndSkip32BitInt(newttl);
23✔
786
      }
23✔
787
      dpm.skipRData();
59✔
788
    }
59✔
789
  }
36✔
790
  catch(...)
36✔
791
  {
36✔
792
    return;
4✔
793
  }
4✔
794
}
36✔
795

796
static bool checkIfPacketContainsRecords(const PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
797
{
28✔
798
  auto length = packet.size();
28✔
799
  if (length < sizeof(dnsheader)) {
28!
800
    return false;
×
801
  }
×
802

803
  try {
28✔
804
    const dnsheader_aligned dh(packet.data());
28✔
805
    DNSPacketMangler dpm(const_cast<char*>(reinterpret_cast<const char*>(packet.data())), length);
28✔
806

807
    const uint16_t qdcount = ntohs(dh->qdcount);
28✔
808
    for (size_t n = 0; n < qdcount; ++n) {
56✔
809
      dpm.skipDomainName();
28✔
810
      /* type and class */
811
      dpm.skipBytes(4);
28✔
812
    }
28✔
813
    const size_t recordsCount = static_cast<size_t>(ntohs(dh->ancount)) + ntohs(dh->nscount) + ntohs(dh->arcount);
28✔
814
    for (size_t n = 0; n < recordsCount; ++n) {
44✔
815
      dpm.skipDomainName();
41✔
816
      uint16_t dnstype = dpm.get16BitInt();
41✔
817
      uint16_t dnsclass = dpm.get16BitInt();
41✔
818
      if (dnsclass == QClass::IN && qtypes.count(dnstype) > 0) {
41!
819
        return true;
25✔
820
      }
25✔
821
      /* ttl */
822
      dpm.skipBytes(4);
16✔
823
      dpm.skipRData();
16✔
824
    }
16✔
825
  }
28✔
826
  catch (...) {
28✔
827
  }
×
828

829
  return false;
3✔
830
}
28✔
831

832
static int rewritePacketWithoutRecordTypes(const PacketBuffer& initialPacket, PacketBuffer& newContent, const std::unordered_set<QType>& qtypes)
833
{
25✔
834
  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};
25✔
835

836
  if (initialPacket.size() < sizeof(dnsheader)) {
25!
837
    return EINVAL;
×
838
  }
×
839
  try {
25✔
840
    const dnsheader_aligned dh(initialPacket.data());
25✔
841

842
    if (ntohs(dh->qdcount) == 0)
25!
843
      return ENOENT;
×
844
    auto packetView = std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size());
25✔
845

846
    PacketReader pr(packetView);
25✔
847

848
    size_t idx = 0;
25✔
849
    DNSName rrname;
25✔
850
    uint16_t qdcount = ntohs(dh->qdcount);
25✔
851
    uint16_t ancount = ntohs(dh->ancount);
25✔
852
    uint16_t nscount = ntohs(dh->nscount);
25✔
853
    uint16_t arcount = ntohs(dh->arcount);
25✔
854
    uint16_t rrtype;
25✔
855
    uint16_t rrclass;
25✔
856
    string blob;
25✔
857
    struct dnsrecordheader ah;
25✔
858

859
    rrname = pr.getName();
25✔
860
    rrtype = pr.get16BitInt();
25✔
861
    rrclass = pr.get16BitInt();
25✔
862

863
    GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh->opcode);
25✔
864
    pw.getHeader()->id=dh->id;
25✔
865
    pw.getHeader()->qr=dh->qr;
25✔
866
    pw.getHeader()->aa=dh->aa;
25✔
867
    pw.getHeader()->tc=dh->tc;
25✔
868
    pw.getHeader()->rd=dh->rd;
25✔
869
    pw.getHeader()->ra=dh->ra;
25✔
870
    pw.getHeader()->ad=dh->ad;
25✔
871
    pw.getHeader()->cd=dh->cd;
25✔
872
    pw.getHeader()->rcode=dh->rcode;
25✔
873

874
    /* consume remaining qd if any */
875
    if (qdcount > 1) {
25!
876
      for(idx = 1; idx < qdcount; idx++) {
×
877
        rrname = pr.getName();
×
878
        rrtype = pr.get16BitInt();
×
879
        rrclass = pr.get16BitInt();
×
880
        (void) rrtype;
×
881
        (void) rrclass;
×
882
      }
×
883
    }
×
884

885
    /* copy AN */
886
    for (idx = 0; idx < ancount; idx++) {
74✔
887
      rrname = pr.getName();
49✔
888
      pr.getDnsrecordheader(ah);
49✔
889
      pr.xfrBlob(blob);
49✔
890

891
      if (qtypes.find(ah.d_type) == qtypes.end()) {
49✔
892
        // if this is not a safe type
893
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
16!
894
          // "unsafe" types might contain compressed data, so cancel rewrite
895
          newContent.clear();
×
896
          return EIO;
×
897
        }
×
898
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
16✔
899
        pw.xfrBlob(blob);
16✔
900
      }
16✔
901
    }
49✔
902

903
    /* copy NS */
904
    for (idx = 0; idx < nscount; idx++) {
25!
905
      rrname = pr.getName();
×
906
      pr.getDnsrecordheader(ah);
×
907
      pr.xfrBlob(blob);
×
908

909
      if (qtypes.find(ah.d_type) == qtypes.end()) {
×
910
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
×
911
          // "unsafe" types might contain compressed data, so cancel rewrite
912
          newContent.clear();
×
913
          return EIO;
×
914
        }
×
915
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
×
916
        pw.xfrBlob(blob);
×
917
      }
×
918
    }
×
919
    /* copy AR */
920
    for (idx = 0; idx < arcount; idx++) {
65✔
921
      rrname = pr.getName();
44✔
922
      pr.getDnsrecordheader(ah);
44✔
923
      pr.xfrBlob(blob);
44✔
924

925
      if (qtypes.find(ah.d_type) == qtypes.end()) {
44✔
926
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
32✔
927
          // "unsafe" types might contain compressed data, so cancel rewrite
928
          newContent.clear();
4✔
929
          return EIO;
4✔
930
        }
4✔
931
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
28✔
932
        pw.xfrBlob(blob);
28✔
933
      }
28✔
934
    }
44✔
935
    pw.commit();
21✔
936

937
  }
21✔
938
  catch (...)
25✔
939
  {
25✔
940
    newContent.clear();
×
941
    return EIO;
×
942
  }
×
943
  return 0;
21✔
944
}
25✔
945

946
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes)
947
{
20✔
948
  return clearDNSPacketRecordTypes(reinterpret_cast<PacketBuffer&>(packet), qtypes);
20✔
949
}
20✔
950

951
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
952
{
28✔
953
  if (!checkIfPacketContainsRecords(packet, qtypes)) {
28✔
954
    return;
3✔
955
  }
3✔
956

957
  PacketBuffer newContent;
25✔
958

959
  auto result = rewritePacketWithoutRecordTypes(packet, newContent, qtypes);
25✔
960
  if (!result) {
25✔
961
    packet = std::move(newContent);
21✔
962
  }
21✔
963
}
25✔
964

965
// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
966
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned& aligned_dh)
967
{
1,583,027✔
968
  if (length < sizeof(dnsheader)) {
1,583,027!
969
    return;
×
970
  }
×
971
  try {
1,583,027✔
972
    const dnsheader* dhp = aligned_dh.get();
1,583,027✔
973
    const uint64_t dqcount = ntohs(dhp->qdcount);
1,583,027✔
974
    const uint64_t numrecords = ntohs(dhp->ancount) + ntohs(dhp->nscount) + ntohs(dhp->arcount);
1,583,027✔
975
    DNSPacketMangler dpm(packet, length);
1,583,027✔
976

977
    for (uint64_t rec = 0; rec < dqcount; ++rec) {
3,182,095✔
978
      dpm.skipDomainName();
1,599,068✔
979
      /* type and class */
980
      dpm.skipBytes(4);
1,599,068✔
981
    }
1,599,068✔
982

983
    for(uint64_t rec = 0; rec < numrecords; ++rec) {
3,189,139✔
984
      dpm.skipDomainName();
1,606,112✔
985

986
      uint16_t dnstype = dpm.get16BitInt();
1,606,112✔
987
      /* class */
988
      dpm.skipBytes(2);
1,606,112✔
989

990
      if (dnstype != QType::OPT) { // not aging that one with a stick
1,606,646✔
991
        dpm.decreaseAndSkip32BitInt(seconds);
1,605,408✔
992
      } else {
4,296,038,216✔
993
        dpm.skipBytes(4);
4,294,968,526✔
994
      }
4,294,968,526✔
995
      dpm.skipRData();
1,606,112✔
996
    }
1,606,112✔
997
  }
1,583,027✔
998
  catch(...) {
1,583,027✔
999
  }
4✔
1000
}
1,583,027✔
1001

1002
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned& aligned_dh)
1003
{
2,856✔
1004
  ageDNSPacket(packet.data(), packet.length(), seconds, aligned_dh);
2,856✔
1005
}
2,856✔
1006

1007
void shuffleDNSPacket(char* packet, size_t length, const dnsheader_aligned& aligned_dh)
1008
{
899,976✔
1009
  if (length < sizeof(dnsheader)) {
899,976!
1010
    return;
×
1011
  }
×
1012
  try {
899,976✔
1013
    const dnsheader* dhp = aligned_dh.get();
899,976✔
1014
    const uint16_t ancount = ntohs(dhp->ancount);
899,976✔
1015
    if (ancount == 1) {
899,976✔
1016
      // quick exit, nothing to shuffle
1017
      return;
599,976✔
1018
    }
599,976✔
1019

1020
    DNSPacketMangler dpm(packet, length);
300,000✔
1021

1022
    const uint16_t qdcount = ntohs(dhp->qdcount);
300,000✔
1023

1024
    for(size_t iter = 0; iter < qdcount; ++iter) {
600,000✔
1025
      dpm.skipDomainName();
300,000✔
1026
      /* type and class */
1027
      dpm.skipBytes(4);
300,000✔
1028
    }
300,000✔
1029

1030
    // for now shuffle only first rrset, only As and AAAAs
1031
    uint16_t rrset_type = 0;
300,000✔
1032
    DNSName rrset_dnsname{};
300,000✔
1033
    std::vector<std::pair<uint32_t, uint32_t>> rrdata_indexes;
300,000✔
1034
    rrdata_indexes.reserve(ancount);
300,000✔
1035

1036
    for(size_t iter = 0; iter < ancount; ++iter) {
1,560,000✔
1037
      auto domain_start = dpm.getOffset();
1,380,000✔
1038
      dpm.skipDomainName();
1,380,000✔
1039
      const uint16_t dnstype = dpm.get16BitInt();
1,380,000✔
1040
      if (dnstype == QType::A || dnstype == QType::AAAA) {
1,380,000✔
1041
        if (rrdata_indexes.empty()) {
1,320,000✔
1042
          rrset_type = dnstype;
300,000✔
1043
          rrset_dnsname = DNSName(packet, length, domain_start, true);
300,000✔
1044
        } else {
1,020,000✔
1045
          if (dnstype != rrset_type) {
1,020,000✔
1046
            break;
60,000✔
1047
          }
60,000✔
1048
          if (DNSName(packet, length, domain_start, true) != rrset_dnsname) {
960,000✔
1049
            break;
60,000✔
1050
          }
60,000✔
1051
        }
960,000✔
1052
        /* class */
1053
        dpm.skipBytes(2);
1,200,000✔
1054

1055
        /* ttl */
1056
        dpm.skipBytes(4);
1,200,000✔
1057
        rrdata_indexes.push_back(dpm.skipRDataAndReturnOffsets());
1,200,000✔
1058
      } else {
1,200,000✔
1059
        if (!rrdata_indexes.empty()) {
60,000!
1060
          break;
×
1061
        }
×
1062
        /* class */
1063
        dpm.skipBytes(2);
60,000✔
1064

1065
        /* ttl */
1066
        dpm.skipBytes(4);
60,000✔
1067
        dpm.skipRData();
60,000✔
1068
      }
60,000✔
1069
    }
1,380,000✔
1070

1071
    if (rrdata_indexes.size() >= 2) {
300,000!
1072
      using uid = std::uniform_int_distribution<std::vector<std::pair<uint32_t, uint32_t>>::size_type>;
300,000✔
1073
      uid dist;
300,000✔
1074

1075
      pdns::dns_random_engine randomEngine;
300,000✔
1076
      for (auto swapped = rrdata_indexes.size() - 1; swapped > 0; --swapped) {
1,200,000✔
1077
        auto swapped_with = dist(randomEngine, uid::param_type(0, swapped));
900,000✔
1078
        if (swapped != swapped_with) {
900,000✔
1079
          dpm.swapInPlace(rrdata_indexes.at(swapped), rrdata_indexes.at(swapped_with));
575,199✔
1080
        }
575,199✔
1081
      }
900,000✔
1082
    }
300,000✔
1083
  }
300,000✔
1084
  catch(...) {
899,976✔
1085
  }
×
1086
}
899,976✔
1087

1088
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
1089
{
2,035,041✔
1090
  uint32_t result = std::numeric_limits<uint32_t>::max();
2,035,041✔
1091
  if(length < sizeof(dnsheader)) {
2,035,041!
1092
    return result;
×
1093
  }
×
1094
  try
2,035,041✔
1095
  {
2,035,041✔
1096
    const dnsheader_aligned dh(packet);
2,035,041✔
1097
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
2,035,041✔
1098

1099
    const uint16_t qdcount = ntohs(dh->qdcount);
2,035,041✔
1100
    for(size_t n = 0; n < qdcount; ++n) {
4,079,851✔
1101
      dpm.skipDomainName();
2,044,810✔
1102
      /* type and class */
1103
      dpm.skipBytes(4);
2,044,810✔
1104
    }
2,044,810✔
1105
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
2,035,041✔
1106
    for(size_t n = 0; n < numrecords; ++n) {
4,120,099✔
1107
      dpm.skipDomainName();
2,085,169✔
1108
      const uint16_t dnstype = dpm.get16BitInt();
2,085,169✔
1109
      /* class */
1110
      const uint16_t dnsclass = dpm.get16BitInt();
2,085,169✔
1111

1112
      if(dnstype == QType::OPT) {
2,085,169✔
1113
        break;
111✔
1114
      }
111✔
1115

1116
      /* report it if we see a SOA record in the AUTHORITY section */
1117
      if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
2,085,058!
1118
        *seenAuthSOA = true;
21✔
1119
      }
21✔
1120

1121
      const uint32_t ttl = dpm.get32BitInt();
2,085,058✔
1122
      result = std::min(result, ttl);
2,085,058✔
1123

1124
      dpm.skipRData();
2,085,058✔
1125
    }
2,085,058✔
1126
  }
2,035,041✔
1127
  catch(...)
2,035,041✔
1128
  {
2,035,041✔
1129
  }
7✔
1130
  return result;
2,078,454✔
1131
}
2,035,041✔
1132

1133
uint32_t getDNSPacketLength(const char* packet, size_t length)
1134
{
58✔
1135
  uint32_t result = length;
58✔
1136
  if(length < sizeof(dnsheader)) {
58!
1137
    return result;
×
1138
  }
×
1139
  try
58✔
1140
  {
58✔
1141
    const dnsheader_aligned dh(packet);
58✔
1142
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
58✔
1143

1144
    const uint16_t qdcount = ntohs(dh->qdcount);
58✔
1145
    for(size_t n = 0; n < qdcount; ++n) {
116✔
1146
      dpm.skipDomainName();
58✔
1147
      /* type and class */
1148
      dpm.skipBytes(4);
58✔
1149
    }
58✔
1150
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
58✔
1151
    for(size_t n = 0; n < numrecords; ++n) {
115✔
1152
      dpm.skipDomainName();
57✔
1153
      /* type (2), class (2) and ttl (4) */
1154
      dpm.skipBytes(8);
57✔
1155
      dpm.skipRData();
57✔
1156
    }
57✔
1157
    result = dpm.getOffset();
58✔
1158
  }
58✔
1159
  catch(...)
58✔
1160
  {
58✔
1161
  }
4✔
1162
  return result;
58✔
1163
}
58✔
1164

1165
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1166
{
150✔
1167
  uint16_t result = 0;
150✔
1168
  if(length < sizeof(dnsheader)) {
150!
1169
    return result;
×
1170
  }
×
1171
  try
150✔
1172
  {
150✔
1173
    const dnsheader_aligned dh(packet);
150✔
1174
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
150✔
1175

1176
    const uint16_t qdcount = ntohs(dh->qdcount);
150✔
1177
    for(size_t n = 0; n < qdcount; ++n) {
300✔
1178
      dpm.skipDomainName();
150✔
1179
      if (section == 0) {
150✔
1180
        uint16_t dnstype = dpm.get16BitInt();
8✔
1181
        if (dnstype == type) {
8✔
1182
          result++;
4✔
1183
        }
4✔
1184
        /* class */
1185
        dpm.skipBytes(2);
8✔
1186
      } else {
142✔
1187
        /* type and class */
1188
        dpm.skipBytes(4);
142✔
1189
      }
142✔
1190
    }
150✔
1191
    const uint16_t ancount = ntohs(dh->ancount);
150✔
1192
    for(size_t n = 0; n < ancount; ++n) {
326✔
1193
      dpm.skipDomainName();
176✔
1194
      if (section == 1) {
176✔
1195
        uint16_t dnstype = dpm.get16BitInt();
88✔
1196
        if (dnstype == type) {
88✔
1197
          result++;
44✔
1198
        }
44✔
1199
        /* class */
1200
        dpm.skipBytes(2);
88✔
1201
      } else {
88✔
1202
        /* type and class */
1203
        dpm.skipBytes(4);
88✔
1204
      }
88✔
1205
      /* ttl */
1206
      dpm.skipBytes(4);
176✔
1207
      dpm.skipRData();
176✔
1208
    }
176✔
1209
    const uint16_t nscount = ntohs(dh->nscount);
150✔
1210
    for(size_t n = 0; n < nscount; ++n) {
186✔
1211
      dpm.skipDomainName();
36✔
1212
      if (section == 2) {
36✔
1213
        uint16_t dnstype = dpm.get16BitInt();
8✔
1214
        if (dnstype == type) {
8✔
1215
          result++;
4✔
1216
        }
4✔
1217
        /* class */
1218
        dpm.skipBytes(2);
8✔
1219
      } else {
28✔
1220
        /* type and class */
1221
        dpm.skipBytes(4);
28✔
1222
      }
28✔
1223
      /* ttl */
1224
      dpm.skipBytes(4);
36✔
1225
      dpm.skipRData();
36✔
1226
    }
36✔
1227
    const uint16_t arcount = ntohs(dh->arcount);
150✔
1228
    for(size_t n = 0; n < arcount; ++n) {
452✔
1229
      dpm.skipDomainName();
302✔
1230
      if (section == 3) {
302✔
1231
        uint16_t dnstype = dpm.get16BitInt();
118✔
1232
        if (dnstype == type) {
118✔
1233
          result++;
38✔
1234
        }
38✔
1235
        /* class */
1236
        dpm.skipBytes(2);
118✔
1237
      } else {
190✔
1238
        /* type and class */
1239
        dpm.skipBytes(4);
184✔
1240
      }
184✔
1241
      /* ttl */
1242
      dpm.skipBytes(4);
302✔
1243
      dpm.skipRData();
302✔
1244
    }
302✔
1245
  }
150✔
1246
  catch(...)
150✔
1247
  {
150✔
1248
  }
×
1249
  return result;
150✔
1250
}
150✔
1251

1252
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
1253
{
3,358✔
1254
  if (length < sizeof(dnsheader)) {
3,358!
1255
    return false;
×
1256
  }
×
1257

1258
  *payloadSize = 0;
3,358✔
1259
  *z = 0;
3,358✔
1260

1261
  try
3,358✔
1262
  {
3,358✔
1263
    const dnsheader_aligned dh(packet);
3,358✔
1264
    if (dh->arcount == 0) {
3,358✔
1265
      // The OPT pseudo-RR, if present, has to be in the additional section (https://datatracker.ietf.org/doc/html/rfc6891#section-6.1.1)
1266
      return false;
3,154✔
1267
    }
3,154✔
1268

1269
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
204✔
1270

1271
    const uint16_t qdcount = ntohs(dh->qdcount);
204✔
1272
    for(size_t n = 0; n < qdcount; ++n) {
408✔
1273
      dpm.skipDomainName();
204✔
1274
      /* type and class */
1275
      dpm.skipBytes(4);
204✔
1276
    }
204✔
1277
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
204✔
1278
    for(size_t n = 0; n < numrecords; ++n) {
258✔
1279
      dpm.skipDomainName();
257✔
1280
      const auto dnstype = dpm.get16BitInt();
257✔
1281

1282
      if (dnstype == QType::OPT) {
257✔
1283
        const auto dnsclass = dpm.get16BitInt();
203✔
1284
        /* skip extended rcode and version */
1285
        dpm.skipBytes(2);
203✔
1286
        *z = dpm.get16BitInt();
203✔
1287
        *payloadSize = dnsclass;
203✔
1288
        return true;
203✔
1289
      }
203✔
1290
      /* skip class */
1291
      dpm.skipBytes(2);
54✔
1292
      /* TTL */
1293
      dpm.skipBytes(4);
54✔
1294
      dpm.skipRData();
54✔
1295
    }
54✔
1296
  }
204✔
1297
  catch(...)
3,358✔
1298
  {
3,358✔
1299
  }
6✔
1300

1301
  return false;
7✔
1302
}
3,358✔
1303

1304
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
1305
{
144✔
1306
  if (packet.size() < sizeof(dnsheader)) {
144!
1307
    return false;
×
1308
  }
×
1309

1310
  try
144✔
1311
  {
144✔
1312
    const dnsheader_aligned dh(packet.data());
144✔
1313
    uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
144✔
1314
    PacketReader reader(packet);
144✔
1315

1316
    uint64_t n;
144✔
1317
    for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
288✔
1318
      (void) reader.getName();
144✔
1319
      /* type and class */
1320
      reader.skip(4);
144✔
1321
    }
144✔
1322

1323
    for (n = 0; n < numrecords; ++n) {
369✔
1324
      (void) reader.getName();
282✔
1325

1326
      uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
282✔
1327
      uint16_t dnstype = reader.get16BitInt();
282✔
1328
      uint16_t dnsclass = reader.get16BitInt();
282✔
1329

1330
      if (dnstype == QType::OPT) {
282✔
1331
        // not getting near that one with a stick
1332
        break;
27✔
1333
      }
27✔
1334

1335
      uint32_t dnsttl = reader.get32BitInt();
255✔
1336
      uint16_t contentLength = reader.get16BitInt();
255✔
1337
      uint16_t pos = reader.getPosition();
255✔
1338

1339
      bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
255✔
1340
      if (done) {
255✔
1341
        return true;
30✔
1342
      }
30✔
1343

1344
      reader.skip(contentLength);
225✔
1345
    }
225✔
1346
  }
144✔
1347
  catch (...) {
144✔
1348
    return false;
×
1349
  }
×
1350

1351
  return true;
114✔
1352
}
144✔
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