/src/pdns/pdns/dnsdistdist/dnsdist-idstate.hh
Line | Count | Source |
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 <cstdint> |
25 | | #include <ctime> |
26 | | #include <memory> |
27 | | #include <optional> |
28 | | #include <string_view> |
29 | | #include <unordered_map> |
30 | | #include <utility> |
31 | | #include <vector> |
32 | | |
33 | | #include "config.h" |
34 | | #include "dnscrypt.hh" |
35 | | #include "dnsdist-configuration.hh" |
36 | | #include "dnsdist-edns.hh" |
37 | | #include "dnsname.hh" |
38 | | #include "dnsdist-logging.hh" |
39 | | #include "dnsdist-protocols.hh" |
40 | | #include "ednsextendederror.hh" |
41 | | #include "gettime.hh" |
42 | | #include "iputils.hh" |
43 | | #include "noinitvector.hh" |
44 | | #include "dnsdist-opentelemetry.hh" |
45 | | #include "uuid-utils.hh" |
46 | | |
47 | | #ifndef DISABLE_PROTOBUF |
48 | | #include "dnsdist-protobuf.hh" |
49 | | #include "remote_logger.hh" |
50 | | #endif |
51 | | |
52 | | struct ClientState; |
53 | | struct DOHUnitInterface; |
54 | | struct DOQUnit; |
55 | | struct DOH3Unit; |
56 | | class DNSCryptQuery; |
57 | | class DNSDistPacketCache; |
58 | | |
59 | | using QTag = std::unordered_map<string, string>; |
60 | | using HeadersMap = std::unordered_map<std::string, std::string>; |
61 | | |
62 | | struct StopWatch |
63 | | { |
64 | | StopWatch(bool realTime = false) : |
65 | 0 | d_needRealTime(realTime) |
66 | 0 | { |
67 | 0 | } |
68 | | |
69 | | void start() |
70 | 0 | { |
71 | 0 | d_start = getCurrentTime(); |
72 | 0 | } |
73 | | |
74 | | void set(const struct timespec& from) |
75 | 0 | { |
76 | 0 | d_start = from; |
77 | 0 | } |
78 | | |
79 | | double udiff() const |
80 | 0 | { |
81 | 0 | struct timespec now = getCurrentTime(); |
82 | 0 | return 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0; |
83 | 0 | } |
84 | | |
85 | | double udiffAndSet() |
86 | 0 | { |
87 | 0 | struct timespec now = getCurrentTime(); |
88 | 0 | auto ret = 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0; |
89 | 0 | d_start = now; |
90 | 0 | return ret; |
91 | 0 | } |
92 | | |
93 | | struct timespec getStartTime() const |
94 | 0 | { |
95 | 0 | return d_start; |
96 | 0 | } |
97 | | |
98 | | struct timespec d_start{ |
99 | | 0, 0}; |
100 | | |
101 | | private: |
102 | | struct timespec getCurrentTime() const |
103 | 0 | { |
104 | 0 | struct timespec now; |
105 | 0 | if (gettime(&now, d_needRealTime) < 0) { |
106 | 0 | unixDie("Getting timestamp"); |
107 | 0 | } |
108 | 0 | return now; |
109 | 0 | } |
110 | | |
111 | | bool d_needRealTime; |
112 | | }; |
113 | | |
114 | | class CrossProtocolContext; |
115 | | |
116 | | struct InternalQueryState |
117 | | { |
118 | | struct ProtoBufData |
119 | | { |
120 | | std::optional<boost::uuids::uuid> uniqueId{std::nullopt}; // 17 |
121 | | std::string d_deviceName; |
122 | | std::string d_deviceID; |
123 | | std::string d_requestorID; |
124 | | }; |
125 | | |
126 | | /** |
127 | | * @brief Returns the Tracer, but only if OpenTelemetry tracing is globally enabled |
128 | | * |
129 | | * @return |
130 | | */ |
131 | | std::shared_ptr<pdns::trace::dnsdist::Tracer>& getTracer() |
132 | 0 | { |
133 | | #ifdef DISABLE_PROTOBUF |
134 | | return d_OTTracer; |
135 | | #else |
136 | 0 | if ((d_OTTracingEnabledInConfiguration && !*d_OTTracingEnabledInConfiguration) || d_OTTracer != nullptr) { |
137 | 0 | return d_OTTracer; |
138 | 0 | } |
139 | 0 | if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_openTelemetryTracing) { |
140 | | // OpenTelemetry tracing is enabled, but we don't have a tracer yet |
141 | 0 | d_OTTracingEnabledInConfiguration = true; |
142 | 0 | d_OTTracer = pdns::trace::dnsdist::Tracer::getTracer(); |
143 | 0 | d_OTTracer->setScopeSpanName("dnsdist/queryFromFrontend"); |
144 | 0 | } |
145 | 0 | else { |
146 | 0 | d_OTTracingEnabledInConfiguration = false; |
147 | 0 | } |
148 | 0 | return d_OTTracer; |
149 | 0 | #endif |
150 | 0 | } |
151 | | |
152 | | /** |
153 | | * @brief Returns a Tracer::Closer, but only if OpenTelemetry tracing is needed |
154 | | * |
155 | | * @return |
156 | | */ |
157 | | std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloser([[maybe_unused]] const std::string_view& name, [[maybe_unused]] const SpanID& parentSpanID); |
158 | | std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloser([[maybe_unused]] const std::string_view& name, [[maybe_unused]] const std::string_view& parentSpanName); |
159 | | std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloser([[maybe_unused]] const std::string_view& name); |
160 | | std::optional<pdns::trace::dnsdist::Tracer::Closer> getRulesCloser([[maybe_unused]] const std::string_view& ruleName, [[maybe_unused]] const std::string& ruleType); |
161 | | |
162 | | InternalQueryState() |
163 | 0 | { |
164 | 0 | origDest.sin4.sin_family = 0; |
165 | 0 | } |
166 | | |
167 | | InternalQueryState(InternalQueryState&& rhs) = default; |
168 | | InternalQueryState& operator=(InternalQueryState&& rhs) = default; |
169 | | |
170 | | InternalQueryState(const InternalQueryState& orig) = delete; |
171 | | InternalQueryState& operator=(const InternalQueryState& orig) = delete; |
172 | | ~InternalQueryState(); |
173 | | |
174 | | bool isXSK() const noexcept |
175 | 0 | { |
176 | 0 | #ifdef HAVE_XSK |
177 | 0 | return !xskPacketHeader.empty(); |
178 | 0 | #else |
179 | 0 | return false; |
180 | 0 | #endif /* HAVE_XSK */ |
181 | 0 | } |
182 | | |
183 | | InternalQueryState partialCloneForXFR() const; |
184 | | |
185 | | std::shared_ptr<const Logr::Logger> getLogger(std::shared_ptr<const Logr::Logger> parent = nullptr) const; |
186 | | |
187 | | std::optional<Netmask> subnet{std::nullopt}; // 40 |
188 | | std::string poolName; // 32 |
189 | | #if !defined(DISABLE_PROTOBUF) |
190 | | std::string d_rawProtobufContent; // protobuf-encoded content to add to protobuf messages // 32 |
191 | | #endif /* DISABLE_PROTOBUF */ |
192 | | ComboAddress origRemote; // 28 |
193 | | ComboAddress origDest; // 28 |
194 | | ComboAddress hopRemote; |
195 | | ComboAddress hopLocal; |
196 | | DNSName qname; // 24 |
197 | | #ifdef HAVE_XSK |
198 | | PacketBuffer xskPacketHeader; // 24 |
199 | | #endif /* HAVE_XSK */ |
200 | | StopWatch queryRealTime{true}; // 24 |
201 | | private: |
202 | | std::shared_ptr<pdns::trace::dnsdist::Tracer> d_OTTracer{nullptr}; |
203 | | |
204 | | public: |
205 | | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; // 16 |
206 | | std::unique_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; // 8 |
207 | | std::unique_ptr<QTag> qTag{nullptr}; // 8 |
208 | | std::unique_ptr<PacketBuffer> d_packet{nullptr}; // Initial packet, so we can restart the query from the response path if needed // 8 |
209 | | std::unique_ptr<ProtoBufData> d_protoBufData{nullptr}; |
210 | | #ifndef DISABLE_PROTOBUF |
211 | | std::vector<std::pair<std::string, std::shared_ptr<RemoteLoggerInterface>>> delayedResponseMsgs; |
212 | | std::vector<std::shared_ptr<RemoteLoggerInterface>> ottraceLoggers; |
213 | | #endif |
214 | | std::unique_ptr<std::vector<dnsdist::edns::SetExtendedDNSErrorOperation>> d_extendedErrors{nullptr}; |
215 | | std::optional<uint32_t> tempFailureTTL{std::nullopt}; // 8 |
216 | | ClientState* cs{nullptr}; // 8 |
217 | | std::unique_ptr<DOHUnitInterface> du; // 8 |
218 | | size_t d_proxyProtocolPayloadSize{0}; // 8 |
219 | | std::unique_ptr<DOQUnit> doqu{nullptr}; // 8 |
220 | | std::unique_ptr<DOH3Unit> doh3u{nullptr}; // 8 |
221 | | int32_t d_streamID{-1}; // 4 |
222 | | uint32_t cacheKey{0}; // 4 |
223 | | uint32_t cacheKeyNoECS{0}; // 4 |
224 | | // DoH-only: if we received a TC=1 answer, we had to retry over TCP and thus we need the TCP cache key */ |
225 | | uint32_t cacheKeyTCP{0}; // 4 |
226 | | uint32_t ttlCap{0}; // cap the TTL _after_ inserting into the packet cache // 4 |
227 | | int backendFD{-1}; // 4 |
228 | | int delayMsec{0}; |
229 | | uint16_t qtype{0}; // 2 |
230 | | uint16_t qclass{0}; // 2 |
231 | | // origID is in network-byte order |
232 | | uint16_t origID{0}; // 2 |
233 | | uint16_t origFlags{0}; // 2 |
234 | | uint16_t cacheFlags{0}; // DNS flags as sent to the backend // 2 |
235 | | uint16_t udpPayloadSize{0}; // Max UDP payload size from the query // 2 |
236 | | uint16_t sendTraceParentToDownstreamID{0}; // Whether or not to add a TRACEPARENT EDNS option to downstreams, set to non-0 for the EDNS Option ID |
237 | | std::optional<bool> dnssecOK; |
238 | | #ifndef DISABLE_PROTOBUF |
239 | | std::optional<bool> d_OTTracingEnabledInConfiguration; // Whether OpenTelemetry tracing is enabled in the configuration. This prevents having to check the configuration several times for the same query |
240 | | #endif /* DISABLE_PROTOBUF */ |
241 | | dnsdist::Protocol protocol; // 1 |
242 | | uint8_t restartCount{0}; // 1 |
243 | | bool ednsAdded{false}; |
244 | | bool ecsAdded{false}; |
245 | | bool skipCache{false}; |
246 | | bool useZeroScope{false}; |
247 | | bool forwardedOverUDP{false}; |
248 | | bool selfGenerated{false}; |
249 | | bool cacheHit{false}; |
250 | | bool staleCacheHit{false}; |
251 | | bool tracingEnabled{false}; // Whether or not Open Telemetry tracing is enabled for this query |
252 | | bool rulesAppliedToQuery{false}; // Whether applyRulesToQuery has been called for the query, used to determine if we need to trace |
253 | | struct rulesAppliedToQuerySetter |
254 | | { |
255 | | rulesAppliedToQuerySetter(bool& pastProcessRules) : |
256 | | d_rulesAppliedToQuery(pastProcessRules) |
257 | 0 | { |
258 | 0 | d_rulesAppliedToQuery = false; |
259 | 0 | } |
260 | | ~rulesAppliedToQuerySetter() |
261 | 0 | { |
262 | 0 | d_rulesAppliedToQuery = true; |
263 | 0 | } |
264 | | |
265 | | private: |
266 | | bool& d_rulesAppliedToQuery; |
267 | | }; |
268 | | }; |
269 | | |
270 | | struct IDState |
271 | | { |
272 | | IDState() |
273 | 0 | { |
274 | 0 | } |
275 | | |
276 | | IDState(const IDState& orig) = delete; |
277 | | IDState(IDState&& rhs) noexcept : |
278 | | internal(std::move(rhs.internal)) |
279 | 0 | { |
280 | 0 | inUse.store(rhs.inUse.load()); |
281 | 0 | age.store(rhs.age.load()); |
282 | 0 | } |
283 | | |
284 | | IDState& operator=(IDState&& rhs) noexcept |
285 | 0 | { |
286 | 0 | inUse.store(rhs.inUse.load()); |
287 | 0 | age.store(rhs.age.load()); |
288 | 0 | internal = std::move(rhs.internal); |
289 | 0 | return *this; |
290 | 0 | } |
291 | | |
292 | | bool isInUse() const |
293 | 0 | { |
294 | 0 | return inUse; |
295 | 0 | } |
296 | | |
297 | | /* For performance reasons we don't want to use a lock here, but that means |
298 | | we need to be very careful when modifying this value. Modifications happen |
299 | | from: |
300 | | - one of the UDP or DoH 'client' threads receiving a query, selecting a backend |
301 | | then picking one of the states associated to this backend (via the idOffset). |
302 | | Most of the time this state should not be in use and usageIndicator is -1, but we |
303 | | might not yet have received a response for the query previously associated to this |
304 | | state, meaning that we will 'reuse' this state and erase the existing state. |
305 | | If we ever receive a response for this state, it will be discarded. This is |
306 | | mostly fine for UDP except that we still need to be careful in order to miss |
307 | | the 'outstanding' counters, which should only be increased when we are picking |
308 | | an empty state, and not when reusing ; |
309 | | For DoH, though, we have dynamically allocated a DOHUnit object that needs to |
310 | | be freed, as well as internal objects. |
311 | | - one of the UDP receiver threads receiving a response from a backend, picking |
312 | | the corresponding state and sending the response to the client ; |
313 | | - the 'healthcheck' thread scanning the states to actively discover timeouts, |
314 | | mostly to keep some counters like the 'outstanding' one sane. |
315 | | |
316 | | We have two flags: |
317 | | - inUse tells us if there currently is an in-flight query whose state is stored |
318 | | in this state |
319 | | - locked tells us whether someone currently owns the state, so no-one else can touch |
320 | | it |
321 | | */ |
322 | | InternalQueryState internal; |
323 | | std::atomic<uint16_t> age{0}; |
324 | | |
325 | | class StateGuard |
326 | | { |
327 | | public: |
328 | | StateGuard(IDState& ids) : |
329 | | d_ids(ids) |
330 | 0 | { |
331 | 0 | } |
332 | | ~StateGuard() |
333 | 0 | { |
334 | 0 | d_ids.release(); |
335 | 0 | } |
336 | | StateGuard(const StateGuard&) = delete; |
337 | | StateGuard(StateGuard&&) = delete; |
338 | | StateGuard& operator=(const StateGuard&) = delete; |
339 | | StateGuard& operator=(StateGuard&&) = delete; |
340 | | |
341 | | private: |
342 | | IDState& d_ids; |
343 | | }; |
344 | | |
345 | | [[nodiscard]] std::optional<StateGuard> acquire() |
346 | 0 | { |
347 | 0 | bool expected = false; |
348 | 0 | if (locked.compare_exchange_strong(expected, true)) { |
349 | 0 | return std::optional<StateGuard>(*this); |
350 | 0 | } |
351 | 0 | return std::nullopt; |
352 | 0 | } |
353 | | |
354 | | void release() |
355 | 0 | { |
356 | 0 | locked.store(false); |
357 | 0 | } |
358 | | |
359 | | std::atomic<bool> inUse{false}; // 1 |
360 | | |
361 | | private: |
362 | | std::atomic<bool> locked{false}; // 1 |
363 | | }; |