• 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

73.08
/pdns/dnsparser.hh
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
#pragma once
23
#include <atomic>
24
#include <map>
25
#include <sstream>
26
#include <stdexcept>
27
#include <iostream>
28
#include <unordered_set>
29
#include <utility>
30
#include <vector>
31
#include <cerrno>
32
// #include <netinet/in.h>
33
#include "misc.hh"
34

35
#include "dns.hh"
36
#include "dnswriter.hh"
37
#include "dnsname.hh"
38
#include "noinitvector.hh"
39
#include "pdnsexception.hh"
40
#include "iputils.hh"
41
#include "svc-records.hh"
42

43
/** DNS records have three representations:
44
    1) in the packet
45
    2) parsed in a class, ready for use
46
    3) in the zone
47

48
    We should implement bidirectional transitions between 1&2 and 2&3.
49
    Currently we have: 1 -> 2
50
                       2 -> 3
51

52
    We can add:        2 -> 1  easily by reversing the packetwriter
53
    And we might be able to reverse 2 -> 3 as well
54
*/
55

56
#include "namespaces.hh"
57

58
class MOADNSException : public runtime_error
59
{
60
public:
61
  MOADNSException(const string& str) : runtime_error(str)
2✔
62
  {}
84✔
63
};
64

65

66
class MOADNSParser;
67

68
class PacketReader
69
{
70
public:
71
  PacketReader(const std::string_view& content, uint16_t initialPos=sizeof(dnsheader), bool internalRepresentation = false)
72
    : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content), d_internal(internalRepresentation)
965✔
73
  {
1,598,007✔
74
    if(content.size() > std::numeric_limits<uint16_t>::max())
1,598,007!
75
      throw std::out_of_range("packet too large");
×
76

77
    d_recordlen = (uint16_t) content.size();
1,598,007✔
78
    not_used = 0;
1,598,007✔
79
  }
1,598,007✔
80

81
  uint32_t get32BitInt();
82
  uint16_t get16BitInt();
83
  uint8_t get8BitInt();
84

85
  void xfrNodeOrLocatorID(NodeOrLocatorID& val);
86
  void xfr48BitInt(uint64_t& val);
87

88
  void xfr32BitInt(uint32_t& val)
89
  {
3,270,603✔
90
    val=get32BitInt();
3,270,603✔
91
  }
3,270,603✔
92

93
  void xfrIP(uint32_t& val)
94
  {
915,694✔
95
    xfr32BitInt(val);
915,694✔
96
    val=htonl(val);
915,694✔
97
  }
915,694✔
98

99
  void xfrIP6(std::string &val) {
15,811✔
100
    xfrBlob(val, 16);
15,811✔
101
  }
15,811✔
102

103
  void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
63✔
104
    string blob;
63✔
105
    if (version == 4) xfrBlob(blob, 4);
63✔
106
    else if (version == 6) xfrBlob(blob, 16);
20!
107
    else throw runtime_error("invalid IP protocol");
×
108
    val = makeComboAddressFromRaw(version, blob);
63✔
109
  }
63✔
110

111
  void xfrCAPort(ComboAddress &val) {
×
112
    uint16_t port;
×
113
    xfr16BitInt(port);
×
114
    val.sin4.sin_port = port;
×
115
  }
×
116

117
  void xfrTime(uint32_t& val)
118
  {
1,495,240✔
119
    xfr32BitInt(val);
1,495,240✔
120
  }
1,495,240✔
121

122

123
  void xfr16BitInt(uint16_t& val)
124
  {
1,794,205✔
125
    val=get16BitInt();
1,794,205✔
126
  }
1,794,205✔
127

128
  void xfrType(uint16_t& val)
129
  {
747,737✔
130
    xfr16BitInt(val);
747,737✔
131
  }
747,737✔
132

133

134
  void xfr8BitInt(uint8_t& val)
135
  {
2,558,906✔
136
    val=get8BitInt();
2,558,906✔
137
  }
2,558,906✔
138

139
  void xfrName(DNSName& name, bool /* compress */ = false)
140
  {
970,239✔
141
    name = getName();
970,239✔
142
  }
970,239✔
143

144
  void xfrText(string &text, bool multi=false, bool lenField=true)
145
  {
136,269✔
146
    text=getText(multi, lenField);
136,269✔
147
  }
136,269✔
148

149
  void xfrUnquotedText(string &text, bool lenField){
21✔
150
    text=getUnquotedText(lenField);
21✔
151
  }
21✔
152

