Coverage Report

Created: 2026-07-24 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dnsdist-idstate.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 <cstdint>
25
#include <ctime>
26
#include <memory>
27
#include <optional>
28
#include <string_view>
29
#include <unordered_map>
30
#include <utility>
31
#include <vector>
32
33
#include "config.h"
34
#include "dnscrypt.hh"
35
#include "dnsdist-configuration.hh"
36
#include "dnsdist-edns.hh"
37
#include "dnsname.hh"
38
#include "dnsdist-logging.hh"
39
#include "dnsdist-protocols.hh"
40
#include "ednsextendederror.hh"
41
#include "gettime.hh"
42
#include "iputils.hh"
43
#include "noinitvector.hh"
44
#include "dnsdist-opentelemetry.hh"
45
#include "uuid-utils.hh"
46
47
#ifndef DISABLE_PROTOBUF
48
#include "dnsdist-protobuf.hh"
49
#include "remote_logger.hh"
50
#endif
51
52
struct ClientState;
53
struct DOHUnitInterface;
54
struct DOQUnit;
55
struct DOH3Unit;
56
class DNSCryptQuery;
57
class DNSDistPacketCache;
58
59
using QTag = std::unordered_map<string, string>;
60
using HeadersMap = std::unordered_map<std::string, std::string>;
61
62
struct StopWatch
63
{
64
  StopWatch(bool realTime = false) :
65
0
    d_needRealTime(realTime)
66
0
  {
67
0
  }
68
69
  void start()
70
0
  {
71
0
    d_start = getCurrentTime();
72
0
  }
73
74
  void set(const struct timespec& from)
75
0
  {
76
0
    d_start = from;
77
0
  }
78
79
  double udiff() const
80
0
  {
81
0
    struct timespec now = getCurrentTime();
82
0
    return 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
83
0
  }
84
85
  double udiffAndSet()
86
0
  {
87
0
    struct timespec now = getCurrentTime();
88
0
    auto ret = 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
89
0
    d_start = now;
90
0
    return ret;
91
0
  }
92
93
  struct timespec getStartTime() const
94
0
  {
95
0
    return d_start;
96
0
  }
97
98
  struct timespec d_start{
99
    0, 0};
100
101
private:
102
  struct timespec getCurrentTime() const
103
0
  {
104
0
    struct timespec now;
105
0
    if (gettime(&now, d_needRealTime) < 0) {
106
0
      unixDie("Getting timestamp");
107
0
    }
108
0
    return now;
109
0
  }
110
111
  bool d_needRealTime;
112
};
113
114
class CrossProtocolContext;
115
116
struct InternalQueryState
117
{
118
  struct ProtoBufData
119
  {
120
    std::optional<boost::uuids::uuid> uniqueId{std::nullopt}; // 17
121
    std::string d_deviceName;
122
    std::string d_deviceID;
123
    std::string d_requestorID;
124
  };
125
126
  /**
127
   * @brief Returns the Tracer, but only if OpenTelemetry tracing is globally enabled
128
   *
129
   * @return
130
   */
131
  std::shared_ptr<pdns::trace::dnsdist::Tracer>& getTracer()
132
0
  {
133
#ifdef DISABLE_PROTOBUF
134
    return d_OTTracer;
135
#else
136
0
    if ((d_OTTracingEnabledInConfiguration && !*d_OTTracingEnabledInConfiguration) || d_OTTracer != nullptr) {
137
0
      return d_OTTracer;
138
0
    }
139
0
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_openTelemetryTracing) {
140
      // OpenTelemetry tracing is enabled, but we don't have a tracer yet
141
0
      d_OTTracingEnabledInConfiguration = true;
142
0
      d_OTTracer = pdns::trace::dnsdist::Tracer::getTracer();
143
0
      d_OTTracer->setScopeSpanName("dnsdist/queryFromFrontend");
144
0
    }
145
0
    else {
146
0
      d_OTTracingEnabledInConfiguration = false;
147
0
    }
148
0
    return d_OTTracer;
149
0
#endif
150
0
  }
151
152
  /**
153
   * @brief Returns a Tracer::Closer, but only if OpenTelemetry tracing is needed
154
   *
155
   * @return
156
   */
157
  std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloser([[maybe_unused]] const std::string_view& name, [[maybe_unused]] const SpanID& parentSpanID);
158
  std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloser([[maybe_unused]] const std::string_view& name, [[maybe_unused]] const std::string_view& parentSpanName);
159
  std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloser([[maybe_unused]] const std::string_view& name);
