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

PowerDNS / pdns / 17765900968

16 Sep 2025 12:33PM UTC coverage: 65.987% (-0.04%) from 66.029%
17765900968

Pull #16108

github

web-flow
Merge 4059c5fe8 into 2e297650d
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42424 of 93030 branches covered (45.6%)

Branch coverage included in aggregate %.

9 of 135 new or added lines in 6 files covered. (6.67%)

34 existing lines in 8 files now uncovered.

128910 of 166619 relevant lines covered (77.37%)

5500581.66 hits per line

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

78.51
/pdns/dnsparser.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#include "dnsparser.hh"
23
#include "dnswriter.hh"
24
#include <boost/algorithm/string.hpp>
25
#include <boost/format.hpp>
26

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

151
  return i->second(content);
1,681,652✔
152
}
1,682,057✔
153

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

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

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

169
  return i->second(dr, pr);
2,454,874✔
170
}
2,456,426✔
171

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

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

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

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

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

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

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

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

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

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

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

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

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

249
  unsigned int n=0;
1,416,441✔
250

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

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

262
    struct dnsrecordheader ah;
1,416,441✔
263
    vector<unsigned char> record;
1,416,441✔
264
    bool seenTSIG = false;
1,416,441✔
265
    validPacket=true;
1,416,441✔
266
    d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
1,416,441✔
267
    for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
3,721,339✔
268
      DNSRecord dr;
2,304,900✔
269

270
      if(n < d_header.ancount)
2,304,900✔
271
        dr.d_place=DNSResourceRecord::ANSWER;
2,002,570✔
272
      else if(n < d_header.ancount + d_header.nscount)
302,330✔
273
        dr.d_place=DNSResourceRecord::AUTHORITY;
94,855✔
274
      else
207,475✔
275
        dr.d_place=DNSResourceRecord::ADDITIONAL;
207,475✔
276

277
      unsigned int recordStartPos=pr.getPosition();
2,304,900✔
278

279
      DNSName name=pr.getName();
2,304,900✔
280

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

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

289
      if (query &&
2,304,900✔
290
          !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section
2,304,900!
291
          (dr.d_place == DNSResourceRecord::ANSWER || dr.d_place == DNSResourceRecord::AUTHORITY || (dr.d_type != QType::OPT && dr.d_type != QType::TSIG && dr.d_type != QType::SIG && dr.d_type != QType::TKEY) || ((dr.d_type == QType::TSIG || dr.d_type == QType::SIG || dr.d_type == QType::TKEY) && dr.d_class != QClass::ANY))) {
2,304,900!
292
//        cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
293
        dr.setContent(std::make_shared<UnknownRecordContent>(dr, pr));
6,658✔
294
      }
6,658✔
295
      else {
2,298,242✔
296
//        cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
297
        dr.setContent(DNSRecordContent::make(dr, pr, d_header.opcode));
2,298,242✔
298
      }
2,298,242✔
299

300
      if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
2,304,900✔
301
        throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has an unexpected record ("+std::to_string(dr.d_type)+") after a TSIG one.");
2✔
302
      }
2✔
303

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

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

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

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

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

354
  return false;
×
355
}
×
356

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

361
  for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
27,100,283✔
362
    p[n] = d_content.at(d_pos++);
24,636,410✔
363
  }
24,636,410✔
364

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

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

374

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

384
  dest.resize(len);
5,984✔
385

386
  for (uint16_t n = 0; n < len; ++n) {
10,298,203✔
387
    dest.at(n) = d_content.at(d_pos++);
10,292,219✔
388
  }
10,292,219✔
389
}
5,984✔
390

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

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

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

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

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

437
  return ret;
3,271,750✔
438
}
3,271,750✔
439

440

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

448
  return ret;
4,632,566✔
449
}
4,632,566✔
450

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

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

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

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

480
  for (char letter : name) {
14,452,203✔
481
    const unsigned uch = static_cast<unsigned char>(letter);
14,452,203✔
482
    if (uch >= 127 || uch < 32) {
14,452,203✔
483
      snprintf(ebuf.data(), ebuf.size(), "\\%03u", uch);
2,525✔
484
      ret += ebuf.data();
2,525✔
485
    }
2,525✔
486
    else if (letter == '"' || letter == '\\'){
14,449,678✔
487
      ret += '\\';
1,102✔
488
      ret += letter;
1,102✔
489
    }
1,102✔
490
    else {
14,448,576✔
491
      ret += letter;
14,448,576✔
492
    }
14,448,576✔
493
  }
