Coverage Report

Created: 2025-06-13 06:27

/src/pdns/pdns/dnsparser.hh
Line
Count
Source (jump to first uncovered line)
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)
62
0
  {}
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)
73
0
  {
74
0
    if(content.size() > std::numeric_limits<uint16_t>::max())
75
0
      throw std::out_of_range("packet too large");
76
0
77
0
    d_recordlen = (uint16_t) content.size();
78
0
    not_used = 0;
79
0
  }
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
0
  {
90
0
    val=get32BitInt();
91
0
  }
92
93
  void xfrIP(uint32_t& val)
94
0
  {
95
0
    xfr32BitInt(val);
96
0
    val=htonl(val);
97
0
  }
98
99
0
  void xfrIP6(std::string &val) {
100
0
    xfrBlob(val, 16);
101
0
  }
102
103
0
  void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
104
0
    string blob;
105
0
    if (version == 4) xfrBlob(blob, 4);
106
0
    else if (version == 6) xfrBlob(blob, 16);
107
0
    else throw runtime_error("invalid IP protocol");
108
0
    val = makeComboAddressFromRaw(version, blob);
109
0
  }
110
111
0
  void xfrCAPort(ComboAddress &val) {
112
0
    uint16_t port;
113
0
    xfr16BitInt(port);
114
0
    val.sin4.sin_port = port;
115
0
  }
116
117
  void xfrTime(uint32_t& val)
118
0
  {
119
0
    xfr32BitInt(val);
120
0
  }
121
122
123
  void xfr16BitInt(uint16_t& val)
124
0
  {
125
0
    val=get16BitInt();
126
0
  }
127
128
  void xfrType(uint16_t& val)
129
0
  {
130
0
    xfr16BitInt(val);
131
0
  }
132
133
134
  void xfr8BitInt(uint8_t& val)
135
0
  {
136
0
    val=get8BitInt();
137
0
  }
138
139
  void xfrName(DNSName& name, bool /* compress */ = false)
140
0
  {
141
0
    name = getName();
142
0
  }
143
144
  void xfrText(string &text, bool multi=false, bool lenField=true)
145
0
  {
146
0
    text=getText(multi, lenField);
147
0
  }
148
149
0
  void xfrUnquotedText(string &text, bool lenField){
150
0
    text=getUnquotedText(lenField);
151
0
  }
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
0
  bool eof() { return true; };
169
0
  const string getRemaining() const {
170
0
    return "";
171
0
  };
172
173
  uint16_t getPosition() const
174
0
  {
175
0
    return d_pos;
176
0
  }
177
178
  void skip(uint16_t n)
179
0
  {
180
0
    d_pos += n;
181
0
  }
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;
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
0
  {
208
0
    vector<uint8_t> packet;
209
0
    DNSPacketWriter packetWriter(packet, g_rootdnsname, QType::A);
210
0
211
0
    if (canonic) {
212
0
      packetWriter.setCanonic(true);
213
0
    }
214
0
    if (lowerCase) {
215
0
      packetWriter.setLowercase(true);
216
0
    }
217
0
218
0
    packetWriter.startRecord(qname, getType());
219
0
    toPacket(packetWriter);
220
0
221
0
    string record;
222
0
    if (full) {
223
0
      packetWriter.getWireFormatContent(record); // needs to be called before commit()
224
0
    } else {
225
0
      packetWriter.getRecordPayload(record); // needs to be called before commit()
226
0
    }
227
0
    return record;
228
0
  }
229
230
  virtual bool operator==(const DNSRecordContent& rhs) const
231
0
  {
232
0
    return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
233
0
  }
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
0
  void doRecordCheck(const struct DNSRecord&){}
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
0
  {
247
0
    assert(!d_locked.load()); // NOLINT: it's the API
248
0
    if(f)
249
0
      getTypemap()[pair(cl,ty)]=f;
250
0
    if(z)
251
0
      getZmakermap()[pair(cl,ty)]=z;
252
0
253
0
    getT2Namemap().emplace(pair(cl, ty), name);
254
0
    getN2Typemap().emplace(name, pair(cl, ty));
255
0
  }
256
257
  static bool isUnknownType(const string& name)
258
0
  {
259
0
    return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
260
0
  }
261
262
  static uint16_t TypeToNumber(const string& name)
263
0
  {
264
0
    n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
265
0
    if(iter != getN2Typemap().end())
266
0
      return iter->second.second;
267
0
268
0
    if (isUnknownType(name)) {
269
0
      return pdns::checked_stoi<uint16_t>(name.substr(4));
270
0
    }
271
0
272
0
    throw runtime_error("Unknown DNS type '"+name+"'");
273
0
  }
274
275
  static const string NumberToType(uint16_t num, uint16_t classnum = QClass::IN)
