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

PowerDNS / pdns / 17611467588

10 Sep 2025 10:53AM UTC coverage: 66.01% (+0.03%) from 65.978%
17611467588

Pull #16108

github

web-flow
Merge b6eb1a724 into 29382c4af
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42255 of 92634 branches covered (45.62%)

Branch coverage included in aggregate %.

9 of 120 new or added lines in 6 files covered. (7.5%)

12 existing lines in 5 files now uncovered.

128490 of 166031 relevant lines covered (77.39%)

5519579.39 hits per line

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

79.79
/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,371✔
86
  dnsheader dnsheader;
158,371✔
87
  memset(&dnsheader, 0, sizeof(dnsheader));
158,371✔
88
  dnsheader.qdcount=htons(1);
158,371✔
89
  dnsheader.ancount=htons(1);
158,371✔
90

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

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

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

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

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

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

117
  DNSRecord dr;
158,371✔
118
  dr.d_class = qclass;
158,371✔
119
  dr.d_type = qtype;
158,371✔
120
  dr.d_name = qname;
158,371✔
121
  dr.d_clen = serialized.size();
158,371✔
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,371✔
124
  /* needed to get the record boundaries right */
125
  reader.getDnsrecordheader(drh);
158,371✔
126
  auto content = DNSRecordContent::make(dr, reader, Opcode::Query);
158,371✔
127
  return content;
158,371✔
128
}
158,371✔
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,681,876✔
146
  auto i = getZmakermap().find(pair(qclass, qtype));
1,681,876✔
147
  if(i==getZmakermap().end()) {
1,681,876✔
148
    return std::make_shared<UnknownRecordContent>(content);
405✔
149
  }
405✔
150

151
  return i->second(content);
1,681,471✔
152
}
1,681,876✔
153

154
std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr, PacketReader& pr, uint16_t oc)
155
{
2,456,584✔
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,584✔
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,344✔
163

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

169
  return i->second(dr, pr);
2,454,792✔
170
}
2,456,344✔
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,009✔
181
  static DNSRecordContent::typemap_t typemap;
9,085,009✔
182
  return typemap;
9,085,009✔
183
}
9,085,009✔
184

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

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

197
DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
198
{
7,535,657✔
199
  static DNSRecordContent::zmakermap_t zmakermap;
7,535,657✔
200
  return zmakermap;
7,535,657✔
201
}
7,535,657✔
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,952✔
210
  d_type = rr.qtype.getCode();
511,952✔
211
  d_ttl = rr.ttl;
511,952✔
212
  d_class = rr.qclass;
511,952✔
213
  d_place = DNSResourceRecord::ANSWER;
511,952✔
214
  d_clen = 0;
511,952✔
215
  d_content = DNSRecordContent::make(d_type, rr.qclass, rr.content);
511,952✔
216
}
511,952✔
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,414,995✔
233
  if (packet.size() < sizeof(dnsheader))
1,414,995!
234
    throw MOADNSException("Packet shorter than minimal header");
×
235

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

238
  if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
1,414,995!
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,414,995✔
242
  d_header.ancount=ntohs(d_header.ancount);
1,414,995✔
243
  d_header.nscount=ntohs(d_header.nscount);
1,414,995✔
244
  d_header.arcount=ntohs(d_header.arcount);
1,414,995✔
245

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

249
  unsigned int n=0;
1,414,995✔
250

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

256
    for(n=0;n < d_header.qdcount; ++n) {
2,822,304✔
257
      d_qname=pr.getName();
1,407,309✔
258
      d_qtype=pr.get16BitInt();
1,407,309✔
259
      d_qclass=pr.get16BitInt();
1,407,309✔
260
    }
1,407,309✔
261

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

270
      if(n < d_header.ancount)
2,304,885✔
271
        dr.d_place=DNSResourceRecord::ANSWER;
2,002,540✔
272
      else if(n < d_header.ancount + d_header.nscount)
302,345✔
273
        dr.d_place=DNSResourceRecord::AUTHORITY;
94,579✔
274
      else
207,766✔
275
        dr.d_place=DNSResourceRecord::ADDITIONAL;
207,766✔
276

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

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

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

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

289
      if (query &&
2,304,885✔
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,885!
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,885!
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,227✔
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,227✔
298
      }
