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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

80.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 "namespaces.hh"
28
#include "noinitvector.hh"
29

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

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

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

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

52
  string out;
31✔
53
  out.reserve(total + 1);
31✔
54

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

63
  d_record.insert(d_record.end(), out.begin(), out.end());
28✔
64
}
28✔
65

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

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

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

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

95
  uint16_t pos=0;
1,095✔
96
  memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
1,095✔
97

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

101
  memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
1,095✔
102

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

109
  memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
1,095✔
110
  if (!serialized.empty()) {
1,095✔
111
    memcpy(&packet[pos], serialized.c_str(), serialized.size());
1,092✔
112
    pos += (uint16_t) serialized.size();
1,092✔
113
    (void) pos;
1,092✔
114
  }
1,092✔
115

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

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

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

139
  return i->second(dr, pr);
×
140
}
×
141

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

150
  return i->second(content);
306,577✔
151
}
306,618✔
152

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

161
  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
480,331✔
162

163
  auto i = getTypemap().find(pair(searchclass, dr.d_type));
480,331✔
164
  if(i==getTypemap().end() || !i->second) {
480,333✔
165
    return std::make_shared<UnknownRecordContent>(dr, pr);
212✔
166
  }
212✔
167

168
  return i->second(dr, pr);
480,119✔
169
}
480,331✔
170

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

178
DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
179
{
976,691✔
180
  static DNSRecordContent::typemap_t typemap;
976,691✔
181
  return typemap;
976,691✔
182
}
976,691✔
183

184
DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
185
{
857,078✔
186
  static DNSRecordContent::n2typemap_t n2typemap;
857,078✔
187
  return n2typemap;
857,078✔
188
}
857,078✔
189

190
DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
191
{
831,814✔
192
  static DNSRecordContent::t2namemap_t t2namemap;
831,814✔
193
  return t2namemap;
831,814✔
194
}
831,814✔
195

196
DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
197
{
630,476✔
198
  static DNSRecordContent::zmakermap_t zmakermap;
630,476✔
199
  return zmakermap;
630,476✔
200
}
630,476✔
201

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

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

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

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

235
  memcpy(&d_header, packet.data(), sizeof(dnsheader));
1,208,725✔
236

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

240
  d_header.qdcount=ntohs(d_header.qdcount);
1,208,725✔
241
  d_header.ancount=ntohs(d_header.ancount);
1,208,725✔
242
  d_header.nscount=ntohs(d_header.nscount);
1,208,725✔
243
  d_header.arcount=ntohs(d_header.arcount);
1,208,725✔
244

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

248
  unsigned int n=0;
1,208,725✔
249

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

255
    for(n=0;n < d_header.qdcount; ++n) {
2,412,561✔
256
      d_qname=pr.getName();
1,203,836✔
257
      d_qtype=pr.get16BitInt();
1,203,836✔
258
      d_qclass=pr.get16BitInt();
1,203,836✔
259
    }
1,203,836✔
260

261
    struct dnsrecordheader ah;
1,208,725✔
262
    vector<unsigned char> record;
1,208,725✔
263
    bool seenTSIG = false;
1,208,725✔
264
    validPacket=true;
1,208,725✔
265
    d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
1,208,725✔
266
    for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
1,687,374✔
267
      DNSRecord dr;
478,651✔
268

269
      if(n < d_header.ancount)
478,651✔
270
        dr.d_place=DNSResourceRecord::ANSWER;
432,969✔
271
      else if(n < d_header.ancount + d_header.nscount)
45,682✔
272
        dr.d_place=DNSResourceRecord::AUTHORITY;
20,121✔
273
      else
25,561✔
274
        dr.d_place=DNSResourceRecord::ADDITIONAL;
25,561✔
275

276
      unsigned int recordStartPos=pr.getPosition();
478,651✔
277

278
      DNSName name=pr.getName();
478,651✔
279

280
      pr.getDnsrecordheader(ah);
478,651✔
281
      dr.d_ttl=ah.d_ttl;
478,651✔
282
      dr.d_type=ah.d_type;
478,651✔
283
      dr.d_class=ah.d_class;
478,651✔
284

285
      dr.d_name = std::move(name);
478,651✔
286
      dr.d_clen = ah.d_clen;
478,651✔
287

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

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

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

311
      d_answers.emplace_back(std::move(dr));
478,649✔
312
    }
478,649✔
313

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

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

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

353
  return false;
×
354
}
×
355

356
void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
357
{
480,477✔
358
  unsigned char *p = reinterpret_cast<unsigned char*>(&ah);
480,477✔
359

360
  for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
5,253,673✔
361
    p[n] = d_content.at(d_pos++);
4,773,196✔
362
  }