153
  void xfrBlob(string& blob);
154
  void xfrBlobNoSpaces(string& blob, int len);
155
  void xfrBlob(string& blob, int length);
156
  void xfrHexBlob(string& blob, bool keepReading=false);
157
  void xfrSvcParamKeyVals(set<SvcParam> &kvs);
158

159
  void getDnsrecordheader(struct dnsrecordheader &ah);
160
  void copyRecord(vector<unsigned char>& dest, uint16_t len);
161
  void copyRecord(unsigned char* dest, uint16_t len);
162

163
  DNSName getName();
164
  string getText(bool multi, bool lenField);
165
  string getUnquotedText(bool lenField);
166

167

168
  bool eof() { return true; };
2,083,839✔
169
  const string getRemaining() const {
×
170
    return "";
×
171
  };
×
172

173
  uint16_t getPosition() const
174
  {
2,305,504✔
175
    return d_pos;
2,305,504✔
176
  }
2,305,504✔
177

178
  void skip(uint16_t n)
179
  {
379✔
180
    d_pos += n;
379✔
181
  }
379✔
182

183
private:
184
  uint16_t d_pos;
185
  uint16_t d_startrecordpos; // needed for getBlob later on
186
  uint16_t d_recordlen;      // ditto
187
  uint16_t not_used; // Aligns the whole class on 8-byte boundaries
188
  const std::string_view d_content;
189
  bool d_internal;
190
};
191

192
struct DNSRecord;
193

194
class DNSRecordContent
195
{
196
public:
197
  static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr);
198
  static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr, uint16_t opcode);
199
  static std::shared_ptr<DNSRecordContent> make(uint16_t qtype, uint16_t qclass, const string& zone);
200
  static string upgradeContent(const DNSName& qname, const QType& qtype, const string& content);
201

202
  virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
203
  virtual ~DNSRecordContent() = default;
14,135,255✔
204
  virtual void toPacket(DNSPacketWriter& pw) const = 0;
205
  // returns the wire format of the content or the full record, possibly including compressed pointers pointing to the owner name (unless canonic or lowerCase are set)
206
  [[nodiscard]] string serialize(const DNSName& qname, bool canonic = false, bool lowerCase = false, bool full = false) const
207
  {
4,464,052✔
208
    vector<uint8_t> packet;
4,464,052✔
209
    DNSPacketWriter packetWriter(packet, g_rootdnsname, QType::A);
4,464,052✔
210

211
    if (canonic) {
4,464,052✔
212
      packetWriter.setCanonic(true);
2,770,063✔
213
    }
2,770,063✔
214
    if (lowerCase) {
4,464,052✔
215
      packetWriter.setLowercase(true);
2,640,995✔
216
    }
2,640,995✔
217

218
    packetWriter.startRecord(qname, getType());
4,464,052✔
219
    toPacket(packetWriter);
4,464,052✔
220

221
    string record;
4,464,052✔
222
    if (full) {
4,464,052✔
223
      packetWriter.getWireFormatContent(record); // needs to be called before commit()
15,787✔
224
    } else {
4,448,265✔
225
      packetWriter.getRecordPayload(record); // needs to be called before commit()
4,448,265✔
226
    }
4,448,265✔
227
    return record;
4,464,052✔
228
  }
4,464,052✔
229

230
  virtual bool operator==(const DNSRecordContent& rhs) const
231
  {
7,112✔
232
    return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
7,112!
233
  }
7,112✔
234

235
  // parse the content in wire format, possibly including compressed pointers pointing to the owner name.
236
  // internalRepresentation is set when the data comes from an internal source,
237
  // such as the LMDB backend.
238
  static shared_ptr<DNSRecordContent> deserialize(const DNSName& qname, uint16_t qtype, const string& serialized, uint16_t qclass=QClass::IN, bool internalRepresentation = false);
239

240
  void doRecordCheck(const struct DNSRecord&){}
1,168,133✔
241

242
  typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
243
  typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str);
244

245
  static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
246
  {
4,286,748✔
247
    assert(!d_locked.load()); // NOLINT: it's the API
4,286,748✔
248
    if(f)
4,286,748✔
249
      getTypemap()[pair(cl,ty)]=f;
4,171,905✔
250
    if(z)
4,286,748✔
251
      getZmakermap()[pair(cl,ty)]=z;
4,171,905✔
252

253
    getT2Namemap().emplace(pair(cl, ty), name);
4,286,748✔
254
    getN2Typemap().emplace(name, pair(cl, ty));
4,286,748✔
255
  }
