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

PowerDNS / pdns / 12321902803

13 Dec 2024 07:34PM UTC coverage: 66.359% (+1.6%) from 64.78%
12321902803

Pull #14970

github

web-flow
Merge e3a7df61c into 3dfd8e317
Pull Request #14970: boost > std optional

26084 of 54744 branches covered (47.65%)

Branch coverage included in aggregate %.

14 of 15 new or added lines in 2 files covered. (93.33%)

1863 existing lines in 52 files now uncovered.

85857 of 113946 relevant lines covered (75.35%)

4412729.59 hits per line

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

77.96
/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
{
395✔
34
  // parse the input
35
  vector<string> parts;
395✔
36
  stringtok(parts, zone);
395✔
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"))) {
395✔
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) != "\\#") {
389✔
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) : "";
386✔
47
  auto total = pdns::checked_stoi<unsigned int>(parts.at(1));
386✔
48
  if (relevant.size() % 2 || (relevant.size() / 2) != total) {
386✔
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;
380✔
53
  out.reserve(total + 1);
380✔
54

55
  for (unsigned int n = 0; n < total; ++n) {
2,146✔
56
    int c;
1,769✔
57
    if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
1,769✔
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);
1,766✔
61
  }
1,766✔
62

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

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

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

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

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

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

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

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

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

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

116
  DNSRecord dr;
11,564✔
117
  dr.d_class = qclass;
11,564✔
118
  dr.d_type = qtype;
11,564✔
119
  dr.d_name = qname;
11,564✔
120
  dr.d_clen = serialized.size();
11,564✔
121
  PacketReader pr(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()), packet.size() - serialized.size() - sizeof(dnsrecordheader));
11,564✔
122
  /* needed to get the record boundaries right */
123
  pr.getDnsrecordheader(drh);
11,564✔
124
  auto content = DNSRecordContent::make(dr, pr, Opcode::Query);
11,564✔
125
  return content;
11,564✔
126
}
11,564✔
127

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

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

138
  return i->second(dr, pr);
268✔
139
}
269✔
140

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

149
  return i->second(content);
1,566,845✔
150
}
1,567,228✔
151

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

160
  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
2,225,292✔
161

162
  auto i = getTypemap().find(pair(searchclass, dr.d_type));
2,225,292✔
163
  if(i==getTypemap().end() || !i->second) {
2,225,294✔
164
    return std::make_shared<UnknownRecordContent>(dr, pr);
790✔
165
  }
790✔
166

167
  return i->second(dr, pr);
2,224,502✔
168
}
2,225,292✔
169

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

177
DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
178
{
7,534,979✔
179
  static DNSRecordContent::typemap_t typemap;
7,534,979✔
180
  return typemap;
7,534,979✔
181
}
7,534,979✔
182

183
DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
184
{
13,856,549✔
185
  static DNSRecordContent::n2typemap_t n2typemap;
13,856,549✔
186
  return n2typemap;
13,856,549✔
187
}
13,856,549✔
188

189
DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
190
{
7,702,274✔
191
  static DNSRecordContent::t2namemap_t t2namemap;
7,702,274✔
192
  return t2namemap;
7,702,274✔
193
}
7,702,274✔
194

195
DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
196
{
6,220,158✔
197
  static DNSRecordContent::zmakermap_t zmakermap;
6,220,158✔
198
  return zmakermap;
6,220,158✔
199
}
6,220,158✔
200

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

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

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

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

234
  memcpy(&d_header, packet.data(), sizeof(dnsheader));
1,370,980✔
235

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

239
  d_header.qdcount=ntohs(d_header.qdcount);
1,370,980✔
240
  d_header.ancount=ntohs(d_header.ancount);
1,370,980✔
241
  d_header.nscount=ntohs(d_header.nscount);
1,370,980✔
242
  d_header.arcount=ntohs(d_header.arcount);
1,370,980✔
243

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

247
  unsigned int n=0;
1,370,980✔
248

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

254
    for(n=0;n < d_header.qdcount; ++n) {
2,732,932✔
255
      d_qname=pr.getName();
1,361,952✔
256
      d_qtype=pr.get16BitInt();
1,361,952✔
257
      d_qclass=pr.get16BitInt();
1,361,952✔
258
    }