276
0
  {
277
0
    auto iter = getT2Namemap().find(pair(classnum, num));
278
0
    if(iter == getT2Namemap().end())
279
0
      return "TYPE" + std::to_string(num);
280
0
      //      throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
281
0
    return iter->second;
282
0
  }
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
0
  {
293
0
    d_locked.store(true);
294
0
  }
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)
314
0
  {}
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
0
    d_place(place) {}
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
0
  {
343
0
    std::stringstream s;
344
0
    s << indent << "Content = " << d_content->getZoneRepresentation() << std::endl;
345
0
    s << indent << "Type = " << d_type << std::endl;
346
0
    s << indent << "Class = " << d_class << std::endl;
347
0
    s << indent << "TTL = " << d_ttl << std::endl;
348
0
    s << indent << "clen = " << d_clen << std::endl;
349
0
    s << indent << "Place = " << std::to_string(d_place) << std::endl;
350
0
    return s.str();
351
0
  }
352
353
  [[nodiscard]] std::string toString() const
354
0
  {
355
0
    std::string ret(d_name.toLogString());
356
0
    ret += '|';
357
0
    ret += QType(d_type).toString();
358
0
    ret += '|';
359
0
    ret += getContent()->getZoneRepresentation();
360
0
    return ret;
361
0
  }
362
363
  void setContent(const std::shared_ptr<const DNSRecordContent>& content)
364
0
  {
365
0
    d_content = content;
366
0
  }
367
368
  void setContent(std::shared_ptr<const DNSRecordContent>&& content)
369
0
  {
370
0
    d_content = std::move(content);
371
0
  }
372
373
  [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const
374
0
  {
375
0
    return d_content;
376
0
  }
377
378
  bool operator<(const DNSRecord& rhs) const
379
0
  {
380
0
    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
0
      return true;
382
0
383
0
    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
0
      return false;
385
0
386
0
    string lzrp, rzrp;
387
0
    if(d_content)
388
0
      lzrp=toLower(d_content->getZoneRepresentation());
389
0
    if(rhs.d_content)
390
0
      rzrp=toLower(rhs.d_content->getZoneRepresentation());
391
0
392
0
    return lzrp < rzrp;
393
0
  }
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
0
  {
398
0
    auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
399
0
    auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
400
0
401
0
    if(a.d_name.canonCompare(b.d_name))
402
0
      return true;
403
0
    if(b.d_name.canonCompare(a.d_name))
404
0
      return false;
405
0
406
0
    if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl))
407
0
      return true;
408
0
409
0
    if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl))
410
0
      return false;
411
0
412
0
    string lzrp, rzrp;
413
0
    if(a.d_content)
414
0
      lzrp = a.d_content->getZoneRepresentation();
415
0
    if(b.d_content)
416
0
      rzrp = b.d_content->getZoneRepresentation();
417
0
418
0
    switch (a.d_type) {
419
0
    case QType::TXT:
420
0
    case QType::SPF:
421
0
#if !defined(RECURSOR)
422
0
    case QType::LUA:
423
0
#endif
424
0
      return lzrp < rzrp;
425
0
    default:
426
0
      return toLower(lzrp) < toLower(rzrp);
427
0
    }
428
0
  }
429
430
  bool operator==(const DNSRecord& rhs) const
431
0
  {
432
0
    if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) {
433
0
      return false;
434
0
    }
435
0
436
0
    return *d_content == *rhs.d_content;
437
0
  }
438
439
  [[nodiscard]] size_t sizeEstimate() const
440
0
  {
441
0
    return sizeof(*this) + d_name.sizeEstimate() + (d_content ? d_content->sizeEstimate() : 0U);
442
0
  }
443
};
444
445
struct DNSZoneRecord
446
{
447
  domainid_t domain_id{UnknownDomainID};
448
  uint8_t scopeMask{0};
449
  int signttl{0};
450
  DNSName wildcardname;
451
  bool auth{true};
452
  bool disabled{false};
453
  DNSRecord dr;
454
455
0
  bool operator<(const DNSZoneRecord& other) const {
456
0
    return dr.d_ttl < other.dr.d_ttl;
457
0
  }
458
};
459
460
class UnknownRecordContent : public DNSRecordContent
461
{
462
public:
463
  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
464
    : d_dr(dr)
465
0
  {
466
0
    pr.copyRecord(d_record, dr.d_clen);
467
0
  }
468
469
  UnknownRecordContent(const string& zone);
470
471
  string getZoneRepresentation(bool noDot) const override;
472
  void toPacket(DNSPacketWriter& pw) const override;
473
  uint16_t getType() const override
474
0
  {
475
0
    return d_dr.d_type;
476
0
  }
477
478
  const vector<uint8_t>& getRawContent() const
479
0
  {
480
0
    return d_record;
481
0
  }
482
483
  [[nodiscard]] size_t sizeEstimate() const override
484
0
  {
485
0
    return sizeof(*this) + d_dr.sizeEstimate() + d_record.size();
486
0
  }
487
488
private:
489
  DNSRecord d_dr;
490
  vector<uint8_t> d_record;
491
};
492
493
//! This class can be used to parse incoming packets, and is copyable
494
class MOADNSParser : public boost::noncopyable
495
{
496
public:
497
  //! Parse from a string
498
  MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
499
0
  {
500
0
    init(query, buffer);
501
0
  }
502
503
  //! Parse from a pointer and length
504
  MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
505
0
  {
506
0
    init(query, std::string_view(packet, len));
507
0
  }
508
509
  DNSName d_qname;
510
  uint16_t d_qclass, d_qtype;
511
  dnsheader d_header;
512
513
  using answers_t = vector<DNSRecord>;
514
515
  //! All answers contained in this packet (everything *but* the question section)
516
  answers_t d_answers;
517
518
  uint16_t getTSIGPos() const
519
0
  {
520
0
    return d_tsigPos;
521
0
  }
522
523
  bool hasEDNS() const;
524
525
private:
526
  void init(bool query, const std::string_view& packet);
527
  uint16_t d_tsigPos;
528
};
529
530
string simpleCompress(const string& label, const string& root="");
531
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&);
532
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&);
533
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor);
534
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes);
535
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes);
536
void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes);
537
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
538
uint32_t getDNSPacketLength(const char* packet, size_t length);
539
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
540
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
541
/* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata
542
   to the visitor. Stops whenever the visitor returns false or at the end of the packet */
