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