1,361,952✔
259

260
    struct dnsrecordheader ah;
1,370,980✔
261
    vector<unsigned char> record;
1,370,980✔
262
    bool seenTSIG = false;
1,370,980✔
263
    validPacket=true;
1,370,980✔
264
    d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
1,370,980✔
265
    for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
3,586,096✔
266
      DNSRecord dr;
2,215,116✔
267

268
      if(n < d_header.ancount)
2,215,116✔
269
        dr.d_place=DNSResourceRecord::ANSWER;
1,983,567✔
270
      else if(n < d_header.ancount + d_header.nscount)
231,549✔
271
        dr.d_place=DNSResourceRecord::AUTHORITY;
67,874✔
272
      else
163,675✔
273
        dr.d_place=DNSResourceRecord::ADDITIONAL;
163,675✔
274

275
      unsigned int recordStartPos=pr.getPosition();
2,215,116✔
276

277
      DNSName name=pr.getName();
2,215,116✔
278

279
      pr.getDnsrecordheader(ah);
2,215,116✔
280
      dr.d_ttl=ah.d_ttl;
2,215,116✔
281
      dr.d_type=ah.d_type;
2,215,116✔
282
      dr.d_class=ah.d_class;
2,215,116✔
283

284
      dr.d_name = std::move(name);
2,215,116✔
285
      dr.d_clen = ah.d_clen;
2,215,116✔
286

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

298
      if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
2,215,116!
UNCOV
299
        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.");
×
UNCOV
300
      }
×
301

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

310
      d_answers.emplace_back(std::move(dr));
2,215,116✔
311
    }
2,215,116✔
312

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

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

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

352
  return false;
×
353
}
×
354

355
void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
356
{
2,227,403✔
357
  unsigned char *p = reinterpret_cast<unsigned char*>(&ah);
2,227,403✔
358

359
  for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
24,454,930✔
360
    p[n] = d_content.at(d_pos++);
22,227,527✔
361
  }
22,227,527✔
362

363
  ah.d_type = ntohs(ah.d_type);
2,227,403✔
364
  ah.d_class = ntohs(ah.d_class);
2,227,403✔
365
  ah.d_clen = ntohs(ah.d_clen);
2,227,403✔
366
  ah.d_ttl = ntohl(ah.d_ttl);
2,227,403✔
367

368
  d_startrecordpos = d_pos; // needed for getBlob later on
2,227,403✔
369
  d_recordlen = ah.d_clen;
2,227,403✔
370
}
2,227,403✔
371

372

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

382
  dest.resize(len);
2,782✔
383

384
  for (uint16_t n = 0; n < len; ++n) {
167,121✔
385
    dest.at(n) = d_content.at(d_pos++);
164,339✔
386
  }
164,339✔
387
}
2,782✔
388

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

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

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

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

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

435
  return ret;
3,184,752✔
436
}
3,184,752✔
437

438

439
uint16_t PacketReader::get16BitInt()
440
{
4,468,289✔
441
  uint16_t ret=0;
4,468,289✔
442
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
4,468,289✔
443
  ret<<=8;
4,468,289✔
444
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
4,468,289✔
445

446
  return ret;
4,468,289✔
447
}
4,468,289✔
448

449
uint8_t PacketReader::get8BitInt()
450
{
2,515,973✔
451
  return d_content.at(d_pos++);
2,515,973✔
452
}
2,515,973✔
453

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

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

472
static string txtEscape(const string &name)
473
{
7,444✔
474
  string ret;
7,444✔
475
  char ebuf[5];
7,444✔
476

477
  for(char i : name) {
357,539✔
478
    if((unsigned char) i >= 127 || (unsigned char) i < 32) {
357,539✔
479
      snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)i);
1,741✔
480
      ret += ebuf;
1,741✔
481
    }
1,741✔
482
    else if(i=='"' || i=='\\'){
355,798✔
483
      ret += '\\';
904✔
484
      ret += i;
904✔
485
    }
904✔
486
    else
354,894✔
487
      ret += i;
354,894✔
488
  }
357,539✔
489
  return ret;
