Coverage Report

Created: 2026-05-30 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dnsparser.hh
Line
Count
Source
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
8.22k
  MOADNSException(const string& str) : runtime_error(str)
62
8.22k
  {}
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
10.3k
    : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content), d_internal(internalRepresentation)
73
10.3k
  {
74
10.3k
    if(content.size() > std::numeric_limits<uint16_t>::max())
75
0
      throw std::out_of_range("packet too large");
76
77
10.3k
    d_recordlen = (uint16_t) content.size();
78
10.3k
    not_used = 0;
79
10.3k
  }
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
7.29k
  {
90
7.29k
    val=get32BitInt();
91
7.29k
  }
92
93
  void xfrIP(uint32_t& val)
94
  {
95
    xfr32BitInt(val);
96
    val=htonl(val);
97
  }
98
99
  void xfrIP6(std::string &val) {
100
    xfrBlob(val, 16);
101
  }
102
103
1.09k
  void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
104
1.09k
    string blob;
105
1.09k
    if (version == 4) xfrBlob(blob, 4);
106
88
    else if (version == 6) xfrBlob(blob, 16);
107
0
    else throw runtime_error("invalid IP protocol");
108
1.09k
    val = makeComboAddressFromRaw(version, blob);
109
1.09k
  }
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
  {
119
    xfr32BitInt(val);
120
  }
121
122
123
  void xfr16BitInt(uint16_t& val)
124
29.3k
  {
125
29.3k
    val=get16BitInt();
126
29.3k
  }
127
128
  void xfrType(uint16_t& val)
129
1.14k
  {
130
1.14k
    xfr16BitInt(val);
131
1.14k
  }
132
133
134
  void xfr8BitInt(uint8_t& val)
135
23.9k
  {
136
23.9k
    val=get8BitInt();
137
23.9k
  }
138
139
  void xfrName(DNSName& name, bool /* compress */ = false)
140
11.1k
  {
141
11.1k
    name = getName();
142
11.1k
  }
143
144
  void xfrText(string &text, bool multi=false, bool lenField=true)
145
  {
146
    text=getText(multi, lenField);
147
  }
148
149
  void xfrUnquotedText(string &text, bool lenField){
150
    text=getUnquotedText(lenField);
151
  }
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
  /* Whether the current position is at (or after, which would indicate a problem
168
     and should have been detected earlier) the end of wire data corresponding to
169
     the current DNS record */
170
  bool eof() const
171
  {
172
    return d_pos >= (d_startrecordpos + d_recordlen);
173
  }
174
175
  /* Returns a string, formatted for human/log consumption, containing information
176
     about the wire data from the current position to the end of the current DNS record */
177
  std::string getRemaining() const {
178
    return "Remaining data from PacketReader, current position " + std::to_string(d_pos) + " in packet of size " + std::to_string(d_content.size()) + ", end of record expected at " + std::to_string(d_startrecordpos + d_recordlen) + ": " + makeHexDump(std::string(d_content.begin() + d_pos, d_content.begin() + d_startrecordpos + d_recordlen));
179
  };
180
181
#if defined(PDNS_AUTH) // [
182
  /* This method moves the position to the end of the current DNS record.
183
     The only case where it makes sense to call this method is when processing ENT
184
     records, and only because of a bug in the authoritative server used to insert
185
     non-empty content for some ENT records (see https://github.com/PowerDNS/pdns/pull/17000)
186
  */
187
  void consumeRemaining()
188
  {
189
    d_pos = (d_startrecordpos + d_recordlen);
190
  }
191
#endif // ]
192
193
  uint16_t getPosition() const
194
86.3k
  {
195
86.3k
    return d_pos;
196
86.3k
  }
197
198
  void skip(uint16_t n)
199
0
  {
200
0
    size_t stop = d_pos;
201
0
    stop += n;
202
0
    if (stop > d_content.size()) {
203
0
      throw std::out_of_range("Attempt to skip bytes (" + std::to_string(n) + " starting at " + std::to_string(d_pos) + ") farther than the packet's end (" + std::to_string(d_content.size()) + ")");
204
0
    }
205
0
    d_pos += n;
206
0
  }
207
208
private:
209
  uint16_t d_pos;
210
  uint16_t d_startrecordpos; // needed for getBlob later on
211
  uint16_t d_recordlen;      // ditto