4,286,748✔
256

257
  static bool isUnknownType(const string& name)
258
  {
23,966✔
259
    return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
23,966✔
260
  }
23,966✔
261

262
  static uint16_t TypeToNumber(const string& name)
263
  {
5,254,144✔
264
    n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
5,254,144✔
265
    if(iter != getN2Typemap().end())
5,254,144✔
266
      return iter->second.second;
5,253,256✔
267

268
    if (isUnknownType(name)) {
888✔
269
      return pdns::checked_stoi<uint16_t>(name.substr(4));
886✔
270
    }
886✔
271

272
    throw runtime_error("Unknown DNS type '"+name+"'");
2✔
273
  }
888✔
274

275
  static const string NumberToType(uint16_t num, uint16_t classnum = QClass::IN)
276
  {
2,400,038✔
277
    auto iter = getT2Namemap().find(pair(classnum, num));
2,400,038✔
278
    if(iter == getT2Namemap().end())
2,400,038✔
279
      return "TYPE" + std::to_string(num);
393,762✔
280
      //      throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
281
    return iter->second;
2,006,276✔
282
  }
2,400,038✔
283

284
  /**
285
   * \brief Return whether we have implemented a content representation for this type
286
   */
287
  static bool isRegisteredType(uint16_t rtype, uint16_t rclass = QClass::IN);
288

289
  virtual uint16_t getType() const = 0;
290

291
  static void lock()
292
  {
38,281✔
293
    d_locked.store(true);
38,281✔
294
  }
38,281✔
295

296
  [[nodiscard]] virtual size_t sizeEstimate() const = 0;
297

298
protected:
299
  typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
300
  typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
301
  typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
302
  typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
303
  static typemap_t& getTypemap();
304
  static t2namemap_t& getT2Namemap();
305
  static n2typemap_t& getN2Typemap();
306
  static zmakermap_t& getZmakermap();
307
  static std::atomic<bool> d_locked;
308
};
309

310
struct DNSRecord
311
{
312
  DNSRecord() :
313
    d_class(QClass::IN)
748✔
314
  {}
5,863,465✔
315
  explicit DNSRecord(const DNSResourceRecord& rr);
316
  DNSRecord(const std::string& name,
317
            std::shared_ptr<DNSRecordContent> content,
318
            const uint16_t type,
319
            const uint16_t qclass = QClass::IN,
320
            const uint32_t ttl = 86400,
321
            const uint16_t clen = 0,
322
            const DNSResourceRecord::Place place = DNSResourceRecord::ANSWER) :
323
    d_name(DNSName(name)),
324
    d_content(std::move(content)),
325
    d_type(type),
326
    d_class(qclass),
327
    d_ttl(ttl),
328
    d_clen(clen),
329
    d_place(place) {}
296✔
330

331
  DNSName d_name;
332
private:
333
  std::shared_ptr<const DNSRecordContent> d_content;
334
public:
335
  uint16_t d_type{};
336
  uint16_t d_class{};
337
  uint32_t d_ttl{};
338
  uint16_t d_clen{};
339
  DNSResourceRecord::Place d_place{DNSResourceRecord::ANSWER};
340

341
  [[nodiscard]] std::string print(const std::string& indent = "") const
342
  {
536✔
343
    std::stringstream s;
536✔
344
    s << indent << "Content = " << d_content->getZoneRepresentation() << std::endl;
536✔
345
    s << indent << "Type = " << d_type << std::endl;
536✔
346
    s << indent << "Class = " << d_class << std::endl;
536✔
347
    s << indent << "TTL = " << d_ttl << std::endl;
536✔
348
    s << indent << "clen = " << d_clen << std::endl;
536✔
349
    s << indent << "Place = " << std::to_string(d_place) << std::endl;
536✔
350
    return s.str();
536✔
351
  }
536✔
352

353
  [[nodiscard]] std::string toString() const
354
  {
826✔
355
    std::string ret(d_name.toLogString());
826✔
356
    ret += '|';
826✔
357
    ret += QType(d_type).toString();
826✔
358
    ret += '|';
826✔
359
    ret += getContent()->getZoneRepresentation();
826✔
360
    return ret;
826✔
361
  }
826✔
362

363
  void setContent(const std::shared_ptr<const DNSRecordContent>& content)
364
  {
767,973✔
365
    d_content = content;
767,973✔
366
  }
767,973✔
367

368
  void setContent(std::shared_ptr<const DNSRecordContent>&& content)
369
  {
4,591,444✔
370
    d_content = std::move(content);
4,591,444✔
371
  }
4,591,444✔
372

373
  [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const
374
  {
8,926,601✔
375
    return d_content;
8,926,601✔
376
  }
8,926,601✔
377

378
  bool operator<(const DNSRecord& rhs) const
379
  {
×
380
    if(std::tie(d_name, d_type, d_class, d_ttl) < std::tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
×
381
      return true;
×
382

×
383
    if(std::tie(d_name, d_type, d_class, d_ttl) != std::tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
×
384
      return false;
×
385

×
386
    string lzrp, rzrp;
×
387
    if(d_content)
×
388
      lzrp=toLower(d_content->getZoneRepresentation());
×
389
    if(rhs.d_content)
×
390
      rzrp=toLower(rhs.d_content->getZoneRepresentation());
×
391

×
392
    return lzrp < rzrp;
×
393
  }
×
394

395
  // this orders in canonical order and keeps the SOA record on top
396
  static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
397
  {
398
    auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
399
    auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
400

401
    int res = a.d_name.canonCompare_three_way(b.d_name, true);
402
    if (res < 0) {
403
      return true;
404
    }
405
    if (res > 0) {
406
      return false;
407
    }
408

409
    if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl))
410
      return true;
411

412
    if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl))
413
      return false;
414

415
    string lzrp, rzrp;
416
    if(a.d_content)
417
      lzrp = a.d_content->getZoneRepresentation();
418
    if(b.d_content)
419
      rzrp = b.d_content->getZoneRepresentation();
420

421
    switch (a.d_type) {
422
    case QType::TXT:
423
    case QType::SPF:
424
#if !defined(RECURSOR)
425
    case QType::LUA:
426
#endif
427
      return lzrp < rzrp;
428
    default:
429
      return toLower(lzrp) < toLower(rzrp);
430
    }
431
  }