7,444✔
490
}
7,444✔
491

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

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

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

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

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

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

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

547
void PacketReader::xfrBlob(string& blob)
548
{
1,240,032✔
549
  try {
1,240,032✔
550
    if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
1,240,032✔
551
      if (d_pos > (d_startrecordpos + d_recordlen)) {
1,105,423!
552
        throw std::out_of_range("xfrBlob out of record range");
×
553
      }
×
554
      blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
1,105,423✔
555
    }
1,105,423✔
556
    else {
134,609✔
557
      blob.clear();
134,609✔
558
    }
134,609✔
559

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

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

572
void PacketReader::xfrBlob(string& blob, int length)
573
{
524,626✔
574
  if(length) {
524,626✔
575
    if (length < 0) {
522,822!
576
      throw std::out_of_range("xfrBlob out of range (negative length)");
×
577
    }
×
578

579
    blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
522,822✔
580

581
    d_pos += length;
522,822✔
582
  }
522,822✔
583
  else {
1,804✔
584
    blob.clear();
1,804✔
585
  }
1,804✔
586
}
524,626✔
587

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

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

603
    switch (key)
1,168✔
604
    {
1,168✔
605
    case SvcParam::mandatory: {
16✔
606
      if (len % 2 != 0) {
16!
607
        throw std::out_of_range("mandatory SvcParam has invalid length");
×
608
      }
×
609
      if (len == 0) {
16!
610
        throw std::out_of_range("empty 'mandatory' values");
×
611
      }
×
612
      std::set<SvcParam::SvcParamKey> paramKeys;
16✔
613
      size_t stop = d_pos + len;
16✔
614
      while (d_pos < stop) {
35✔
615
        uint16_t keyval;
19✔
616
        xfr16BitInt(keyval);
19✔
617
        paramKeys.insert(static_cast<SvcParam::SvcParamKey>(keyval));
19✔
618
      }
19✔
619
      kvs.insert(SvcParam(key, std::move(paramKeys)));
16✔
620
      break;
16✔
621
    }
16✔
622
    case SvcParam::alpn: {
739✔
623
      size_t stop = d_pos + len;
739✔
624
      std::vector<string> alpns;
739✔
625
      while (d_pos < stop) {
1,686✔
626
        string alpn;
947✔
627
        uint8_t alpnLen = 0;
947✔
628
        xfr8BitInt(alpnLen);
947✔
629
        if (alpnLen == 0) {
947!
630
          throw std::out_of_range("alpn length of 0");
×
631
        }
×
632
        xfrBlob(alpn, alpnLen);
947✔
633
        alpns.push_back(alpn);
947✔
634
      }
947✔
635
      kvs.insert(SvcParam(key, std::move(alpns)));
739✔
636
      break;
739✔
637
    }
739✔
638
    case SvcParam::no_default_alpn: {
3✔
639
      if (len != 0) {
3!
640
        throw std::out_of_range("invalid length for no-default-alpn");
×
641
      }
×
642
      kvs.insert(SvcParam(key));
3✔
643
      break;
3✔
644
    }
3✔
645
    case SvcParam::port: {
360✔
646
      if (len != 2) {
360!
647
        throw std::out_of_range("invalid length for port");
×
648
      }
×
649
      uint16_t port;
360✔
650
      xfr16BitInt(port);
360✔
651
      kvs.insert(SvcParam(key, port));
360✔
652
      break;
360✔
653
    }
360✔
654
    case SvcParam::ipv4hint: /* fall-through */
16✔
655
    case SvcParam::ipv6hint: {
25✔
656
      size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16);
25✔
657
      if (len % addrLen != 0) {
25!
658
        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
×
659
      }
×
660
      vector<ComboAddress> addresses;
25✔
661
      auto stop = d_pos + len;
25✔
662
      while (d_pos < stop)
60✔
663
      {
35✔
664
        ComboAddress addr;
35✔
665
        xfrCAWithoutPort(key, addr);
35✔
666
        addresses.push_back(addr);
35✔
667
      }
35✔
668
      kvs.insert(SvcParam(key, std::move(addresses)));
25✔
669
      break;
25✔
670
    }
25✔
671
    case SvcParam::ech: {
7✔
672
      std::string blob;
7✔
673
      blob.reserve(len);
7✔
674
      xfrBlobNoSpaces(blob, len);
7✔
675
      kvs.insert(SvcParam(key, blob));
7✔
676
      break;
7✔
677
    }
25✔
678
    default: {
18✔
679
      std::string blob;
18✔
680
      blob.reserve(len);
18✔
681
      xfrBlob(blob, len);
18✔
682
      kvs.insert(SvcParam(key, blob));
18✔
683
      break;
18✔
684
    }
25✔
685
    }