212
  uint16_t not_used; // Aligns the whole class on 8-byte boundaries
213
  const std::string_view d_content;
214
  bool d_internal;
215
};
216
217
struct DNSRecord;
218
219
class DNSRecordContent
220
{
221
public:
222
  static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr);
223
  static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr, uint16_t opcode);
224
  static std::shared_ptr<DNSRecordContent> make(uint16_t qtype, uint16_t qclass, const string& zone);
225
  static string upgradeContent(const DNSName& qname, const QType& qtype, const string& content);
226
227
  virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
228
77.5k
  virtual ~DNSRecordContent() = default;
229
  virtual void toPacket(DNSPacketWriter& pw) const = 0;
230
  // 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)
231
  [[nodiscard]] string serialize(const DNSName& qname, bool canonic = false, bool lowerCase = false, bool full = false) const
232
0
  {
233
0
    vector<uint8_t> packet;
234
0
    DNSPacketWriter packetWriter(packet, g_rootdnsname, QType::A);
235
236
0
    if (canonic) {
237
0
      packetWriter.setCanonic(true);
238
0
    }
239
0
    if (lowerCase) {
240
0
      packetWriter.setLowercase(true);
241
0
    }
242
243
0
    packetWriter.startRecord(qname, getType());
244
0
    toPacket(packetWriter);
245
246
0
    string record;
247
0
    if (full) {
248
0
      packetWriter.getWireFormatContent(record); // needs to be called before commit()
249
0
    } else {
250
0
      packetWriter.getRecordPayload(record); // needs to be called before commit()
251
0
    }
252
0
    return record;
253
0
  }
254
255
  virtual bool operator==(const DNSRecordContent& rhs) const
256
0
  {
257
0
    return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
258
0
  }
259
260
  // parse the content in wire format, possibly including compressed pointers pointing to the owner name.
261
  // internalRepresentation is set when the data comes from an internal source,
262
  // such as the LMDB backend.
263
  static shared_ptr<DNSRecordContent> deserialize(const DNSName& qname, uint16_t qtype, const string& serialized, uint16_t qclass=QClass::IN, bool internalRepresentation = false);
264
265
  void doRecordCheck(const struct DNSRecord&){}
266
267
  typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
268
  typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str);
269
270
  static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
271
  {
272
    assert(!d_locked.load()); // NOLINT: it's the API
273
    if(f)
274
      getTypemap()[pair(cl,ty)]=f;
275
    if(z)
276
      getZmakermap()[pair(cl,ty)]=z;
277
278
    getT2Namemap().emplace(pair(cl, ty), name);
279
    getN2Typemap().emplace(name, pair(cl, ty));
280
  }
281
282
  static bool isUnknownType(const string& name)
283
  {
284
    return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
285
  }
286
287
  static uint16_t TypeToNumber(const string& name)
288
  {
289
    n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
290
    if(iter != getN2Typemap().end())
291
      return iter->second.second;
292
293
    if (isUnknownType(name)) {
294
      return pdns::checked_stoi<uint16_t>(name.substr(4));
295
    }
296
297
    throw runtime_error("Unknown DNS type '"+name+"'");
298
  }
299
300
  static const string NumberToType(uint16_t num, uint16_t classnum = QClass::IN)
301
0
  {
302
0
    auto iter = getT2Namemap().find(pair(classnum, num));
303
0
    if(iter == getT2Namemap().end())
304
0
      return "TYPE" + std::to_string(num);
305
0
      //      throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
306
0
    return iter->second;
307
0
  }
308
309
  /**
310
   * \brief Return whether we have implemented a content representation for this type
311
   */
312
  static bool isRegisteredType(uint16_t rtype, uint16_t rclass = QClass::IN);
313
314
  virtual uint16_t getType() const = 0;
315
316
  static void lock()
317
  {
318
    d_locked.store(true);
319
  }
320
321
  [[nodiscard]] virtual size_t sizeEstimate() const = 0;
322
323
protected:
324
  typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
325
  typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
326
  typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
327
  typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
328
  static typemap_t& getTypemap();
329
  static t2namemap_t& getT2Namemap();
330
  static n2typemap_t& getN2Typemap();
331
  static zmakermap_t& getZmakermap();
332
  static std::atomic<bool> d_locked;