2,298,227✔
299

300
      if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
2,304,885✔
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,883!
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,883✔
313
    }
2,304,883✔
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,414,995✔
322
  catch(const std::out_of_range &re) {
1,414,995✔
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,414,995✔
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,768✔
359
  unsigned char *p = reinterpret_cast<unsigned char*>(&ah);
2,463,768✔
360

361
  for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
27,098,562✔
362
    p[n] = d_content.at(d_pos++);
24,634,794✔
363
  }
24,634,794✔
364

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

370
  d_startrecordpos = d_pos; // needed for getBlob later on
2,463,768✔
371
  d_recordlen = ah.d_clen;
2,463,768✔
372
}
2,463,768✔
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,664✔
428
  uint32_t ret=0;
3,271,664✔
429
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,271,664✔
430
  ret<<=8;
3,271,664✔
431
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,271,664✔
432
  ret<<=8;
3,271,664✔
433
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,271,664✔
434
  ret<<=8;
3,271,664✔
435
  ret+=static_cast<uint8_t>(d_content.at(d_pos++));
3,271,664✔
436

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

440

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

448
  return ret;
4,625,082✔
449
}
4,625,082✔
450

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

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

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

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

480
  for (char letter : name) {
14,452,200✔
481
    const unsigned uch = static_cast<unsigned char>(letter);
14,452,200✔
482
    if (uch >= 127 || uch < 32) {
14,452,200✔
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,675✔
487
      ret += '\\';
1,102✔
488
      ret += letter;
1,102✔
489
    }
1,102✔
490
    else {
14,448,573✔
491
      ret += letter;
14,448,573✔
492
    }
14,448,573✔
493
  }
14,452,200✔
494
  return ret;
138,832✔
495
}
138,832✔
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,269✔
500
  string ret;
136,269✔
501
  ret.reserve(40);
136,269✔
502
  while(d_pos < d_startrecordpos + d_recordlen ) {
269,487✔
503
    if(!ret.empty()) {
139,216✔
504
      ret.append(1,' ');
2,950✔
505
    }
2,950✔
506
    uint16_t labellen;
139,216✔
507
    if(lenField)
139,216✔
508
      labellen=static_cast<uint8_t>(d_content.at(d_pos++));
139,192✔
509
    else
24✔
510
      labellen=d_recordlen - (d_pos - d_startrecordpos);
24✔
511

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

523
  if (ret.empty() && !lenField) {
136,269!
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,266✔
528
}
136,269✔
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,257✔
554
  try {
1,296,257✔
555
    if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
1,296,257✔
556
      if (d_pos > (d_startrecordpos + d_recordlen)) {
1,124,020!
557
        throw std::out_of_range("xfrBlob out of record range");
×
558
      }
×
559
      blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
1,124,020✔
560
    }
1,124,020✔
561
    else {
172,237✔
562
      blob.clear();
172,237✔
563
    }
172,237✔
564

565
    d_pos = d_startrecordpos + d_recordlen;
1,296,257✔
566
  }
1,296,257✔
567
  catch(...)
1,296,257✔
568
  {
1,296,257✔
569
    throw std::out_of_range("xfrBlob out of range");
9✔
570
  }
9✔
571
}
1,296,257✔
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,112✔
579
  if(length) {
537,112✔
580
    if (length < 0) {
534,611!
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,611✔
585

586
    d_pos += length;
534,611✔
587
  }
534,611✔
588
  else {
2,501✔
589
    blob.clear();
2,501✔
590
  }
2,501✔
591
}
537,112✔
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,003✔
703
  xfrBlob(blob);
