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