Coverage Report

Created: 2023-09-25 07:00

/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 <map>
24
#include <sstream>
25
#include <stdexcept>
26
#include <iostream>
27
#include <unordered_set>
28
#include <utility>
29
#include <vector>
30
#include <cerrno>
31
// #include <netinet/in.h>
32
#include "misc.hh"
33
34
#include "dns.hh"
35
#include "dnswriter.hh"
36
#include "dnsname.hh"
37
#include "noinitvector.hh"
38
#include "pdnsexception.hh"
39
#include "iputils.hh"
40
#include "svc-records.hh"
41
42
/** DNS records have three representations:
43
    1) in the packet
44
    2) parsed in a class, ready for use
45
    3) in the zone
46
47
    We should implement bidirectional transitions between 1&2 and 2&3.
48
    Currently we have: 1 -> 2
49
                       2 -> 3
50
51
    We can add:        2 -> 1  easily by reversing the packetwriter
52
    And we might be able to reverse 2 -> 3 as well
53
*/
54
55
#include "namespaces.hh"
56
57
class MOADNSException : public runtime_error
58
{
59
public:
60
  MOADNSException(const string& str) : runtime_error(str)
61
0
  {}
62
};
63
64
65
class MOADNSParser;
66
67
class PacketReader
68
{
69
public:
70
  PacketReader(const std::string_view& content, uint16_t initialPos=sizeof(dnsheader))
71
    : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content)
72
0
  {
73
0
    if(content.size() > std::numeric_limits<uint16_t>::max())
74
0
      throw std::out_of_range("packet too large");
75
76
0
    d_recordlen = (uint16_t) content.size();
77
0
    not_used = 0;
78
0
  }
79
80
  uint32_t get32BitInt();
81
  uint16_t get16BitInt();
82
  uint8_t get8BitInt();
83
84
  void xfrNodeOrLocatorID(NodeOrLocatorID& val);
85
  void xfr48BitInt(uint64_t& val);
86
87
  void xfr32BitInt(uint32_t& val)
88
0
  {
89
0
    val=get32BitInt();
90
0
  }
91
92
  void xfrIP(uint32_t& val)
93
0
  {
94
0
    xfr32BitInt(val);
95
0
    val=htonl(val);
96
0
  }
97
98
0
  void xfrIP6(std::string &val) {
99
0
    xfrBlob(val, 16);
100
0
  }
101
102
0
  void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
103
0
    string blob;
104
0
    if (version == 4) xfrBlob(blob, 4);
105
0
    else if (version == 6) xfrBlob(blob, 16);
106
0
    else throw runtime_error("invalid IP protocol");
107
0
    val = makeComboAddressFromRaw(version, blob);
108
0
  }
109
110
0
  void xfrCAPort(ComboAddress &val) {
111
0
    uint16_t port;
112
0
    xfr16BitInt(port);
113
0
    val.sin4.sin_port = port;
114
0
  }
115
116
  void xfrTime(uint32_t& val)
117
0
  {
118
0
    xfr32BitInt(val);
119
0
  }
120
121
122
  void xfr16BitInt(uint16_t& val)
123
0
  {
124
0
    val=get16BitInt();
125
0
  }
126
127
  void xfrType(uint16_t& val)
128
0
  {
129
0
    xfr16BitInt(val);
130
0
  }
131
132
133
  void xfr8BitInt(uint8_t& val)
134
0
  {
135
0
    val=get8BitInt();
136
0
  }
137
138
  void xfrName(DNSName& name, bool /* compress */ = false, bool /* noDot */ = false)
139
0
  {
140
0
    name = getName();
141
0
  }
142
143
  void xfrText(string &text, bool multi=false, bool lenField=true)
144
0
  {
145
0
    text=getText(multi, lenField);
146
0
  }
147
148
0
  void xfrUnquotedText(string &text, bool lenField){
149
0
    text=getUnquotedText(lenField);
150
0
  }
151
152
  void xfrBlob(string& blob);