6,003✔
704
}
6,003✔
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
{
828,312✔
950
  if (length < sizeof(dnsheader)) {
828,312!
951
    return;
×
952
  }
×
953
  try {
828,312✔
954
    const dnsheader* dhp = aligned_dh.get();
828,312✔
955
    const uint64_t dqcount = ntohs(dhp->qdcount);
828,312✔
956
    const uint64_t numrecords = ntohs(dhp->ancount) + ntohs(dhp->nscount) + ntohs(dhp->arcount);
828,312✔
957
    DNSPacketMangler dpm(packet, length);
828,312✔
958

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

965
    for(uint64_t rec = 0; rec < numrecords; ++rec) {
1,678,706✔
966
      dpm.skipDomainName();
850,394✔
967

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

972
      if (dnstype != QType::OPT) { // not aging that one with a stick
850,397✔
973
        dpm.decreaseAndSkip32BitInt(seconds);
849,506✔
974
      } else {
2,148,324,127✔
975
        dpm.skipBytes(4);
2,147,484,530✔
976
      }
2,147,484,530✔
977
      dpm.skipRData();
850,394✔
978
    }
850,394✔
979
  }
828,312✔
980
  catch(...) {
828,312✔
981
  }
3✔
982
}
828,312✔
983

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

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

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

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

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

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

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

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

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

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

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

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

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

NEW
1054
    if (indexes.size() > 2) {
×
NEW
1055
      using uid = std::uniform_int_distribution<std::vector<uint32_t>::size_type>;
×
NEW
1056
      uid dist;
×
1057

NEW
1058
      pdns::dns_random_engine randomEngine;
×
NEW
1059
      for (auto swapped = indexes.size() - 2; swapped > 0; --swapped) {
×
NEW
1060
        auto swapped_with = dist(randomEngine, uid::param_type(0, swapped));
×
NEW
1061
        if (swapped != swapped_with) {
×
NEW
1062
          auto start_first = indexes[swapped_with];
×
NEW
1063
          auto end_first = indexes[swapped_with+1];
×
NEW
1064
          auto start_second = indexes[swapped];
×
NEW
1065
          auto end_second = indexes[swapped+1];
×
NEW
1066
          auto diff = dpm.swapInPlace(start_first, end_first, start_second, end_second);
×
NEW
1067
          if (diff != 0) {
×
NEW
1068
            for (auto moved = swapped_with+1; moved<swapped; moved++) {
×
NEW
1069
              indexes[moved] = indexes[moved]+diff;
×
NEW
1070
            }
×
NEW
1071
          }
×
NEW
1072
        }
×
NEW
1073
      }
×
NEW
1074
    }
×
NEW
1075
  }
×
NEW
1076
  catch(...) {
×
NEW
1077
  }
×
NEW
1078
}
×
1079

1080
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
1081
{
1,189,138✔
1082
  uint32_t result = std::numeric_limits<uint32_t>::max();
1,189,138✔
1083
  if(length < sizeof(dnsheader)) {
1,189,138!
1084
    return result;
×
1085
  }
×
1086
  try
1,189,138✔
1087
  {
1,189,138✔
1088
    const dnsheader_aligned dh(packet);
1,189,138✔
1089
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
1,189,138✔
1090

1091
    const uint16_t qdcount = ntohs(dh->qdcount);
1,189,138✔
1092
    for(size_t n = 0; n < qdcount; ++n) {
2,352,778✔
1093
      dpm.skipDomainName();
1,163,640✔
1094
      /* type and class */
1095
      dpm.skipBytes(4);
1,163,640✔
1096
    }
1,163,640✔
1097
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1,189,138✔
1098
    for(size_t n = 0; n < numrecords; ++n) {
2,375,870✔
1099
      dpm.skipDomainName();
1,186,826✔
1100
      const uint16_t dnstype = dpm.get16BitInt();
1,186,826✔
1101
      /* class */
1102
      const uint16_t dnsclass = dpm.get16BitInt();
1,186,826✔
1103

1104
      if(dnstype == QType::OPT) {
1,186,826✔
1105
        break;
94✔
1106
      }
94✔
1107

1108
      /* report it if we see a SOA record in the AUTHORITY section */
1109
      if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
1,186,732!
1110
        *seenAuthSOA = true;
16✔
1111
      }
16✔
1112

1113
      const uint32_t ttl = dpm.get32BitInt();
1,186,732✔
1114
      result = std::min(result, ttl);
1,186,732✔
1115

1116
      dpm.skipRData();
1,186,732✔
1117
    }
1,186,732✔
1118
  }
1,189,138✔
1119
  catch(...)
1,189,138✔
1120
  {
1,189,138✔
1121
  }
6✔
1122
  return result;
1,189,138✔
1123
}
1,189,138✔
1124