1,168✔
686
  }
1,168✔
687
}
793✔
688

689

690
void PacketReader::xfrHexBlob(string& blob, bool /* keepReading */)
691
{
5,609✔
692
  xfrBlob(blob);
5,609✔
693
}
5,609✔
694

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

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

735
    uint64_t n;
2✔
736
    for(n=0; n < ntohs(dh.qdcount) ; ++n) {
4✔
737
      dpm.skipDomainName();
2✔
738
      /* type and class */
739
      dpm.skipBytes(4);
2✔
740
    }
2✔
741

742
    for(n=0; n < numrecords; ++n) {
12✔
743
      dpm.skipDomainName();
10✔
744

745
      uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
10!
746
      uint16_t dnstype = dpm.get16BitInt();
10✔
747
      uint16_t dnsclass = dpm.get16BitInt();
10✔
748

749
      if(dnstype == QType::OPT) // not getting near that one with a stick
10!
750
        break;
×
751

752
      uint32_t dnsttl = dpm.get32BitInt();
10✔
753
      uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
10✔
754
      if (newttl) {
10✔
755
        dpm.rewindBytes(sizeof(newttl));
1✔
756
        dpm.setAndSkip32BitInt(newttl);
1✔
757
      }
1✔
758
      dpm.skipRData();
10✔
759
    }
10✔
760
  }
2✔
761
  catch(...)
2✔
762
  {
2✔
763
    return;
1✔
764
  }
1✔
765
}
2✔
766

767
static bool checkIfPacketContainsRecords(const PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
768
{
5✔
769
  auto length = packet.size();
5✔
770
  if (length < sizeof(dnsheader)) {
5!
771
    return false;
×
772
  }
×
773

774
  try {
5✔
775
    const dnsheader_aligned dh(packet.data());
5✔
776
    DNSPacketMangler dpm(const_cast<char*>(reinterpret_cast<const char*>(packet.data())), length);
5✔
777

778
    const uint16_t qdcount = ntohs(dh->qdcount);
5✔
779
    for (size_t n = 0; n < qdcount; ++n) {
10✔
780
      dpm.skipDomainName();
5✔
781
      /* type and class */
782
      dpm.skipBytes(4);
5✔
783
    }
5✔
784
    const size_t recordsCount = static_cast<size_t>(ntohs(dh->ancount)) + ntohs(dh->nscount) + ntohs(dh->arcount);
5✔
785
    for (size_t n = 0; n < recordsCount; ++n) {
8!
786
      dpm.skipDomainName();
8✔
787
      uint16_t dnstype = dpm.get16BitInt();
8✔
788
      uint16_t dnsclass = dpm.get16BitInt();
8✔
789
      if (dnsclass == QClass::IN && qtypes.count(dnstype) > 0) {
8!
790
        return true;
5✔
791
      }
5✔
792
      /* ttl */
793
      dpm.skipBytes(4);
3✔
794
      dpm.skipRData();
3✔
795
    }
3✔
796
  }
5✔
797
  catch (...) {
5✔
798
  }
×
799

UNCOV
800
  return false;
×
801
}
5✔
802

803
static int rewritePacketWithoutRecordTypes(const PacketBuffer& initialPacket, PacketBuffer& newContent, const std::unordered_set<QType>& qtypes)
804
{
5✔
805
  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};
5✔
806

807
  if (initialPacket.size() < sizeof(dnsheader)) {
5!
808
    return EINVAL;
×
809
  }
×
810
  try {
5✔
811
    const dnsheader_aligned dh(initialPacket.data());
5✔
812

813
    if (ntohs(dh->qdcount) == 0)
5!
814
      return ENOENT;
×
815
    auto packetView = std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size());
5✔
816

817
    PacketReader pr(packetView);
5✔
818

819
    size_t idx = 0;
5✔
820
    DNSName rrname;
5✔
821
    uint16_t qdcount = ntohs(dh->qdcount);
5✔
822
    uint16_t ancount = ntohs(dh->ancount);
5✔
823
    uint16_t nscount = ntohs(dh->nscount);
5✔
824
    uint16_t arcount = ntohs(dh->arcount);
5✔
825
    uint16_t rrtype;
5✔
826
    uint16_t rrclass;
5✔
827
    string blob;
5✔
828
    struct dnsrecordheader ah;
5✔
829

830
    rrname = pr.getName();
5✔
831
    rrtype = pr.get16BitInt();
5✔
832
    rrclass = pr.get16BitInt();
5✔
833

834
    GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh->opcode);