153
  void xfrBlobNoSpaces(string& blob, int len);
154
  void xfrBlob(string& blob, int length);
155
  void xfrHexBlob(string& blob, bool keepReading=false);
156
  void xfrSvcParamKeyVals(set<SvcParam> &kvs);
157
158
  void getDnsrecordheader(struct dnsrecordheader &ah);
159
  void copyRecord(vector<unsigned char>& dest, uint16_t len);
160
  void copyRecord(unsigned char* dest, uint16_t len);
161
162
  DNSName getName();
163
  string getText(bool multi, bool lenField);
164
  string getUnquotedText(bool lenField);
165
166
167
0
  bool eof() { return true; };
168
0
  const string getRemaining() const {
169
0
    return "";
170
0
  };
171
172
  uint16_t getPosition() const
173
0
  {
174
0
    return d_pos;
175
0
  }
176
177
  void skip(uint16_t n)
178
0
  {
179
0
    d_pos += n;
180
0
  }
181
182
private:
183
  uint16_t d_pos;
184
  uint16_t d_startrecordpos; // needed for getBlob later on
185
  uint16_t d_recordlen;      // ditto
186
  uint16_t not_used; // Aligns the whole class on 8-byte boundaries
187
  const std::string_view d_content;
188
};
189
190
struct DNSRecord;
191
192
class DNSRecordContent
193
{
194
public:
195
  static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr);
196
  static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t opcode);
197
  static std::shared_ptr<DNSRecordContent> mastermake(uint16_t qtype, uint16_t qclass, const string& zone);
198
  static string upgradeContent(const DNSName& qname, const QType& qtype, const string& content);
199
200
  virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
201
0
  virtual ~DNSRecordContent() {}
202
  virtual void toPacket(DNSPacketWriter& pw) const = 0;
203
  // returns the wire format of the content, possibly including compressed pointers pointing to the owner name (unless canonic or lowerCase are set)
204
  string serialize(const DNSName& qname, bool canonic=false, bool lowerCase=false) const
205
0
  {
206
0
    vector<uint8_t> packet;
207
0
    DNSPacketWriter pw(packet, g_rootdnsname, 1);
208
0
    if(canonic)
209
0
      pw.setCanonic(true);
210
211
0
    if(lowerCase)
212
0
      pw.setLowercase(true);
213
214
0
    pw.startRecord(qname, this->getType());
215
0
    this->toPacket(pw);
216
217
0
    string record;
218
0
    pw.getRecordPayload(record); // needs to be called before commit()
219
0
    return record;
220
0
  }
221
222
  virtual bool operator==(const DNSRecordContent& rhs) const
223
0
  {
224
0
    return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
225
0
  }
226
227
  // parse the content in wire format, possibly including compressed pointers pointing to the owner name
228
  static shared_ptr<DNSRecordContent> deserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
229
230
0
  void doRecordCheck(const struct DNSRecord&){}
231
232
  typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
233
  typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str);
234
235
  static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
236
110
  {
237
110
    if(f)
238
107
      getTypemap()[pair(cl,ty)]=f;
239
110
    if(z)
240
107
      getZmakermap()[pair(cl,ty)]=z;
241
242
110
    getT2Namemap().emplace(pair(cl, ty), name);
243
110
    getN2Typemap().emplace(name, pair(cl, ty));
244
110
  }
245
246
  static void unregist(uint16_t cl, uint16_t ty)
247
0
  {
248
0
    auto key = pair(cl, ty);
249
0
    getTypemap().erase(key);
250
0
    getZmakermap().erase(key);
251
0
  }
252
253
  static bool isUnknownType(const string& name)
254
2.78k
  {
255
2.78k
    return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
256
2.78k
  }
257
258
  static uint16_t TypeToNumber(const string& name)
259
153k
  {
260
153k
    n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
261
153k
    if(iter != getN2Typemap().end())
262
150k
      return iter->second.second;
263
264
2.78k
    if (isUnknownType(name)) {
265
2.26k
      return pdns::checked_stoi<uint16_t>(name.substr(4));
266
2.26k
    }
267
268
511
    throw runtime_error("Unknown DNS type '"+name+"'");
269
2.78k
  }
