Coverage Report

Created: 2024-11-12 06:21

/src/pdns/pdns/dnsdistdist/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
8.02k
  MOADNSException(const string& str) : runtime_error(str)
61
8.02k
  {}
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
10.5k
    : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content)
72
10.5k
  {
73
10.5k
    if(content.size() > std::numeric_limits<uint16_t>::max())
74
0
      throw std::out_of_range("packet too large");
75
76
10.5k
    d_recordlen = (uint16_t) content.size();
77
10.5k
    not_used = 0;
78
10.5k
  }
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
31.8k
  {
89
31.8k
    val=get32BitInt();
90
31.8k
  }
91
92
  void xfrIP(uint32_t& val)
93
  {
94
    xfr32BitInt(val);
95
    val=htonl(val);
96
  }
97
98
  void xfrIP6(std::string &val) {
99
    xfrBlob(val, 16);
100
  }
101
102
32.2k
  void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
103
32.2k
    string blob;
104
32.2k
    if (version == 4) xfrBlob(blob, 4);
105
5.42k
    else if (version == 6) xfrBlob(blob, 16);
106
0
    else throw runtime_error("invalid IP protocol");
107
32.2k
    val = makeComboAddressFromRaw(version, blob);
108
32.2k
  }
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
  {
118
    xfr32BitInt(val);
119
  }
120
121
122
  void xfr16BitInt(uint16_t& val)
123
353k
  {
124
353k
    val=get16BitInt();
125
353k
  }
126
127
  void xfrType(uint16_t& val)
128
1.63k
  {
129
1.63k
    xfr16BitInt(val);
130
1.63k
  }
131
132
133
  void xfr8BitInt(uint8_t& val)
134
185k
  {
135
185k
    val=get8BitInt();
136
185k
  }
137
138
  void xfrName(DNSName& name, bool /* compress */ = false, bool /* noDot */ = false)
139
39.9k
  {
140
39.9k
    name = getName();
141
39.9k
  }
142
143
  void xfrText(string &text, bool multi=false, bool lenField=true)
144
  {
145
    text=getText(multi, lenField);
146
  }
147
148
  void xfrUnquotedText(string &text, bool lenField){
149
    text=getUnquotedText(lenField);
150
  }
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
  bool eof() { return true; };
168
0
  const string getRemaining() const {
169
0
    return "";
170
0
  };
171
172
  uint16_t getPosition() const
173
156k
  {
174
156k
    return d_pos;
175
156k
  }
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> make(const DNSRecord& dr, PacketReader& pr);
196
  static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr, uint16_t opcode);
197
  static std::shared_ptr<DNSRecordContent> make(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
152k
  virtual ~DNSRecordContent() = default;
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
  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
  {
237
    if(f)
238
      getTypemap()[pair(cl,ty)]=f;
239
    if(z)
240
      getZmakermap()[pair(cl,ty)]=z;
241
242
    getT2Namemap().emplace(pair(cl, ty), name);
243
    getN2Typemap().emplace(name, pair(cl, ty));
244
  }
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
  {
255
    return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
256
  }
257
258
  static uint16_t TypeToNumber(const string& name)
259
  {
260
    n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
261
    if(iter != getN2Typemap().end())
262
      return iter->second.second;
263
264
    if (isUnknownType(name)) {
265
      return pdns::checked_stoi<uint16_t>(name.substr(4));
266
    }
267
268
    throw runtime_error("Unknown DNS type '"+name+"'");
269
  }
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
0
      //      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
156k
    d_class(QClass::IN)
302
156k
  {}
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
  [[nodiscard]] std::string toString() const
342
0
  {
343
0
    std::string ret(d_name.toLogString());
344
0
    ret += '|';
345
0
    ret += QType(d_type).toString();
346
0
    ret += '|';
347
0
    ret += getContent()->getZoneRepresentation();
348
0
    return ret;
349
0
  }
350
351
  void setContent(const std::shared_ptr<const DNSRecordContent>& content)
352
0
  {
353
0
    d_content = content;
354
0
  }
355
356
  void setContent(std::shared_ptr<const DNSRecordContent>&& content)
357
146k
  {
358
146k
    d_content = std::move(content);
359
146k
  }
360
361
  [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const
362
0
  {
363
0
    return d_content;
364
0
  }
365
366
  bool operator<(const DNSRecord& rhs) const
367
0
  {
368
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))
369
0
      return true;
370
0
371
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))
372
0
      return false;