14,452,203✔
494
  return ret;
138,831✔
495
}
138,831✔
496

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

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

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

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

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

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

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

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

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

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

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

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

586
    d_pos += length;
534,419✔
587
  }
534,419✔
588
  else {
2,610✔
589
    blob.clear();
2,610✔
590
  }
2,610✔
591
}
537,029✔
592

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

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

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

700

701
void PacketReader::xfrHexBlob(string& blob, bool /* keepReading */)
702
{
6,007✔
703
  xfrBlob(blob);
6,007✔
704
}
6,007✔
705

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

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

746
    uint64_t n;
31✔
747
    for(n=0; n < ntohs(dh.qdcount) ; ++n) {
62✔
748
      dpm.skipDomainName();
31✔
749
      /* type and class */
750
      dpm.skipBytes(4);
31✔
751
    }
31✔
752

753
    for(n=0; n < numrecords; ++n) {
80✔
754
      dpm.skipDomainName();
49✔
755

756
      uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
49!
757
      uint16_t dnstype = dpm.get16BitInt();
49✔
758
      uint16_t dnsclass = dpm.get16BitInt();
49✔
759

760
      if(dnstype == QType::OPT) // not getting near that one with a stick
49!
761
        break;
×
762

763
      uint32_t dnsttl = dpm.get32BitInt();
49✔
764
      uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
49✔
765
      if (newttl) {
49✔
766
        dpm.rewindBytes(sizeof(newttl));
22✔
767
        dpm.setAndSkip32BitInt(newttl);
22✔
768
      }
22✔
769
      dpm.skipRData();
49✔
770
    }
49✔
771
  }
31✔
772
  catch(...)
31✔
773
  {
31✔
774
    return;
3✔
775
  }
3✔
776
}
31✔
777

778
static bool checkIfPacketContainsRecords(const PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
779
{
22✔
780
  auto length = packet.size();
22✔
781
  if (length < sizeof(dnsheader)) {
22!
782
    return false;
×
783
  }
×
784

785
  try {
22✔
786
    const dnsheader_aligned dh(packet.data());
22✔
787
    DNSPacketMangler dpm(const_cast<char*>(reinterpret_cast<const char*>(packet.data())), length);
22✔
788

789
    const uint16_t qdcount = ntohs(dh->qdcount);
22✔
790
    for (size_t n = 0; n < qdcount; ++n) {
44✔
791
      dpm.skipDomainName();
22✔
792
      /* type and class */
793
      dpm.skipBytes(4);
22✔
794
    }
22✔
795
    const size_t recordsCount = static_cast<size_t>(ntohs(dh->ancount)) + ntohs(dh->nscount) + ntohs(dh->arcount);
22✔
796
    for (size_t n = 0; n < recordsCount; ++n) {
35✔
797
      dpm.skipDomainName();
33✔
798
      uint16_t dnstype = dpm.get16BitInt();
33✔
799
      uint16_t dnsclass = dpm.get16BitInt();
33✔
800
      if (dnsclass == QClass::IN && qtypes.count(dnstype) > 0) {
33!
801
        return true;
20✔
802
      }
20✔
803
      /* ttl */
804
      dpm.skipBytes(4);
13✔
805
      dpm.skipRData();
13✔
806
    }
13✔
807
  }
22✔
808
  catch (...) {
22✔
809
  }
×
810

811
  return false;
2✔
812
}
22✔
813

814
static int rewritePacketWithoutRecordTypes(const PacketBuffer& initialPacket, PacketBuffer& newContent, const std::unordered_set<QType>& qtypes)
815
{
20✔
816
  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};
20✔
817

818
  if (initialPacket.size() < sizeof(dnsheader)) {
20!
819
    return EINVAL;
×
820
  }
×
821
  try {
20✔
822
    const dnsheader_aligned dh(initialPacket.data());
20✔
823

824
    if (ntohs(dh->qdcount) == 0)
20!
825
      return ENOENT;
×
826
    auto packetView = std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size());
20✔
827

828
    PacketReader pr(packetView);
20✔
829

830
    size_t idx = 0;
20✔
831
    DNSName rrname;
20✔
832
    uint16_t qdcount = ntohs(dh->qdcount);
20✔
833
    uint16_t ancount = ntohs(dh->ancount);
20✔
834
    uint16_t nscount = ntohs(dh->nscount);
20✔
835
    uint16_t arcount = ntohs(dh->arcount);
20✔
836
    uint16_t rrtype;
20✔
837
    uint16_t rrclass;
20✔
838
    string blob;
20✔
839
    struct dnsrecordheader ah;
20✔
840

841
    rrname = pr.getName();
20✔
842
    rrtype = pr.get16BitInt();
20✔
843
    rrclass = pr.get16BitInt();
20✔
844

845
    GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh->opcode);