270
271
  static const string NumberToType(uint16_t num, uint16_t classnum = QClass::IN)
272
0
  {
273
0
    auto iter = getT2Namemap().find(pair(classnum, num));
274
0
    if(iter == getT2Namemap().end())
275
0
      return "TYPE" + std::to_string(num);
276
      //      throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
277
0
    return iter->second;
278
0
  }
279
280
  /**
281
   * \brief Return whether we have implemented a content representation for this type
282
   */
283
  static bool isRegisteredType(uint16_t rtype, uint16_t rclass = QClass::IN);
284
285
  virtual uint16_t getType() const = 0;
286
287
protected:
288
  typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
289
  typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
290
  typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
291
  typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
292
  static typemap_t& getTypemap();
293
  static t2namemap_t& getT2Namemap();
294
  static n2typemap_t& getN2Typemap();
295
  static zmakermap_t& getZmakermap();
296
};
297
298
struct DNSRecord
299
{
300
  DNSRecord() :
301
    d_class(QClass::IN)
302
0
  {}
303
  explicit DNSRecord(const DNSResourceRecord& rr);
304
  DNSRecord(const std::string& name,
305
            std::shared_ptr<DNSRecordContent> content,
306
            const uint16_t type,
307
            const uint16_t qclass = QClass::IN,
308
            const uint32_t ttl = 86400,
309
            const uint16_t clen = 0,
310
            const DNSResourceRecord::Place place = DNSResourceRecord::ANSWER) :
311
    d_name(DNSName(name)),
312
    d_content(std::move(content)),
313
    d_type(type),
314
    d_class(qclass),
315
    d_ttl(ttl),
316
    d_clen(clen),
317
0
    d_place(place) {}
318
319
  DNSName d_name;
320
private:
321
  std::shared_ptr<const DNSRecordContent> d_content;
322
public:
323
  uint16_t d_type{};
324
  uint16_t d_class{};
325
  uint32_t d_ttl{};
326
  uint16_t d_clen{};
327
  DNSResourceRecord::Place d_place{DNSResourceRecord::ANSWER};
328
329
  [[nodiscard]] std::string print(const std::string& indent = "") const
330
0
  {
331
0
    std::stringstream s;
332
0
    s << indent << "Content = " << d_content->getZoneRepresentation() << std::endl;
333
0
    s << indent << "Type = " << d_type << std::endl;
334
0
    s << indent << "Class = " << d_class << std::endl;
335
0
    s << indent << "TTL = " << d_ttl << std::endl;
336
0
    s << indent << "clen = " << d_clen << std::endl;
337
0
    s << indent << "Place = " << std::to_string(d_place) << std::endl;
338
0
    return s.str();
339
0
  }
340
341
  void setContent(const std::shared_ptr<const DNSRecordContent>& content)
342
0
  {
343
0
    d_content = content;
344
0
  }
345
346
  void setContent(std::shared_ptr<const DNSRecordContent>&& content)
347
0
  {
348
0
    d_content = std::move(content);
349
0
  }
350
351
  [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const
352
0
  {
353
0
    return d_content;
354
0
  }
355
356
  bool operator<(const DNSRecord& rhs) const
357
0
  {
358
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))
359
0
      return true;
360
0
361
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))
362
0
      return false;
363
0
364
0
    string lzrp, rzrp;
365
0
    if(d_content)
366
0
      lzrp=toLower(d_content->getZoneRepresentation());
367
0
    if(rhs.d_content)
368
0
      rzrp=toLower(rhs.d_content->getZoneRepresentation());
369
0
370
0
    return lzrp < rzrp;
371
0
  }
372
373
  // this orders in canonical order and keeps the SOA record on top
374
  static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
