/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 | | }; |