20✔
846
    pw.getHeader()->id=dh->id;
20✔
847
    pw.getHeader()->qr=dh->qr;
20✔
848
    pw.getHeader()->aa=dh->aa;
20✔
849
    pw.getHeader()->tc=dh->tc;
20✔
850
    pw.getHeader()->rd=dh->rd;
20✔
851
    pw.getHeader()->ra=dh->ra;
20✔
852
    pw.getHeader()->ad=dh->ad;
20✔
853
    pw.getHeader()->cd=dh->cd;
20✔
854
    pw.getHeader()->rcode=dh->rcode;
20✔
855

856
    /* consume remaining qd if any */
857
    if (qdcount > 1) {
20!
858
      for(idx = 1; idx < qdcount; idx++) {
×
859
        rrname = pr.getName();
×
860
        rrtype = pr.get16BitInt();
×
861
        rrclass = pr.get16BitInt();
×
862
        (void) rrtype;
×
863
        (void) rrclass;
×
864
      }
×
865
    }
×
866

867
    /* copy AN */
868
    for (idx = 0; idx < ancount; idx++) {
60✔
869
      rrname = pr.getName();
40✔
870
      pr.getDnsrecordheader(ah);
40✔
871
      pr.xfrBlob(blob);
40✔
872

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

885
    /* copy NS */
886
    for (idx = 0; idx < nscount; idx++) {
20!
887
      rrname = pr.getName();
×
888
      pr.getDnsrecordheader(ah);
×
889
      pr.xfrBlob(blob);
×
890

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

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

919
  }
17✔
920
  catch (...)
20✔
921
  {
20✔
922
    newContent.clear();
×
923
    return EIO;
×
924
  }
×
925
  return 0;
17✔
926
}
20✔
927

928
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes)
929
{
15✔
930
  return clearDNSPacketRecordTypes(reinterpret_cast<PacketBuffer&>(packet), qtypes);
15✔
931
}
15✔
932

933
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
934
{
22✔
935
  if (!checkIfPacketContainsRecords(packet, qtypes)) {
22✔
936
    return;
2✔
937
  }
2✔
938

939
  PacketBuffer newContent;
20✔
940

941
  auto result = rewritePacketWithoutRecordTypes(packet, newContent, qtypes);
20✔
942
  if (!result) {
20✔
943
    packet = std::move(newContent);
17✔
944
  }
17✔
945
}
20✔
946

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

959
    for (uint64_t rec = 0; rec < dqcount; ++rec) {
1,671,569✔
960
      dpm.skipDomainName();
844,459✔
961
      /* type and class */
962
      dpm.skipBytes(4);
844,459✔
963
    }
844,459✔
964

965
    for(uint64_t rec = 0; rec < numrecords; ++rec) {
1,678,331✔
966
      dpm.skipDomainName();
851,221✔
967

968
      uint16_t dnstype = dpm.get16BitInt();
851,221✔
969
      /* class */
970
      dpm.skipBytes(2);
851,221✔
971

972
      if (dnstype != QType::OPT) { // not aging that one with a stick
851,313✔
973
        dpm.decreaseAndSkip32BitInt(seconds);
850,445✔
974
      } else {
4,295,382,531✔
975
        dpm.skipBytes(4);
4,294,968,155✔
976
      }
4,294,968,155✔
977
      dpm.skipRData();
851,221✔
978
    }