160
  std::optional<pdns::trace::dnsdist::Tracer::Closer> getRulesCloser([[maybe_unused]] const std::string_view& ruleName, [[maybe_unused]] const std::string& ruleType);
161
162
  InternalQueryState()
163
0
  {
164
0
    origDest.sin4.sin_family = 0;
165
0
  }
166
167
  InternalQueryState(InternalQueryState&& rhs) = default;
168
  InternalQueryState& operator=(InternalQueryState&& rhs) = default;
169
170
  InternalQueryState(const InternalQueryState& orig) = delete;
171
  InternalQueryState& operator=(const InternalQueryState& orig) = delete;
172
  ~InternalQueryState();
173
174
  bool isXSK() const noexcept
175
0
  {
176
0
#ifdef HAVE_XSK
177
0
    return !xskPacketHeader.empty();
178
0
#else
179
0
    return false;
180
0
#endif /* HAVE_XSK */
181
0
  }
182
183
  InternalQueryState partialCloneForXFR() const;
184
185
  std::shared_ptr<const Logr::Logger> getLogger(std::shared_ptr<const Logr::Logger> parent = nullptr) const;
186
187
  std::optional<Netmask> subnet{std::nullopt}; // 40
188
  std::string poolName; // 32
189
#if !defined(DISABLE_PROTOBUF)
190
  std::string d_rawProtobufContent; // protobuf-encoded content to add to protobuf messages // 32
191
#endif /* DISABLE_PROTOBUF */
192
  ComboAddress origRemote; // 28
193
  ComboAddress origDest; // 28
194
  ComboAddress hopRemote;
195
  ComboAddress hopLocal;
196
  DNSName qname; // 24
197
#ifdef HAVE_XSK
198
  PacketBuffer xskPacketHeader; // 24
199
#endif /* HAVE_XSK */
200
  StopWatch queryRealTime{true}; // 24
201
private:
202
  std::shared_ptr<pdns::trace::dnsdist::Tracer> d_OTTracer{nullptr};
203
204
public:
205
  std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; // 16
206
  std::unique_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; // 8
207
  std::unique_ptr<QTag> qTag{nullptr}; // 8
208
  std::unique_ptr<PacketBuffer> d_packet{nullptr}; // Initial packet, so we can restart the query from the response path if needed // 8
209
  std::unique_ptr<ProtoBufData> d_protoBufData{nullptr};
210
#ifndef DISABLE_PROTOBUF
211
  std::vector<std::pair<std::string, std::shared_ptr<RemoteLoggerInterface>>> delayedResponseMsgs;
212
  std::vector<std::shared_ptr<RemoteLoggerInterface>> ottraceLoggers;
213
#endif
214
  std::unique_ptr<std::vector<dnsdist::edns::SetExtendedDNSErrorOperation>> d_extendedErrors{nullptr};
215
  std::optional<uint32_t> tempFailureTTL{std::nullopt}; // 8
216
  ClientState* cs{nullptr}; // 8
217
  std::unique_ptr<DOHUnitInterface> du; // 8
218
  size_t d_proxyProtocolPayloadSize{0}; // 8
219
  std::unique_ptr<DOQUnit> doqu{nullptr}; // 8
220
  std::unique_ptr<DOH3Unit> doh3u{nullptr}; // 8
221
  int32_t d_streamID{-1}; // 4
222
  uint32_t cacheKey{0}; // 4
223
  uint32_t cacheKeyNoECS{0}; // 4
224
  // DoH-only: if we received a TC=1 answer, we had to retry over TCP and thus we need the TCP cache key */
225
  uint32_t cacheKeyTCP{0}; // 4
226
  uint32_t ttlCap{0}; // cap the TTL _after_ inserting into the packet cache // 4
227
  int backendFD{-1}; // 4
228
  int delayMsec{0};
229
  uint16_t qtype{0}; // 2
230
  uint16_t qclass{0}; // 2
231
  // origID is in network-byte order
232
  uint16_t origID{0}; // 2
233
  uint16_t origFlags{0}; // 2
234
  uint16_t cacheFlags{0}; // DNS flags as sent to the backend // 2
235
  uint16_t udpPayloadSize{0}; // Max UDP payload size from the query // 2
236
  uint16_t sendTraceParentToDownstreamID{0}; // Whether or not to add a TRACEPARENT EDNS option to downstreams, set to non-0 for the EDNS Option ID
237
  std::optional<bool> dnssecOK;
238
#ifndef DISABLE_PROTOBUF
239
  std::optional<bool> d_OTTracingEnabledInConfiguration; // Whether OpenTelemetry tracing is enabled in the configuration. This prevents having to check the configuration several times for the same query