333
};
334
335
struct DNSRecord
336
{
337
  DNSRecord() :
338
86.3k
    d_class(QClass::IN)
339
86.3k
  {}
340
  explicit DNSRecord(const DNSResourceRecord& rr);
341
  DNSRecord(const std::string& name,
342
            std::shared_ptr<DNSRecordContent> content,
343
            const uint16_t type,
344
            const uint16_t qclass = QClass::IN,
345
            const uint32_t ttl = 86400,
346
            const uint16_t clen = 0,
347
            const DNSResourceRecord::Place place = DNSResourceRecord::ANSWER) :
348
    d_name(DNSName(name)),
349
    d_content(std::move(content)),
350
    d_type(type),
351
    d_class(qclass),
352
    d_ttl(ttl),
353
    d_clen(clen),
354
0
    d_place(place) {}
355
356
  DNSName d_name;
357
private:
358
  std::shared_ptr<const DNSRecordContent> d_content;
359
public:
360
  uint16_t d_type{};
361
  uint16_t d_class{};
362
  uint32_t d_ttl{};
363
  uint16_t d_clen{};
364
  DNSResourceRecord::Place d_place{DNSResourceRecord::ANSWER};
365
366
  [[nodiscard]] std::string print(const std::string& indent = "") const
367
0
  {
368
0
    std::stringstream s;
369
0
    s << indent << "Content = " << d_content->getZoneRepresentation() << std::endl;
370
0
    s << indent << "Type = " << d_type << std::endl;
371
0
    s << indent << "Class = " << d_class << std::endl;
372
0
    s << indent << "TTL = " << d_ttl << std::endl;
373
0
    s << indent << "clen = " << d_clen << std::endl;
374
0
    s << indent << "Place = " << std::to_string(d_place) << std::endl;
375
0
    return s.str();
376
0
  }
377
378
  [[nodiscard]] std::string toString() const
379
0
  {
380
0
    std::string ret(d_name.toLogString());
381
0
    ret += '|';
382
0
    ret += QType(d_type).toString();
383
0
    ret += '|';
384
0
    ret += getContent()->getZoneRepresentation();
385
0
    return ret;
386
0
  }
387
388
  void setContent(const std::shared_ptr<const DNSRecordContent>& content)
389
0
  {
390
0
    d_content = content;
391
0
  }
392
393
  void setContent(std::shared_ptr<const DNSRecordContent>&& content)
394
76.5k
  {
395
76.5k
    d_content = std::move(content);
396
76.5k
  }
397
398
  [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const
399
0
  {
400
0
    return d_content;
401
0
  }
402
403
  bool operator<(const DNSRecord& rhs) const
404
0
  {
405
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))
406
0
      return true;
407
0
408
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))
409
0
      return false;
410
0
411
0
    string lzrp, rzrp;
412
0
    if(d_content)
413
0
      lzrp=toLower(d_content->getZoneRepresentation());
414
0
    if(rhs.d_content)
415
0
      rzrp=toLower(rhs.d_content->getZoneRepresentation());
416
0
417
0
    return lzrp < rzrp;
418
0
  }
419
420
  // this orders in canonical order and keeps the SOA record on top
421
  static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
422
0
  {
423
0
    auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
424
0
    auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
425
0
426
0
    int res = a.d_name.canonCompare_three_way(b.d_name, true);
427
0
    if (res < 0) {
428
0
      return true;
429
0
    }
430
0
    if (res > 0) {
431
0
      return false;
432
0
    }
433
0
434
0
    if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl))
435
0
      return true;
436
0
437
0
    if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl))
438
0
      return false;
439
0
440
0
    string lzrp, rzrp;
441
0
    if(a.d_content)
442
0
      lzrp = a.d_content->getZoneRepresentation();
443
0
    if(b.d_content)
444
0
      rzrp = b.d_content->getZoneRepresentation();
445
0
446
0
    switch (a.d_type) {
447
0
    case QType::TXT:
448
0
    case QType::SPF:
449
0
#if !defined(RECURSOR)
450
0
    case QType::LUA:
451
0
#endif
452
0
      return lzrp < rzrp;
453
0
    default:
454
0
      return toLower(lzrp) < toLower(rzrp);
455
0
    }
456
0
  }
457
458
  bool operator==(const DNSRecord& rhs) const
459
0
  {
460
0
    if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) {
461
0
      return false;
462
0
    }