4,773,196✔
363

364
  ah.d_type = ntohs(ah.d_type);
480,477✔
365
  ah.d_class = ntohs(ah.d_class);
480,477✔
366
  ah.d_clen = ntohs(ah.d_clen);
480,477✔
367
  ah.d_ttl = ntohl(ah.d_ttl);
480,477✔
368

369
  d_startrecordpos = d_pos; // needed for getBlob later on
480,477✔
370
  d_recordlen = ah.d_clen;
480,477✔
371
}
480,477✔
372

373

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

383
  dest.resize(len);
731✔
384

385
  for (uint16_t n = 0; n < len; ++n) {
9,965,327✔
386
    dest.at(n) = d_content.at(d_pos++);
9,964,596✔
387
  }
9,964,596✔
388
}
731✔
389

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

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

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

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

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

436
  return ret;
449,725✔
437
}
449,725✔
438

439

440
uint16_t PacketReader::get16BitInt()
441
{
2,403,734✔
442
  uint16_t ret=0;
2,403,734✔
443
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
2,403,734✔
444
  ret<<=8;
2,403,734✔
445
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
2,403,734✔
446

447
  return ret;
2,403,734✔
448
}
2,403,734✔
449

450
uint8_t PacketReader::get8BitInt()
451
{
37,499✔
452
  return d_content.at(d_pos++);
37,499✔
453
}
37,499✔
454

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

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

473
static string txtEscape(const string &name)
474
{
443✔
475
  string ret;
443✔
476
  char ebuf[5];
443✔
477

478
  for(char i : name) {
29,042✔
479
    if((unsigned char) i >= 127 || (unsigned char) i < 32) {
29,042!
480
      snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)i);
15✔
481
      ret += ebuf;
15✔
482
    }
15✔
483
    else if(i=='"' || i=='\\'){
29,027!
484
      ret += '\\';
9✔
485
      ret += i;
9✔
486
    }
9✔
487
    else
29,018✔
488
      ret += i;
29,018✔
489
  }
29,042✔
490
  return ret;
443✔
491
}
443✔
492

493
// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
494
string PacketReader::getText(bool multi, bool lenField)
495
{
378✔
496
  string ret;
378✔
497
  ret.reserve(40);
378✔
498
  while(d_pos < d_startrecordpos + d_recordlen ) {
791✔
499
    if(!ret.empty()) {
452✔
500
      ret.append(1,' ');
77✔
501
    }
77✔
502
    uint16_t labellen;
452✔
503
    if(lenField)
452✔
504
      labellen=static_cast<uint8_t>(d_content.at(d_pos++));
428✔
505
    else
24✔
506
      labellen=d_recordlen - (d_pos - d_startrecordpos);
24✔
507

508
    ret.append(1,'"');
452✔
509
    if(labellen) { // no need to do anything for an empty string
452✔
510
      string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
443✔
511
      ret.append(txtEscape(val)); // the end is one beyond the packet
443✔
512
    }
443✔
513
    ret.append(1,'"');
452✔
514
    d_pos+=labellen;
452✔
515
    if(!multi)
452✔
516
      break;
39✔
517
  }
452✔
518

519
  if (ret.empty() && !lenField) {
378!
520
    // all lenField == false cases (CAA and URI at the time of this writing) want that emptiness to be explicit
521
    return "\"\"";
3✔
522
  }
3✔
523
  return ret;
375✔
524
}
378✔
525

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

534
  /* think unsigned overflow */
535
  if (stop_at < d_pos) {
21!
536
    throw std::out_of_range("getUnquotedText out of record range");
×
537
  }
×
538

539
  if(stop_at == d_pos)
21!
540
    return "";
×
541

542
  d_pos++;
21✔
543
  string ret(d_content.substr(d_pos, stop_at-d_pos));
21✔
544
  d_pos = stop_at;
21✔
545
  return ret;
21✔
546
}
21✔
547

548
void PacketReader::xfrBlob(string& blob)
549
{
27,917✔
550
  try {
27,917✔
551
    if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
27,917✔
552
      if (d_pos > (d_startrecordpos + d_recordlen)) {
18,018!
553
        throw std::out_of_range("xfrBlob out of record range");
×
554
      }
×
555
      blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
18,018✔
556
    }
18,018✔
557
    else {
9,899✔
558
      blob.clear();
9,899✔
559
    }
9,899✔
560

561
    d_pos = d_startrecordpos + d_recordlen;
27,917✔
562
  }
