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