851,221✔
979
  }
827,110✔
980
  catch(...) {
827,110✔
981
  }
3✔
982
}
827,110✔
983

984
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned& aligned_dh)
985
{
2,928✔
986
  ageDNSPacket(packet.data(), packet.length(), seconds, aligned_dh);
2,928✔
987
}
2,928✔
988

989
void shuffleDNSPacket(char* packet, size_t length, const dnsheader_aligned& aligned_dh)
NEW
990
{
×
NEW
991
  if (length < sizeof(dnsheader)) {
×
NEW
992
    return;
×
NEW
993
  }
×
NEW
994
  try {
×
NEW
995
    DNSPacketMangler dpm(packet, length);
×
NEW
996
    const dnsheader* dhp = aligned_dh.get();
×
NEW
997
    const uint16_t ancount = ntohs(dhp->ancount);
×
NEW
998
    if (ancount == 1) {
×
999
      // quick exit, nothing to shuffle
NEW
1000
      return;
×
NEW
1001
    }
×
1002

NEW
1003
    const uint16_t qdcount = ntohs(dhp->qdcount);
×
1004

NEW
1005
    for(size_t iter = 0; iter < qdcount; ++iter) {
×
NEW
1006
      dpm.skipDomainName();
×
1007
      /* type and class */
NEW
1008
      dpm.skipBytes(4);
×
NEW
1009
    }
×
1010

1011
    // Shuffle only if here are only A or AAAA records in answer section,
1012
    // with the exception of non-A records at the start,
1013
    // and only if all their pointers are pointing to position the A records.
1014
    // As we shuffle by swapping memory directly, this is preventing
1015
    // breaking compression pointers.
1016

NEW
1017
    if (ntohs(dhp->nscount) != 0) {
×
1018
      // Authority is non-empty - also don't shuffle.
NEW
1019
      return;
×
NEW
1020
    }
×
1021

NEW
1022
    std::vector<uint32_t> indexes;
×
NEW
1023
    indexes.reserve(ancount);
×
1024

NEW
1025
    for(size_t iter = 0; iter < ancount; ++iter) {
×
NEW
1026
      const uint32_t start = dpm.getOffset();
×
1027

NEW
1028
      const std::optional<size_t> domain_pointer = dpm.skipDomainName();
×
NEW
1029
      const uint16_t dnstype = dpm.get16BitInt();
×
1030

NEW
1031
      if (!(dnstype == QType::A || dnstype == QType::AAAA)) {
×
NEW
1032
        if (!indexes.empty()) {
×
1033
          // non-As after As started - bailing out
NEW
1034
          return;
×
NEW
1035
        }
×
NEW
1036
      } else {
×
NEW
1037
        indexes.push_back(start);
×
NEW
1038
        const bool pointer_before_as = domain_pointer.value_or(0) < indexes[0];
×
NEW
1039
        if (!pointer_before_as) {
×
1040
          // pointers could break by shuffling - bailing out
NEW
1041
          return;
×
NEW
1042
        }
×
NEW
1043
      }
×
1044

1045
      /* class */
NEW
1046
      dpm.skipBytes(2);
×
1047

1048
      /* ttl */
NEW
1049
      dpm.skipBytes(4);
×
NEW
1050
      dpm.skipRData();
×
NEW
1051
    }
×
1052

NEW
1053
    indexes.push_back(dpm.getOffset());
×
1054

1055
    // also check additional section
NEW
1056
    const uint16_t arcount = ntohs(dhp->arcount);
×
NEW
1057
    for(size_t iter = 0; iter < arcount; ++iter) {
×
NEW
1058
      const std::optional<size_t> domain_pointer = dpm.skipDomainName();
×
NEW
1059
      const bool pointer_before_as = domain_pointer.value_or(0) < indexes[0];
×
NEW
1060
      if (!pointer_before_as) {
×
1061
        // pointers could break by shuffling - bailing out
NEW
1062
        return;
×
NEW
1063
      }
×
NEW
1064
      const uint16_t dnstype = dpm.get16BitInt();
×
NEW
1065
      if (!(dnstype == QType::OPT || dnstype == QType::RRSIG)){
×
1066
        // anything else than OPT - might potentionally have pointers in rdata
NEW
1067
        return;
×
NEW
1068
      }
×
1069

1070
      /* type and class */
NEW
1071
      dpm.skipBytes(2);
×
1072

1073
      /* ttl */
NEW
1074
      dpm.skipBytes(4);
×
NEW
1075
      dpm.skipRData();
×
NEW
1076
    }
×
1077

NEW
1078
    if (indexes.size() > 2) {
×
NEW
1079
      using uid = std::uniform_int_distribution<std::vector<uint32_t>::size_type>;
×
NEW
1080
      uid dist;
×
1081

NEW
1082
      pdns::dns_random_engine randomEngine;
×
NEW
1083
      for (auto swapped = indexes.size() - 2; swapped > 0; --swapped) {
×
NEW
1084
        auto swapped_with = dist(randomEngine, uid::param_type(0, swapped));
×
NEW
1085
        if (swapped != swapped_with) {
×
NEW
1086
          auto start_first = indexes[swapped_with];
×
NEW
1087
          auto end_first = indexes[swapped_with+1];
×
NEW
1088
          auto start_second = indexes[swapped];
×
NEW
1089
          auto end_second = indexes[swapped+1];
×
NEW
1090
          auto diff = dpm.swapInPlace(start_first, end_first, start_second, end_second);
×
NEW
1091
          if (diff != 0) {
×
NEW
1092
            for (auto moved = swapped_with+1; moved<swapped; moved++) {
×
NEW
1093
              indexes[moved] = indexes[moved]+diff;
×
NEW
1094
            }
×
NEW
1095
          }
×
NEW
1096
        }
×
NEW
1097
      }
×
NEW
1098
    }
×
NEW
1099
  }
×
NEW
1100
  catch(...) {
×
NEW
1101
  }
×
NEW
1102
}
×
1103