27,917✔
563
  catch(...)
27,917✔
564
  {
27,917✔
565
    throw std::out_of_range("xfrBlob out of range");
9✔
566
  }
9✔
567
}
27,917✔
568

569
void PacketReader::xfrBlobNoSpaces(string& blob, int length) {
56✔
570
  xfrBlob(blob, length);
56✔
571
}
56✔
572

573
void PacketReader::xfrBlob(string& blob, int length)
574
{
16,350✔
575
  if(length) {
16,350✔
576
    if (length < 0) {
14,454!
577
      throw std::out_of_range("xfrBlob out of range (negative length)");
×
578
    }
×
579

580
    blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
14,454✔
581

582
    d_pos += length;
14,454✔
583
  }
14,454✔
584
  else {
1,896✔
585
    blob.clear();
1,896✔
586
  }
1,896✔
587
}
16,350✔
588

589
void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
97✔
590
  while (d_pos < (d_startrecordpos + d_recordlen)) {
250✔
591
    if (d_pos + 2 > (d_startrecordpos + d_recordlen)) {
153!
592
      throw std::out_of_range("incomplete key");
×
593
    }
×
594
    uint16_t keyInt;
153✔
595
    xfr16BitInt(keyInt);
153✔
596
    auto key = static_cast<SvcParam::SvcParamKey>(keyInt);
153✔
597
    uint16_t len;
153✔
598
    xfr16BitInt(len);
153✔
599

600
    if (d_pos + len > (d_startrecordpos + d_recordlen)) {
153!
601
      throw std::out_of_range("record is shorter than SVCB lengthfield implies");
×
602
    }
×
603

604
    switch (key)
153✔
605
    {
153✔
606
    case SvcParam::mandatory: {
19✔
607
      if (len % 2 != 0) {
19!
608
        throw std::out_of_range("mandatory SvcParam has invalid length");
×
609
      }
×
610
      if (len == 0) {
19!
611
        throw std::out_of_range("empty 'mandatory' values");
×
612
      }
×
613
      std::set<SvcParam::SvcParamKey> paramKeys;
19✔
614
      size_t stop = d_pos + len;
19✔
615
      while (d_pos < stop) {
41✔
616
        uint16_t keyval;
22✔
617
        xfr16BitInt(keyval);
22✔
618
        paramKeys.insert(static_cast<SvcParam::SvcParamKey>(keyval));
22✔
619
      }
22✔
620
      kvs.insert(SvcParam(key, std::move(paramKeys)));
19✔
621
      break;
19✔
622
    }
19✔
623
    case SvcParam::alpn: {
43✔
624
      size_t stop = d_pos + len;
43✔
625
      std::vector<string> alpns;
43✔
626
      while (d_pos < stop) {
116✔
627
        string alpn;
73✔
628
        uint8_t alpnLen = 0;
73✔
629
        xfr8BitInt(alpnLen);
73✔
630
        if (alpnLen == 0) {
73!
631
          throw std::out_of_range("alpn length of 0");
×
632
        }
×
633
        xfrBlob(alpn, alpnLen);
73✔
634
        alpns.push_back(alpn);
73✔
635
      }
73✔
636
      kvs.insert(SvcParam(key, std::move(alpns)));
43✔
637
      break;
43✔
638
    }
43✔
639
    case SvcParam::no_default_alpn: {
5✔
640
      if (len != 0) {
5!
641
        throw std::out_of_range("invalid length for no-default-alpn");
×
642
      }
×
643
      kvs.insert(SvcParam(key));
5✔
644
      break;
5✔
645
    }
5✔
646
    case SvcParam::port: {
16✔
647
      if (len != 2) {
16!
648
        throw std::out_of_range("invalid length for port");
×
649
      }
×
650
      uint16_t port;
16✔
651
      xfr16BitInt(port);
16✔
652
      kvs.insert(SvcParam(key, port));
16✔
653
      break;
16✔
654
    }
16✔
655
    case SvcParam::ipv4hint: /* fall-through */
27✔
656
    case SvcParam::ipv6hint: {
40✔
657
      size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16);
40✔
658
      if (len % addrLen != 0) {
40!
659
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
×
660
      }
×
661
      vector<ComboAddress> addresses;
40✔
662
      auto stop = d_pos + len;
40✔
663
      while (d_pos < stop)
89✔
664
      {
49✔
665
        ComboAddress addr;
49✔
666
        xfrCAWithoutPort(key, addr);
49✔
667
        addresses.push_back(addr);
49✔
668
      }
49✔
669
      // If there were no addresses, and the input comes from internal
670
      // representation, we can reasonably assume this is the serialization
671
      // of "auto".
672
      bool doAuto{d_internal && len == 0};
40!
673
      auto param = SvcParam(key, std::move(addresses));
40✔
674
      param.setAutoHint(doAuto);
40✔
675
      kvs.insert(param);
40✔
676
      break;
40✔
677
    }
40✔
678
    case SvcParam::ech: {
8✔
679
      std::string blob;
8✔
680
      blob.reserve(len);
8✔
681
      xfrBlobNoSpaces(blob, len);
8✔
682
      kvs.insert(SvcParam(key, blob));
8✔
683
      break;
8✔
684
    }
40✔
685
    default: {
22✔
686
      std::string blob;
22✔
687
      blob.reserve(len);
22✔
688
      xfrBlob(blob, len);
22✔
689
      kvs.insert(SvcParam(key, blob));
22✔
690
      break;
22✔
691
    }
40✔
692
    }