463
0
464
0
    return *d_content == *rhs.d_content;
465
0
  }
466
467
  [[nodiscard]] size_t sizeEstimate() const
468
0
  {
469
0
    return sizeof(*this) + d_name.sizeEstimate() + (d_content ? d_content->sizeEstimate() : 0U);
470
0
  }
471
};
472
473
struct DNSZoneRecord
474
{
475
  domainid_t domain_id{UnknownDomainID};
476
  uint8_t scopeMask{0};
477
  int signttl{0};
478
  DNSName wildcardname;
479
  bool auth{true};
480
  bool disabled{false};
481
  DNSRecord dr;
482
483
0
  bool operator<(const DNSZoneRecord& other) const {
484
0
    return dr.d_ttl < other.dr.d_ttl;
485
0
  }
486
};
487
488
class UnknownRecordContent : public DNSRecordContent
489
{
490
public:
491
  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
492
51.3k
    : d_dr(dr)
493
51.3k
  {
494
51.3k
    pr.copyRecord(d_record, dr.d_clen);
495
51.3k
  }
496
497
  UnknownRecordContent(const string& zone);
498
499
  string getZoneRepresentation(bool noDot) const override;
500
  void toPacket(DNSPacketWriter& pw) const override;
501
  uint16_t getType() const override
502
0
  {
503
0
    return d_dr.d_type;
504
0
  }
505
506
  const vector<uint8_t>& getRawContent() const
507
0
  {
508
0
    return d_record;
509
0
  }
510
511
  [[nodiscard]] size_t sizeEstimate() const override
512
0
  {
513
0
    return sizeof(*this) + d_dr.sizeEstimate() + d_record.size();
514
0
  }
515
516
private:
517
  DNSRecord d_dr;
518
  vector<uint8_t> d_record;
519
};
520
521
//! This class can be used to parse incoming packets, and is copyable
522
class MOADNSParser : public boost::noncopyable
523
{
524
public:
525
  //! Parse from a string
526
  MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
527
0
  {
528
0
    init(query, buffer);
529
0
  }
530
531
  //! Parse from a pointer and length
532
  MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
533
  {
534
    init(query, std::string_view(packet, len));
535
  }
536
537
  DNSName d_qname;
538
  uint16_t d_qclass, d_qtype;
539
  dnsheader d_header;
540
541
  using answers_t = vector<DNSRecord>;
542
543
  //! All answers contained in this packet (everything *but* the question section)
544
  answers_t d_answers;
545
546
  uint16_t getTSIGPos() const
547
0
  {
548
0
    return d_tsigPos;
549
0
  }
550
551
  bool hasEDNS() const;
552
553
private:
554
  void init(bool query, const std::string_view& packet);
555
  uint16_t d_tsigPos;
556
};
557
558
string simpleCompress(const string& label, const string& root="");
559
void shuffleDNSPacket(char* packet, size_t length, const dnsheader_aligned& aligned_dh);
560
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&);
561
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&);
562
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor);
563
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes);
564
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes);
565
void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes);
566
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
567
uint32_t getDNSPacketLength(const char* packet, size_t length);
568
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
569
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
570
/* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata
571
   to the visitor. Stops whenever the visitor returns false or at the end of the packet */
572
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor);
573
574
template<typename T>
575
std::shared_ptr<const T> getRR(const DNSRecord& dr)
576
{
577
  return std::dynamic_pointer_cast<const T>(dr.getContent());
578
}
579
580
/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
581
 *  If you survive that, feel free to read from the pointer */