1104
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
1105
{
1,188,382✔
1106
  uint32_t result = std::numeric_limits<uint32_t>::max();
1,188,382✔
1107
  if(length < sizeof(dnsheader)) {
1,188,382!
1108
    return result;
×
1109
  }
×
1110
  try
1,188,382✔
1111
  {
1,188,382✔
1112
    const dnsheader_aligned dh(packet);
1,188,382✔
1113
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1,188,382✔
1114

1115
    const uint16_t qdcount = ntohs(dh->qdcount);
1,188,382✔
1116
    for(size_t n = 0; n < qdcount; ++n) {
2,360,711✔
1117
      dpm.skipDomainName();
1,172,329✔
1118
      /* type and class */
1119
      dpm.skipBytes(4);
1,172,329✔
1120
    }
1,172,329✔
1121
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1,188,382✔
1122
    for(size_t n = 0; n < numrecords; ++n) {
2,372,620✔
1123
      dpm.skipDomainName();
1,184,332✔
1124
      const uint16_t dnstype = dpm.get16BitInt();
1,184,332✔
1125
      /* class */
1126
      const uint16_t dnsclass = dpm.get16BitInt();
1,184,332✔
1127

1128
      if(dnstype == QType::OPT) {
1,184,332✔
1129
        break;
94✔
1130
      }
94✔
1131

1132
      /* report it if we see a SOA record in the AUTHORITY section */
1133
      if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
1,184,238!
1134
        *seenAuthSOA = true;
16✔
1135
      }
16✔
1136

1137
      const uint32_t ttl = dpm.get32BitInt();
1,184,238✔
1138
      result = std::min(result, ttl);
1,184,238✔
1139

1140
      dpm.skipRData();
1,184,238✔
1141
    }
1,184,238✔
1142
  }
1,188,382✔
1143
  catch(...)
1,188,382✔
1144
  {
1,188,382✔
1145
  }
6✔
1146
  return result;
1,188,382✔
1147
}
1,188,382✔
1148