1125
uint32_t getDNSPacketLength(const char* packet, size_t length)
1126
{
48✔
1127
  uint32_t result = length;
48✔
1128
  if(length < sizeof(dnsheader)) {
48!
1129
    return result;
×
1130
  }
×
1131
  try
48✔
1132
  {
48✔
1133
    const dnsheader_aligned dh(packet);
48✔
1134
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
48✔
1135

1136
    const uint16_t qdcount = ntohs(dh->qdcount);
48✔
1137
    for(size_t n = 0; n < qdcount; ++n) {
96✔
1138
      dpm.skipDomainName();
48✔
1139
      /* type and class */
1140
      dpm.skipBytes(4);
48✔
1141
    }
48✔
1142
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
48✔
1143
    for(size_t n = 0; n < numrecords; ++n) {
90✔
1144
      dpm.skipDomainName();
42✔
1145
      /* type (2), class (2) and ttl (4) */
1146
      dpm.skipBytes(8);
42✔
1147
      dpm.skipRData();
42✔
1148
    }
42✔
1149
    result = dpm.getOffset();
48✔
1150
  }
48✔
1151
  catch(...)
48✔
1152
  {
48✔
1153
  }
3✔
1154
  return result;
48✔
1155
}
48✔
1156

1157
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1158
{
114✔
1159
  uint16_t result = 0;
114✔
1160
  if(length < sizeof(dnsheader)) {
114!
1161
    return result;
×
1162
  }
×
1163
  try
114✔
1164
  {
114✔
1165
    const dnsheader_aligned dh(packet);
114✔
1166
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
114✔
1167

1168
    const uint16_t qdcount = ntohs(dh->qdcount);
114✔
1169
    for(size_t n = 0; n < qdcount; ++n) {
228✔
1170
      dpm.skipDomainName();
114✔
1171
      if (section == 0) {
114✔
1172
        uint16_t dnstype = dpm.get16BitInt();
6✔
1173
        if (dnstype == type) {
6✔
1174
          result++;
3✔
1175
        }
3✔
1176
        /* class */
1177
        dpm.skipBytes(2);
6✔
1178
      } else {
108✔
1179
        /* type and class */
1180
        dpm.skipBytes(4);
108✔
1181
      }
108✔
1182
    }
114✔
1183
    const uint16_t ancount = ntohs(dh->ancount);
114✔
1184
    for(size_t n = 0; n < ancount; ++n) {
246✔
1185
      dpm.skipDomainName();
132✔
1186
      if (section == 1) {
132✔
1187
        uint16_t dnstype = dpm.get16BitInt();
66✔
1188
        if (dnstype == type) {
66✔
1189
          result++;
33✔
1190
        }
33✔
1191
        /* class */
1192
        dpm.skipBytes(2);
66✔
1193
      } else {
66✔
1194
        /* type and class */
1195
        dpm.skipBytes(4);
66✔
1196
      }
66✔
1197
      /* ttl */
1198
      dpm.skipBytes(4);
132✔
1199
      dpm.skipRData();
132✔
1200
    }
132✔
1201
    const uint16_t nscount = ntohs(dh->nscount);
114✔
1202
    for(size_t n = 0; n < nscount; ++n) {
141✔
1203
      dpm.skipDomainName();
27✔
1204
      if (section == 2) {
27✔
1205
        uint16_t dnstype = dpm.get16BitInt();
6✔
1206
        if (dnstype == type) {
6✔
1207
          result++;
3✔
1208
        }
3✔
1209
        /* class */
1210
        dpm.skipBytes(2);
6✔
1211
      } else {
21✔
1212
        /* type and class */
1213
        dpm.skipBytes(4);
21✔
1214
      }
21✔
1215
      /* ttl */
1216
      dpm.skipBytes(4);
27✔
1217
      dpm.skipRData();
27✔
1218
    }
27✔
1219
    const uint16_t arcount = ntohs(dh->arcount);
114✔
1220
    for(size_t n = 0; n < arcount; ++n) {
342✔
1221
      dpm.skipDomainName();
228✔
1222
      if (section == 3) {
228✔
1223
        uint16_t dnstype = dpm.get16BitInt();
90✔
1224
        if (dnstype == type) {
90✔
1225
          result++;
29✔
1226
        }
29✔
1227
        /* class */
1228
        dpm.skipBytes(2);
90✔
1229
      } else {
144✔
1230
        /* type and class */
1231
        dpm.skipBytes(4);
138✔
1232
      }
138✔
1233
      /* ttl */
1234
      dpm.skipBytes(4);
228✔
1235
      dpm.skipRData();
228✔
1236
    }
228✔
1237
  }
114✔
1238
  catch(...)
114✔
1239
  {
114✔
1240
  }
×
1241
  return result;
114✔
1242
}
114✔
1243