5✔
835
    pw.getHeader()->id=dh->id;
5✔
836
    pw.getHeader()->qr=dh->qr;
5✔
837
    pw.getHeader()->aa=dh->aa;
5✔
838
    pw.getHeader()->tc=dh->tc;
5✔
839
    pw.getHeader()->rd=dh->rd;
5✔
840
    pw.getHeader()->ra=dh->ra;
5✔
841
    pw.getHeader()->ad=dh->ad;
5✔
842
    pw.getHeader()->cd=dh->cd;
5✔
843
    pw.getHeader()->rcode=dh->rcode;
5✔
844

845
    /* consume remaining qd if any */
846
    if (qdcount > 1) {
5!
847
      for(idx = 1; idx < qdcount; idx++) {
×
848
        rrname = pr.getName();
×
849
        rrtype = pr.get16BitInt();
×
850
        rrclass = pr.get16BitInt();
×
851
        (void) rrtype;
×
852
        (void) rrclass;
×
853
      }
×
854
    }
×
855

856
    /* copy AN */
857
    for (idx = 0; idx < ancount; idx++) {
14✔
858
      rrname = pr.getName();
9✔
859
      pr.getDnsrecordheader(ah);
9✔
860
      pr.xfrBlob(blob);
9✔
861

862
      if (qtypes.find(ah.d_type) == qtypes.end()) {
9✔
863
        // if this is not a safe type
864
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
3!
865
          // "unsafe" types might countain compressed data, so cancel rewrite
866
          newContent.clear();
×
867
          return EIO;
×
868
        }
×
869
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
3✔
870
        pw.xfrBlob(blob);
3✔
871
      }
3✔
872
    }
9✔
873

874
    /* copy NS */
875
    for (idx = 0; idx < nscount; idx++) {
5!
876
      rrname = pr.getName();
×
877
      pr.getDnsrecordheader(ah);
×
878
      pr.xfrBlob(blob);
×
879

880
      if (qtypes.find(ah.d_type) == qtypes.end()) {
×
881
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
×
882
          // "unsafe" types might countain compressed data, so cancel rewrite
883
          newContent.clear();
×
884
          return EIO;
×
885
        }
×
886
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
×
887
        pw.xfrBlob(blob);
×
888
      }
×
889
    }
×
890
    /* copy AR */
891
    for (idx = 0; idx < arcount; idx++) {
15✔
892
      rrname = pr.getName();
11✔
893
      pr.getDnsrecordheader(ah);
11✔
894
      pr.xfrBlob(blob);
11✔
895

896
      if (qtypes.find(ah.d_type) == qtypes.end()) {
11✔
897
        if (safeTypes.find(ah.d_type) == safeTypes.end()) {
8✔
898
          // "unsafe" types might countain compressed data, so cancel rewrite
899
          newContent.clear();
1✔
900
          return EIO;
1✔
901
        }
1✔
902
        pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
7✔
903
        pw.xfrBlob(blob);
7✔
904
      }
7✔
905
    }
11✔
906
    pw.commit();
4✔
907

908
  }
4✔
909
  catch (...)
5✔
910
  {
5✔
911
    newContent.clear();
×
912
    return EIO;
×
913
  }
×
914
  return 0;