153✔
693
  }
153✔
694
}
97✔
695

696

697
void PacketReader::xfrHexBlob(string& blob, bool /* keepReading */)
698
{
2,660✔
699
  xfrBlob(blob);
2,660✔
700
}
2,660✔
701

702
//FIXME400 remove this method completely
703
string simpleCompress(const string& elabel, const string& root)
704
{
×
705
  string label=elabel;
×
706
  // FIXME400: this relies on the semi-canonical escaped output from getName
707
  if(strchr(label.c_str(), '\\')) {
×
708
    boost::replace_all(label, "\\.", ".");
×
709
    boost::replace_all(label, "\\032", " ");
×
710
    boost::replace_all(label, "\\\\", "\\");
×
711
  }
×
712
  typedef vector<pair<unsigned int, unsigned int> > parts_t;
×
713
  parts_t parts;
×
714
  vstringtok(parts, label, ".");
×
715
  string ret;
×
716
  ret.reserve(label.size()+4);
×
717
  for(const auto & part : parts) {
×
718
    if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + part.first, 1 + label.length() - part.first)) { // also match trailing 0, hence '1 +'
×
719
      const unsigned char rootptr[2]={0xc0,0x11};
×
720
      ret.append((const char *) rootptr, 2);
×
721
      return ret;
×
722
    }
×
723
    ret.append(1, (char)(part.second - part.first));
×
724
    ret.append(label.c_str() + part.first, part.second - part.first);
×
725
  }
×
726
  ret.append(1, (char)0);
×
727
  return ret;
×
728
}
×
729

730
// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
731
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor)
732
{
29✔
733
  if(length < sizeof(dnsheader))
29!
734
    return;
×
735
  try
29✔
736
  {
29✔
737
    dnsheader dh;
29✔
738
    memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh));
29✔
739
    uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
29✔
740
    DNSPacketMangler dpm(packet, length);
29✔
741

742
    uint64_t n;
29✔
743
    for(n=0; n < ntohs(dh.qdcount) ; ++n) {
58✔
744
      dpm.skipDomainName();
29✔
745
      /* type and class */
746
      dpm.skipBytes(4);
29✔
747
    }
29✔
748

749
    for(n=0; n < numrecords; ++n) {
76✔
750
      dpm.skipDomainName();
47✔
751

752
      uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
47!
753
      uint16_t dnstype = dpm.get16BitInt();
47✔
754
      uint16_t dnsclass = dpm.get16BitInt();
47✔
755

756
      if(dnstype == QType::OPT) // not getting near that one with a stick
47!
757
        break;
×
758

759
      uint32_t dnsttl = dpm.get32BitInt();
47✔
760
      uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
47✔
761
      if (newttl) {
47✔
762
        dpm.rewindBytes(sizeof(newttl));
20✔
763
        dpm.setAndSkip32BitInt(newttl);
20✔
764
      }
20✔
765
      dpm.skipRData();
47✔
766
    }
47✔
767
  }
29✔
768
  catch(...)
29✔
769
  {
29✔
770
    return;
3✔
771
  }
3✔
772
}
29✔
773