1244
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
1245
{
3,300✔
1246
  if (length < sizeof(dnsheader)) {
3,300!
1247
    return false;
×
1248
  }
×
1249

1250
  *payloadSize = 0;
3,300✔
1251
  *z = 0;
3,300✔
1252

1253
  try
3,300✔
1254
  {
3,300✔
1255
    const dnsheader_aligned dh(packet);
3,300✔
1256
    if (dh->arcount == 0) {
3,300✔
1257
      // The OPT pseudo-RR, if present, has to be in the additional section (https://datatracker.ietf.org/doc/html/rfc6891#section-6.1.1)
1258
      return false;
3,123✔
1259
    }
3,123✔
1260

1261
    DNSPacketMangler dpm(const_cast<char*>(packet), length);
177✔
1262

1263
    const uint16_t qdcount = ntohs(dh->qdcount);
177✔
1264
    for(size_t n = 0; n < qdcount; ++n) {
354✔
1265
      dpm.skipDomainName();
177✔
1266
      /* type and class */
1267
      dpm.skipBytes(4);
177✔
1268
    }
177✔
1269
    const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
177✔
1270
    for(size_t n = 0; n < numrecords; ++n) {
231✔
1271
      dpm.skipDomainName();
230✔
1272
      const auto dnstype = dpm.get16BitInt();
230✔
1273

1274
      if (dnstype == QType::OPT) {
230✔
1275
        const auto dnsclass = dpm.get16BitInt();
176✔
1276
        /* skip extended rcode and version */
1277
        dpm.skipBytes(2);
176✔
1278
        *z = dpm.get16BitInt();
176✔
1279
        *payloadSize = dnsclass;
176✔
1280
        return true;
176✔
1281
      }
176✔
1282
      /* skip class */
1283
      dpm.skipBytes(2);
54✔
1284
      /* TTL */
1285
      dpm.skipBytes(4);
54✔
1286
      dpm.skipRData();
54✔
1287
    }
54✔
1288
  }
177✔
1289
  catch(...)
3,300✔
1290
  {
3,300✔
1291
  }
4✔
1292

1293
  return false;
5✔
1294
}
3,300✔
1295

1296
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
1297
{
96✔
1298
  if (packet.size() < sizeof(dnsheader)) {
96!
1299
    return false;
×
1300
  }
×
1301

1302
  try
96✔
1303
  {
96✔
1304
    const dnsheader_aligned dh(packet.data());
96✔
1305
    uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
96✔
1306
    PacketReader reader(packet);
96✔
1307

1308
    uint64_t n;
96✔
1309
    for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
192✔
1310
      (void) reader.getName();
96✔
1311
      /* type and class */
1312
      reader.skip(4);
96✔
1313
    }
96✔
1314

1315
    for (n = 0; n < numrecords; ++n) {
246✔
1316
      (void) reader.getName();
188✔
1317

1318
      uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
188✔
1319
      uint16_t dnstype = reader.get16BitInt();
188✔
1320
      uint16_t dnsclass = reader.get16BitInt();
188✔
1321

1322
      if (dnstype == QType::OPT) {
188✔
1323
        // not getting near that one with a stick
1324
        break;
18✔
1325
      }
18✔
1326

1327
      uint32_t dnsttl = reader.get32BitInt();
170✔
1328
      uint16_t contentLength = reader.get16BitInt();
170✔
1329
      uint16_t pos = reader.getPosition();
170✔
1330

1331
      bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
170✔
1332
      if (done) {
170✔
1333
        return true;
20✔
1334
      }
20✔
1335

1336
      reader.skip(contentLength);
150✔
1337
    }
150✔
1338
  }
96✔
1339
  catch (...) {
96✔
1340
    return false;
×
1341
  }
×
1342

1343
  return true;
76✔
1344
}
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