582
class DNSPacketMangler
583
{
584
public:
585
  explicit DNSPacketMangler(std::string& packet)
586
    : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
587
0
  {}
588
  DNSPacketMangler(char* packet, size_t length)
589
0
    : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
590
0
  {}
591
592
  /*! Advances past a wire-format domain name
593
   * The name is not checked for adherence to length restrictions.
594
   * Compression pointers are not followed.
595
   */
596
  void skipDomainName()
597
0
  {
598
0
    uint8_t len;
599
0
    while((len=get8BitInt())) {
600
0
      if(len >= 0xc0) { // extended label
601
0
        get8BitInt();
602
0
        return;
603
0
      }
604
0
      skipBytes(len);
605
0
    }
606
0
  }
607
608
  void skipBytes(uint16_t bytes)
609
0
  {
610
0
    moveOffset(bytes);
611
0
  }
612
  void rewindBytes(uint16_t by)
613
0
  {
614
0
    rewindOffset(by);
615
0
  }
616
  uint32_t get32BitInt()
617
0
  {
618
0
    const char* p = d_packet + d_offset;
619
0
    moveOffset(4);
620
0
    uint32_t ret;
621
0
    memcpy(&ret, p, sizeof(ret));
622
0
    return ntohl(ret);
623
0
  }
624
  uint16_t get16BitInt()
625
0
  {
626
0
    const char* p = d_packet + d_offset;
627
0
    moveOffset(2);
628
0
    uint16_t ret;
629
0
    memcpy(&ret, p, sizeof(ret));
630
0
    return ntohs(ret);
631
0
  }
632
633
  uint8_t get8BitInt()
634
0
  {
635
0
    const char* p = d_packet + d_offset;
636
0
    moveOffset(1);
637
0
    return *p;
638
0
  }
639
640
  void skipRData()
641
0
  {
642
0
    auto toskip = get16BitInt();
643
0
    moveOffset(toskip);
644
0
  }
645
646
  std::pair<uint32_t, uint32_t> skipRDataAndReturnOffsets()
647
0
  {
648
0
    auto toskip = get16BitInt();
649
0
    uint32_t start = d_offset;
650
0
    moveOffset(toskip);
651
0
    uint32_t end = d_offset;
652
0
    return std::pair<uint32_t,uint32_t>(start, end);
653
0
  }
654
655
  void decreaseAndSkip32BitInt(uint32_t decrease)
656
0
  {
657
0
    const char *p = d_packet + d_offset;
658
0
    moveOffset(4);
659
660
0
    uint32_t tmp;
661
0
    memcpy(&tmp, p, sizeof(tmp));
662
0
    tmp = ntohl(tmp);
663
0
    if (tmp > decrease) {
664
0
      tmp -= decrease;
665
0
    } else {
666
0
      tmp = 0;
667
0
    }
668
0
    tmp = htonl(tmp);
669
0
    memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
670
0
  }
671
672
  void setAndSkip32BitInt(uint32_t value)
673
0
  {
674
0
    moveOffset(4);
675
676
0
    value = htonl(value);
677
0
    memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
678
0
  }
679
680
  uint32_t getOffset() const
681
0
  {
682
0
    return d_offset;
683
0
  }
684
685
0
  void swapInPlace(std::pair<uint32_t, uint32_t> a, std::pair<uint32_t, uint32_t> b) {
686
    // some basic range checks
687
0
    if (b.first < a.first) {
688
0
      std::swap(a, b);
689
0
    }
690
0
    if (a.second-a.first != b.second-b.first) {
691
0
      throw std::out_of_range("swap: segments have different lengths");
692
0
    }
693
0
    if (a.second <= a.first) {
694
0
      throw std::out_of_range("swap: ending of segment before start of segment");
695
0
    }
696
0
    if (a.second > b.first) {
697
0
      throw std::out_of_range("swap: overlapping segments");
698
0
    }
699
0
    if (b.second > d_length) {
700
0
      throw std::out_of_range("swap: ending of segment after end of array");
701
0
    }
702
    // don't allow to swap what we haven't read yet
703
0
    if (b.second > d_offset) {
704
0
      throw std::out_of_range("swap: ending of segment after current offset");
705
0
    }
706
0
    std::swap_ranges(d_packet+a.first, d_packet+a.second, d_packet+b.first);
707
0
  }
708
709
private:
710
  void moveOffset(uint16_t by)
711
0
  {
712
0
    d_notyouroffset += by;
713
0
    if(d_notyouroffset > d_length)
714
0
      throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
715
0
      + std::to_string(d_length) );
716
0
  }
717
718
  void rewindOffset(uint16_t by)
719
0
  {
720
0
    if(d_notyouroffset < by)
721
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
722
0
                              + std::to_string(by));
723
0
    d_notyouroffset -= by;
724
0
    if(d_notyouroffset < 12)
725
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
726
0
                              + std::to_string(12));
727
0
  }
728
729
  char* d_packet;
730
  size_t d_length;
731
732
  uint32_t d_notyouroffset;  // only 'moveOffset' can touch this
733
  const uint32_t&  d_offset; // look.. but don't touch
734
};
735
736
string txtEscape(const string &name);