240
#endif /* DISABLE_PROTOBUF */
241
  dnsdist::Protocol protocol; // 1
242
  uint8_t restartCount{0}; // 1
243
  bool ednsAdded{false};
244
  bool ecsAdded{false};
245
  bool skipCache{false};
246
  bool useZeroScope{false};
247
  bool forwardedOverUDP{false};
248
  bool selfGenerated{false};
249
  bool cacheHit{false};
250
  bool staleCacheHit{false};
251
  bool tracingEnabled{false}; // Whether or not Open Telemetry tracing is enabled for this query
252
  bool rulesAppliedToQuery{false}; // Whether applyRulesToQuery has been called for the query, used to determine if we need to trace
253
  struct rulesAppliedToQuerySetter
254
  {
255
    rulesAppliedToQuerySetter(bool& pastProcessRules) :
256
      d_rulesAppliedToQuery(pastProcessRules)
257
0
    {
258
0
      d_rulesAppliedToQuery = false;
259
0
    }
260
    ~rulesAppliedToQuerySetter()
261
0
    {
262
0
      d_rulesAppliedToQuery = true;
263
0
    }
264
265
  private:
266
    bool& d_rulesAppliedToQuery;
267
  };
268
};
269
270
struct IDState
271
{
272
  IDState()
273
0
  {
274
0
  }
275
276
  IDState(const IDState& orig) = delete;
277
  IDState(IDState&& rhs) noexcept :
278
    internal(std::move(rhs.internal))
279
0
  {
280
0
    inUse.store(rhs.inUse.load());
281
0
    age.store(rhs.age.load());
282
0
  }
283
284
  IDState& operator=(IDState&& rhs) noexcept
285
0
  {
286
0
    inUse.store(rhs.inUse.load());
287
0
    age.store(rhs.age.load());
288
0
    internal = std::move(rhs.internal);
289
0
    return *this;
290
0
  }
291
292
  bool isInUse() const
293
0
  {
294
0
    return inUse;
295
0
  }
296
297
  /* For performance reasons we don't want to use a lock here, but that means
298
     we need to be very careful when modifying this value. Modifications happen
299
     from:
300
     - one of the UDP or DoH 'client' threads receiving a query, selecting a backend
301
       then picking one of the states associated to this backend (via the idOffset).
302
       Most of the time this state should not be in use and usageIndicator is -1, but we
303
       might not yet have received a response for the query previously associated to this
304
       state, meaning that we will 'reuse' this state and erase the existing state.
305
       If we ever receive a response for this state, it will be discarded. This is
306
       mostly fine for UDP except that we still need to be careful in order to miss
307
       the 'outstanding' counters, which should only be increased when we are picking
308
       an empty state, and not when reusing ;
309
       For DoH, though, we have dynamically allocated a DOHUnit object that needs to
310
       be freed, as well as internal objects.
311
     - one of the UDP receiver threads receiving a response from a backend, picking
312
       the corresponding state and sending the response to the client ;
313
     - the 'healthcheck' thread scanning the states to actively discover timeouts,
314
       mostly to keep some counters like the 'outstanding' one sane.
315
316
     We have two flags:
317
     - inUse tells us if there currently is an in-flight query whose state is stored
318
       in this state
319
     - locked tells us whether someone currently owns the state, so no-one else can touch
320
       it
321
  */
322
  InternalQueryState internal;
323
  std::atomic<uint16_t> age{0};
324
325
  class StateGuard
326
  {
327
  public:
328
    StateGuard(IDState& ids) :
329
      d_ids(ids)
330
0
    {
331
0
    }
332
    ~StateGuard()
333
0
    {
334
0
      d_ids.release();
335
0
    }
336
    StateGuard(const StateGuard&) = delete;
337
    StateGuard(StateGuard&&) = delete;
338
    StateGuard& operator=(const StateGuard&) = delete;
339
    StateGuard& operator=(StateGuard&&) = delete;
340
341
  private:
342
    IDState& d_ids;
343
  };
344
345
  [[nodiscard]] std::optional<StateGuard> acquire()
346
0
  {
347
0
    bool expected = false;
348
0
    if (locked.compare_exchange_strong(expected, true)) {
349
0
      return std::optional<StateGuard>(*this);
350
0
    }
351
0
    return std::nullopt;
352
0
  }
353
354
  void release()
355
0
  {
356
0
    locked.store(false);
357
0
  }
358
359
  std::atomic<bool> inUse{false}; // 1
360
361
private:
362
  std::atomic<bool> locked{false}; // 1
363
};