Coverage Report

Created: 2026-09-07 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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(&timesp, 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 */