373
0
374
0
    string lzrp, rzrp;
375
0
    if(d_content)
376
0
      lzrp=toLower(d_content->getZoneRepresentation());
377
0
    if(rhs.d_content)
378
0
      rzrp=toLower(rhs.d_content->getZoneRepresentation());
379
0
380
0
    return lzrp < rzrp;
381
0
  }
382
383
  // this orders in canonical order and keeps the SOA record on top
384
  static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
385
0
  {
386
0
    auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
387
0
    auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
388
0
389
0
    if(a.d_name.canonCompare(b.d_name))
390
0
      return true;
391
0
    if(b.d_name.canonCompare(a.d_name))
392
0
      return false;
393
0
394
0
    if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl))
395
0
      return true;
396
0
397
0
    if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl))
398
0
      return false;
399
0
400
0
    string lzrp, rzrp;
401
0
    if(a.d_content)
402
0
      lzrp = a.d_content->getZoneRepresentation();
403
0
    if(b.d_content)
404
0
      rzrp = b.d_content->getZoneRepresentation();
405
0
406
0
    switch (a.d_type) {
407
0
    case QType::TXT:
408
0
    case QType::SPF:
409
0
#if !defined(RECURSOR)
410
0
    case QType::LUA:
411
0
#endif
412
0
      return lzrp < rzrp;
413
0
    default:
414
0
      return toLower(lzrp) < toLower(rzrp);
415
0
    }
416
0
  }
417
418
  bool operator==(const DNSRecord& rhs) const
419
0
  {
420
0
    if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) {
421
0
      return false;
422
0
    }
423
0
424
0
    return *d_content == *rhs.d_content;
425
0
  }
426
};
427
428
struct DNSZoneRecord
429
{
430
  int domain_id{-1};
431
  uint8_t scopeMask{0};
432
  int signttl{0};
433
  DNSName wildcardname;
434
  bool auth{true};
435
  bool disabled{false};
436
  DNSRecord dr;
437
};
438
439
class UnknownRecordContent : public DNSRecordContent
440
{
441
public:
442
  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
443
75.0k
    : d_dr(dr)
444
75.0k
  {
445
75.0k
    pr.copyRecord(d_record, dr.d_clen);
446
75.0k
  }
447
448
  UnknownRecordContent(const string& zone);
449
450
  string getZoneRepresentation(bool noDot) const override;
451
  void toPacket(DNSPacketWriter& pw) const override;
452
  uint16_t getType() const override
453
0
  {
454
0
    return d_dr.d_type;
455
0
  }
456
457
  const vector<uint8_t>& getRawContent() const
458
0
  {
459
0
    return d_record;
460
0
  }
461
462
private:
463
  DNSRecord d_dr;
464
  vector<uint8_t> d_record;
465
};
466
467
//! This class can be used to parse incoming packets, and is copyable
468
class MOADNSParser : public boost::noncopyable
469
{
470
public:
471
  //! Parse from a string
472
  MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
473
0
  {
474
0
    init(query, buffer);
475
0
  }
476
477
  //! Parse from a pointer and length
478
  MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
479
  {
480
    init(query, std::string_view(packet, len));
481
  }
482
483
  DNSName d_qname;
484
  uint16_t d_qclass, d_qtype;
485
  dnsheader d_header;
486
487
  using answers_t = vector<DNSRecord>;
488
489
  //! All answers contained in this packet (everything *but* the question section)
490
  answers_t d_answers;
491
492
  uint16_t getTSIGPos() const
493
0
  {
494
0
    return d_tsigPos;
495
0
  }
496
497
  bool hasEDNS() const;
498
499
private:
500
  void init(bool query, const std::string_view& packet);
501
  uint16_t d_tsigPos;
502
};
503
504
string simpleCompress(const string& label, const string& root="");
505
void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&);
506
void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&);
507
void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor);
508
void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes);
509
void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes);
510
void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes);
511
uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
512
uint32_t getDNSPacketLength(const char* packet, size_t length);
513
uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
514
bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
515
/* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata
516
   to the visitor. Stops whenever the visitor returns false or at the end of the packet */
