/src/pdns/pdns/dnsdistdist/protozero.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 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include "iputils.hh" |
27 | | #include "gettime.hh" |
28 | | #include "uuid-utils.hh" |
29 | | |
30 | | #ifndef DISABLE_PROTOBUF |
31 | | |
32 | | #include <protozero/pbf_writer.hpp> |
33 | | |
34 | | namespace pdns |
35 | | { |
36 | | namespace ProtoZero |
37 | | { |
38 | | class Message |
39 | | { |
40 | | public: |
41 | | enum class MetaValueField : protozero::pbf_tag_type |
42 | | { |
43 | | stringVal = 1, |
44 | | intVal = 2 |
45 | | }; |
46 | | enum class HTTPVersion : protozero::pbf_tag_type |
47 | | { |
48 | | HTTP1 = 1, |
49 | | HTTP2 = 2, |
50 | | HTTP3 = 3 |
51 | | }; |
52 | | enum class MetaField : protozero::pbf_tag_type |
53 | | { |
54 | | key = 1, |
55 | | value = 2 |
56 | | }; |
57 | | enum class Event : protozero::pbf_tag_type |
58 | | { |
59 | | ts = 1, |
60 | | event = 2, |
61 | | start = 3, |
62 | | boolVal = 4, |
63 | | intVal = 5, |
64 | | stringVal = 6, |
65 | | bytesVal = 7, |
66 | | custom = 8 |
67 | | }; |
68 | | enum class MessageType : int32_t |
69 | | { |
70 | | DNSQueryType = 1, |
71 | | DNSResponseType = 2, |
72 | | DNSOutgoingQueryType = 3, |
73 | | DNSIncomingResponseType = 4, |
74 | | InternalType = 5 |
75 | | }; |
76 | | enum class Field : protozero::pbf_tag_type |
77 | | { |
78 | | type = 1, |
79 | | messageId = 2, |
80 | | serverIdentity = 3, |
81 | | socketFamily = 4, |
82 | | socketProtocol = 5, |
83 | | from = 6, |
84 | | to = 7, |
85 | | inBytes = 8, |
86 | | timeSec = 9, |
87 | | timeUsec = 10, |
88 | | id = 11, |
89 | | question = 12, |
90 | | response = 13, |
91 | | originalRequestorSubnet = 14, |
92 | | requestorId = 15, |
93 | | initialRequestId = 16, |
94 | | deviceId = 17, |
95 | | newlyObservedDomain = 18, |
96 | | deviceName = 19, |
97 | | fromPort = 20, |
98 | | toPort = 21, |
99 | | meta = 22, |
100 | | trace = 23, |
101 | | httpVersion = 24, |
102 | | workerId = 25, |
103 | | packetCacheHit = 26, |
104 | | outgoingQueries = 27, |
105 | | headerFlags = 28, |
106 | | ednsVersion = 29, |
107 | | openTelemetryData = 30, |
108 | | ede = 31, |
109 | | edeText = 32, |
110 | | openTelemetryTraceID = 33, |
111 | | }; |
112 | | enum class QuestionField : protozero::pbf_tag_type |
113 | | { |
114 | | qName = 1, |
115 | | qType = 2, |
116 | | qClass = 3 |
117 | | }; |
118 | | enum class ResponseField : protozero::pbf_tag_type |
119 | | { |
120 | | rcode = 1, |
121 | | rrs = 2, |
122 | | appliedPolicy = 3, |
123 | | tags = 4, |
124 | | queryTimeSec = 5, |
125 | | queryTimeUsec = 6, |
126 | | appliedPolicyType = 7, |
127 | | appliedPolicyTrigger = 8, |
128 | | appliedPolicyHit = 9, |
129 | | appliedPolicyKind = 10, |
130 | | validationState = 11 |
131 | | }; |
132 | | enum class RRField : protozero::pbf_tag_type |
133 | | { |
134 | | name = 1, |
135 | | type = 2, |
136 | | class_ = 3, |
137 | | ttl = 4, |
138 | | rdata = 5, |
139 | | udr = 6 |
140 | | }; |
141 | | enum class TransportProtocol : protozero::pbf_tag_type |
142 | | { |
143 | | UDP = 1, |
144 | | TCP = 2, |
145 | | DoT = 3, |
146 | | DoH = 4, |
147 | | DNSCryptUDP = 5, |
148 | | DNSCryptTCP = 6, |
149 | | DoQ = 7 |
150 | | }; |
151 | | |
152 | | Message(std::string& buffer) : |
153 | 0 | d_buffer(buffer), d_message{d_buffer} |
154 | 0 | { |
155 | 0 | } |
156 | 0 | ~Message() = default; |
157 | | Message(const Message&) = delete; |
158 | | Message(Message&&) = delete; |
159 | | Message& operator=(const Message&) = delete; |
160 | | Message& operator=(Message&&) = delete; |
161 | | |
162 | | void setRequest(const boost::uuids::uuid& uniqueId, const ComboAddress& requestor, const ComboAddress& local, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t qid, TransportProtocol proto, size_t len); |
163 | | void setResponse(const DNSName& qname, uint16_t qtype, uint16_t qclass); |
164 | | |
165 | | void setType(MessageType mtype) |
166 | 0 | { |
167 | 0 | add_enum(d_message, Field::type, static_cast<int32_t>(mtype)); |
168 | 0 | } |
169 | | |
170 | | void setHTTPVersion(HTTPVersion version) |
171 | 0 | { |
172 | 0 | add_enum(d_message, Field::httpVersion, static_cast<int32_t>(version)); |
173 | 0 | } |
174 | | |
175 | | void setMessageIdentity(const boost::uuids::uuid& uniqueId) |
176 | 0 | { |
177 | 0 | add_bytes(d_message, Field::messageId, reinterpret_cast<const char*>(uniqueId.begin()), uniqueId.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): it's the API |
178 | 0 | } |
179 | | |
180 | | void setServerIdentity(const std::string& serverIdentity) |
181 | 0 | { |
182 | 0 | add_bytes(d_message, Field::serverIdentity, serverIdentity.data(), serverIdentity.length()); |
183 | 0 | } |
184 | | |
185 | | void setSocketFamily(int family) |
186 | 0 | { |
187 | 0 | add_enum(d_message, Field::socketFamily, family == AF_INET ? 1 : 2); |
188 | 0 | } |
189 | | |
190 | | void setSocketProtocol(TransportProtocol proto) |
191 | 0 | { |
192 | 0 | add_enum(d_message, Field::socketProtocol, static_cast<int32_t>(proto)); |
193 | 0 | } |
194 | | |
195 | | void setFrom(const ComboAddress& address) |
196 | 0 | { |
197 | 0 | encodeComboAddress(static_cast<protozero::pbf_tag_type>(Field::from), address); |
198 | 0 | } |
199 | | |
200 | | void setTo(const ComboAddress& address) |
201 | 0 | { |
202 | 0 | encodeComboAddress(static_cast<protozero::pbf_tag_type>(Field::to), address); |
203 | 0 | } |
204 | | |
205 | | void setInBytes(uint64_t len) |
206 | 0 | { |
207 | 0 | add_uint64(d_message, Field::inBytes, len); |
208 | 0 | } |
209 | | |
210 | | void setTime() |
211 | 0 | { |
212 | 0 | timespec timesp{}; |
213 | 0 | gettime(×p, true); |
214 | 0 |
|
215 | 0 | setTime(timesp.tv_sec, timesp.tv_nsec / 1000); |
216 | 0 | } |
217 | | |
218 | | void setTime(time_t sec, uint32_t usec) |
219 | 0 | { |
220 | 0 | // coverity[store_truncates_time_t] |
221 | 0 | add_uint32(d_message, Field::timeSec, sec); |
222 | 0 | add_uint32(d_message, Field::timeUsec, usec); |
223 | 0 | } |
224 | | |
225 | | void setId(uint16_t qid) |
226 | 0 | { |
227 | 0 | add_uint32(d_message, Field::id, ntohs(qid)); |
228 | 0 | } |
229 | | |
230 | | void setQuestion(const DNSName& qname, uint16_t qtype, uint16_t qclass) |
231 | 0 | { |
232 | 0 | protozero::pbf_writer pbf_question{d_message, static_cast<protozero::pbf_tag_type>(Field::question)}; |
233 | 0 | encodeDNSName(pbf_question, d_buffer, static_cast<protozero::pbf_tag_type>(QuestionField::qName), qname); |
234 | 0 | pbf_question.add_uint32(static_cast<protozero::pbf_tag_type>(QuestionField::qType), qtype); |
235 | 0 | pbf_question.add_uint32(static_cast<protozero::pbf_tag_type>(QuestionField::qClass), qclass); |
236 | 0 | } |
237 | | |
238 | | void setMeta(const std::string& key, const std::unordered_set<std::string>& stringVal, const std::unordered_set<int64_t>& intVal) |
239 | 0 | { |
240 | 0 | protozero::pbf_writer pbf_meta{d_message, static_cast<protozero::pbf_tag_type>(Field::meta)}; |
241 | 0 | pbf_meta.add_string(static_cast<protozero::pbf_tag_type>(MetaField::key), key); |
242 | 0 | protozero::pbf_writer pbf_meta_value{pbf_meta, static_cast<protozero::pbf_tag_type>(MetaField::value)}; |
243 | 0 | for (const auto& str : stringVal) { |
244 | 0 | pbf_meta_value.add_string(static_cast<protozero::pbf_tag_type>(MetaValueField::stringVal), str); |
245 | 0 | } |
246 | 0 | for (const auto& val : intVal) { |
247 | 0 | pbf_meta_value.add_uint64(static_cast<protozero::pbf_tag_type>(MetaValueField::intVal), val); |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | void setEDNSSubnet(const Netmask& netmask, uint8_t mask) |
252 | 0 | { |
253 | 0 | encodeNetmask(static_cast<protozero::pbf_tag_type>(Field::originalRequestorSubnet), netmask, mask); |
254 | 0 | } |
255 | | |
256 | | void setRequestorId(const std::string& req) |
257 | 0 | { |
258 | 0 | if (!req.empty()) { |
259 | 0 | add_string(d_message, Field::requestorId, req); |
260 | 0 | } |
261 | 0 | } |
262 | | |
263 | | void setInitialRequestID(const boost::uuids::uuid& uniqueId) |
264 | 0 | { |
265 | 0 | add_bytes(d_message, Field::initialRequestId, reinterpret_cast<const char*>(uniqueId.begin()), uniqueId.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): it's the API |
266 | 0 | } |
267 | | |
268 | | void setDeviceId(const std::string& deviceId) |
269 | 0 | { |
270 | 0 | if (!deviceId.empty()) { |
271 | 0 | add_string(d_message, Field::deviceId, deviceId); |
272 | 0 | } |
273 | 0 | } |
274 | | |
275 | | void setNewlyObservedDomain(bool nod) |
276 | 0 | { |
277 | 0 | add_bool(d_message, Field::newlyObservedDomain, nod); |
278 | 0 | } |
279 | | |
280 | | void setDeviceName(const std::string& name) |
281 | 0 | { |
282 | 0 | if (!name.empty()) { |
283 | 0 | add_string(d_message, Field::deviceName, name); |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | | void setFromPort(in_port_t port) |
288 | 0 | { |
289 | 0 | add_uint32(d_message, Field::fromPort, port); |
290 | 0 | } |
291 | | |
292 | | void setToPort(in_port_t port) |
293 | 0 | { |
294 | 0 | add_uint32(d_message, Field::toPort, port); |
295 | 0 | } |
296 | | |
297 | | void setWorkerId(uint64_t wid) |
298 | 0 | { |
299 | 0 | add_uint64(d_message, Field::workerId, wid); |
300 | 0 | } |
301 | | |
302 | | void setPacketCacheHit(bool hit) |
303 | 0 | { |
304 | 0 | add_bool(d_message, Field::packetCacheHit, hit); |
305 | 0 | } |
306 | | |
307 | | void setOutgoingQueries(uint32_t num) |
308 | 0 | { |
309 | 0 | add_uint32(d_message, Field::outgoingQueries, num); |
310 | 0 | } |
311 | | |
312 | | void setHeaderFlags(uint16_t flags) |
313 | 0 | { |
314 | 0 | add_uint32(d_message, Field::headerFlags, flags); |
315 | 0 | } |
316 | | |
317 | | void setEDNSVersion(uint32_t version) |
318 | 0 | { |
319 | 0 | add_uint32(d_message, Field::ednsVersion, version); |
320 | 0 | } |
321 | | |
322 | | void setOpenTelemetryData(const std::string& data) |
323 | 0 | { |
324 | 0 | if (!data.empty()) { |
325 | 0 | add_string(d_message, Field::openTelemetryData, data); |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | void setEDE(const uint16_t ede) |
330 | 0 | { |
331 | 0 | add_uint32(d_message, Field::ede, ede); |
332 | 0 | } |
333 | | |
334 | | void setEDEText(const std::string edeText) |
335 | 0 | { |
336 | 0 | if (!edeText.empty()) { |
337 | 0 | add_string(d_message, Field::edeText, edeText); |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | void setOpenTelemetryTraceID(const std::array<uint8_t, 16>& traceID) |
342 | 0 | { |
343 | 0 | add_bytes(d_message, Field::openTelemetryTraceID, reinterpret_cast<const char*>(traceID.data()), traceID.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): it's the API |
344 | 0 | } |
345 | | |
346 | | void startResponse() |
347 | 0 | { |
348 | 0 | d_response = protozero::pbf_writer{d_message, static_cast<protozero::pbf_tag_type>(Field::response)}; |
349 | 0 | } |
350 | | |
351 | | void commitResponse() |
352 | 0 | { |
353 | 0 | d_response.commit(); |
354 | 0 | } |
355 | | |
356 | | void setResponseCode(uint8_t rcode) |
357 | 0 | { |
358 | 0 | d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::rcode), rcode); |
359 | 0 | } |
360 | | |
361 | | void setNetworkErrorResponseCode() |
362 | 0 | { |
363 | 0 | /* special code meaning 'network error', like a timeout */ |
364 | 0 | d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::rcode), 65536); |
365 | 0 | } |
366 | | |
367 | | void setAppliedPolicy(const std::string& policy) |
368 | 0 | { |
369 | 0 | d_response.add_string(static_cast<protozero::pbf_tag_type>(ResponseField::appliedPolicy), policy); |
370 | 0 | } |
371 | | |
372 | | void addPolicyTags(const std::unordered_set<std::string>& tags) |
373 | 0 | { |
374 | 0 | for (const auto& tag : tags) { |
375 | 0 | addPolicyTag(tag); |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | void addPolicyTag(const string& tag) |
380 | 0 | { |
381 | 0 | d_response.add_string(static_cast<protozero::pbf_tag_type>(ResponseField::tags), tag); |
382 | 0 | } |
383 | | |
384 | | void setQueryTime(uint32_t sec, uint32_t usec) |
385 | 0 | { |
386 | 0 | d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::queryTimeSec), sec); |
387 | 0 | d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::queryTimeUsec), usec); |
388 | 0 | } |
389 | | |
390 | | void addRRsFromPacket(const char* packet, size_t len, bool includeCNAME = false); |
391 | | void addRR(const DNSName& name, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& blob); |
392 | | |
393 | | protected: |
394 | | void encodeComboAddress(protozero::pbf_tag_type type, const ComboAddress& address); |
395 | | void encodeNetmask(protozero::pbf_tag_type type, const Netmask& subnet, uint8_t mask); |
396 | | static void encodeDNSName(protozero::pbf_writer& pbf, std::string& buffer, protozero::pbf_tag_type type, const DNSName& name); |
397 | | |
398 | | static void add_enum(protozero::pbf_writer& writer, Field type, int32_t value) |
399 | 0 | { |
400 | 0 | writer.add_enum(static_cast<protozero::pbf_tag_type>(type), value); |
401 | 0 | } |
402 | | |
403 | | static void add_bool(protozero::pbf_writer& writer, Field type, bool value) |
404 | 0 | { |
405 | 0 | writer.add_bool(static_cast<protozero::pbf_tag_type>(type), value); |
406 | 0 | } |
407 | | |
408 | | static void add_uint32(protozero::pbf_writer& writer, Field type, uint32_t value) |
409 | 0 | { |
410 | 0 | writer.add_uint32(static_cast<protozero::pbf_tag_type>(type), value); |
411 | 0 | } |
412 | | |
413 | | static void add_uint64(protozero::pbf_writer& writer, Field type, uint64_t value) |
414 | 0 | { |
415 | 0 | writer.add_uint64(static_cast<protozero::pbf_tag_type>(type), value); |
416 | 0 | } |
417 | | |
418 | | static void add_bytes(protozero::pbf_writer& writer, Field type, const char* data, size_t len) |
419 | 0 | { |
420 | 0 | writer.add_bytes(static_cast<protozero::pbf_tag_type>(type), data, len); |
421 | 0 | } |
422 | | |
423 | | static void add_string(protozero::pbf_writer& writer, Field type, const std::string& str) |
424 | 0 | { |
425 | 0 | writer.add_string(static_cast<protozero::pbf_tag_type>(type), str); |
426 | 0 | } |
427 | | |
428 | | // NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes) |
429 | | std::string& d_buffer; |
430 | | protozero::pbf_writer d_message; |
431 | | protozero::pbf_writer d_response; |
432 | | // NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes) |
433 | | }; |
434 | | }; |
435 | | }; |
436 | | |
437 | | #endif /* DISABLE_PROTOBUF */ |