432

433
  bool operator==(const DNSRecord& rhs) const
434
  {
19,358✔
435
    if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) {
19,358!
436
      return false;
11,849✔
437
    }
11,849✔
438

439
    return *d_content == *rhs.d_content;
7,509✔
440
  }
19,358✔
441

442
  [[nodiscard]] size_t sizeEstimate() const
443
  {
×
444
    return sizeof(*this) + d_name.sizeEstimate() + (d_content ? d_content->sizeEstimate() : 0U);
×
445
  }
×
446
};
447

448
struct DNSZoneRecord
449
{
450
  domainid_t domain_id{UnknownDomainID};
451
  uint8_t scopeMask{0};
452
  int signttl{0};
453
  DNSName wildcardname;
454
  bool auth{true};
455
  bool disabled{false};
456
  DNSRecord dr;
457

458
  bool operator<(const DNSZoneRecord& other) const {
58,621✔
459
    return dr.d_ttl < other.dr.d_ttl;
58,621✔
460
  }
58,621✔
461
};
462

463
class UnknownRecordContent : public DNSRecordContent
464
{
465
public:
466
  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
467
    : d_dr(dr)
748✔
468
  {
8,451✔
469
    pr.copyRecord(d_record, dr.d_clen);
8,451✔
470
  }
8,451✔
471

472
  UnknownRecordContent(const string& zone);
473

474
  string getZoneRepresentation(bool noDot) const override;
475
  void toPacket(DNSPacketWriter& pw) const override;
476
  uint16_t getType() const override
477
  {
1,028✔
478
    return d_dr.d_type;
1,028✔
479
  }
1,028✔
480

481
  const vector<uint8_t>& getRawContent() const
482
  {
59✔
483
    return d_record;
59✔
484
  }
59✔
485

486
  [[nodiscard]] size_t sizeEstimate() const override
487
  {
×
488
    return sizeof(*this) + d_dr.sizeEstimate() + d_record.size();
×
489
  }
×
490

491
private:
492
  DNSRecord d_dr;
493
  vector<uint8_t> d_record;
494
};
495

