Coverage Report

Created: 2025-06-13 06:28

/src/pdns/pdns/dnsdistdist/dnsdist-idstate.hh
Line
Count
Source (jump to first uncovered line)
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 <unordered_map>
25
26
#include "config.h"
27
#include "dnscrypt.hh"
28
#include "dnsname.hh"
29
#include "dnsdist-protocols.hh"
30
#include "ednsextendederror.hh"
31
#include "gettime.hh"
32
#include "iputils.hh"
33
#include "noinitvector.hh"
34
#include "uuid-utils.hh"
35
36
struct ClientState;
37
struct DOHUnitInterface;
38
struct DOQUnit;
39
struct DOH3Unit;
40
class DNSCryptQuery;
41
class DNSDistPacketCache;
42
43
using QTag = std::unordered_map<string, string>;
44
using HeadersMap = std::unordered_map<std::string, std::string>;
45
46
struct StopWatch
47
{
48
  StopWatch(bool realTime = false) :
49
    d_needRealTime(realTime)
50
0
  {
51
0
  }
52
53
  void start()
54
0
  {
55
0
    d_start = getCurrentTime();
56
0
  }
57
58
  void set(const struct timespec& from)
59
0
  {
60
0
    d_start = from;
61
0
  }
62
63
  double udiff() const
64
0
  {
65
0
    struct timespec now = getCurrentTime();
66
0
    return 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
67
0
  }
68
69
  double udiffAndSet()
70
0
  {
71
0
    struct timespec now = getCurrentTime();
72
0
    auto ret = 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
73
0
    d_start = now;
74
0
    return ret;
75
0
  }
76
77
  struct timespec getStartTime() const
78
0
  {
79
0
    return d_start;
80
0
  }
81
82
  struct timespec d_start{
83
    0, 0};
84
85
private:
86
  struct timespec getCurrentTime() const
87
0
  {
88
0
    struct timespec now;
89
0
    if (gettime(&now, d_needRealTime) < 0) {
90
0
      unixDie("Getting timestamp");
91
0
    }
92
0
    return now;
93
0
  }
94
95
  bool d_needRealTime;
96
};
97
98
class CrossProtocolContext;
99
100
struct InternalQueryState
101
{
102
  struct ProtoBufData
103
  {
104
    std::optional<boost::uuids::uuid> uniqueId{std::nullopt}; // 17
105
    std::string d_deviceName;
106
    std::string d_deviceID;
107
    std::string d_requestorID;
108
  };
109
110
  InternalQueryState()
111
0
  {
112
0
    origDest.sin4.sin_family = 0;
113
0
  }
114
115
  InternalQueryState(InternalQueryState&& rhs) = default;
116
  InternalQueryState& operator=(InternalQueryState&& rhs) = default;
117
118
  InternalQueryState(const InternalQueryState& orig) = delete;
119
  InternalQueryState& operator=(const InternalQueryState& orig) = delete;
120
121
  bool isXSK() const noexcept
122
0
  {
123
0
#ifdef HAVE_XSK
124
0
    return !xskPacketHeader.empty();
125
0
#else
126
0
    return false;
127
0
#endif /* HAVE_XSK */
128
0
  }
129
130
  InternalQueryState partialCloneForXFR() const;
131
132
  boost::optional<Netmask> subnet{boost::none}; // 40
133
  std::string poolName; // 32
134
  ComboAddress origRemote; // 28
135
  ComboAddress origDest; // 28
136
  ComboAddress hopRemote;
137
  ComboAddress hopLocal;
138
  DNSName qname; // 24
139
#ifdef HAVE_XSK
140
  PacketBuffer xskPacketHeader; // 24
141
#endif /* HAVE_XSK */
142
  StopWatch queryRealTime{true}; // 24
143
  std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; // 16
144
  std::unique_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; // 8
145
  std::unique_ptr<QTag> qTag{nullptr}; // 8
146
  std::unique_ptr<PacketBuffer> d_packet{nullptr}; // Initial packet, so we can restart the query from the response path if needed // 8
147
  std::unique_ptr<ProtoBufData> d_protoBufData{nullptr};
148
  std::unique_ptr<EDNSExtendedError> d_extendedError{nullptr};
149
  boost::optional<uint32_t> tempFailureTTL{boost::none}; // 8
150
  ClientState* cs{nullptr}; // 8
151
  std::unique_ptr<DOHUnitInterface> du; // 8
152
  size_t d_proxyProtocolPayloadSize{0}; // 8
153
  std::unique_ptr<DOQUnit> doqu{nullptr}; // 8
154
  std::unique_ptr<DOH3Unit> doh3u{nullptr}; // 8
155
  int32_t d_streamID{-1}; // 4
156
  uint32_t cacheKey{0}; // 4
157
  uint32_t cacheKeyNoECS{0}; // 4
158
  // DoH-only: if we received a TC=1 answer, we had to retry over TCP and thus we need the TCP cache key */
159
  uint32_t cacheKeyTCP{0}; // 4
160
  uint32_t ttlCap{0}; // cap the TTL _after_ inserting into the packet cache // 4
161
  int backendFD{-1}; // 4
162
  int delayMsec{0};
163
  uint16_t qtype{0}; // 2
164
  uint16_t qclass{0}; // 2
165
  // origID is in network-byte order
166
  uint16_t origID{0}; // 2
167
  uint16_t origFlags{0}; // 2
168
  uint16_t cacheFlags{0}; // DNS flags as sent to the backend // 2
169
  uint16_t udpPayloadSize{0}; // Max UDP payload size from the query // 2
170
  dnsdist::Protocol protocol; // 1
171
  bool ednsAdded{false};
172
  bool ecsAdded{false};
173
  bool skipCache{false};
174
  bool dnssecOK{false};
175
  bool useZeroScope{false};
176
  bool forwardedOverUDP{false};
177
  bool selfGenerated{false};
178
  bool cacheHit{false};
179
  bool staleCacheHit{false};
180
};
181
182
struct IDState
183
{
184
  IDState()
185
0
  {
186
0
  }
187
188
  IDState(const IDState& orig) = delete;
189
  IDState(IDState&& rhs) noexcept :
190
    internal(std::move(rhs.internal))
191
0
  {
192
0
    inUse.store(rhs.inUse.load());
193
0
    age.store(rhs.age.load());
194
0
  }
195
196
  IDState& operator=(IDState&& rhs) noexcept
197
0
  {
198
0
    inUse.store(rhs.inUse.load());
199
0
    age.store(rhs.age.load());
200
0
    internal = std::move(rhs.internal);
201
0
    return *this;
202
0
  }
203
204
  bool isInUse() const
205
0
  {
206
0
    return inUse;
207
0
  }
208
209
  /* For performance reasons we don't want to use a lock here, but that means
210
     we need to be very careful when modifying this value. Modifications happen
211
     from:
212
     - one of the UDP or DoH 'client' threads receiving a query, selecting a backend
213
       then picking one of the states associated to this backend (via the idOffset).
214
       Most of the time this state should not be in use and usageIndicator is -1, but we
215
       might not yet have received a response for the query previously associated to this
216
       state, meaning that we will 'reuse' this state and erase the existing state.
217
       If we ever receive a response for this state, it will be discarded. This is
218
       mostly fine for UDP except that we still need to be careful in order to miss
219
       the 'outstanding' counters, which should only be increased when we are picking
220
       an empty state, and not when reusing ;
221
       For DoH, though, we have dynamically allocated a DOHUnit object that needs to
222
       be freed, as well as internal objects internals to libh2o.
223
     - one of the UDP receiver threads receiving a response from a backend, picking
224
       the corresponding state and sending the response to the client ;
225
     - the 'healthcheck' thread scanning the states to actively discover timeouts,
226
       mostly to keep some counters like the 'outstanding' one sane.
227
228
     We have two flags:
229
     - inUse tells us if there currently is a in-flight query whose state is stored
230
       in this state
231
     - locked tells us whether someone currently owns the state, so no-one else can touch
232
       it
233
  */
234
  InternalQueryState internal;
235
  std::atomic<uint16_t> age{0};
236
237
  class StateGuard
238
  {
239
  public:
240
    StateGuard(IDState& ids) :
241
      d_ids(ids)
242
0
    {
243
0
    }
244
    ~StateGuard()
245
0
    {
246
0
      d_ids.release();
247
0
    }
248
    StateGuard(const StateGuard&) = delete;
249
    StateGuard(StateGuard&&) = delete;
250
    StateGuard& operator=(const StateGuard&) = delete;
251
    StateGuard& operator=(StateGuard&&) = delete;
252
253
  private:
254
    IDState& d_ids;
255
  };
256
257
  [[nodiscard]] std::optional<StateGuard> acquire()
258
0
  {
259
0
    bool expected = false;
260
0
    if (locked.compare_exchange_strong(expected, true)) {
261
0
      return std::optional<StateGuard>(*this);
262
0
    }
263
0
    return std::nullopt;
264
0
  }
265
266
  void release()
267
0
  {
268
0
    locked.store(false);
269
0
  }
270
271
  std::atomic<bool> inUse{false}; // 1
272
273
private:
274
  std::atomic<bool> locked{false}; // 1
275
};