375
0
  {
376
0
    auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
377
0
    auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
378
0
379
0
    if(a.d_name.canonCompare(b.d_name))
380
0
      return true;
381
0
    if(b.d_name.canonCompare(a.d_name))
382
0
      return false;
383
0
384
0
    if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl))
385
0
      return true;
386
0
387
0
    if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl))
388
0
      return false;
389
0
390
0
    string lzrp, rzrp;
391
0
    if(a.d_content)
392
0
      lzrp = a.d_content->getZoneRepresentation();
393
0
    if(b.d_content)
394
0
      rzrp = b.d_content->getZoneRepresentation();
395
0
396
0
    switch (a.d_type) {
397
0
    case QType::TXT:
398
0
    case QType::SPF:
399
0
#if !defined(RECURSOR)
400
0
    case QType::LUA:
401
0
#endif
402
0
      return lzrp < rzrp;
403
0
    default:
404
0
      return toLower(lzrp) < toLower(rzrp);
405
0
    }
406
0
  }
407
408
  bool operator==(const DNSRecord& rhs) const
409
0
  {
410
0
    if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) {
411
0
      return false;
412
0
    }
413
0
414
0
    return *d_content == *rhs.d_content;
415
0
  }
416
};
417
418
struct DNSZoneRecord
419
{
420
  int domain_id{-1};
421
  uint8_t scopeMask{0};
422
  int signttl{0};
423
  DNSName wildcardname;
424
  bool auth{true};
425
  bool disabled{false};
426
  DNSRecord dr;
427
};
428
429
class UnknownRecordContent : public DNSRecordContent
430
{
431
public:
432
  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
433
    : d_dr(dr)
434
0
  {
435
0
    pr.copyRecord(d_record, dr.d_clen);
436
0
  }
437
438
  UnknownRecordContent(const string& zone);
439
440
  string getZoneRepresentation(bool noDot) const override;
441
  void toPacket(DNSPacketWriter& pw) const override;
442
  uint16_t getType() const override
443
0
  {
444
0
    return d_dr.d_type;
445
0
  }
446
447
  const vector<uint8_t>& getRawContent() const
448
0
  {
449
0
    return d_record;
450
0
  }
451
452
private:
453
  DNSRecord d_dr;
454
  vector<uint8_t> d_record;
455
};
456
457
//! This class can be used to parse incoming packets, and is copyable
458
class MOADNSParser : public boost::noncopyable
459
{
460
public:
461
  //! Parse from a string
462
  MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
463
0
  {
464
0
    init(query, buffer);
465
0
  }
466
467
  //! Parse from a pointer and length
468
  MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
469
0
  {
470
0
    init(query, std::string_view(packet, len));
471
0
  }
472
473
  DNSName d_qname;
474
  uint16_t d_qclass, d_qtype;
475
  //uint8_t d_rcode;
476
  dnsheader d_header;
477
478
  typedef vector<pair<DNSRecord, uint16_t > > answers_t;
479
480
  //! All answers contained in this packet (everything *but* the question section)
481
  answers_t d_answers;
482
483
  uint16_t getTSIGPos() const
484
0
  {
485
0
    return d_tsigPos;
486
0
  }
487
488
  bool hasEDNS() const;
489
490
private:
491
  void init(bool query, const std::string_view& packet);
492
  uint16_t d_tsigPos;
493
};
494
495
string simpleCompress(const string& label, const string& root="");
496
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&);
497
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&);
498
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor);
499
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes);
500
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes);
501
void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes);
502
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
503
uint32_t getDNSPacketLength(const char* packet, size_t length);
504
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
505
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
506
/* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata
507
   to the visitor. Stops whenever the visitor returns false or at the end of the packet */
508
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor);
509
510
template<typename T>
511
std::shared_ptr<const T> getRR(const DNSRecord& dr)
512
0
{
513
0
  return std::dynamic_pointer_cast<const T>(dr.getContent());
514
0
}
Unexecuted instantiation: std::__1::shared_ptr<OPTRecordContent const> getRR<OPTRecordContent>(DNSRecord const&)
Unexecuted instantiation: std::__1::shared_ptr<ARecordContent const> getRR<ARecordContent>(DNSRecord const&)
Unexecuted instantiation: std::__1::shared_ptr<AAAARecordContent const> getRR<AAAARecordContent>(DNSRecord const&)
515
516
/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
517
 *  If you survive that, feel free to read from the pointer */