496
//! This class can be used to parse incoming packets, and is copyable
497
class MOADNSParser : public boost::noncopyable
498
{
499
public:
500
  //! Parse from a string
501
  MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
502
  {
1,386,797✔
503
    init(query, buffer);
1,386,797✔
504
  }
1,386,797✔
505

506
  //! Parse from a pointer and length
507
  MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
577✔
508
  {
31,049✔
509
    init(query, std::string_view(packet, len));
31,049✔
510
  }
31,049✔
511

512
  DNSName d_qname;
513
  uint16_t d_qclass, d_qtype;
514
  dnsheader d_header;
515

516
  using answers_t = vector<DNSRecord>;
517

518
  //! All answers contained in this packet (everything *but* the question section)
519
  answers_t d_answers;
520

521
  uint16_t getTSIGPos() const
522
  {
1,347,407✔
523
    return d_tsigPos;
1,347,407✔
524
  }
1,347,407✔
525

526
  bool hasEDNS() const;
527

528
private:
529
  void init(bool query, const std::string_view& packet);
530
  uint16_t d_tsigPos;
531
};
532

533
string simpleCompress(const string& label, const string& root="");
534
void shuffleDNSPacket(char* packet, size_t length, const dnsheader_aligned& aligned_dh);
535
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&);
536
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&);
537
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor);
538
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes);
539
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes);
540
void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes);
541
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
542
uint32_t getDNSPacketLength(const char* packet, size_t length);
543
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
544
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
545
/* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata
546
   to the visitor. Stops whenever the visitor returns false or at the end of the packet */
547
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor);
548

549
template<typename T>
550
std::shared_ptr<const T> getRR(const DNSRecord& dr)
551
{
1,674,989✔
552
  return std::dynamic_pointer_cast<const T>(dr.getContent());
1,674,989✔
553
}
1,674,989✔
554

555
/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
556
 *  If you survive that, feel free to read from the pointer */