4✔
915
}
5✔
916

917
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes)
918
{
5✔
919
  return clearDNSPacketRecordTypes(reinterpret_cast<PacketBuffer&>(packet), qtypes);
5✔
920
}
5✔
921

922
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
923
{
5✔
924
  if (!checkIfPacketContainsRecords(packet, qtypes)) {
5!
UNCOV
925
    return;
×
UNCOV
926
  }
×
927

928
  PacketBuffer newContent;
5✔
929

930
  auto result = rewritePacketWithoutRecordTypes(packet, newContent, qtypes);
5✔
931
  if (!result) {
5✔
932
    packet = std::move(newContent);
4✔
933
  }
4✔
934
}
5✔
935

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

948
    for (uint64_t rec = 0; rec < dqcount; ++rec) {
4,768✔
949
      dpm.skipDomainName();
2,384✔
950
      /* type and class */
951
      dpm.skipBytes(4);
2,384✔
952
    }
2,384✔
953

954
    for(uint64_t rec = 0; rec < numrecords; ++rec) {
10,713✔
955
      dpm.skipDomainName();
8,329✔
956

957
      uint16_t dnstype = dpm.get16BitInt();
8,329✔
958
      /* class */
959
      dpm.skipBytes(2);
8,329✔
960

961
      if (dnstype != QType::OPT) { // not aging that one with a stick
8,337✔
962
        dpm.decreaseAndSkip32BitInt(seconds);
8,206✔
963
      } else {
2,147,483,972✔
964
        dpm.skipBytes(4);
2,147,483,766✔
965
      }
2,147,483,766✔
966
      dpm.skipRData();
8,329✔
967
    }
8,329✔
968
  }
2,384✔
969
  catch(...) {
2,384✔
970
  }
1✔
971
}
2,384✔
972

973
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned& aligned_dh)
974
{
2,390✔
975
  ageDNSPacket(packet.data(), packet.length(), seconds, aligned_dh);
2,390✔
976
}
2,390✔
977

978
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
979
{
10✔
980
  uint32_t result = std::numeric_limits<uint32_t>::max();
10✔
981
  if(length < sizeof(dnsheader)) {
10!
982
    return result;
×
983
  }
×
984
  try
10✔
985
  {
10✔
986
    const dnsheader_aligned dh(packet);
10✔
987
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
10✔
988

989
    const uint16_t qdcount = ntohs(dh->qdcount);
10✔
990
    for(size_t n = 0; n < qdcount; ++n) {
20✔
991
      dpm.skipDomainName();
10✔
992
      /* type and class */
993
      dpm.skipBytes(4);
10✔
994
    }
10✔
995
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
10✔
996
    for(size_t n = 0; n < numrecords; ++n) {
28✔
997
      dpm.skipDomainName();
19✔
998
      const uint16_t dnstype = dpm.get16BitInt();
19✔
999
      /* class */
1000
      const uint16_t dnsclass = dpm.get16BitInt();
19✔
1001

1002
      if(dnstype == QType::OPT) {
19✔
1003
        break;
1✔
1004
      }
1✔
1005

1006
      /* report it if we see a SOA record in the AUTHORITY section */
1007
      if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
18!
1008
        *seenAuthSOA = true;
2✔
1009
      }
2✔
1010

1011
      const uint32_t ttl = dpm.get32BitInt();
18✔
1012
      if (result > ttl) {
18✔
1013
        result = ttl;
11✔
1014
      }
11✔
1015

1016
      dpm.skipRData();
18✔
1017
    }
18✔
1018
  }
10✔
1019
  catch(...)
10✔
1020
  {
10✔
1021
  }
1✔
1022
  return result;
10✔
1023
}
10✔
1024