1149
uint32_t getDNSPacketLength(const char* packet, size_t length)
1150
{
48✔
1151
  uint32_t result = length;
48✔
1152
  if(length < sizeof(dnsheader)) {
48!
1153
    return result;
×
1154
  }
×
1155
  try
48✔
1156
  {
48✔
1157
    const dnsheader_aligned dh(packet);
48✔
1158
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
48✔
1159

1160
    const uint16_t qdcount = ntohs(dh->qdcount);
48✔
1161
    for(size_t n = 0; n < qdcount; ++n) {
96✔
1162
      dpm.skipDomainName();
48✔
1163
      /* type and class */
1164
      dpm.skipBytes(4);
48✔
1165
    }
48✔
1166
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
48✔
1167
    for(size_t n = 0; n < numrecords; ++n) {
90✔
1168
      dpm.skipDomainName();
42✔
1169
      /* type (2), class (2) and ttl (4) */
1170
      dpm.skipBytes(8);
42✔
1171
      dpm.skipRData();
42✔
1172
    }
42✔
1173
    result = dpm.getOffset();
48✔
1174
  }
48✔
1175
  catch(...)
48✔
1176
  {
48✔
1177
  }
3✔
1178
  return result;
48✔
1179
}
48✔
1180

1181
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1182
{
114✔
1183
  uint16_t result = 0;
114✔
1184
  if(length < sizeof(dnsheader)) {
114!
1185
    return result;
×
1186
  }
×
1187
  try
114✔
1188
  {
114✔
1189
    const dnsheader_aligned dh(packet);
114✔
1190
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
114✔
1191

1192
    const uint16_t qdcount = ntohs(dh->qdcount);
114✔
1193
    for(size_t n = 0; n < qdcount; ++n) {
228✔
1194
      dpm.skipDomainName();
114✔
1195
      if (section == 0) {
114✔
1196
        uint16_t dnstype = dpm.get16BitInt();
6✔
1197
        if (dnstype == type) {
6✔
1198
          result++;
3✔
1199
        }
3✔
1200
        /* class */
1201
        dpm.skipBytes(2);
6✔
1202
      } else {
108✔
1203
        /* type and class */
1204
        dpm.skipBytes(4);
108✔
1205
      }
108✔
1206
    }
114✔
1207
    const uint16_t ancount = ntohs(dh->ancount);
114✔
1208
    for(size_t n = 0; n < ancount; ++n) {
246✔
1209
      dpm.skipDomainName();
132✔
1210
      if (section == 1) {
132✔
1211
        uint16_t dnstype = dpm.get16BitInt();
66✔
1212
        if (dnstype == type) {
66✔
1213
          result++;
33✔
1214
        }
33✔
1215
        /* class */
1216
        dpm.skipBytes(2);
66✔
1217
      } else {
66✔
1218
        /* type and class */
1219
        dpm.skipBytes(4);
66✔
1220
      }
66✔
1221
      /* ttl */
1222
      dpm.skipBytes(4);
132✔
1223
      dpm.skipRData();
132✔
1224
    }
132✔
1225
    const uint16_t nscount = ntohs(dh->nscount);
114✔
1226
    for(size_t n = 0; n < nscount; ++n) {
141✔
1227
      dpm.skipDomainName();
27✔
1228
      if (section == 2) {
27✔
1229
        uint16_t dnstype = dpm.get16BitInt();
6✔
1230
        if (dnstype == type) {
6✔
1231
          result++;
3✔
1232
        }
3✔
1233
        /* class */
1234
        dpm.skipBytes(2);
6✔
1235
      } else {
21✔
1236
        /* type and class */
1237
        dpm.skipBytes(4);
21✔
1238
      }
21✔
1239
      /* ttl */
1240
      dpm.skipBytes(4);
27✔
1241
      dpm.skipRData();
27✔
1242
    }
27✔
1243
    const uint16_t arcount = ntohs(dh->arcount);
114✔
1244
    for(size_t n = 0; n < arcount; ++n) {
342✔
1245
      dpm.skipDomainName();
228✔
1246
      if (section == 3) {
228✔
1247
        uint16_t dnstype = dpm.get16BitInt();
90✔
1248
        if (dnstype == type) {
90✔
1249
          result++;
29✔
1250
        }
29✔
1251
        /* class */
1252
        dpm.skipBytes(2);
90✔
1253
      } else {
144✔
1254
        /* type and class */
1255
        dpm.skipBytes(4);
138✔
1256
      }
138✔
1257
      /* ttl */
1258
      dpm.skipBytes(4);
228✔
1259
      dpm.skipRData();
228✔
1260
    }
228✔
1261
  }
114✔
1262
  catch(...)
114✔
1263
  {
114✔
1264
  }
×
1265
  return result;
114✔
1266
}
114✔
1267

