Coverage Report

Created: 2026-09-03 06:37

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