1025
uint32_t getDNSPacketLength(const char* packet, size_t length)
1026
{
4✔
1027
  uint32_t result = length;
4✔
1028
  if(length < sizeof(dnsheader)) {
4!
1029
    return result;
×
1030
  }
×
1031
  try
4✔
1032
  {
4✔
1033
    const dnsheader_aligned dh(packet);
4✔
1034
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
4✔
1035

1036
    const uint16_t qdcount = ntohs(dh->qdcount);
4✔
1037
    for(size_t n = 0; n < qdcount; ++n) {
8✔
1038
      dpm.skipDomainName();
4✔
1039
      /* type and class */
1040
      dpm.skipBytes(4);
4✔
1041
    }
4✔
1042
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
4✔
1043
    for(size_t n = 0; n < numrecords; ++n) {
16✔
1044
      dpm.skipDomainName();
12✔
1045
      /* type (2), class (2) and ttl (4) */
1046
      dpm.skipBytes(8);
12✔
1047
      dpm.skipRData();
12✔
1048
    }
12✔
1049
    result = dpm.getOffset();
4✔
1050
  }
4✔
1051
  catch(...)
4✔
1052
  {
4✔
1053
  }
1✔
1054
  return result;
4✔
1055
}
4✔
1056

1057
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1058
{
36✔
1059
  uint16_t result = 0;
36✔
1060
  if(length < sizeof(dnsheader)) {
36!
1061
    return result;
×
1062
  }
×
1063
  try
36✔
1064
  {
36✔
1065
    const dnsheader_aligned dh(packet);
36✔
1066
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
36✔
1067

1068
    const uint16_t qdcount = ntohs(dh->qdcount);
36✔
1069
    for(size_t n = 0; n < qdcount; ++n) {
72✔
1070
      dpm.skipDomainName();
36✔
1071
      if (section == 0) {
36✔
1072
        uint16_t dnstype = dpm.get16BitInt();
2✔
1073
        if (dnstype == type) {
2✔
1074
          result++;
1✔
1075
        }
1✔
1076
        /* class */
1077
        dpm.skipBytes(2);
2✔
1078
      } else {
34✔
1079
        /* type and class */
1080
        dpm.skipBytes(4);
34✔
1081
      }
34✔
1082
    }
36✔
1083
    const uint16_t ancount = ntohs(dh->ancount);
36✔
1084
    for(size_t n = 0; n < ancount; ++n) {
80✔
1085
      dpm.skipDomainName();
44✔
1086
      if (section == 1) {
44✔
1087
        uint16_t dnstype = dpm.get16BitInt();
22✔
1088
        if (dnstype == type) {
22✔
1089
          result++;
11✔
1090
        }
11✔
1091
        /* class */
1092
        dpm.skipBytes(2);
22✔
1093
      } else {
22✔
1094
        /* type and class */
1095
        dpm.skipBytes(4);
22✔
1096
      }
22✔
1097
      /* ttl */
1098
      dpm.skipBytes(4);
44✔
1099
      dpm.skipRData();
44✔
1100
    }
44✔
1101
    const uint16_t nscount = ntohs(dh->nscount);
36✔
1102
    for(size_t n = 0; n < nscount; ++n) {
45✔
1103
      dpm.skipDomainName();
9✔
1104
      if (section == 2) {
9✔
1105
        uint16_t dnstype = dpm.get16BitInt();
2✔
1106
        if (dnstype == type) {
2✔
1107
          result++;
1✔
1108
        }
1✔
1109
        /* class */
1110
        dpm.skipBytes(2);
2✔
1111
      } else {
7✔
1112
        /* type and class */
1113
        dpm.skipBytes(4);
7✔
1114
      }
7✔
1115
      /* ttl */
1116
      dpm.skipBytes(4);
9✔
1117
      dpm.skipRData();
9✔
1118
    }
9✔
1119
    const uint16_t arcount = ntohs(dh->arcount);
36✔
1120
    for(size_t n = 0; n < arcount; ++n) {
110✔
1121
      dpm.skipDomainName();
74✔
1122
      if (section == 3) {
74✔
1123
        uint16_t dnstype = dpm.get16BitInt();
28✔
1124
        if (dnstype == type) {
28✔
1125
          result++;
9✔
1126
        }
9✔
1127
        /* class */
1128
        dpm.skipBytes(2);
28✔
1129
      } else {
46✔
1130
        /* type and class */
1131
        dpm.skipBytes(4);
46✔
1132
      }
46✔
1133
      /* ttl */
1134
      dpm.skipBytes(4);
74✔
1135
      dpm.skipRData();
74✔
1136
    }
74✔
1137
  }
36✔
1138
  catch(...)
36✔
1139
  {
36✔
1140
  }
×
1141
  return result;
36✔
1142
}
36✔
1143

