/src/pdns/pdns/dnsdist.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 | | #include "config.h" |
24 | | #include "ext/luawrapper/include/LuaContext.hpp" |
25 | | |
26 | | #include <memory> |
27 | | #include <mutex> |
28 | | #include <string> |
29 | | #include <thread> |
30 | | #include <time.h> |
31 | | #include <unistd.h> |
32 | | #include <unordered_map> |
33 | | |
34 | | #include <boost/variant.hpp> |
35 | | |
36 | | #include "circular_buffer.hh" |
37 | | #include "dnscrypt.hh" |
38 | | #include "dnsdist-cache.hh" |
39 | | #include "dnsdist-dynbpf.hh" |
40 | | #include "dnsdist-idstate.hh" |
41 | | #include "dnsdist-lbpolicies.hh" |
42 | | #include "dnsdist-protocols.hh" |
43 | | #include "dnsname.hh" |
44 | | #include "doh.hh" |
45 | | #include "ednsoptions.hh" |
46 | | #include "iputils.hh" |
47 | | #include "misc.hh" |
48 | | #include "mplexer.hh" |
49 | | #include "noinitvector.hh" |
50 | | #include "sholder.hh" |
51 | | #include "tcpiohandler.hh" |
52 | | #include "uuid-utils.hh" |
53 | | #include "proxy-protocol.hh" |
54 | | #include "stat_t.hh" |
55 | | |
56 | | uint64_t uptimeOfProcess(const std::string& str); |
57 | | |
58 | | extern uint16_t g_ECSSourcePrefixV4; |
59 | | extern uint16_t g_ECSSourcePrefixV6; |
60 | | extern bool g_ECSOverride; |
61 | | |
62 | | using QTag = std::unordered_map<string, string>; |
63 | | |
64 | | class IncomingTCPConnectionState; |
65 | | |
66 | | struct ClientState; |
67 | | |
68 | | struct DNSQuestion |
69 | | { |
70 | | DNSQuestion(InternalQueryState& ids_, PacketBuffer& data_): |
71 | 0 | data(data_), ids(ids_), ecsPrefixLength(ids.origRemote.sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), ecsOverride(g_ECSOverride) { |
72 | 0 | } |
73 | | DNSQuestion(const DNSQuestion&) = delete; |
74 | | DNSQuestion& operator=(const DNSQuestion&) = delete; |
75 | | DNSQuestion(DNSQuestion&&) = default; |
76 | 0 | virtual ~DNSQuestion() = default; |
77 | | |
78 | | std::string getTrailingData() const; |
79 | | bool setTrailingData(const std::string&); |
80 | | const PacketBuffer& getData() const |
81 | 0 | { |
82 | 0 | return data; |
83 | 0 | } |
84 | | PacketBuffer& getMutableData() |
85 | 0 | { |
86 | 0 | return data; |
87 | 0 | } |
88 | | |
89 | | dnsheader* getHeader() |
90 | 0 | { |
91 | 0 | if (data.size() < sizeof(dnsheader)) { |
92 | 0 | throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer"); |
93 | 0 | } |
94 | 0 | return reinterpret_cast<dnsheader*>(&data.at(0)); |
95 | 0 | } |
96 | | |
97 | | const dnsheader* getHeader() const |
98 | 0 | { |
99 | 0 | if (data.size() < sizeof(dnsheader)) { |
100 | 0 | throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer"); |
101 | 0 | } |
102 | 0 | return reinterpret_cast<const dnsheader*>(&data.at(0)); |
103 | 0 | } |
104 | | |
105 | | bool hasRoomFor(size_t more) const |
106 | 0 | { |
107 | 0 | return data.size() <= getMaximumSize() && (getMaximumSize() - data.size()) >= more; |
108 | 0 | } |
109 | | |
110 | | size_t getMaximumSize() const |
111 | 0 | { |
112 | 0 | if (overTCP()) { |
113 | 0 | return std::numeric_limits<uint16_t>::max(); |
114 | 0 | } |
115 | 0 | return 4096; |
116 | 0 | } |
117 | | |
118 | | dnsdist::Protocol getProtocol() const |
119 | 0 | { |
120 | 0 | return ids.protocol; |
121 | 0 | } |
122 | | |
123 | | bool overTCP() const |
124 | 0 | { |
125 | 0 | return !(ids.protocol == dnsdist::Protocol::DoUDP || ids.protocol == dnsdist::Protocol::DNSCryptUDP); |
126 | 0 | } |
127 | | |
128 | 0 | void setTag(std::string&& key, std::string&& value) { |
129 | 0 | if (!ids.qTag) { |
130 | 0 | ids.qTag = std::make_unique<QTag>(); |
131 | 0 | } |
132 | 0 | ids.qTag->insert_or_assign(std::move(key), std::move(value)); |
133 | 0 | } |
134 | | |
135 | 0 | void setTag(const std::string& key, const std::string& value) { |
136 | 0 | if (!ids.qTag) { |
137 | 0 | ids.qTag = std::make_unique<QTag>(); |
138 | 0 | } |
139 | 0 | ids.qTag->insert_or_assign(key, value); |
140 | 0 | } |
141 | | |
142 | | const struct timespec& getQueryRealTime() const |
143 | 0 | { |
144 | 0 | return ids.queryRealTime.d_start; |
145 | 0 | } |
146 | | |
147 | | bool isAsynchronous() const |
148 | 0 | { |
149 | 0 | return asynchronous; |
150 | 0 | } |
151 | | |
152 | | std::shared_ptr<IncomingTCPConnectionState> getIncomingTCPState() const |
153 | 0 | { |
154 | 0 | return d_incomingTCPState; |
155 | 0 | } |
156 | | |
157 | | ClientState* getFrontend() const |
158 | 0 | { |
159 | 0 | return ids.cs; |
160 | 0 | } |
161 | | |
162 | | protected: |
163 | | PacketBuffer& data; |
164 | | |
165 | | public: |
166 | | InternalQueryState& ids; |
167 | | std::unique_ptr<Netmask> ecs{nullptr}; |
168 | | std::string sni; /* Server Name Indication, if any (DoT or DoH) */ |
169 | | mutable std::unique_ptr<EDNSOptionViewMap> ednsOptions; /* this needs to be mutable because it is parsed just in time, when DNSQuestion is read-only */ |
170 | | std::shared_ptr<IncomingTCPConnectionState> d_incomingTCPState{nullptr}; |
171 | | std::unique_ptr<std::vector<ProxyProtocolValue>> proxyProtocolValues{nullptr}; |
172 | | uint16_t ecsPrefixLength; |
173 | | uint8_t ednsRCode{0}; |
174 | | bool ecsOverride; |
175 | | bool useECS{true}; |
176 | | bool addXPF{true}; |
177 | | bool asynchronous{false}; |
178 | | }; |
179 | | |
180 | | struct DownstreamState; |
181 | | |
182 | | struct DNSResponse : DNSQuestion |
183 | | { |
184 | | DNSResponse(InternalQueryState& ids_, PacketBuffer& data_, const std::shared_ptr<DownstreamState>& downstream): |
185 | 0 | DNSQuestion(ids_, data_), d_downstream(downstream) { } |
186 | | DNSResponse(const DNSResponse&) = delete; |
187 | | DNSResponse& operator=(const DNSResponse&) = delete; |
188 | | DNSResponse(DNSResponse&&) = default; |
189 | | |
190 | | const std::shared_ptr<DownstreamState>& d_downstream; |
191 | | }; |
192 | | |
193 | | /* so what could you do: |
194 | | drop, |
195 | | fake up nxdomain, |
196 | | provide actual answer, |
197 | | allow & and stop processing, |
198 | | continue processing, |
199 | | modify header: (servfail|refused|notimp), set TC=1, |
200 | | send to pool */ |
201 | | |
202 | | class DNSAction |
203 | | { |
204 | | public: |
205 | | enum class Action : uint8_t { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, ServFail, None, NoOp, NoRecurse, SpoofRaw, SpoofPacket }; |
206 | | static std::string typeToString(const Action& action) |
207 | 0 | { |
208 | 0 | switch(action) { |
209 | 0 | case Action::Drop: |
210 | 0 | return "Drop"; |
211 | 0 | case Action::Nxdomain: |
212 | 0 | return "Send NXDomain"; |
213 | 0 | case Action::Refused: |
214 | 0 | return "Send Refused"; |
215 | 0 | case Action::Spoof: |
216 | 0 | return "Spoof an answer"; |
217 | 0 | case Action::SpoofPacket: |
218 | 0 | return "Spoof a raw answer from bytes"; |
219 | 0 | case Action::SpoofRaw: |
220 | 0 | return "Spoof an answer from raw bytes"; |
221 | 0 | case Action::Allow: |
222 | 0 | return "Allow"; |
223 | 0 | case Action::HeaderModify: |
224 | 0 | return "Modify the header"; |
225 | 0 | case Action::Pool: |
226 | 0 | return "Route to a pool"; |
227 | 0 | case Action::Delay: |
228 | 0 | return "Delay"; |
229 | 0 | case Action::Truncate: |
230 | 0 | return "Truncate over UDP"; |
231 | 0 | case Action::ServFail: |
232 | 0 | return "Send ServFail"; |
233 | 0 | case Action::None: |
234 | 0 | case Action::NoOp: |
235 | 0 | return "Do nothing"; |
236 | 0 | case Action::NoRecurse: |
237 | 0 | return "Set rd=0"; |
238 | 0 | } |
239 | 0 |
|
240 | 0 | return "Unknown"; |
241 | 0 | } |
242 | | |
243 | | virtual Action operator()(DNSQuestion*, string* ruleresult) const =0; |
244 | | virtual ~DNSAction() |
245 | 0 | { |
246 | 0 | } |
247 | | virtual string toString() const = 0; |
248 | | virtual std::map<string, double> getStats() const |
249 | 0 | { |
250 | 0 | return {{}}; |
251 | 0 | } |
252 | | virtual void reload() |
253 | 0 | { |
254 | 0 | } |
255 | | }; |
256 | | |
257 | | class DNSResponseAction |
258 | | { |
259 | | public: |
260 | | enum class Action : uint8_t { Allow, Delay, Drop, HeaderModify, ServFail, None }; |
261 | | virtual Action operator()(DNSResponse*, string* ruleresult) const =0; |
262 | | virtual ~DNSResponseAction() |
263 | 0 | { |
264 | 0 | } |
265 | | virtual string toString() const = 0; |
266 | | virtual void reload() |
267 | 0 | { |
268 | 0 | } |
269 | | }; |
270 | | |
271 | | struct DynBlock |
272 | | { |
273 | | DynBlock(): action(DNSAction::Action::None), warning(false) |
274 | 0 | { |
275 | 0 | until.tv_sec = 0; |
276 | 0 | until.tv_nsec = 0; |
277 | 0 | } |
278 | | |
279 | | DynBlock(const std::string& reason_, const struct timespec& until_, const DNSName& domain_, DNSAction::Action action_): reason(reason_), domain(domain_), until(until_), action(action_), warning(false) |
280 | 0 | { |
281 | 0 | } |
282 | | |
283 | | DynBlock(const DynBlock& rhs): reason(rhs.reason), domain(rhs.domain), until(rhs.until), action(rhs.action), warning(rhs.warning), bpf(rhs.bpf) |
284 | 0 | { |
285 | 0 | blocks.store(rhs.blocks); |
286 | 0 | } |
287 | | |
288 | | DynBlock(DynBlock&& rhs): reason(std::move(rhs.reason)), domain(std::move(rhs.domain)), until(rhs.until), action(rhs.action), warning(rhs.warning), bpf(rhs.bpf) |
289 | 0 | { |
290 | 0 | blocks.store(rhs.blocks); |
291 | 0 | } |
292 | | |
293 | | DynBlock& operator=(const DynBlock& rhs) |
294 | 0 | { |
295 | 0 | reason = rhs.reason; |
296 | 0 | until = rhs.until; |
297 | 0 | domain = rhs.domain; |
298 | 0 | action = rhs.action; |
299 | 0 | blocks.store(rhs.blocks); |
300 | 0 | warning = rhs.warning; |
301 | 0 | bpf = rhs.bpf; |
302 | 0 | return *this; |
303 | 0 | } |
304 | | |
305 | | DynBlock& operator=(DynBlock&& rhs) |
306 | 0 | { |
307 | 0 | reason = std::move(rhs.reason); |
308 | 0 | until = rhs.until; |
309 | 0 | domain = std::move(rhs.domain); |
310 | 0 | action = rhs.action; |
311 | 0 | blocks.store(rhs.blocks); |
312 | 0 | warning = rhs.warning; |
313 | 0 | bpf = rhs.bpf; |
314 | 0 | return *this; |
315 | 0 | } |
316 | | |
317 | | string reason; |
318 | | DNSName domain; |
319 | | struct timespec until; |
320 | | mutable std::atomic<unsigned int> blocks; |
321 | | DNSAction::Action action{DNSAction::Action::None}; |
322 | | bool warning{false}; |
323 | | bool bpf{false}; |
324 | | }; |
325 | | |
326 | | extern GlobalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange>> g_dynblockNMG; |
327 | | |
328 | | extern vector<pair<struct timeval, std::string> > g_confDelta; |
329 | | |
330 | | using pdns::stat_t; |
331 | | |
332 | | struct DNSDistStats |
333 | | { |
334 | | stat_t responses{0}; |
335 | | stat_t servfailResponses{0}; |
336 | | stat_t queries{0}; |
337 | | stat_t frontendNXDomain{0}; |
338 | | stat_t frontendServFail{0}; |
339 | | stat_t frontendNoError{0}; |
340 | | stat_t nonCompliantQueries{0}; |
341 | | stat_t nonCompliantResponses{0}; |
342 | | stat_t rdQueries{0}; |
343 | | stat_t emptyQueries{0}; |
344 | | stat_t aclDrops{0}; |
345 | | stat_t dynBlocked{0}; |
346 | | stat_t ruleDrop{0}; |
347 | | stat_t ruleNXDomain{0}; |
348 | | stat_t ruleRefused{0}; |
349 | | stat_t ruleServFail{0}; |
350 | | stat_t ruleTruncated{0}; |
351 | | stat_t selfAnswered{0}; |
352 | | stat_t downstreamTimeouts{0}; |
353 | | stat_t downstreamSendErrors{0}; |
354 | | stat_t truncFail{0}; |
355 | | stat_t noPolicy{0}; |
356 | | stat_t cacheHits{0}; |
357 | | stat_t cacheMisses{0}; |
358 | | stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}, latencySum{0}, latencyCount{0}; |
359 | | stat_t securityStatus{0}; |
360 | | stat_t dohQueryPipeFull{0}; |
361 | | stat_t dohResponsePipeFull{0}; |
362 | | stat_t outgoingDoHQueryPipeFull{0}; |
363 | | stat_t proxyProtocolInvalid{0}; |
364 | | stat_t tcpQueryPipeFull{0}; |
365 | | stat_t tcpCrossProtocolQueryPipeFull{0}; |
366 | | stat_t tcpCrossProtocolResponsePipeFull{0}; |
367 | | double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0}; |
368 | | double latencyTCPAvg100{0}, latencyTCPAvg1000{0}, latencyTCPAvg10000{0}, latencyTCPAvg1000000{0}; |
369 | | double latencyDoTAvg100{0}, latencyDoTAvg1000{0}, latencyDoTAvg10000{0}, latencyDoTAvg1000000{0}; |
370 | | double latencyDoHAvg100{0}, latencyDoHAvg1000{0}, latencyDoHAvg10000{0}, latencyDoHAvg1000000{0}; |
371 | | typedef std::function<uint64_t(const std::string&)> statfunction_t; |
372 | | typedef boost::variant<stat_t*, pdns::stat_t_trait<double>*, double*, statfunction_t> entry_t; |
373 | | |
374 | | std::vector<std::pair<std::string, entry_t>> entries{ |
375 | | {"responses", &responses}, |
376 | | {"servfail-responses", &servfailResponses}, |
377 | | {"queries", &queries}, |
378 | | {"frontend-nxdomain", &frontendNXDomain}, |
379 | | {"frontend-servfail", &frontendServFail}, |
380 | | {"frontend-noerror", &frontendNoError}, |
381 | | {"acl-drops", &aclDrops}, |
382 | | {"rule-drop", &ruleDrop}, |
383 | | {"rule-nxdomain", &ruleNXDomain}, |
384 | | {"rule-refused", &ruleRefused}, |
385 | | {"rule-servfail", &ruleServFail}, |
386 | | {"rule-truncated", &ruleTruncated}, |
387 | | {"self-answered", &selfAnswered}, |
388 | | {"downstream-timeouts", &downstreamTimeouts}, |
389 | | {"downstream-send-errors", &downstreamSendErrors}, |
390 | | {"trunc-failures", &truncFail}, |
391 | | {"no-policy", &noPolicy}, |
392 | | {"latency0-1", &latency0_1}, |
393 | | {"latency1-10", &latency1_10}, |
394 | | {"latency10-50", &latency10_50}, |
395 | | {"latency50-100", &latency50_100}, |
396 | | {"latency100-1000", &latency100_1000}, |
397 | | {"latency-slow", &latencySlow}, |
398 | | {"latency-avg100", &latencyAvg100}, |
399 | | {"latency-avg1000", &latencyAvg1000}, |
400 | | {"latency-avg10000", &latencyAvg10000}, |
401 | | {"latency-avg1000000", &latencyAvg1000000}, |
402 | | {"latency-tcp-avg100", &latencyTCPAvg100}, |
403 | | {"latency-tcp-avg1000", &latencyTCPAvg1000}, |
404 | | {"latency-tcp-avg10000", &latencyTCPAvg10000}, |
405 | | {"latency-tcp-avg1000000", &latencyTCPAvg1000000}, |
406 | | {"latency-dot-avg100", &latencyDoTAvg100}, |
407 | | {"latency-dot-avg1000", &latencyDoTAvg1000}, |
408 | | {"latency-dot-avg10000", &latencyDoTAvg10000}, |
409 | | {"latency-dot-avg1000000", &latencyDoTAvg1000000}, |
410 | | {"latency-doh-avg100", &latencyDoHAvg100}, |
411 | | {"latency-doh-avg1000", &latencyDoHAvg1000}, |
412 | | {"latency-doh-avg10000", &latencyDoHAvg10000}, |
413 | | {"latency-doh-avg1000000", &latencyDoHAvg1000000}, |
414 | | {"uptime", uptimeOfProcess}, |
415 | | {"real-memory-usage", getRealMemoryUsage}, |
416 | | {"special-memory-usage", getSpecialMemoryUsage}, |
417 | | {"udp-in-errors", std::bind(udpErrorStats, "udp-in-errors")}, |
418 | | {"udp-noport-errors", std::bind(udpErrorStats, "udp-noport-errors")}, |
419 | | {"udp-recvbuf-errors", std::bind(udpErrorStats, "udp-recvbuf-errors")}, |
420 | | {"udp-sndbuf-errors", std::bind(udpErrorStats, "udp-sndbuf-errors")}, |
421 | | {"udp-in-csum-errors", std::bind(udpErrorStats, "udp-in-csum-errors")}, |
422 | | {"udp6-in-errors", std::bind(udp6ErrorStats, "udp6-in-errors")}, |
423 | | {"udp6-recvbuf-errors", std::bind(udp6ErrorStats, "udp6-recvbuf-errors")}, |
424 | | {"udp6-sndbuf-errors", std::bind(udp6ErrorStats, "udp6-sndbuf-errors")}, |
425 | | {"udp6-noport-errors", std::bind(udp6ErrorStats, "udp6-noport-errors")}, |
426 | | {"udp6-in-csum-errors", std::bind(udp6ErrorStats, "udp6-in-csum-errors")}, |
427 | | {"tcp-listen-overflows", std::bind(tcpErrorStats, "ListenOverflows")}, |
428 | | {"noncompliant-queries", &nonCompliantQueries}, |
429 | | {"noncompliant-responses", &nonCompliantResponses}, |
430 | | {"proxy-protocol-invalid", &proxyProtocolInvalid}, |
431 | | {"rdqueries", &rdQueries}, |
432 | | {"empty-queries", &emptyQueries}, |
433 | | {"cache-hits", &cacheHits}, |
434 | | {"cache-misses", &cacheMisses}, |
435 | | {"cpu-iowait", getCPUIOWait}, |
436 | | {"cpu-steal", getCPUSteal}, |
437 | | {"cpu-sys-msec", getCPUTimeSystem}, |
438 | | {"cpu-user-msec", getCPUTimeUser}, |
439 | | {"fd-usage", getOpenFileDescriptors}, |
440 | | {"dyn-blocked", &dynBlocked}, |
441 | | {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }}, |
442 | | {"security-status", &securityStatus}, |
443 | | {"doh-query-pipe-full", &dohQueryPipeFull}, |
444 | | {"doh-response-pipe-full", &dohResponsePipeFull}, |
445 | | {"outgoing-doh-query-pipe-full", &outgoingDoHQueryPipeFull}, |
446 | | {"tcp-query-pipe-full", &tcpQueryPipeFull}, |
447 | | {"tcp-cross-protocol-query-pipe-full", &tcpCrossProtocolQueryPipeFull}, |
448 | | {"tcp-cross-protocol-response-pipe-full", &tcpCrossProtocolResponsePipeFull}, |
449 | | // Latency histogram |
450 | | {"latency-sum", &latencySum}, |
451 | | {"latency-count", &latencyCount}, |
452 | | }; |
453 | | std::map<std::string, stat_t, std::less<>> customCounters; |
454 | | std::map<std::string, pdns::stat_t_trait<double>, std::less<>> customGauges; |
455 | | }; |
456 | | |
457 | | extern struct DNSDistStats g_stats; |
458 | | |
459 | | class BasicQPSLimiter |
460 | | { |
461 | | public: |
462 | | BasicQPSLimiter() |
463 | 0 | { |
464 | 0 | } |
465 | | |
466 | | BasicQPSLimiter(unsigned int burst): d_tokens(burst) |
467 | 0 | { |
468 | 0 | d_prev.start(); |
469 | 0 | } |
470 | | |
471 | | virtual ~BasicQPSLimiter() |
472 | 0 | { |
473 | 0 | } |
474 | | |
475 | | bool check(unsigned int rate, unsigned int burst) const // this is not quite fair |
476 | 0 | { |
477 | 0 | if (checkOnly(rate, burst)) { |
478 | 0 | addHit(); |
479 | 0 | return true; |
480 | 0 | } |
481 | 0 |
|
482 | 0 | return false; |
483 | 0 | } |
484 | | |
485 | | bool checkOnly(unsigned int rate, unsigned int burst) const // this is not quite fair |
486 | 0 | { |
487 | 0 | auto delta = d_prev.udiffAndSet(); |
488 | 0 |
|
489 | 0 | if (delta > 0.0) { // time, frequently, does go backwards.. |
490 | 0 | d_tokens += 1.0 * rate * (delta/1000000.0); |
491 | 0 | } |
492 | 0 |
|
493 | 0 | if (d_tokens > burst) { |
494 | 0 | d_tokens = burst; |
495 | 0 | } |
496 | 0 |
|
497 | 0 | bool ret = false; |
498 | 0 | if (d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise |
499 | 0 | ret = true; |
500 | 0 | } |
501 | 0 |
|
502 | 0 | return ret; |
503 | 0 | } |
504 | | |
505 | | virtual void addHit() const |
506 | 0 | { |
507 | 0 | --d_tokens; |
508 | 0 | } |
509 | | |
510 | | bool seenSince(const struct timespec& cutOff) const |
511 | 0 | { |
512 | 0 | return cutOff < d_prev.d_start; |
513 | 0 | } |
514 | | |
515 | | protected: |
516 | | mutable StopWatch d_prev; |
517 | | mutable double d_tokens{0.0}; |
518 | | }; |
519 | | |
520 | | class QPSLimiter : public BasicQPSLimiter |
521 | | { |
522 | | public: |
523 | | QPSLimiter(): BasicQPSLimiter() |
524 | 0 | { |
525 | 0 | } |
526 | | |
527 | | QPSLimiter(unsigned int rate, unsigned int burst): BasicQPSLimiter(burst), d_rate(rate), d_burst(burst), d_passthrough(false) |
528 | 0 | { |
529 | 0 | d_prev.start(); |
530 | 0 | } |
531 | | |
532 | | unsigned int getRate() const |
533 | 0 | { |
534 | 0 | return d_passthrough ? 0 : d_rate; |
535 | 0 | } |
536 | | |
537 | | bool check() const // this is not quite fair |
538 | 0 | { |
539 | 0 | if (d_passthrough) { |
540 | 0 | return true; |
541 | 0 | } |
542 | 0 |
|
543 | 0 | return BasicQPSLimiter::check(d_rate, d_burst); |
544 | 0 | } |
545 | | |
546 | | bool checkOnly() const |
547 | 0 | { |
548 | 0 | if (d_passthrough) { |
549 | 0 | return true; |
550 | 0 | } |
551 | 0 |
|
552 | 0 | return BasicQPSLimiter::checkOnly(d_rate, d_burst); |
553 | 0 | } |
554 | | |
555 | | void addHit() const override |
556 | 0 | { |
557 | 0 | if (!d_passthrough) { |
558 | 0 | --d_tokens; |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | private: |
563 | | unsigned int d_rate{0}; |
564 | | unsigned int d_burst{0}; |
565 | | bool d_passthrough{true}; |
566 | | }; |
567 | | |
568 | | typedef std::unordered_map<string, unsigned int> QueryCountRecords; |
569 | | typedef std::function<std::tuple<bool, string>(const DNSQuestion* dq)> QueryCountFilter; |
570 | | struct QueryCount { |
571 | | QueryCount() |
572 | 0 | { |
573 | 0 | } |
574 | | ~QueryCount() |
575 | 0 | { |
576 | 0 | } |
577 | | SharedLockGuarded<QueryCountRecords> records; |
578 | | QueryCountFilter filter; |
579 | | bool enabled{false}; |
580 | | }; |
581 | | |
582 | | extern QueryCount g_qcount; |
583 | | |
584 | | struct ClientState |
585 | | { |
586 | | ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set<int>& cpus_): cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort) |
587 | 0 | { |
588 | 0 | } |
589 | | |
590 | | stat_t queries{0}; |
591 | | stat_t nonCompliantQueries{0}; |
592 | | mutable stat_t responses{0}; |
593 | | mutable stat_t tcpDiedReadingQuery{0}; |
594 | | mutable stat_t tcpDiedSendingResponse{0}; |
595 | | mutable stat_t tcpGaveUp{0}; |
596 | | mutable stat_t tcpClientTimeouts{0}; |
597 | | mutable stat_t tcpDownstreamTimeouts{0}; |
598 | | /* current number of connections to this frontend */ |
599 | | mutable stat_t tcpCurrentConnections{0}; |
600 | | /* maximum number of concurrent connections to this frontend reached */ |
601 | | mutable stat_t tcpMaxConcurrentConnections{0}; |
602 | | stat_t tlsNewSessions{0}; // A new TLS session has been negotiated, no resumption |
603 | | stat_t tlsResumptions{0}; // A TLS session has been resumed, either via session id or via a TLS ticket |
604 | | stat_t tlsUnknownTicketKey{0}; // A TLS ticket has been presented but we don't have the associated key (might have expired) |
605 | | stat_t tlsInactiveTicketKey{0}; // A TLS ticket has been successfully resumed but the key is no longer active, we should issue a new one |
606 | | stat_t tls10queries{0}; // valid DNS queries received via TLSv1.0 |
607 | | stat_t tls11queries{0}; // valid DNS queries received via TLSv1.1 |
608 | | stat_t tls12queries{0}; // valid DNS queries received via TLSv1.2 |
609 | | stat_t tls13queries{0}; // valid DNS queries received via TLSv1.3 |
610 | | stat_t tlsUnknownqueries{0}; // valid DNS queries received via unknown TLS version |
611 | | pdns::stat_t_trait<double> tcpAvgQueriesPerConnection{0.0}; |
612 | | /* in ms */ |
613 | | pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0}; |
614 | | std::set<int> cpus; |
615 | | std::string interface; |
616 | | ComboAddress local; |
617 | | std::vector<std::pair<ComboAddress, int>> d_additionalAddresses; |
618 | | std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr}; |
619 | | std::shared_ptr<TLSFrontend> tlsFrontend{nullptr}; |
620 | | std::shared_ptr<DOHFrontend> dohFrontend{nullptr}; |
621 | | std::shared_ptr<BPFFilter> d_filter{nullptr}; |
622 | | size_t d_maxInFlightQueriesPerConn{1}; |
623 | | size_t d_tcpConcurrentConnectionsLimit{0}; |
624 | | int udpFD{-1}; |
625 | | int tcpFD{-1}; |
626 | | int tcpListenQueueSize{SOMAXCONN}; |
627 | | int fastOpenQueueSize{0}; |
628 | | bool muted{false}; |
629 | | bool tcp; |
630 | | bool reuseport; |
631 | | bool ready{false}; |
632 | | |
633 | | int getSocket() const |
634 | 0 | { |
635 | 0 | return udpFD != -1 ? udpFD : tcpFD; |
636 | 0 | } |
637 | | |
638 | | bool isUDP() const |
639 | 0 | { |
640 | 0 | return udpFD != -1; |
641 | 0 | } |
642 | | |
643 | | bool isTCP() const |
644 | 0 | { |
645 | 0 | return udpFD == -1; |
646 | 0 | } |
647 | | |
648 | | bool isDoH() const |
649 | 0 | { |
650 | 0 | return dohFrontend != nullptr; |
651 | 0 | } |
652 | | |
653 | | bool hasTLS() const |
654 | 0 | { |
655 | 0 | return tlsFrontend != nullptr || (dohFrontend != nullptr && dohFrontend->isHTTPS()); |
656 | 0 | } |
657 | | |
658 | | dnsdist::Protocol getProtocol() const |
659 | 0 | { |
660 | 0 | if (dnscryptCtx) { |
661 | 0 | if (udpFD != -1) { |
662 | 0 | return dnsdist::Protocol::DNSCryptUDP; |
663 | 0 | } |
664 | 0 | return dnsdist::Protocol::DNSCryptTCP; |
665 | 0 | } |
666 | 0 | if (isDoH()) { |
667 | 0 | return dnsdist::Protocol::DoH; |
668 | 0 | } |
669 | 0 | else if (hasTLS()) { |
670 | 0 | return dnsdist::Protocol::DoT; |
671 | 0 | } |
672 | 0 | else if (udpFD != -1) { |
673 | 0 | return dnsdist::Protocol::DoUDP; |
674 | 0 | } |
675 | 0 | else { |
676 | 0 | return dnsdist::Protocol::DoTCP; |
677 | 0 | } |
678 | 0 | } |
679 | | |
680 | | std::string getType() const |
681 | 0 | { |
682 | 0 | std::string result = udpFD != -1 ? "UDP" : "TCP"; |
683 | 0 |
|
684 | 0 | if (dohFrontend) { |
685 | 0 | if (dohFrontend->isHTTPS()) { |
686 | 0 | result += " (DNS over HTTPS)"; |
687 | 0 | } |
688 | 0 | else { |
689 | 0 | result += " (DNS over HTTP)"; |
690 | 0 | } |
691 | 0 | } |
692 | 0 | else if (tlsFrontend) { |
693 | 0 | result += " (DNS over TLS)"; |
694 | 0 | } |
695 | 0 | else if (dnscryptCtx) { |
696 | 0 | result += " (DNSCrypt)"; |
697 | 0 | } |
698 | 0 |
|
699 | 0 | return result; |
700 | 0 | } |
701 | | |
702 | | void detachFilter(int socket) |
703 | 0 | { |
704 | 0 | if (d_filter) { |
705 | 0 | d_filter->removeSocket(socket); |
706 | 0 | d_filter = nullptr; |
707 | 0 | } |
708 | 0 | } |
709 | | |
710 | | void attachFilter(shared_ptr<BPFFilter> bpf, int socket) |
711 | 0 | { |
712 | 0 | detachFilter(socket); |
713 | 0 |
|
714 | 0 | bpf->addSocket(socket); |
715 | 0 | d_filter = bpf; |
716 | 0 | } |
717 | | |
718 | | void detachFilter() |
719 | 0 | { |
720 | 0 | if (d_filter) { |
721 | 0 | detachFilter(getSocket()); |
722 | 0 | for (const auto& [addr, socket] : d_additionalAddresses) { |
723 | 0 | (void) addr; |
724 | 0 | if (socket != -1) { |
725 | 0 | detachFilter(socket); |
726 | 0 | } |
727 | 0 | } |
728 | 0 |
|
729 | 0 | d_filter = nullptr; |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | void attachFilter(shared_ptr<BPFFilter> bpf) |
734 | 0 | { |
735 | 0 | detachFilter(); |
736 | 0 |
|
737 | 0 | bpf->addSocket(getSocket()); |
738 | 0 | for (const auto& [addr, socket] : d_additionalAddresses) { |
739 | 0 | (void) addr; |
740 | 0 | if (socket != -1) { |
741 | 0 | bpf->addSocket(socket); |
742 | 0 | } |
743 | 0 | } |
744 | 0 | d_filter = bpf; |
745 | 0 | } |
746 | | |
747 | | void updateTCPMetrics(size_t nbQueries, uint64_t durationMs) |
748 | 0 | { |
749 | 0 | tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0); |
750 | 0 | tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0); |
751 | 0 | } |
752 | | }; |
753 | | |
754 | | struct CrossProtocolQuery; |
755 | | |
756 | | struct DownstreamState: public std::enable_shared_from_this<DownstreamState> |
757 | | { |
758 | | DownstreamState(const DownstreamState&) = delete; |
759 | | DownstreamState(DownstreamState&&) = delete; |
760 | | DownstreamState& operator=(const DownstreamState&) = delete; |
761 | | DownstreamState& operator=(DownstreamState&&) = delete; |
762 | | |
763 | | typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t; |
764 | | enum class Availability : uint8_t { Up, Down, Auto, Lazy }; |
765 | | enum class LazyHealthCheckMode : uint8_t { TimeoutOnly, TimeoutOrServFail }; |
766 | | |
767 | | struct Config |
768 | | { |
769 | | Config() |
770 | 0 | { |
771 | 0 | } |
772 | | Config(const ComboAddress& remote_): remote(remote_) |
773 | 0 | { |
774 | 0 | } |
775 | | |
776 | | TLSContextParameters d_tlsParams; |
777 | | set<string> pools; |
778 | | std::set<int> d_cpus; |
779 | | checkfunc_t checkFunction; |
780 | | std::optional<boost::uuids::uuid> id; |
781 | | DNSName checkName{"a.root-servers.net."}; |
782 | | ComboAddress remote; |
783 | | ComboAddress sourceAddr; |
784 | | std::string sourceItfName; |
785 | | std::string d_tlsSubjectName; |
786 | | std::string d_dohPath; |
787 | | std::string name; |
788 | | std::string nameWithAddr; |
789 | | size_t d_numberOfSockets{1}; |
790 | | size_t d_maxInFlightQueriesPerConn{1}; |
791 | | size_t d_tcpConcurrentConnectionsLimit{0}; |
792 | | int order{1}; |
793 | | int d_weight{1}; |
794 | | int tcpConnectTimeout{5}; |
795 | | int tcpRecvTimeout{30}; |
796 | | int tcpSendTimeout{30}; |
797 | | int d_qpsLimit{0}; |
798 | | unsigned int checkInterval{1}; |
799 | | unsigned int sourceItf{0}; |
800 | | QType checkType{QType::A}; |
801 | | uint16_t checkClass{QClass::IN}; |
802 | | uint16_t d_retries{5}; |
803 | | uint16_t xpfRRCode{0}; |
804 | | uint16_t checkTimeout{1000}; /* in milliseconds */ |
805 | | uint16_t d_lazyHealthCheckSampleSize{100}; |
806 | | uint16_t d_lazyHealthCheckMinSampleCount{1}; |
807 | | uint16_t d_lazyHealthCheckFailedInterval{30}; |
808 | | uint16_t d_lazyHealthCheckMaxBackOff{3600}; |
809 | | uint8_t d_lazyHealthCheckThreshold{20}; |
810 | | LazyHealthCheckMode d_lazyHealthCheckMode{LazyHealthCheckMode::TimeoutOrServFail}; |
811 | | uint8_t maxCheckFailures{1}; |
812 | | uint8_t minRiseSuccesses{1}; |
813 | | Availability availability{Availability::Auto}; |
814 | | bool d_tlsSubjectIsAddr{false}; |
815 | | bool mustResolve{false}; |
816 | | bool useECS{false}; |
817 | | bool useProxyProtocol{false}; |
818 | | bool setCD{false}; |
819 | | bool disableZeroScope{false}; |
820 | | bool tcpFastOpen{false}; |
821 | | bool ipBindAddrNoPort{true}; |
822 | | bool reconnectOnUp{false}; |
823 | | bool d_tcpCheck{false}; |
824 | | bool d_tcpOnly{false}; |
825 | | bool d_addXForwardedHeaders{false}; // for DoH backends |
826 | | bool d_lazyHealthCheckUseExponentialBackOff{false}; |
827 | | bool d_upgradeToLazyHealthChecks{false}; |
828 | | }; |
829 | | |
830 | | DownstreamState(DownstreamState::Config&& config, std::shared_ptr<TLSCtx> tlsCtx, bool connect); |
831 | | DownstreamState(const ComboAddress& remote): DownstreamState(DownstreamState::Config(remote), nullptr, false) |
832 | 0 | { |
833 | 0 | } |
834 | | |
835 | | ~DownstreamState(); |
836 | | |
837 | | Config d_config; |
838 | | stat_t sendErrors{0}; |
839 | | stat_t outstanding{0}; |
840 | | stat_t reuseds{0}; |
841 | | stat_t queries{0}; |
842 | | stat_t responses{0}; |
843 | | stat_t nonCompliantResponses{0}; |
844 | | struct { |
845 | | stat_t sendErrors{0}; |
846 | | stat_t reuseds{0}; |
847 | | stat_t queries{0}; |
848 | | } prev; |
849 | | stat_t tcpDiedSendingQuery{0}; |
850 | | stat_t tcpDiedReadingResponse{0}; |
851 | | stat_t tcpGaveUp{0}; |
852 | | stat_t tcpReadTimeouts{0}; |
853 | | stat_t tcpWriteTimeouts{0}; |
854 | | stat_t tcpConnectTimeouts{0}; |
855 | | /* current number of connections to this backend */ |
856 | | stat_t tcpCurrentConnections{0}; |
857 | | /* maximum number of concurrent connections to this backend reached */ |
858 | | stat_t tcpMaxConcurrentConnections{0}; |
859 | | /* number of times we had to enforce the maximum concurrent connections limit */ |
860 | | stat_t tcpTooManyConcurrentConnections{0}; |
861 | | stat_t tcpReusedConnections{0}; |
862 | | stat_t tcpNewConnections{0}; |
863 | | stat_t tlsResumptions{0}; |
864 | | pdns::stat_t_trait<double> tcpAvgQueriesPerConnection{0.0}; |
865 | | /* in ms */ |
866 | | pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0}; |
867 | | pdns::stat_t_trait<double> queryLoad{0.0}; |
868 | | pdns::stat_t_trait<double> dropRate{0.0}; |
869 | | |
870 | | SharedLockGuarded<std::vector<unsigned int>> hashes; |
871 | | LockGuarded<std::unique_ptr<FDMultiplexer>> mplexer{nullptr}; |
872 | | private: |
873 | | LockGuarded<std::map<uint16_t, IDState>> d_idStatesMap; |
874 | | vector<IDState> idStates; |
875 | | |
876 | | struct LazyHealthCheckStats |
877 | | { |
878 | | boost::circular_buffer<bool> d_lastResults; |
879 | | time_t d_nextCheck{0}; |
880 | | enum class LazyStatus: uint8_t { Healthy = 0, PotentialFailure, Failed }; |
881 | | LazyStatus d_status{LazyStatus::Healthy}; |
882 | | }; |
883 | | LockGuarded<LazyHealthCheckStats> d_lazyHealthCheckStats; |
884 | | |
885 | | public: |
886 | | std::shared_ptr<TLSCtx> d_tlsCtx{nullptr}; |
887 | | std::vector<int> sockets; |
888 | | StopWatch sw; |
889 | | QPSLimiter qps; |
890 | | std::atomic<uint64_t> idOffset{0}; |
891 | | size_t socketsOffset{0}; |
892 | | double latencyUsec{0.0}; |
893 | | double latencyUsecTCP{0.0}; |
894 | | unsigned int d_nextCheck{0}; |
895 | | uint16_t currentCheckFailures{0}; |
896 | | uint8_t consecutiveSuccessfulChecks{0}; |
897 | | std::atomic<bool> hashesComputed{false}; |
898 | | std::atomic<bool> connected{false}; |
899 | | bool upStatus{false}; |
900 | | |
901 | | private: |
902 | | void connectUDPSockets(); |
903 | | |
904 | | std::thread tid; |
905 | | std::mutex connectLock; |
906 | | std::atomic_flag threadStarted; |
907 | | bool d_stopped{false}; |
908 | | public: |
909 | | |
910 | | void start(); |
911 | | |
912 | | bool isUp() const |
913 | 0 | { |
914 | 0 | if (d_config.availability == Availability::Down) { |
915 | 0 | return false; |
916 | 0 | } |
917 | 0 | else if (d_config.availability == Availability::Up) { |
918 | 0 | return true; |
919 | 0 | } |
920 | 0 | return upStatus; |
921 | 0 | } |
922 | | |
923 | 0 | void setUp() { |
924 | 0 | d_config.availability = Availability::Up; |
925 | 0 | } |
926 | | |
927 | | void setUpStatus(bool newStatus) |
928 | 0 | { |
929 | 0 | upStatus = newStatus; |
930 | 0 | if (!upStatus) { |
931 | 0 | latencyUsec = 0.0; |
932 | 0 | latencyUsecTCP = 0.0; |
933 | 0 | } |
934 | 0 | } |
935 | | void setDown() |
936 | 0 | { |
937 | 0 | d_config.availability = Availability::Down; |
938 | 0 | latencyUsec = 0.0; |
939 | 0 | latencyUsecTCP = 0.0; |
940 | 0 | } |
941 | 0 | void setAuto() { |
942 | 0 | d_config.availability = Availability::Auto; |
943 | 0 | } |
944 | 0 | void setLazyAuto() { |
945 | 0 | d_config.availability = Availability::Lazy; |
946 | 0 | d_lazyHealthCheckStats.lock()->d_lastResults.set_capacity(d_config.d_lazyHealthCheckSampleSize); |
947 | 0 | } |
948 | | bool healthCheckRequired(std::optional<time_t> currentTime = std::nullopt); |
949 | | |
950 | 0 | const string& getName() const { |
951 | 0 | return d_config.name; |
952 | 0 | } |
953 | 0 | const string& getNameWithAddr() const { |
954 | 0 | return d_config.nameWithAddr; |
955 | 0 | } |
956 | | void setName(const std::string& newName) |
957 | 0 | { |
958 | 0 | d_config.name = newName; |
959 | 0 | d_config.nameWithAddr = newName.empty() ? d_config.remote.toStringWithPort() : (d_config.name + " (" + d_config.remote.toStringWithPort()+ ")"); |
960 | 0 | } |
961 | | |
962 | | string getStatus() const |
963 | 0 | { |
964 | 0 | string status; |
965 | 0 | if (d_config.availability == DownstreamState::Availability::Up) { |
966 | 0 | status = "UP"; |
967 | 0 | } |
968 | 0 | else if (d_config.availability == DownstreamState::Availability::Down) { |
969 | 0 | status = "DOWN"; |
970 | 0 | } |
971 | 0 | else { |
972 | 0 | status = (upStatus ? "up" : "down"); |
973 | 0 | } |
974 | 0 | return status; |
975 | 0 | } |
976 | | |
977 | | bool reconnect(); |
978 | | void hash(); |
979 | | void setId(const boost::uuids::uuid& newId); |
980 | | void setWeight(int newWeight); |
981 | | void stop(); |
982 | | bool isStopped() const |
983 | 0 | { |
984 | 0 | return d_stopped; |
985 | 0 | } |
986 | | const boost::uuids::uuid& getID() const |
987 | 0 | { |
988 | 0 | return *d_config.id; |
989 | 0 | } |
990 | | |
991 | | void updateTCPMetrics(size_t nbQueries, uint64_t durationMs) |
992 | 0 | { |
993 | 0 | tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0); |
994 | 0 | tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0); |
995 | 0 | } |
996 | | |
997 | | void updateTCPLatency(double udiff) |
998 | 0 | { |
999 | 0 | latencyUsecTCP = (127.0 * latencyUsecTCP / 128.0) + udiff / 128.0; |
1000 | 0 | } |
1001 | | |
1002 | | void incQueriesCount() |
1003 | 0 | { |
1004 | 0 | ++queries; |
1005 | 0 | qps.addHit(); |
1006 | 0 | } |
1007 | | |
1008 | | void incCurrentConnectionsCount(); |
1009 | | |
1010 | | bool doHealthcheckOverTCP() const |
1011 | 0 | { |
1012 | 0 | return d_config.d_tcpOnly || d_config.d_tcpCheck || d_tlsCtx != nullptr; |
1013 | 0 | } |
1014 | | |
1015 | | bool isTCPOnly() const |
1016 | 0 | { |
1017 | 0 | return d_config.d_tcpOnly || d_tlsCtx != nullptr; |
1018 | 0 | } |
1019 | | |
1020 | | bool isDoH() const |
1021 | 0 | { |
1022 | 0 | return !d_config.d_dohPath.empty(); |
1023 | 0 | } |
1024 | | |
1025 | | bool passCrossProtocolQuery(std::unique_ptr<CrossProtocolQuery>&& cpq); |
1026 | | int pickSocketForSending(); |
1027 | | void pickSocketsReadyForReceiving(std::vector<int>& ready); |
1028 | | void handleUDPTimeouts(); |
1029 | | void reportTimeoutOrError(); |
1030 | | void reportResponse(uint8_t rcode); |
1031 | | void submitHealthCheckResult(bool initial, bool newState); |
1032 | | time_t getNextLazyHealthCheck(); |
1033 | | uint16_t saveState(InternalQueryState&&); |
1034 | | void restoreState(uint16_t id, InternalQueryState&&); |
1035 | | std::optional<InternalQueryState> getState(uint16_t id); |
1036 | | |
1037 | | dnsdist::Protocol getProtocol() const |
1038 | 0 | { |
1039 | 0 | if (isDoH()) { |
1040 | 0 | return dnsdist::Protocol::DoH; |
1041 | 0 | } |
1042 | 0 | if (d_tlsCtx != nullptr) { |
1043 | 0 | return dnsdist::Protocol::DoT; |
1044 | 0 | } |
1045 | 0 | if (isTCPOnly()) { |
1046 | 0 | return dnsdist::Protocol::DoTCP; |
1047 | 0 | } |
1048 | 0 | return dnsdist::Protocol::DoUDP; |
1049 | 0 | } |
1050 | | |
1051 | | double getRelevantLatencyUsec() const |
1052 | 0 | { |
1053 | 0 | if (isTCPOnly()) { |
1054 | 0 | return latencyUsecTCP; |
1055 | 0 | } |
1056 | 0 | return latencyUsec; |
1057 | 0 | } |
1058 | | |
1059 | | static int s_udpTimeout; |
1060 | | static bool s_randomizeSockets; |
1061 | | static bool s_randomizeIDs; |
1062 | | private: |
1063 | | void handleUDPTimeout(IDState& ids); |
1064 | | void updateNextLazyHealthCheck(LazyHealthCheckStats& stats, bool checkScheduled, std::optional<time_t> currentTime = std::nullopt); |
1065 | | }; |
1066 | | using servers_t = vector<std::shared_ptr<DownstreamState>>; |
1067 | | |
1068 | | void responderThread(std::shared_ptr<DownstreamState> state); |
1069 | | extern LockGuarded<LuaContext> g_lua; |
1070 | | extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex |
1071 | | |
1072 | | class DNSRule |
1073 | | { |
1074 | | public: |
1075 | | virtual ~DNSRule () |
1076 | 0 | { |
1077 | 0 | } |
1078 | | virtual bool matches(const DNSQuestion* dq) const =0; |
1079 | | virtual string toString() const = 0; |
1080 | | mutable stat_t d_matches{0}; |
1081 | | }; |
1082 | | |
1083 | | struct ServerPool |
1084 | | { |
1085 | | ServerPool(): d_servers(std::make_shared<const ServerPolicy::NumberedServerVector>()) |
1086 | 0 | { |
1087 | 0 | } |
1088 | | |
1089 | | ~ServerPool() |
1090 | 0 | { |
1091 | 0 | } |
1092 | | |
1093 | 0 | const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; }; |
1094 | | |
1095 | | bool getECS() const |
1096 | 0 | { |
1097 | 0 | return d_useECS; |
1098 | 0 | } |
1099 | | |
1100 | | void setECS(bool useECS) |
1101 | 0 | { |
1102 | 0 | d_useECS = useECS; |
1103 | 0 | } |
1104 | | |
1105 | | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; |
1106 | | std::shared_ptr<ServerPolicy> policy{nullptr}; |
1107 | | |
1108 | | size_t poolLoad(); |
1109 | | size_t countServers(bool upOnly); |
1110 | | const std::shared_ptr<const ServerPolicy::NumberedServerVector> getServers(); |
1111 | | void addServer(shared_ptr<DownstreamState>& server); |
1112 | | void removeServer(shared_ptr<DownstreamState>& server); |
1113 | | |
1114 | | private: |
1115 | | SharedLockGuarded<std::shared_ptr<const ServerPolicy::NumberedServerVector>> d_servers; |
1116 | | bool d_useECS{false}; |
1117 | | }; |
1118 | | |
1119 | | enum ednsHeaderFlags { |
1120 | | EDNS_HEADER_FLAG_NONE = 0, |
1121 | | EDNS_HEADER_FLAG_DO = 32768 |
1122 | | }; |
1123 | | |
1124 | | struct DNSDistRuleAction |
1125 | | { |
1126 | | std::shared_ptr<DNSRule> d_rule; |
1127 | | std::shared_ptr<DNSAction> d_action; |
1128 | | std::string d_name; |
1129 | | boost::uuids::uuid d_id; |
1130 | | uint64_t d_creationOrder; |
1131 | | }; |
1132 | | |
1133 | | struct DNSDistResponseRuleAction |
1134 | | { |
1135 | | std::shared_ptr<DNSRule> d_rule; |
1136 | | std::shared_ptr<DNSResponseAction> d_action; |
1137 | | std::string d_name; |
1138 | | boost::uuids::uuid d_id; |
1139 | | uint64_t d_creationOrder; |
1140 | | }; |
1141 | | |
1142 | | extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT; |
1143 | | extern DNSAction::Action g_dynBlockAction; |
1144 | | |
1145 | | extern GlobalStateHolder<ServerPolicy> g_policy; |
1146 | | extern GlobalStateHolder<servers_t> g_dstates; |
1147 | | extern GlobalStateHolder<pools_t> g_pools; |
1148 | | extern GlobalStateHolder<vector<DNSDistRuleAction> > g_ruleactions; |
1149 | | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_respruleactions; |
1150 | | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitrespruleactions; |
1151 | | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredrespruleactions; |
1152 | | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cacheInsertedRespRuleActions; |
1153 | | extern GlobalStateHolder<NetmaskGroup> g_ACL; |
1154 | | |
1155 | | extern ComboAddress g_serverControl; // not changed during runtime |
1156 | | |
1157 | | extern std::vector<shared_ptr<TLSFrontend>> g_tlslocals; |
1158 | | extern std::vector<shared_ptr<DOHFrontend>> g_dohlocals; |
1159 | | extern std::vector<std::unique_ptr<ClientState>> g_frontends; |
1160 | | extern bool g_truncateTC; |
1161 | | extern bool g_fixupCase; |
1162 | | extern int g_tcpRecvTimeout; |
1163 | | extern int g_tcpSendTimeout; |
1164 | | extern uint16_t g_maxOutstanding; |
1165 | | extern std::atomic<bool> g_configurationDone; |
1166 | | extern boost::optional<uint64_t> g_maxTCPClientThreads; |
1167 | | extern uint64_t g_maxTCPQueuedConnections; |
1168 | | extern size_t g_maxTCPQueriesPerConn; |
1169 | | extern size_t g_maxTCPConnectionDuration; |
1170 | | extern size_t g_tcpInternalPipeBufferSize; |
1171 | | extern pdns::stat16_t g_cacheCleaningDelay; |
1172 | | extern pdns::stat16_t g_cacheCleaningPercentage; |
1173 | | extern uint32_t g_staleCacheEntriesTTL; |
1174 | | extern bool g_apiReadWrite; |
1175 | | extern std::string g_apiConfigDirectory; |
1176 | | extern bool g_servFailOnNoPolicy; |
1177 | | extern size_t g_udpVectorSize; |
1178 | | extern bool g_allowEmptyResponse; |
1179 | | extern uint32_t g_socketUDPSendBuffer; |
1180 | | extern uint32_t g_socketUDPRecvBuffer; |
1181 | | |
1182 | | extern shared_ptr<BPFFilter> g_defaultBPFFilter; |
1183 | | extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters; |
1184 | | |
1185 | | struct LocalHolders |
1186 | | { |
1187 | | LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), ruleactions(g_ruleactions.getLocal()), cacheHitRespRuleactions(g_cachehitrespruleactions.getLocal()), cacheInsertedRespRuleActions(g_cacheInsertedRespRuleActions.getLocal()), selfAnsweredRespRuleactions(g_selfansweredrespruleactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal()) |
1188 | 0 | { |
1189 | 0 | } |
1190 | | |
1191 | | LocalStateHolder<NetmaskGroup> acl; |
1192 | | LocalStateHolder<ServerPolicy> policy; |
1193 | | LocalStateHolder<vector<DNSDistRuleAction> > ruleactions; |
1194 | | LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRuleactions; |
1195 | | LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheInsertedRespRuleActions; |
1196 | | LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRuleactions; |
1197 | | LocalStateHolder<servers_t> servers; |
1198 | | LocalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange> > dynNMGBlock; |
1199 | | LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock; |
1200 | | LocalStateHolder<pools_t> pools; |
1201 | | }; |
1202 | | |
1203 | | void tcpAcceptorThread(std::vector<ClientState*> states); |
1204 | | |
1205 | | #ifdef HAVE_DNS_OVER_HTTPS |
1206 | | void dohThread(ClientState* cs); |
1207 | | #endif /* HAVE_DNS_OVER_HTTPS */ |
1208 | | |
1209 | | void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects |
1210 | | void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls |
1211 | | bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect |
1212 | | void resetLuaSideEffect(); // reset to indeterminate state |
1213 | | |
1214 | | bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, unsigned int& qnameWireLength); |
1215 | | |
1216 | | bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs); |
1217 | | |
1218 | | extern std::vector<std::shared_ptr<DNSCryptContext>> g_dnsCryptLocals; |
1219 | | int handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response); |
1220 | | bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp); |
1221 | | |
1222 | | #include "dnsdist-snmp.hh" |
1223 | | |
1224 | | extern bool g_snmpEnabled; |
1225 | | extern bool g_snmpTrapsEnabled; |
1226 | | extern DNSDistSNMPAgent* g_snmpAgent; |
1227 | | extern bool g_addEDNSToSelfGeneratedResponses; |
1228 | | |
1229 | | extern std::set<std::string> g_capabilitiesToRetain; |
1230 | | static const uint16_t s_udpIncomingBufferSize{1500}; // don't accept UDP queries larger than this value |
1231 | | static const size_t s_maxPacketCacheEntrySize{4096}; // don't cache responses larger than this value |
1232 | | |
1233 | | enum class ProcessQueryResult : uint8_t { Drop, SendAnswer, PassToBackend, Asynchronous }; |
1234 | | ProcessQueryResult processQuery(DNSQuestion& dq, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend); |
1235 | | ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend); |
1236 | | bool processResponse(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& respRuleActions, const std::vector<DNSDistResponseRuleAction>& insertedRespRuleActions, DNSResponse& dr, bool muted); |
1237 | | bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::string& ruleresult, bool& drop); |
1238 | | bool processResponseAfterRules(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& cacheInsertedRespRuleActions, DNSResponse& dr, bool muted); |
1239 | | |
1240 | | bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query, ComboAddress& dest); |
1241 | | |
1242 | | ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const PacketBuffer& request, bool healthCheck = false); |
1243 | | bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote); |
1244 | | void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, dnsdist::Protocol incomingProtocol, bool fromBackend); |
1245 | | void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend); |