774
static bool checkIfPacketContainsRecords(const PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
775
{
21✔
776
  auto length = packet.size();
21✔
777
  if (length < sizeof(dnsheader)) {
21!
778
    return false;
×
779
  }
×
780

781
  try {
21✔
782
    const dnsheader_aligned dh(packet.data());
21✔
783
    DNSPacketMangler dpm(const_cast<char*>(reinterpret_cast<const char*>(packet.data())), length);
21✔
784

785
    const uint16_t qdcount = ntohs(dh->qdcount);
21✔
786
    for (size_t n = 0; n < qdcount; ++n) {
42✔
787
      dpm.skipDomainName();
21✔
788
      /* type and class */
789
      dpm.skipBytes(4);
21✔
790
    }
21✔
791
    const size_t recordsCount = static_cast<size_t>(ntohs(dh->ancount)) + ntohs(dh->nscount) + ntohs(dh->arcount);
21✔
792
    for (size_t n = 0; n < recordsCount; ++n) {
34✔
793
      dpm.skipDomainName();
32✔
794
      uint16_t dnstype = dpm.get16BitInt();
32✔
795
      uint16_t dnsclass = dpm.get16BitInt();
32✔
796
      if (dnsclass == QClass::IN && qtypes.count(dnstype) > 0) {
32!
797
        return true;
19✔
798
      }
19✔
799
      /* ttl */
800
      dpm.skipBytes(4);
13✔
801
      dpm.skipRData();
13✔
802
    }
13✔
803
  }
21✔
804
  catch (...) {
21✔
805
  }
×
806

807
  return false;
2✔
808
}
21✔
809

810
static int rewritePacketWithoutRecordTypes(const PacketBuffer& initialPacket, PacketBuffer& newContent, const std::unordered_set<QType>& qtypes)
811
{
19✔
812
  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};
19✔
813

814
  if (initialPacket.size() < sizeof(dnsheader)) {
19!
815
    return EINVAL;
×
816
  }
×
817
  try {
19✔
818
    const dnsheader_aligned dh(initialPacket.data());
19✔
819

820
    if (ntohs(dh->qdcount) == 0)
19!
821
      return ENOENT;
×
822
    auto packetView = std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size());
19✔
823

824
    PacketReader pr(packetView);
19✔
825

826
    size_t idx = 0;
19✔
827
    DNSName rrname;
19✔
828
    uint16_t qdcount = ntohs(dh->qdcount);
19✔
829
    uint16_t ancount = ntohs(dh->ancount);
19✔
830
    uint16_t nscount = ntohs(dh->nscount);
19✔
831
    uint16_t arcount = ntohs(dh->arcount);
19✔
832
    uint16_t rrtype;
19✔
833
    uint16_t rrclass;
19✔
834
    string blob;
19✔
835
    struct dnsrecordheader ah;
19✔
836

837
    rrname = pr.getName();
19✔
838
    rrtype = pr.get16BitInt();
19✔
839
    rrclass = pr.get16BitInt();
19✔
840

841
    GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh->opcode);
19✔
842
    pw.getHeader()->id=dh->id;
19✔
843
    pw.getHeader()->qr=dh->qr;
19✔
844
    pw.getHeader()->aa=dh->aa;
19✔
845
    pw.getHeader()->tc=dh->tc;
19✔
846
    pw.getHeader()->rd=dh->rd;
19✔
847
    pw.getHeader()->ra=dh->ra;
19✔
848
    pw.getHeader()->ad=dh->ad;
19✔
849
    pw.getHeader()->cd=dh->cd;
19✔
850
    pw.getHeader()->rcode=dh->rcode;
19✔
851

852
    /* consume remaining qd if any */
853
    if (qdcount > 1) {
19!
854
      for(idx = 1; idx < qdcount; idx++) {
×
855
        rrname = pr.getName();
×
856
        rrtype = pr.get16BitInt();
×
857
        rrclass = pr.get16BitInt();
×
858
        (void) rrtype;
×
859
        (void) rrclass;
×
860
      }
×
861
    }
×
862

863
    /* copy AN */
864
    for (idx = 0; idx < ancount; idx++) {
58✔
865
      rrname = pr.getName();
39✔
866
      pr.getDnsrecordheader(ah);
39✔
867
      pr.xfrBlob(blob);
39✔
868

869
      if (qtypes.find(ah.d_type) == qtypes.end()) {
39✔
870
        // if this is not a safe type
871
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
13!
872
          // "unsafe" types might countain compressed data, so cancel rewrite
873
          newContent.clear();
×
874
          return EIO;
×
875
        }
×
876
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
13✔
877
        pw.xfrBlob(blob);
13✔
878
      }
13✔
879
    }
39✔
880

881
    /* copy NS */
882
    for (idx = 0; idx < nscount; idx++) {
19!
883
      rrname = pr.getName();
×
884
      pr.getDnsrecordheader(ah);
×
885
      pr.xfrBlob(blob);
×
886

887
      if (qtypes.find(ah.d_type) == qtypes.end()) {
×
888
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
×
889
          // "unsafe" types might countain compressed data, so cancel rewrite
890
          newContent.clear();
×
891
          return EIO;
×
892
        }
×
893
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
×
894
        pw.xfrBlob(blob);
×
895
      }