1144
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
UNCOV
1145
{
×
UNCOV
1146
  if (length < sizeof(dnsheader)) {
×
1147
    return false;
×
1148
  }
×
1149

UNCOV
1150
  *payloadSize = 0;
×
UNCOV
1151
  *z = 0;
×
1152

UNCOV
1153
  try
×
UNCOV
1154
  {
×
UNCOV
1155
    const dnsheader_aligned dh(packet);
×
UNCOV
1156
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
×
1157

UNCOV
1158
    const uint16_t qdcount = ntohs(dh->qdcount);
×
UNCOV
1159
    for(size_t n = 0; n < qdcount; ++n) {
×
UNCOV
1160
      dpm.skipDomainName();
×
1161
      /* type and class */
UNCOV
1162
      dpm.skipBytes(4);
×
UNCOV
1163
    }
×
UNCOV
1164
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
×
UNCOV
1165
    for(size_t n = 0; n < numrecords; ++n) {
×
UNCOV
1166
      dpm.skipDomainName();
×
UNCOV
1167
      const uint16_t dnstype = dpm.get16BitInt();
×
UNCOV
1168
      const uint16_t dnsclass = dpm.get16BitInt();
×
1169

UNCOV
1170
      if(dnstype == QType::OPT) {
×
1171
        /* skip extended rcode and version */
UNCOV
1172
        dpm.skipBytes(2);
×
UNCOV
1173
        *z = dpm.get16BitInt();
×
UNCOV
1174
        *payloadSize = dnsclass;
×
UNCOV
1175
        return true;
×
UNCOV
1176
      }
×
1177

1178
      /* TTL */
UNCOV
1179
      dpm.skipBytes(4);
×
UNCOV
1180
      dpm.skipRData();
×
UNCOV
1181
    }
×
UNCOV
1182
  }
×
UNCOV
1183
  catch(...)
×
UNCOV
1184
  {
×
UNCOV
1185
  }
×
1186

UNCOV
1187
  return false;
×
UNCOV
1188
}
×
1189

1190
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
UNCOV
1191
{
×
UNCOV
1192
  if (packet.size() < sizeof(dnsheader)) {
×
1193
    return false;
×
1194
  }
×
1195

UNCOV
1196
  try
×
UNCOV
1197
  {
×
UNCOV
1198
    const dnsheader_aligned dh(packet.data());
×
UNCOV
1199
    uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
×
UNCOV
1200
    PacketReader reader(packet);
×
1201

UNCOV
1202
    uint64_t n;
×
UNCOV
1203
    for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
×
UNCOV
1204
      (void) reader.getName();
×
1205
      /* type and class */
UNCOV
1206
      reader.skip(4);
×
UNCOV
1207
    }
×
1208

UNCOV
1209
    for (n = 0; n < numrecords; ++n) {
×
UNCOV
1210
      (void) reader.getName();
×
1211

UNCOV
1212
      uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
×
UNCOV
1213
      uint16_t dnstype = reader.get16BitInt();
×
UNCOV
1214
      uint16_t dnsclass = reader.get16BitInt();
×
1215

UNCOV
1216
      if (dnstype == QType::OPT) {
×
1217
        // not getting near that one with a stick
UNCOV
1218
        break;
×
UNCOV
1219
      }
×
1220

UNCOV
1221
      uint32_t dnsttl = reader.get32BitInt();
×
UNCOV
1222
      uint16_t contentLength = reader.get16BitInt();
×
UNCOV
1223
      uint16_t pos = reader.getPosition();
×
1224

UNCOV
1225
      bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
×
UNCOV
1226
      if (done) {
×
UNCOV
1227
        return true;
×
UNCOV
1228
      }
×
1229

UNCOV
1230
      reader.skip(contentLength);
×
UNCOV
1231
    }
×
UNCOV
1232
  }
×
UNCOV
1233
  catch (...) {
×
1234
    return false;
×
1235
  }
×
1236

UNCOV
1237
  return true;
×
UNCOV
1238
}
×
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