517
bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor);
518
519
template<typename T>
520
std::shared_ptr<const T> getRR(const DNSRecord& dr)
521
{
522
  return std::dynamic_pointer_cast<const T>(dr.getContent());
523
}
524
525
/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
526
 *  If you survive that, feel free to read from the pointer */
527
class DNSPacketMangler
528
{
529
public:
530
  explicit DNSPacketMangler(std::string& packet)
531
    : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
532
0
  {}
533
  DNSPacketMangler(char* packet, size_t length)
534
0
    : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
535
0
  {}
536
537
  /*! Advances past a wire-format domain name
538
   * The name is not checked for adherence to length restrictions.
539
   * Compression pointers are not followed.
540
   */
541
  void skipDomainName()
542
0
  {
543
0
    uint8_t len;
544
0
    while((len=get8BitInt())) {
545
0
      if(len >= 0xc0) { // extended label
546
0
        get8BitInt();
547
0
        return;
548
0
      }
549
0
      skipBytes(len);
550
0
    }
551
0
  }
552
553
  void skipBytes(uint16_t bytes)
554
0
  {
555
0
    moveOffset(bytes);
556
0
  }
557
  void rewindBytes(uint16_t by)
558
0
  {
559
0
    rewindOffset(by);
560
0
  }
561
  uint32_t get32BitInt()
562
0
  {
563
0
    const char* p = d_packet + d_offset;
564
0
    moveOffset(4);
565
0
    uint32_t ret;
566
0
    memcpy(&ret, p, sizeof(ret));
567
0
    return ntohl(ret);
568
0
  }
569
  uint16_t get16BitInt()
570
0
  {
571
0
    const char* p = d_packet + d_offset;
572
0
    moveOffset(2);
573
0
    uint16_t ret;
574
0
    memcpy(&ret, p, sizeof(ret));
575
0
    return ntohs(ret);
576
0
  }
577
578
  uint8_t get8BitInt()
579
0
  {
580
0
    const char* p = d_packet + d_offset;
581
0
    moveOffset(1);
582
0
    return *p;
583
0
  }
584
585
  void skipRData()
586
0
  {
587
0
    int toskip = get16BitInt();
588
0
    moveOffset(toskip);
589
0
  }
590
591
  void decreaseAndSkip32BitInt(uint32_t decrease)
592
0
  {
593
0
    const char *p = d_packet + d_offset;
594
0
    moveOffset(4);
595
596
0
    uint32_t tmp;
597
0
    memcpy(&tmp, p, sizeof(tmp));
598
0
    tmp = ntohl(tmp);
599
0
    if (tmp > decrease) {
600
0
      tmp -= decrease;
601
0
    } else {
602
0
      tmp = 0;
603
0
    }
604
0
    tmp = htonl(tmp);
605
0
    memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
606
0
  }
607
608
  void setAndSkip32BitInt(uint32_t value)
609
0
  {
610
0
    moveOffset(4);
611
612
0
    value = htonl(value);
613
0
    memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
614
0
  }
615
616
  uint32_t getOffset() const
617
0
  {
618
0
    return d_offset;
619
0
  }
620
621
private:
622
  void moveOffset(uint16_t by)
623
0
  {
624
0
    d_notyouroffset += by;
625
0
    if(d_notyouroffset > d_length)
626
0
      throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
627
0
      + std::to_string(d_length) );
628
0
  }
629
630
  void rewindOffset(uint16_t by)
631
0
  {
632
0
    if(d_notyouroffset < by)
633
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
634
0
                              + std::to_string(by));
635
0
    d_notyouroffset -= by;
636
0
    if(d_notyouroffset < 12)
637
0
      throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
638
0
                              + std::to_string(12));
639
0
  }
640
641
  char* d_packet;
642
  size_t d_length;
643
644
  uint32_t d_notyouroffset;  // only 'moveOffset' can touch this
645
  const uint32_t&  d_offset; // look.. but don't touch
646
};