557
class DNSPacketMangler
558
{
559
public:
560
  explicit DNSPacketMangler(std::string& packet)
561
    : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
562
  {}
×
563
  DNSPacketMangler(char* packet, size_t length)
564
    : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
2,018,196✔
565
  {}
2,021,461✔
566

567
  /*! Advances past a wire-format domain name
568
   * The name is not checked for adherence to length restrictions.
569
   * Compression pointers are not followed.
570
   * Returns offset of a compression pointer if the domain name ends
571
   * with a pointer, or nullopt if it doesn't.
572
   */
573
  std::optional<size_t> skipDomainName()
574
  {
4,031,447✔
575
    uint8_t len;
4,031,447✔
576
    while((len=get8BitInt())) {
8,447,715✔
577
      if(len >= 0xc0) { // extended label
6,454,460✔
578
        len &= (~0xc0);
2,038,192✔
579
        return (len << 8) + get8BitInt();
2,038,192✔
580
      }
2,038,192✔
581
      skipBytes(len);
4,416,268✔
582
    }
4,416,268✔
583
    return std::nullopt;
1,993,255✔
584
  }
4,031,447✔
585

586
  void skipBytes(uint16_t bytes)
587
  {
7,294,220✔
588
    moveOffset(bytes);
7,294,220✔
589
  }
7,294,220✔
590
  void rewindBytes(uint16_t by)
591
  {
22✔
592
    rewindOffset(by);
22✔
593
  }
22✔
594
  uint32_t get32BitInt()
595
  {
1,190,657✔
596
    const char* p = d_packet + d_offset;
1,190,657✔
597
    moveOffset(4);
1,190,657✔
598
    uint32_t ret;
1,190,657✔
599
    memcpy(&ret, p, sizeof(ret));
1,190,657✔
600
    return ntohl(ret);
1,190,657✔
601
  }
1,190,657✔
602
  uint16_t get16BitInt()
603
  {
5,216,933✔
604
    const char* p = d_packet + d_offset;
5,216,933✔
605
    moveOffset(2);
5,216,933✔
606
    uint16_t ret;
5,216,933✔
607
    memcpy(&ret, p, sizeof(ret));
5,216,933✔
608
    return ntohs(ret);
5,216,933✔
609
  }
5,216,933✔
610

611
  uint8_t get8BitInt()
612
  {
10,368,316✔
613
    const char* p = d_packet + d_offset;
10,368,316✔
614
    moveOffset(1);
10,368,316✔
615
    return *p;
10,368,316✔
616
  }
10,368,316✔
617

618
  void skipRData()
619
  {
2,036,653✔
620
    auto toskip = get16BitInt();
2,036,653✔
621
    moveOffset(toskip);
2,036,653✔
622
  }
2,036,653✔
623

624
  void decreaseAndSkip32BitInt(uint32_t decrease)
625
  {
848,798✔
626
    const char *p = d_packet + d_offset;
848,798✔
627
    moveOffset(4);
848,798✔
628

629
    uint32_t tmp;
848,798✔
630
    memcpy(&tmp, p, sizeof(tmp));
848,798✔
631
    tmp = ntohl(tmp);
848,798✔
632
    if (tmp > decrease) {
849,036✔
633
      tmp -= decrease;
848,828✔
634
    } else {
4,295,389,601✔
635
      tmp = 0;
4,294,967,494✔
636
    }
4,294,967,494✔
637
    tmp = htonl(tmp);
848,798✔
638
    memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
848,798✔
639
  }
848,798✔
640

641
  void setAndSkip32BitInt(uint32_t value)
642
  {
22✔
643
    moveOffset(4);
22✔
644

645
    value = htonl(value);
22✔
646
    memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
22✔
647
  }
22✔
648

649
  uint32_t getOffset() const
650
  {
343✔
651
    return d_offset;
343✔
652
  }
343✔
653

NEW
654
  int32_t swapInPlace(size_t a, size_t end_a, size_t b, size_t end_b) {
×
NEW
655
    char *s = d_packet;
×
NEW
656
    if (b<a) {
×
NEW
657
      std::swap(a, b);
×
NEW
658
      std::swap(end_a, end_b);
×
NEW
659
    }
×
660
    // some basic range checks
NEW
661
    if (end_a < a) {
×
NEW
662
      throw std::out_of_range("swap: ending of segment before start of segment");
×
NEW
663
    }
×
NEW
664
    if (end_b < b) {
×
NEW
665
      throw std::out_of_range("swap: ending of segment before start of segment");
×
NEW
666
    }
×
NEW
667
    if (end_a > b) {
×
NEW
668
      throw std::out_of_range("swap: overlapping segments");
×
NEW
669
    }
×
NEW
670
    if (end_b > d_length) {
×
NEW
671
      throw std::out_of_range("swap: ending of segment after end of array");
×
NEW
672
    }
×
673
    // don't allow to swap what we haven't read yet
NEW
674
    if (end_b > d_offset) {
×
NEW
675
      throw std::out_of_range("swap: ending of segment after current offset");
×
NEW
676
    }
×
677

NEW
678
    const size_t len_b = end_b - b;
×
NEW
679
    const size_t len_a = end_a - a;
×
680

NEW
681
    int32_t diff = len_b-len_a;
×
682

683
    // simple case 1: same lengths
NEW
684
    if (diff == 0) {
×
NEW
685
      std::swap_ranges(s+a, s+end_a, s+b);
×
NEW
686
      return 0;
×
NEW
687
    }
×
688

NEW
689
    const size_t len_gap = b - end_a;
×
690
    // simple case 2: adjacent blocks
NEW
691
    if (len_gap == 0) {
×
NEW
692
      std::rotate(s+a, s+end_a, s+end_b);
×
NEW
693
      return diff;
×
NEW
694
    }
×
695

NEW
696
    if (diff > 0) {
×
697
      // 1234GGGGGabcdef
698
      // abcdGGGGG1234ef
NEW
699
      std::swap_ranges(s+a, s+end_a, s+b);
×
NEW
700
      std::rotate(s+end_a, s+b+end_a, s+end_b);
×
NEW
701
      return diff;
×
NEW
702
    } else {
×
703
      // 123456GGGGGabcd
704
      // abcd56GGGGG1234
NEW
705
      std::swap_ranges(s+a, s+a+end_b, s+b);
×
NEW
706
      std::rotate(s+a+end_b, s+b, s+end_b);
×
NEW
707
      return diff;
×
NEW
708
    }
×
NEW
709
}
×
710

711
private:
712
  void moveOffset(uint16_t by)
713
  {
26,328,658✔
714
    d_notyouroffset += by;
26,328,658✔
715
    if(d_notyouroffset > d_length)
26,328,658✔
716
      throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
21✔
717
      + std::to_string(d_length) );
21✔
718
  }
26,328,658✔
719

720
  void rewindOffset(uint16_t by)
721
  {
22✔
722
    if(d_notyouroffset < by)
22!
723
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
×
724
                              + std::to_string(by));
×
725
    d_notyouroffset -= by;
22✔
726
    if(d_notyouroffset < 12)
22!
727
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
×
728
                              + std::to_string(12));
×
729
  }
22✔
730

731
  char* d_packet;
732
  size_t d_length;
733

734
  uint32_t d_notyouroffset;  // only 'moveOffset' can touch this
735
  const uint32_t&  d_offset; // look.. but don't touch
736
};
737

738
string txtEscape(const string &name);
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