518
class DNSPacketMangler
519
{
520
public:
521
  explicit DNSPacketMangler(std::string& packet)
522
    : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
523
0
  {}
524
  DNSPacketMangler(char* packet, size_t length)
525
    : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
526
0
  {}
527
528
  /*! Advances past a wire-format domain name
529
   * The name is not checked for adherence to length restrictions.
530
   * Compression pointers are not followed.
531
   */
532
  void skipDomainName()
533
0
  {
534
0
    uint8_t len;
535
0
    while((len=get8BitInt())) {
536
0
      if(len >= 0xc0) { // extended label
537
0
        get8BitInt();
538
0
        return;
539
0
      }
540
0
      skipBytes(len);
541
0
    }
542
0
  }
543
544
  void skipBytes(uint16_t bytes)
545
0
  {
546
0
    moveOffset(bytes);
547
0
  }
548
  void rewindBytes(uint16_t by)
549
0
  {
550
0
    rewindOffset(by);
551
0
  }
552
  uint32_t get32BitInt()
553
0
  {
554
0
    const char* p = d_packet + d_offset;
555
0
    moveOffset(4);
556
0
    uint32_t ret;
557
0
    memcpy(&ret, p, sizeof(ret));
558
0
    return ntohl(ret);
559
0
  }
560
  uint16_t get16BitInt()
561
0
  {
562
0
    const char* p = d_packet + d_offset;
563
0
    moveOffset(2);
564
0
    uint16_t ret;
565
0
    memcpy(&ret, p, sizeof(ret));
566
0
    return ntohs(ret);
567
0
  }
568
569
  uint8_t get8BitInt()
570
0
  {
571
0
    const char* p = d_packet + d_offset;
572
0
    moveOffset(1);
573
0
    return *p;
574
0
  }
575
576
  void skipRData()
577
0
  {
578
0
    int toskip = get16BitInt();
579
0
    moveOffset(toskip);
580
0
  }
581
582
  void decreaseAndSkip32BitInt(uint32_t decrease)
583
0
  {
584
0
    const char *p = d_packet + d_offset;
585
0
    moveOffset(4);
586
587
0
    uint32_t tmp;
588
0
    memcpy(&tmp, p, sizeof(tmp));
589
0
    tmp = ntohl(tmp);
590
0
    if (tmp > decrease) {
591
0
      tmp -= decrease;
592
0
    } else {
593
0
      tmp = 0;
594
0
    }
595
0
    tmp = htonl(tmp);
596
0
    memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
597
0
  }
598
599
  void setAndSkip32BitInt(uint32_t value)
600
0
  {
601
0
    moveOffset(4);
602
603
0
    value = htonl(value);
604
0
    memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
605
0
  }
606
607
  uint32_t getOffset() const
608
0
  {
609
0
    return d_offset;
610
0
  }
611
612
private:
613
  void moveOffset(uint16_t by)
614
0
  {
615
0
    d_notyouroffset += by;
616
0
    if(d_notyouroffset > d_length)
617
0
      throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
618
0
      + std::to_string(d_length) );
619
0
  }
620
621
  void rewindOffset(uint16_t by)
622
0
  {
623
0
    if(d_notyouroffset < by)
624
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
625
0
                              + std::to_string(by));
626
0
    d_notyouroffset -= by;
627
0
    if(d_notyouroffset < 12)
628
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
629
0
                              + std::to_string(12));
630
0
  }
631
632
  char* d_packet;
633
  size_t d_length;
634
635
  uint32_t d_notyouroffset;  // only 'moveOffset' can touch this
636
  const uint32_t&  d_offset; // look.. but don't touch
637
};