1268
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
1269
{
3,301✔
1270
  if (length < sizeof(dnsheader)) {
3,301!
1271
    return false;
×
1272
  }
×
1273

1274
  *payloadSize = 0;
3,301✔
1275
  *z = 0;
3,301✔
1276

1277
  try
3,301✔
1278
  {
3,301✔
1279
    const dnsheader_aligned dh(packet);
3,301✔
1280
    if (dh->arcount == 0) {
3,301✔
1281
      // The OPT pseudo-RR, if present, has to be in the additional section (https://datatracker.ietf.org/doc/html/rfc6891#section-6.1.1)
1282
      return false;
3,123✔
1283
    }
3,123✔
1284

1285
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
178✔
1286

1287
    const uint16_t qdcount = ntohs(dh->qdcount);
178✔
1288
    for(size_t n = 0; n < qdcount; ++n) {
356✔
1289
      dpm.skipDomainName();
178✔
1290
      /* type and class */
1291
      dpm.skipBytes(4);
178✔
1292
    }
178✔
1293
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
178✔
1294
    for(size_t n = 0; n < numrecords; ++n) {
232✔
1295
      dpm.skipDomainName();
231✔
1296
      const auto dnstype = dpm.get16BitInt();
231✔
1297

1298
      if (dnstype == QType::OPT) {
231✔
1299
        const auto dnsclass = dpm.get16BitInt();
177✔
1300
        /* skip extended rcode and version */
1301
        dpm.skipBytes(2);
177✔
1302
        *z = dpm.get16BitInt();
177✔
1303
        *payloadSize = dnsclass;
177✔
1304
        return true;
177✔
1305
      }
177✔
1306
      /* skip class */
1307
      dpm.skipBytes(2);
54✔
1308
      /* TTL */
1309
      dpm.skipBytes(4);
54✔
1310
      dpm.skipRData();
54✔
1311
    }
54✔
1312
  }
178✔
1313
  catch(...)
3,301✔
1314
  {
3,301✔
1315
  }
4✔
1316

1317
  return false;
5✔
1318
}
3,301✔
1319

1320
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
1321
{
96✔
1322
  if (packet.size() < sizeof(dnsheader)) {
96!
1323
    return false;
×
1324
  }
×
1325

1326
  try
96✔
1327
  {
96✔
1328
    const dnsheader_aligned dh(packet.data());
96✔
1329
    uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
96✔
1330
    PacketReader reader(packet);
96✔
1331

1332
    uint64_t n;
96✔
1333
    for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
192✔
1334
      (void) reader.getName();
96✔
1335
      /* type and class */
1336
      reader.skip(4);
96✔
1337
    }
96✔
1338

1339
    for (n = 0; n < numrecords; ++n) {
246✔
1340
      (void) reader.getName();
188✔
1341

1342
      uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
188✔
1343
      uint16_t dnstype = reader.get16BitInt();
188✔
1344
      uint16_t dnsclass = reader.get16BitInt();
188✔
1345

1346
      if (dnstype == QType::OPT) {
188✔
1347
        // not getting near that one with a stick
1348
        break;
18✔
1349
      }
18✔
1350

1351
      uint32_t dnsttl = reader.get32BitInt();
170✔
1352
      uint16_t contentLength = reader.get16BitInt();
170✔
1353
      uint16_t pos = reader.getPosition();
170✔
1354

1355
      bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
170✔
1356
      if (done) {
170✔
1357
        return true;
20✔
1358
      }
20✔
1359

1360
      reader.skip(contentLength);
150✔
1361
    }
150✔
1362
  }
96✔
1363
  catch (...) {
96✔
1364
    return false;
×
1365
  }
×
1366

1367
  return true;
76✔
1368
}
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