×
896
    }
×
897
    /* copy AR */
898
    for (idx = 0; idx < arcount; idx++) {
49✔
899
      rrname = pr.getName();
33✔
900
      pr.getDnsrecordheader(ah);
33✔
901
      pr.xfrBlob(blob);
33✔
902

903
      if (qtypes.find(ah.d_type) == qtypes.end()) {
33✔
904
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
24✔
905
          // "unsafe" types might countain compressed data, so cancel rewrite
906
          newContent.clear();
3✔
907
          return EIO;
3✔
908
        }
3✔
909
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
21✔
910
        pw.xfrBlob(blob);
21✔
911
      }
21✔
912
    }
33✔
913
    pw.commit();
16✔
914

915
  }
16✔
916
  catch (...)
19✔
917
  {
19✔
918
    newContent.clear();
×
919
    return EIO;
×
920
  }
×
921
  return 0;
16✔
922
}
19✔
923

924
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes)
925
{
15✔
926
  return clearDNSPacketRecordTypes(reinterpret_cast<PacketBuffer&>(packet), qtypes);
15✔
927
}
15✔
928

929
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
930
{
21✔
931
  if (!checkIfPacketContainsRecords(packet, qtypes)) {
21✔
932
    return;
2✔
933
  }
2✔
934

935
  PacketBuffer newContent;
19✔
936

937
  auto result = rewritePacketWithoutRecordTypes(packet, newContent, qtypes);
19✔
938
  if (!result) {
19✔
939
    packet = std::move(newContent);
16✔
940
  }
16✔
941
}
19✔
942

943
// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
944
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned& aligned_dh)
945
{
947,527✔
946
  if (length < sizeof(dnsheader)) {
947,527!
947
    return;
×
948
  }
×
949
  try {
947,527✔
950
    const dnsheader* dhp = aligned_dh.get();
947,527✔
951
    const uint64_t dqcount = ntohs(dhp->qdcount);
947,527✔
952
    const uint64_t numrecords = ntohs(dhp->ancount) + ntohs(dhp->nscount) + ntohs(dhp->arcount);
947,527✔
953
    DNSPacketMangler dpm(packet, length);
947,527✔
954

955
    for (uint64_t rec = 0; rec < dqcount; ++rec) {
1,895,103✔
956
      dpm.skipDomainName();
947,576✔
957
      /* type and class */
958
      dpm.skipBytes(4);
947,576✔
959
    }
947,576✔
960

961
    for(uint64_t rec = 0; rec < numrecords; ++rec) {
1,898,426✔
962
      dpm.skipDomainName();
950,899✔
963

964
      uint16_t dnstype = dpm.get16BitInt();
950,899✔
965
      /* class */
966
      dpm.skipBytes(2);
950,899✔
967

968
      if (dnstype != QType::OPT) { // not aging that one with a stick
950,906✔
969
        dpm.decreaseAndSkip32BitInt(seconds);
949,697✔
970
      } else {
2,148,426,017✔
971
        dpm.skipBytes(4);
2,147,484,846✔
972
      }
2,147,484,846✔
973
      dpm.skipRData();
950,899✔
974
    }
950,899✔
975
  }
947,527✔
976
  catch(...) {
947,527✔
977
  }
3✔
978
}
947,527✔
979

980
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned& aligned_dh)
981
{
2,330✔
982
  ageDNSPacket(packet.data(), packet.length(), seconds, aligned_dh);
2,330✔
983
}
2,330✔
984

985
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
986
{
1,187,615✔
987
  uint32_t result = std::numeric_limits<uint32_t>::max();
1,187,615✔
988
  if(length < sizeof(dnsheader)) {
1,187,615!
989
    return result;
×
990
  }
×
991
  try
1,187,615✔
992
  {
1,187,615✔
993
    const dnsheader_aligned dh(packet);
1,187,615✔
994
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1,187,615✔
995

996
    const uint16_t qdcount = ntohs(dh->qdcount);
1,187,615✔
997
    for(size_t n = 0; n < qdcount; ++n) {
2,373,305✔
998
      dpm.skipDomainName();
1,185,690✔
999
      /* type and class */
1000
      dpm.skipBytes(4);
1,185,690✔
1001
    }
1,185,690✔
1002
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1,187,615✔
1003
    for(size_t n = 0; n < numrecords; ++n) {
2,384,967✔
1004
      dpm.skipDomainName();
1,197,432✔
1005
      const uint16_t dnstype = dpm.get16BitInt();
1,197,432✔
1006
      /* class */
1007
      const uint16_t dnsclass = dpm.get16BitInt();
1,197,432✔
1008

1009
      if(dnstype == QType::OPT) {
1,197,432✔
1010
        break;
80✔
1011
      }
80✔
1012

1013
      /* report it if we see a SOA record in the AUTHORITY section */
1014
      if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
1,197,352!
1015
        *seenAuthSOA = true;
16✔
1016
      }
16✔
1017

1018
      const uint32_t ttl = dpm.get32BitInt();
1,197,352✔
1019
      if (result > ttl) {
1,197,352✔
1020
        result = ttl;
1,177,933✔
1021
      }
1,177,933✔
1022

1023
      dpm.skipRData();
1,197,352✔
1024
    }
1,197,352✔
1025
  }
