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 | | }; |