Coverage Report

Created: 2026-01-06 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dns.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 "qtype.hh"
24
#include "dnsname.hh"
25
#include <ctime>
26
#include <optional>
27
#include <string_view>
28
#include <sys/types.h>
29
30
#undef BADSIG  // signal.h SIG_ERR
31
32
struct DNSRecord;
33
34
class RCode
35
{
36
public:
37
  enum rcodes_ : uint8_t { NoError=0, FormErr=1, ServFail=2, NXDomain=3, NotImp=4, Refused=5, YXDomain=6, YXRRSet=7, NXRRSet=8, NotAuth=9, NotZone=10};
38
  static std::string to_s(uint8_t rcode);
39
  static std::string to_short_s(uint8_t rcode);
40
  static std::optional<uint8_t> from_short(const std::string_view& rcode_string);
41
  const static std::array<std::string, 24> rcodes_s;
42
};
43
44
class ERCode
45
{
46
public:
47
  enum rcodes_ : uint16_t { BADVERS=16, BADSIG=16, BADKEY=17, BADTIME=18, BADMODE=19, BADNAME=20, BADALG=21, BADTRUNC=22, BADCOOKIE=23 };
48
  static std::string to_s(uint16_t rcode);
49
  static std::string to_short_s(uint16_t rcode);
50
  static std::optional<uint16_t> from_short(const std::string_view& ercode_string);
51
};
52
53
class Opcode
54
{
55
public:
56
  enum opcodes_ : uint8_t { Query=0, IQuery=1, Status=2, Notify=4, Update=5 };
57
  static std::string to_s(uint8_t opcode);
58
  static std::optional<uint8_t> from_lowercase_string(const std::string_view& opcode_string);
59
};
60
61
// This needs to be a signed type, so that serialization of UnknownDomainID
62
// as text is "-1", for compatibility with the remote and pipe backends.
63
// See static_assert there for details.
64
using domainid_t = int32_t;
65
66
constexpr domainid_t UnknownDomainID{-1};
67
68
//! This class represents a resource record
69
class DNSResourceRecord
70
{
71
public:
72
  static DNSResourceRecord fromWire(const DNSRecord& wire);
73
74
  enum Place : uint8_t
75
  {
76
    QUESTION = 0,
77
    ANSWER = 1,
78
    AUTHORITY = 2,
79
    ADDITIONAL = 3
80
  }; //!< Type describing the positioning within, say, a DNSPacket
81
82
  [[nodiscard]] static std::string placeString(uint8_t place);
83
  void setContent(const string& content);
84
  [[nodiscard]] string getZoneRepresentation(bool noDot = false) const;
85
86
  // data
87
  DNSName qname; //!< the name of this record, for example: www.powerdns.com
88
  DNSName ordername;
89
  DNSName wildcardname;
90
  string content; //!< what this record points to. Example: 10.1.2.3
91
92
  // Aligned on 8-byte boundaries on systems where time_t is 8 bytes and int
93
  // is 4 bytes, aka modern linux on x86_64
94
  time_t last_modified{}; //!< Timestamp of last update, if known by the backend
95
96
  uint32_t ttl{}; //!< Time To Live of this record
97
  uint32_t signttl{}; //!< If non-zero, use this TTL as original TTL in the RRSIG
98
99
  domainid_t domain_id{UnknownDomainID}; //!< If a backend implements this, the domain_id of the zone this record is in
100
  QType qtype; //!< qtype of this record, ie A, CNAME, MX etc
101
  uint16_t qclass{1}; //!< class of this record
102
103
  uint8_t scopeMask{};
104
  bool auth{true};
105
  bool disabled{};
106
107
  bool operator==(const DNSResourceRecord& rhs) const;
108
109
  bool operator<(const DNSResourceRecord& other) const
110
0
  {
111
0
    if (qname < other.qname) {
112
0
      return true;
113
0
    }
114
0
    if (qname == other.qname) {
115
0
      return (content < other.content);
116
0
    }
117
0
    return false;
118
0
  }
119
};
120
121
#define GCCPACKATTRIBUTE __attribute__((packed))
122
123
struct dnsrecordheader
124
{
125
  uint16_t d_type;
126
  uint16_t d_class;
127
  uint32_t d_ttl;
128
  uint16_t d_clen;
129
} GCCPACKATTRIBUTE;
130
131
struct EDNS0Record
132
{
133
  uint8_t extRCode, version;
134
  uint16_t extFlags;
135
} GCCPACKATTRIBUTE;
136
137
static_assert(sizeof(EDNS0Record) == 4, "EDNS0Record size must be 4");
138
139
#if __has_include(<endian.h>)
140
#include <endian.h> // Recent posix
141
#elif __has_include(<machine/endian.h>)
142
#include <machine/endian.h> // Common place on older BSD derived systems
143
#else
144
#error no endian.h found
145
#endif
146
147
// Sanity check
148
#if !defined(BYTE_ORDER)
149
#error cannot determine byte order
150
#endif
151
152
struct dnsheader {
153
        uint16_t        id;             /* query identification number */
154
#if BYTE_ORDER == BIG_ENDIAN
155
                        /* fields in third byte */
156
        unsigned        qr: 1;          /* response flag */
157
        unsigned        opcode: 4;      /* purpose of message */
158
        unsigned        aa: 1;          /* authoritative answer */
159
        unsigned        tc: 1;          /* truncated message */
160
        unsigned        rd: 1;          /* recursion desired */
161
                        /* fields in fourth byte */
162
        unsigned        ra: 1;          /* recursion available */
163
        unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
164
        unsigned        ad: 1;          /* authentic data from named */
165
        unsigned        cd: 1;          /* checking disabled by resolver */
166
        unsigned        rcode :4;       /* response code */
167
#elif BYTE_ORDER == LITTLE_ENDIAN
168
                        /* fields in third byte */
169
        unsigned        rd :1;          /* recursion desired */
170
        unsigned        tc :1;          /* truncated message */
171
        unsigned        aa :1;          /* authoritative answer */
172
        unsigned        opcode :4;      /* purpose of message */
173
        unsigned        qr :1;          /* response flag */
174
                        /* fields in fourth byte */
175
        unsigned        rcode :4;       /* response code */
176
        unsigned        cd: 1;          /* checking disabled by resolver */
177
        unsigned        ad: 1;          /* authentic data from named */
178
        unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
179
        unsigned        ra :1;          /* recursion available */
180
#endif
181
                        /* remaining bytes */
182
        uint16_t        qdcount;        /* number of question entries */
183
        uint16_t        ancount;        /* number of answer entries */
184
        uint16_t        nscount;        /* number of authority entries */
185
        uint16_t        arcount;        /* number of resource entries */
186
};
187
188
static_assert(sizeof(dnsheader) == 12, "dnsheader size must be 12");
189
190
class dnsheader_aligned
191
{
192
public:
193
  static bool isMemoryAligned(const void* mem)
194
4.14k
  {
195
4.14k
    return reinterpret_cast<uintptr_t>(mem) % sizeof(uint32_t) == 0; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
196
4.14k
  }
197
198
  dnsheader_aligned(const void* mem)
199
4.14k
  {
200
4.14k
    if (isMemoryAligned(mem)) {
201
3.71k
      d_p = reinterpret_cast<const dnsheader*>(mem);  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
202
3.71k
    }
203
432
    else {
204
432
      memcpy(&d_h, mem, sizeof(dnsheader));
205
432
      d_p = &d_h;
206
432
    }
207
4.14k
  }
208
  dnsheader_aligned(const dnsheader_aligned&) = delete;
209
  dnsheader_aligned(dnsheader_aligned&&) = delete;
210
  dnsheader_aligned& operator=(const dnsheader_aligned&) = delete;
211
  dnsheader_aligned& operator=(dnsheader_aligned&&) = delete;
212
  ~dnsheader_aligned() = default;
213
214
  [[nodiscard]] const dnsheader* get() const
215
3.22k
  {
216
3.22k
    return d_p;
217
3.22k
  }
218
219
  [[nodiscard]] const dnsheader& operator*() const
220
0
  {
221
0
    return *d_p;
222
0
  }
223
224
  [[nodiscard]] const dnsheader* operator->() const
225
3.37k
  {
226
3.37k
    return d_p;
227
3.37k
  }
228
229
private:
230
  dnsheader d_h{};
231
  const dnsheader* d_p{};
232
};
233
234
inline uint16_t* getFlagsFromDNSHeader(dnsheader* dh)
235
0
{
236
0
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
237
0
  return reinterpret_cast<uint16_t*>(reinterpret_cast<char*>(dh) + sizeof(uint16_t));
238
0
}
239
240
inline const uint16_t * getFlagsFromDNSHeader(const dnsheader* dh)
241
0
{
242
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
243
0
  return reinterpret_cast<const uint16_t*>(reinterpret_cast<const char*>(dh) + sizeof(uint16_t));
244
0
}
245
246
2.11k
#define DNS_TYPE_SIZE (2)
247
2.11k
#define DNS_CLASS_SIZE (2)
248
1.01k
#define DNS_TTL_SIZE (4)
249
1.52k
#define DNS_RDLENGTH_SIZE (2)
250
0
#define EDNS_EXTENDED_RCODE_SIZE (1)
251
0
#define EDNS_VERSION_SIZE (1)
252
51.6k
#define EDNS_OPTION_CODE_SIZE (2)
253
51.6k
#define EDNS_OPTION_LENGTH_SIZE (2)
254
255
#if BYTE_ORDER == BIG_ENDIAN
256
#define FLAGS_RD_OFFSET (8)
257
#define FLAGS_CD_OFFSET (12)
258
#elif BYTE_ORDER == LITTLE_ENDIAN
259
#define FLAGS_RD_OFFSET (0)
260
#define FLAGS_CD_OFFSET (12)
261
#endif
262
263
uint32_t hashQuestion(const uint8_t* packet, uint16_t len, uint32_t init, bool& ok);
264
265
struct TSIGTriplet
266
{
267
  DNSName name, algo;
268
  string secret;
269
};