543
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor);
544
545
template<typename T>
546
std::shared_ptr<const T> getRR(const DNSRecord& dr)
547
{
548
  return std::dynamic_pointer_cast<const T>(dr.getContent());
549
}
550
551
/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
552
 *  If you survive that, feel free to read from the pointer */
553
class DNSPacketMangler
554
{
555
public:
556
  explicit DNSPacketMangler(std::string& packet)
557
    : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
558
0
  {}
559
  DNSPacketMangler(char* packet, size_t length)
560
    : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
561
0
  {}
562
563
  /*! Advances past a wire-format domain name
564
   * The name is not checked for adherence to length restrictions.
565
   * Compression pointers are not followed.
566
   */
567
  void skipDomainName()
568
0
  {
569
0
    uint8_t len;
570
0
    while((len=get8BitInt())) {
571
0
      if(len >= 0xc0) { // extended label
572
0
        get8BitInt();
573
0
        return;
574
0
      }
575
0
      skipBytes(len);
576
0
    }
577
0
  }
578
579
  void skipBytes(uint16_t bytes)
580
0
  {
581
0
    moveOffset(bytes);
582
0
  }
583
  void rewindBytes(uint16_t by)
584
0
  {
585
0
    rewindOffset(by);
586
0
  }
587
  uint32_t get32BitInt()
588
0
  {
589
0
    const char* p = d_packet + d_offset;
590
0
    moveOffset(4);
591
0
    uint32_t ret;
592
0
    memcpy(&ret, p, sizeof(ret));
593
0
    return ntohl(ret);
594
0
  }
595
  uint16_t get16BitInt()
596
0
  {
597
0
    const char* p = d_packet + d_offset;
598
0
    moveOffset(2);
599
0
    uint16_t ret;
600
0
    memcpy(&ret, p, sizeof(ret));
601
0
    return ntohs(ret);
602
0
  }
603
604
  uint8_t get8BitInt()
605
0
  {
606
0
    const char* p = d_packet + d_offset;
607
0
    moveOffset(1);
608
0
    return *p;
609
0
  }
610
611
  void skipRData()
612
0
  {
613
0
    int toskip = get16BitInt();
614
0
    moveOffset(toskip);
615
0
  }
616
617
  void decreaseAndSkip32BitInt(uint32_t decrease)
618
0
  {
619
0
    const char *p = d_packet + d_offset;
620
0
    moveOffset(4);
621
0
622
0
    uint32_t tmp;
623
0
    memcpy(&tmp, p, sizeof(tmp));
624
0
    tmp = ntohl(tmp);
625
0
    if (tmp > decrease) {
626
0
      tmp -= decrease;
627
0
    } else {
628
0
      tmp = 0;
629
0
    }
630
0
    tmp = htonl(tmp);
631
0
    memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
632
0
  }
633
634
  void setAndSkip32BitInt(uint32_t value)
635
0
  {
636
0
    moveOffset(4);
637
0
638
0
    value = htonl(value);
639
0
    memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
640
0
  }
641
642
  uint32_t getOffset() const
643
0
  {
644
0
    return d_offset;
645
0
  }
646
647
private:
648
  void moveOffset(uint16_t by)
649
0
  {
650
0
    d_notyouroffset += by;
651
0
    if(d_notyouroffset > d_length)
652
0
      throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
653
0
      + std::to_string(d_length) );
654
0
  }
655
656
  void rewindOffset(uint16_t by)
657
0
  {
658
0
    if(d_notyouroffset < by)
659
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
660
0
                              + std::to_string(by));
661
0
    d_notyouroffset -= by;
662
0
    if(d_notyouroffset < 12)
663
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
664
0
                              + std::to_string(12));
665
0
  }
666
667
  char* d_packet;
668
  size_t d_length;
669
670
  uint32_t d_notyouroffset;  // only 'moveOffset' can touch this
671
  const uint32_t&  d_offset; // look.. but don't touch
672
};