]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-idstate.hh
Merge pull request #12423 from fredmorcos/ddist-tls-provider
[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 /* g++ defines __SANITIZE_THREAD__
93 clang++ supports the nice __has_feature(thread_sanitizer),
94 let's merge them */
95 #if defined(__has_feature)
96 #if __has_feature(thread_sanitizer)
97 #define __SANITIZE_THREAD__ 1
98 #endif
99 #endif
100
101 struct InternalQueryState
102 {
103 static void DeleterPlaceHolder(DOHUnit*)
104 {
105 }
106
107 InternalQueryState() :
108 du(std::unique_ptr<DOHUnit, void (*)(DOHUnit*)>(nullptr, DeleterPlaceHolder))
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}; // 16
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; // Initial packet, so we can restart the query from the response path if needed // 8
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 boost::optional<boost::uuids::uuid> uniqueId{boost::none}; // 17 (placed here to reduce the space lost to padding)
150 bool ednsAdded{false};
151 bool ecsAdded{false};
152 bool skipCache{false};
153 bool dnssecOK{false};
154 bool useZeroScope{false};
155 bool forwardedOverUDP{false};
156 bool selfGenerated{false};
157 };
158
159 struct IDState
160 {
161 IDState()
162 {
163 }
164
165 IDState(const IDState& orig) = delete;
166 IDState(IDState&& rhs) noexcept :
167 internal(std::move(rhs.internal))
168 {
169 inUse.store(rhs.inUse.load());
170 age.store(rhs.age.load());
171 }
172
173 IDState& operator=(IDState&& rhs) noexcept
174 {
175 inUse.store(rhs.inUse.load());
176 age.store(rhs.age.load());
177 internal = std::move(rhs.internal);
178 return *this;
179 }
180
181 bool isInUse() const
182 {
183 return inUse;
184 }
185
186 /* For performance reasons we don't want to use a lock here, but that means
187 we need to be very careful when modifying this value. Modifications happen
188 from:
189 - one of the UDP or DoH 'client' threads receiving a query, selecting a backend
190 then picking one of the states associated to this backend (via the idOffset).
191 Most of the time this state should not be in use and usageIndicator is -1, but we
192 might not yet have received a response for the query previously associated to this
193 state, meaning that we will 'reuse' this state and erase the existing state.
194 If we ever receive a response for this state, it will be discarded. This is
195 mostly fine for UDP except that we still need to be careful in order to miss
196 the 'outstanding' counters, which should only be increased when we are picking
197 an empty state, and not when reusing ;
198 For DoH, though, we have dynamically allocated a DOHUnit object that needs to
199 be freed, as well as internal objects internals to libh2o.
200 - one of the UDP receiver threads receiving a response from a backend, picking
201 the corresponding state and sending the response to the client ;
202 - the 'healthcheck' thread scanning the states to actively discover timeouts,
203 mostly to keep some counters like the 'outstanding' one sane.
204
205 We have two flags:
206 - inUse tells us if there currently is a in-flight query whose state is stored
207 in this state
208 - locked tells us whether someone currently owns the state, so no-one else can touch
209 it
210 */
211 InternalQueryState internal;
212 std::atomic<uint16_t> age{0};
213
214 class StateGuard
215 {
216 public:
217 StateGuard(IDState& ids) :
218 d_ids(ids)
219 {
220 }
221 ~StateGuard()
222 {
223 d_ids.release();
224 }
225 StateGuard(const StateGuard&) = delete;
226 StateGuard(StateGuard&&) = delete;
227 StateGuard& operator=(const StateGuard&) = delete;
228 StateGuard& operator=(StateGuard&&) = delete;
229
230 private:
231 IDState& d_ids;
232 };
233
234 [[nodiscard]] std::optional<StateGuard> acquire()
235 {
236 bool expected = false;
237 if (locked.compare_exchange_strong(expected, true)) {
238 return std::optional<StateGuard>(*this);
239 }
240 return std::nullopt;
241 }
242
243 void release()
244 {
245 locked.store(false);
246 }
247
248 std::atomic<bool> inUse{false}; // 1
249
250 private:
251 std::atomic<bool> locked{false}; // 1
252 };