1,187,615✔
1026
  catch(...)
1,187,615✔
1027
  {
1,187,615✔
1028
  }
5✔
1029
  return result;
1,189,647✔
1030
}
1,187,615✔
1031

1032
uint32_t getDNSPacketLength(const char* packet, size_t length)
1033
{
48✔
1034
  uint32_t result = length;
48✔
1035
  if(length < sizeof(dnsheader)) {
48!
1036
    return result;
×
1037
  }
×
1038
  try
48✔
1039
  {
48✔
1040
    const dnsheader_aligned dh(packet);
48✔
1041
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
48✔
1042

1043
    const uint16_t qdcount = ntohs(dh->qdcount);
48✔
1044
    for(size_t n = 0; n < qdcount; ++n) {
96✔
1045
      dpm.skipDomainName();
48✔
1046
      /* type and class */
1047
      dpm.skipBytes(4);
48✔
1048
    }
48✔
1049
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
48✔
1050
    for(size_t n = 0; n < numrecords; ++n) {
90✔
1051
      dpm.skipDomainName();
42✔
1052
      /* type (2), class (2) and ttl (4) */
1053
      dpm.skipBytes(8);
42✔
1054
      dpm.skipRData();
42✔
1055
    }
42✔
1056
    result = dpm.getOffset();
48✔
1057
  }
48✔
1058
  catch(...)
48✔
1059
  {
48✔
1060
  }
3✔
1061
  return result;
48✔
1062
}
48✔
1063

1064
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1065
{
114✔
1066
  uint16_t result = 0;
114✔
1067
  if(length < sizeof(dnsheader)) {
114!
1068
    return result;
×
1069
  }
×
1070
  try
114✔
1071
  {
114✔
1072
    const dnsheader_aligned dh(packet);
114✔
1073
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
114✔
1074

1075
    const uint16_t qdcount = ntohs(dh->qdcount);
114✔
1076
    for(size_t n = 0; n < qdcount; ++n) {
228✔
1077
      dpm.skipDomainName();
114✔
1078
      if (section == 0) {
114✔
1079
        uint16_t dnstype = dpm.get16BitInt();
6✔
1080
        if (dnstype == type) {
6✔
1081
          result++;
3✔
1082
        }
3✔
1083
        /* class */
1084
        dpm.skipBytes(2);
6✔
1085
      } else {
108✔
1086
        /* type and class */
1087
        dpm.skipBytes(4);
108✔
1088
      }
108✔
1089
    }
114✔
1090
    const uint16_t ancount = ntohs(dh->ancount);
114✔
1091
    for(size_t n = 0; n < ancount; ++n) {
246✔
1092
      dpm.skipDomainName();
132✔
1093
      if (section == 1) {
132✔
1094
        uint16_t dnstype = dpm.get16BitInt();
66✔
1095
        if (dnstype == type) {
66✔
1096
          result++;
33✔
1097
        }
33✔
1098
        /* class */
1099
        dpm.skipBytes(2);
66✔
1100
      } else {
66✔
1101
        /* type and class */
1102
        dpm.skipBytes(4);
66✔
1103
      }
66✔
1104
      /* ttl */
1105
      dpm.skipBytes(4);
132✔
1106
      dpm.skipRData();
132✔
1107
    }
132✔
1108
    const uint16_t nscount = ntohs(dh->nscount);
114✔
1109
    for(size_t n = 0; n < nscount; ++n) {
141✔
1110
      dpm.skipDomainName();
27✔
1111
      if (section == 2) {
27✔
1112
        uint16_t dnstype = dpm.get16BitInt();
6✔
1113
        if (dnstype == type) {
6✔
1114
          result++;
3✔
1115
        }
3✔
1116
        /* class */
1117
        dpm.skipBytes(2);
6✔
1118
      } else {
21✔
1119
        /* type and class */
1120
        dpm.skipBytes(4);
21✔
1121
      }
21✔
1122
      /* ttl */
1123
      dpm.skipBytes(4);
27✔
1124
      dpm.skipRData();
27✔
1125
    }
27✔
1126
    const uint16_t arcount = ntohs(dh->arcount);
114✔
1127
    for(size_t n = 0; n < arcount; ++n) {
342✔
1128
      dpm.skipDomainName();
228✔
1129
      if (section == 3) {
228✔
1130
        uint16_t dnstype = dpm.get16BitInt();
90✔
1131
        if (dnstype == type) {
90✔
1132
          result++;
29✔
1133
        }
29✔
1134
        /* class */
1135
        dpm.skipBytes(2);
90✔
1136
      } else {
144✔
1137
        /* type and class */
1138
        dpm.skipBytes(4);
138✔
1139
      }
138✔
1140
      /* ttl */
1141
      dpm.skipBytes(4);
228✔
1142
      dpm.skipRData();
228✔
1143
    }
228✔
1144
  }
114✔
1145
  catch(...)
114✔
1146
  {
114✔
1147
  }
×
1148
  return result;
114✔
1149
}
114✔
1150

1151
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
1152
{
205✔
1153
  if (length < sizeof(dnsheader)) {
205!
1154
    return false;
×
1155
  }
×
1156

1157
  *payloadSize = 0;
205✔
1158
  *z = 0;
205✔
1159

1160
  try
205✔
1161
  {
205✔
1162
    const dnsheader_aligned dh(packet);
205✔
1163
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
205✔
1164

1165
    const uint16_t qdcount = ntohs(dh->qdcount);
205✔
1166
    for(size_t n = 0; n < qdcount; ++n) {
410✔
1167
      dpm.skipDomainName();
205✔
1168
      /* type and class */
1169
      dpm.skipBytes(4);
205✔
1170
    }
205✔
1171
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
205✔
1172
    for(size_t n = 0; n < numrecords; ++n) {
208✔
1173
      dpm.skipDomainName();
40✔
1174
      const uint16_t dnstype = dpm.get16BitInt();
40✔
1175
      const uint16_t dnsclass = dpm.get16BitInt();
40✔
1176

1177
      if(dnstype == QType::OPT) {
40✔
1178
        /* skip extended rcode and version */
1179
        dpm.skipBytes(2);
37✔
1180
        *z = dpm.get16BitInt();
37✔
1181
        *payloadSize = dnsclass;
37✔
1182
        return true;
37✔
1183
      }
37✔
1184

1185
      /* TTL */
1186
      dpm.skipBytes(4);
3✔
1187
      dpm.skipRData();
3✔
1188
    }
3✔
1189
  }
205✔
1190
  catch(...)
205✔
1191
  {
205✔
1192
  }
4✔
1193

1194
  return false;
172✔
1195
}
205✔
1196

1197
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
1198
{
96✔
1199
  if (packet.size() < sizeof(dnsheader)) {
96!
1200
    return false;
×
1201
  }
×
1202

1203
  try
96✔
1204
  {
96✔
1205
    const dnsheader_aligned dh(packet.data());
96✔
1206
    uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
96✔
1207
    PacketReader reader(packet);
96✔
1208

1209
    uint64_t n;
96✔
1210
    for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
192✔
1211
      (void) reader.getName();
96✔
1212
      /* type and class */
1213
      reader.skip(4);
96✔
1214
    }
96✔
1215

1216
    for (n = 0; n < numrecords; ++n) {
246✔
1217
      (void) reader.getName();
188✔
1218

1219
      uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
188✔
1220
      uint16_t dnstype = reader.get16BitInt();
188✔
1221
      uint16_t dnsclass = reader.get16BitInt();
188✔
1222

1223
      if (dnstype == QType::OPT) {
188✔
1224
        // not getting near that one with a stick
1225
        break;
18✔
1226
      }
18✔
1227

1228
      uint32_t dnsttl = reader.get32BitInt();
170✔
1229
      uint16_t contentLength = reader.get16BitInt();
170✔
1230
      uint16_t pos = reader.getPosition();
170✔
1231

1232
      bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
170✔
1233
      if (done) {
170✔
1234
        return true;
20✔
1235
      }
20✔
1236

1237
      reader.skip(contentLength);
150✔
1238
    }
150✔
1239
  }
96✔
1240
  catch (...) {
96✔
1241
    return false;
×
1242
  }
×
1243

1244
  return true;
76✔
1245
}
96✔
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