]>
Commit | Line | Data |
---|---|---|
12471842 PL |
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 | */ | |
df111b53 | 22 | #pragma once |
11e1e08b | 23 | #include "config.h" |
df111b53 | 24 | #include "ext/luawrapper/include/LuaContext.hpp" |
cbf4e13a | 25 | |
df111b53 | 26 | #include <atomic> |
df111b53 | 27 | #include <mutex> |
cbf4e13a | 28 | #include <string> |
df111b53 | 29 | #include <thread> |
cbf4e13a | 30 | #include <time.h> |
bffca8b9 | 31 | #include <unistd.h> |
cbf4e13a RG |
32 | #include <unordered_map> |
33 | ||
cbf4e13a RG |
34 | #include <boost/variant.hpp> |
35 | ||
f12666f2 | 36 | #include "capabilities.hh" |
9f6a31ff | 37 | #include "circular_buffer.hh" |
11e1e08b | 38 | #include "dnscrypt.hh" |
886e2cf2 | 39 | #include "dnsdist-cache.hh" |
87b515ed | 40 | #include "dnsdist-dynbpf.hh" |
b6e26895 | 41 | #include "dnsdist-lbpolicies.hh" |
cbf4e13a | 42 | #include "dnsname.hh" |
fbf14b03 | 43 | #include "doh.hh" |
cbf4e13a RG |
44 | #include "ednsoptions.hh" |
45 | #include "gettime.hh" | |
46 | #include "iputils.hh" | |
47 | #include "misc.hh" | |
48 | #include "mplexer.hh" | |
49 | #include "sholder.hh" | |
a227f47d | 50 | #include "tcpiohandler.hh" |
d61aa945 | 51 | #include "uuid-utils.hh" |
bde73d5b | 52 | #include "proxy-protocol.hh" |
d8c19b98 | 53 | |
9b73b71c | 54 | void carbonDumpThread(); |
61d1b966 | 55 | uint64_t uptimeOfProcess(const std::string& str); |
bd1c631b | 56 | |
7b925432 RG |
57 | extern uint16_t g_ECSSourcePrefixV4; |
58 | extern uint16_t g_ECSSourcePrefixV6; | |
59 | extern bool g_ECSOverride; | |
26a6373d | 60 | |
15fac047 CH |
61 | typedef std::unordered_map<string, string> QTag; |
62 | ||
7b925432 RG |
63 | struct DNSQuestion |
64 | { | |
e7c732b8 | 65 | DNSQuestion(const DNSName* name, uint16_t type, uint16_t class_, unsigned int consumed_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t queryLen, bool isTcp, const struct timespec* queryTime_): |
4ab01344 RG |
66 | qname(name), local(lc), remote(rem), dh(header), queryTime(queryTime_), size(bufferSize), consumed(consumed_), tempFailureTTL(boost::none), qtype(type), qclass(class_), len(queryLen), ecsPrefixLength(rem->sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), tcp(isTcp), ecsOverride(g_ECSOverride) { |
67 | const uint16_t* flags = getFlagsFromDNSHeader(dh); | |
68 | origFlags = *flags; | |
69 | } | |
dd1a3034 RG |
70 | DNSQuestion(const DNSQuestion&) = delete; |
71 | DNSQuestion& operator=(const DNSQuestion&) = delete; | |
72 | DNSQuestion(DNSQuestion&&) = default; | |
7b925432 | 73 | |
0ed8f0fa RG |
74 | std::string getTrailingData() const; |
75 | bool setTrailingData(const std::string&); | |
76 | ||
7b925432 | 77 | #ifdef HAVE_PROTOBUF |
ec48a28d | 78 | boost::optional<boost::uuids::uuid> uniqueId; |
7b925432 | 79 | #endif |
bd14f087 | 80 | Netmask ecs; |
4ab01344 | 81 | boost::optional<Netmask> subnet; |
046bac5c | 82 | std::string sni; /* Server Name Indication, if any (DoT or DoH) */ |
2a28db86 | 83 | std::string poolname; |
4ab01344 RG |
84 | const DNSName* qname{nullptr}; |
85 | const ComboAddress* local{nullptr}; | |
86 | const ComboAddress* remote{nullptr}; | |
15fac047 | 87 | std::shared_ptr<QTag> qTag{nullptr}; |
bde73d5b | 88 | std::unique_ptr<std::vector<ProxyProtocolValue>> proxyProtocolValues{nullptr}; |
cbf4e13a | 89 | std::shared_ptr<std::map<uint16_t, EDNSOptionView> > ednsOptions; |
4ab01344 RG |
90 | std::shared_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; |
91 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; | |
92 | struct dnsheader* dh{nullptr}; | |
93 | const struct timespec* queryTime{nullptr}; | |
fbf14b03 | 94 | struct DOHUnit* du{nullptr}; |
7b925432 | 95 | size_t size; |
e7c732b8 | 96 | unsigned int consumed{0}; |
4ab01344 RG |
97 | int delayMsec{0}; |
98 | boost::optional<uint32_t> tempFailureTTL; | |
99 | uint32_t cacheKeyNoECS; | |
100 | uint32_t cacheKey; | |
101 | const uint16_t qtype; | |
102 | const uint16_t qclass; | |
7b925432 RG |
103 | uint16_t len; |
104 | uint16_t ecsPrefixLength; | |
4ab01344 | 105 | uint16_t origFlags; |
1ecbd15e | 106 | uint8_t ednsRCode{0}; |
7b925432 RG |
107 | const bool tcp; |
108 | bool skipCache{false}; | |
109 | bool ecsOverride; | |
5b8255ba | 110 | bool useECS{true}; |
5cc8371b | 111 | bool addXPF{true}; |
bd14f087 | 112 | bool ecsSet{false}; |
4ab01344 RG |
113 | bool ecsAdded{false}; |
114 | bool ednsAdded{false}; | |
115 | bool useZeroScope{false}; | |
116 | bool dnssecOK{false}; | |
7b925432 RG |
117 | }; |
118 | ||
119 | struct DNSResponse : DNSQuestion | |
120 | { | |
dd026b9c RG |
121 | DNSResponse(const DNSName* name, uint16_t type, uint16_t class_, unsigned int consumed_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t responseLen, bool isTcp, const struct timespec* queryTime_): |
122 | DNSQuestion(name, type, class_, consumed_, lc, rem, header, bufferSize, responseLen, isTcp, queryTime_) { } | |
dd1a3034 RG |
123 | DNSResponse(const DNSResponse&) = delete; |
124 | DNSResponse& operator=(const DNSResponse&) = delete; | |
125 | DNSResponse(DNSResponse&&) = default; | |
7b925432 RG |
126 | }; |
127 | ||
5c30ec69 LM |
128 | /* so what could you do: |
129 | drop, | |
130 | fake up nxdomain, | |
131 | provide actual answer, | |
132 | allow & and stop processing, | |
133 | continue processing, | |
7b925432 RG |
134 | modify header: (servfail|refused|notimp), set TC=1, |
135 | send to pool */ | |
136 | ||
137 | class DNSAction | |
138 | { | |
139 | public: | |
202c4ab9 | 140 | enum class Action { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, ServFail, None, NoOp, NoRecurse, SpoofRaw }; |
b718792f RG |
141 | static std::string typeToString(const Action& action) |
142 | { | |
143 | switch(action) { | |
144 | case Action::Drop: | |
145 | return "Drop"; | |
146 | case Action::Nxdomain: | |
147 | return "Send NXDomain"; | |
148 | case Action::Refused: | |
149 | return "Send Refused"; | |
150 | case Action::Spoof: | |
151 | return "Spoof an answer"; | |
202c4ab9 RG |
152 | case Action::SpoofRaw: |
153 | return "Spoof an answer from raw bytes"; | |
b718792f RG |
154 | case Action::Allow: |
155 | return "Allow"; | |
156 | case Action::HeaderModify: | |
157 | return "Modify the header"; | |
158 | case Action::Pool: | |
159 | return "Route to a pool"; | |
160 | case Action::Delay: | |
161 | return "Delay"; | |
162 | case Action::Truncate: | |
163 | return "Truncate over UDP"; | |
164 | case Action::ServFail: | |
165 | return "Send ServFail"; | |
166 | case Action::None: | |
477c86a0 | 167 | case Action::NoOp: |
b718792f | 168 | return "Do nothing"; |
3d60b39a | 169 | case Action::NoRecurse: |
170 | return "Set rd=0"; | |
b718792f RG |
171 | } |
172 | ||
173 | return "Unknown"; | |
174 | } | |
175 | ||
7b925432 | 176 | virtual Action operator()(DNSQuestion*, string* ruleresult) const =0; |
205f2081 RG |
177 | virtual ~DNSAction() |
178 | { | |
179 | } | |
7b925432 | 180 | virtual string toString() const = 0; |
b8019cf7 | 181 | virtual std::map<string, double> getStats() const |
7b925432 RG |
182 | { |
183 | return {{}}; | |
184 | } | |
185 | }; | |
186 | ||
187 | class DNSResponseAction | |
188 | { | |
189 | public: | |
5f23eb98 | 190 | enum class Action { Allow, Delay, Drop, HeaderModify, ServFail, None }; |
7b925432 | 191 | virtual Action operator()(DNSResponse*, string* ruleresult) const =0; |
205f2081 RG |
192 | virtual ~DNSResponseAction() |
193 | { | |
194 | } | |
7b925432 RG |
195 | virtual string toString() const = 0; |
196 | }; | |
197 | ||
78ffa782 | 198 | struct DynBlock |
199 | { | |
1d3ba133 | 200 | DynBlock(): action(DNSAction::Action::None), warning(false) |
5708a729 RG |
201 | { |
202 | } | |
203 | ||
1d3ba133 | 204 | DynBlock(const std::string& reason_, const struct timespec& until_, const DNSName& domain_, DNSAction::Action action_): reason(reason_), until(until_), domain(domain_), action(action_), warning(false) |
5708a729 RG |
205 | { |
206 | } | |
207 | ||
1d3ba133 | 208 | DynBlock(const DynBlock& rhs): reason(rhs.reason), until(rhs.until), domain(rhs.domain), action(rhs.action), warning(rhs.warning) |
5708a729 RG |
209 | { |
210 | blocks.store(rhs.blocks); | |
211 | } | |
212 | ||
78ffa782 | 213 | DynBlock& operator=(const DynBlock& rhs) |
214 | { | |
215 | reason=rhs.reason; | |
216 | until=rhs.until; | |
71c94675 | 217 | domain=rhs.domain; |
7b925432 | 218 | action=rhs.action; |
78ffa782 | 219 | blocks.store(rhs.blocks); |
1d3ba133 | 220 | warning=rhs.warning; |
78ffa782 | 221 | return *this; |
222 | } | |
71c94675 | 223 | |
78ffa782 | 224 | string reason; |
225 | struct timespec until; | |
71c94675 | 226 | DNSName domain; |
7b925432 | 227 | DNSAction::Action action; |
78ffa782 | 228 | mutable std::atomic<unsigned int> blocks; |
1d3ba133 | 229 | bool warning; |
78ffa782 | 230 | }; |
231 | ||
232 | extern GlobalStateHolder<NetmaskTree<DynBlock>> g_dynblockNMG; | |
f758857a | 233 | |
234 | extern vector<pair<struct timeval, std::string> > g_confDelta; | |
235 | ||
eb0335ff MC |
236 | extern uint64_t getLatencyCount(const std::string&); |
237 | ||
e48090d1 | 238 | struct DNSDistStats |
239 | { | |
6ad8b29a | 240 | using stat_t=std::atomic<uint64_t>; // aww yiss ;-) |
e48090d1 | 241 | stat_t responses{0}; |
242 | stat_t servfailResponses{0}; | |
243 | stat_t queries{0}; | |
61d10a4d MH |
244 | stat_t frontendNXDomain{0}; |
245 | stat_t frontendServFail{0}; | |
246 | stat_t frontendNoError{0}; | |
e73ec7d3 | 247 | stat_t nonCompliantQueries{0}; |
d08b1cdf | 248 | stat_t nonCompliantResponses{0}; |
643a182a | 249 | stat_t rdQueries{0}; |
2efd427d | 250 | stat_t emptyQueries{0}; |
e48090d1 | 251 | stat_t aclDrops{0}; |
bd1c631b | 252 | stat_t dynBlocked{0}; |
e48090d1 | 253 | stat_t ruleDrop{0}; |
254 | stat_t ruleNXDomain{0}; | |
dd46e5e3 | 255 | stat_t ruleRefused{0}; |
5f23eb98 | 256 | stat_t ruleServFail{0}; |
e48090d1 | 257 | stat_t selfAnswered{0}; |
258 | stat_t downstreamTimeouts{0}; | |
259 | stat_t downstreamSendErrors{0}; | |
6ad8b29a | 260 | stat_t truncFail{0}; |
b8bc7e61 | 261 | stat_t noPolicy{0}; |
886e2cf2 RG |
262 | stat_t cacheHits{0}; |
263 | stat_t cacheMisses{0}; | |
eb0335ff | 264 | stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}, latencySum{0}; |
f29758cc | 265 | stat_t securityStatus{0}; |
5c30ec69 | 266 | |
e16fd59c | 267 | double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0}; |
a1a787dc | 268 | typedef std::function<uint64_t(const std::string&)> statfunction_t; |
72f58a53 | 269 | typedef boost::variant<stat_t*, double*, statfunction_t> entry_t; |
e16fd59c | 270 | std::vector<std::pair<std::string, entry_t>> entries{ |
dd46e5e3 RG |
271 | {"responses", &responses}, |
272 | {"servfail-responses", &servfailResponses}, | |
273 | {"queries", &queries}, | |
61d10a4d MH |
274 | {"frontend-nxdomain", &frontendNXDomain}, |
275 | {"frontend-servfail", &frontendServFail}, | |
276 | {"frontend-noerror", &frontendNoError}, | |
dd46e5e3 | 277 | {"acl-drops", &aclDrops}, |
dd46e5e3 RG |
278 | {"rule-drop", &ruleDrop}, |
279 | {"rule-nxdomain", &ruleNXDomain}, | |
280 | {"rule-refused", &ruleRefused}, | |
5f23eb98 | 281 | {"rule-servfail", &ruleServFail}, |
dd46e5e3 RG |
282 | {"self-answered", &selfAnswered}, |
283 | {"downstream-timeouts", &downstreamTimeouts}, | |
5c30ec69 | 284 | {"downstream-send-errors", &downstreamSendErrors}, |
dd46e5e3 RG |
285 | {"trunc-failures", &truncFail}, |
286 | {"no-policy", &noPolicy}, | |
287 | {"latency0-1", &latency0_1}, | |
288 | {"latency1-10", &latency1_10}, | |
289 | {"latency10-50", &latency10_50}, | |
290 | {"latency50-100", &latency50_100}, | |
291 | {"latency100-1000", &latency100_1000}, | |
292 | {"latency-slow", &latencySlow}, | |
293 | {"latency-avg100", &latencyAvg100}, | |
294 | {"latency-avg1000", &latencyAvg1000}, | |
295 | {"latency-avg10000", &latencyAvg10000}, | |
296 | {"latency-avg1000000", &latencyAvg1000000}, | |
61d1b966 | 297 | {"uptime", uptimeOfProcess}, |
a9b6db56 | 298 | {"real-memory-usage", getRealMemoryUsage}, |
330dcb5c | 299 | {"special-memory-usage", getSpecialMemoryUsage}, |
0d394f35 RG |
300 | {"udp-in-errors", boost::bind(udpErrorStats, "udp-in-errors")}, |
301 | {"udp-noport-errors", boost::bind(udpErrorStats, "udp-noport-errors")}, | |
302 | {"udp-recvbuf-errors", boost::bind(udpErrorStats, "udp-recvbuf-errors")}, | |
303 | {"udp-sndbuf-errors", boost::bind(udpErrorStats, "udp-sndbuf-errors")}, | |
a2aa00ed | 304 | {"noncompliant-queries", &nonCompliantQueries}, |
d08b1cdf | 305 | {"noncompliant-responses", &nonCompliantResponses}, |
643a182a | 306 | {"rdqueries", &rdQueries}, |
2efd427d | 307 | {"empty-queries", &emptyQueries}, |
886e2cf2 RG |
308 | {"cache-hits", &cacheHits}, |
309 | {"cache-misses", &cacheMisses}, | |
0d394f35 RG |
310 | {"cpu-iowait", getCPUIOWait}, |
311 | {"cpu-steal", getCPUSteal}, | |
4f99f3d3 | 312 | {"cpu-sys-msec", getCPUTimeSystem}, |
0d394f35 | 313 | {"cpu-user-msec", getCPUTimeUser}, |
dd46e5e3 | 314 | {"fd-usage", getOpenFileDescriptors}, |
5c30ec69 | 315 | {"dyn-blocked", &dynBlocked}, |
f29758cc | 316 | {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }}, |
eb0335ff MC |
317 | {"security-status", &securityStatus}, |
318 | // Latency histogram | |
319 | {"latency-sum", &latencySum}, | |
320 | {"latency-count", getLatencyCount}, | |
42fae326 | 321 | }; |
e48090d1 | 322 | }; |
323 | ||
324 | extern struct DNSDistStats g_stats; | |
f653b8df | 325 | void doLatencyStats(double udiff); |
e48090d1 | 326 | |
638184e9 | 327 | |
df111b53 | 328 | struct StopWatch |
329 | { | |
58307a85 RG |
330 | StopWatch(bool realTime=false): d_needRealTime(realTime) |
331 | { | |
332 | } | |
df111b53 | 333 | struct timespec d_start{0,0}; |
58307a85 RG |
334 | bool d_needRealTime{false}; |
335 | ||
5c30ec69 | 336 | void start() { |
58307a85 | 337 | if(gettime(&d_start, d_needRealTime) < 0) |
df111b53 | 338 | unixDie("Getting timestamp"); |
5c30ec69 | 339 | |
df111b53 | 340 | } |
cf48b0ce RG |
341 | |
342 | void set(const struct timespec& from) { | |
343 | d_start = from; | |
344 | } | |
5c30ec69 | 345 | |
df111b53 | 346 | double udiff() const { |
347 | struct timespec now; | |
58307a85 | 348 | if(gettime(&now, d_needRealTime) < 0) |
df111b53 | 349 | unixDie("Getting timestamp"); |
5c30ec69 | 350 | |
df111b53 | 351 | return 1000000.0*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec)/1000.0; |
352 | } | |
353 | ||
354 | double udiffAndSet() { | |
355 | struct timespec now; | |
58307a85 | 356 | if(gettime(&now, d_needRealTime) < 0) |
df111b53 | 357 | unixDie("Getting timestamp"); |
5c30ec69 | 358 | |
df111b53 | 359 | auto ret= 1000000.0*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec)/1000.0; |
360 | d_start = now; | |
361 | return ret; | |
362 | } | |
363 | ||
364 | }; | |
365 | ||
67ce0bdd | 366 | class BasicQPSLimiter |
df111b53 | 367 | { |
368 | public: | |
67ce0bdd | 369 | BasicQPSLimiter() |
df111b53 | 370 | { |
371 | } | |
372 | ||
2d29e6b7 | 373 | BasicQPSLimiter(unsigned int burst): d_tokens(burst) |
67ce0bdd RG |
374 | { |
375 | d_prev.start(); | |
376 | } | |
377 | ||
378 | bool check(unsigned int rate, unsigned int burst) const // this is not quite fair | |
379 | { | |
380 | auto delta = d_prev.udiffAndSet(); | |
381 | ||
1a1787b6 | 382 | if(delta > 0.0) // time, frequently, does go backwards.. |
383 | d_tokens += 1.0 * rate * (delta/1000000.0); | |
67ce0bdd RG |
384 | |
385 | if(d_tokens > burst) { | |
386 | d_tokens = burst; | |
387 | } | |
388 | ||
389 | bool ret=false; | |
390 | if(d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise | |
391 | ret=true; | |
392 | --d_tokens; | |
393 | } | |
394 | ||
395 | return ret; | |
396 | } | |
397 | ||
398 | bool seenSince(const struct timespec& cutOff) const | |
399 | { | |
400 | return cutOff < d_prev.d_start; | |
401 | } | |
402 | ||
403 | protected: | |
404 | mutable StopWatch d_prev; | |
405 | mutable double d_tokens; | |
406 | }; | |
407 | ||
408 | class QPSLimiter : public BasicQPSLimiter | |
409 | { | |
410 | public: | |
411 | QPSLimiter(): BasicQPSLimiter() | |
412 | { | |
413 | } | |
414 | ||
2d29e6b7 | 415 | QPSLimiter(unsigned int rate, unsigned int burst): BasicQPSLimiter(burst), d_rate(rate), d_burst(burst), d_passthrough(false) |
df111b53 | 416 | { |
df111b53 | 417 | d_prev.start(); |
418 | } | |
419 | ||
420 | unsigned int getRate() const | |
421 | { | |
67ce0bdd | 422 | return d_passthrough ? 0 : d_rate; |
df111b53 | 423 | } |
424 | ||
425 | int getPassed() const | |
426 | { | |
427 | return d_passed; | |
428 | } | |
67ce0bdd | 429 | |
df111b53 | 430 | int getBlocked() const |
431 | { | |
432 | return d_blocked; | |
433 | } | |
434 | ||
ecbe9133 | 435 | bool check() const // this is not quite fair |
df111b53 | 436 | { |
67ce0bdd | 437 | if (d_passthrough) { |
df111b53 | 438 | return true; |
67ce0bdd | 439 | } |
df111b53 | 440 | |
67ce0bdd RG |
441 | bool ret = BasicQPSLimiter::check(d_rate, d_burst); |
442 | if (ret) { | |
df111b53 | 443 | d_passed++; |
444 | } | |
67ce0bdd | 445 | else { |
df111b53 | 446 | d_blocked++; |
67ce0bdd | 447 | } |
df111b53 | 448 | |
5c30ec69 | 449 | return ret; |
df111b53 | 450 | } |
451 | private: | |
ecbe9133 | 452 | mutable unsigned int d_passed{0}; |
453 | mutable unsigned int d_blocked{0}; | |
67ce0bdd RG |
454 | unsigned int d_rate; |
455 | unsigned int d_burst; | |
456 | bool d_passthrough{true}; | |
df111b53 | 457 | }; |
458 | ||
b5b93e0b RG |
459 | struct ClientState; |
460 | ||
df111b53 | 461 | struct IDState |
462 | { | |
a9489723 | 463 | IDState(): sentTime(true), delayMsec(0), tempFailureTTL(boost::none) { origDest.sin4.sin_family = 0;} |
71b86bd8 | 464 | IDState(const IDState& orig): origRemote(orig.origRemote), origDest(orig.origDest), age(orig.age) |
df111b53 | 465 | { |
a9489723 RG |
466 | usageIndicator.store(orig.usageIndicator.load()); |
467 | origFD = orig.origFD; | |
df111b53 | 468 | origID = orig.origID; |
7b3865cd | 469 | delayMsec = orig.delayMsec; |
acb8f5d5 | 470 | tempFailureTTL = orig.tempFailureTTL; |
df111b53 | 471 | } |
472 | ||
311f19d5 | 473 | static const int64_t unusedIndicator = -1; |
2bf26975 | 474 | |
311f19d5 RG |
475 | static bool isInUse(int64_t usageIndicator) |
476 | { | |
477 | return usageIndicator != unusedIndicator; | |
478 | } | |
479 | ||
480 | bool isInUse() const | |
481 | { | |
482 | return usageIndicator != unusedIndicator; | |
483 | } | |
484 | ||
485 | /* return true if the value has been successfully replaced meaning that | |
486 | no-one updated the usage indicator in the meantime */ | |
487 | bool tryMarkUnused(int64_t expectedUsageIndicator) | |
488 | { | |
489 | return usageIndicator.compare_exchange_strong(expectedUsageIndicator, unusedIndicator); | |
490 | } | |
491 | ||
492 | /* mark as unused no matter what, return true if the state was in use before */ | |
493 | bool markAsUsed() | |
494 | { | |
495 | auto currentGeneration = generation++; | |
496 | return markAsUsed(currentGeneration); | |
497 | } | |
498 | ||
499 | /* mark as unused no matter what, return true if the state was in use before */ | |
500 | bool markAsUsed(int64_t currentGeneration) | |
501 | { | |
502 | int64_t oldUsage = usageIndicator.exchange(currentGeneration); | |
503 | return oldUsage != unusedIndicator; | |
504 | } | |
505 | ||
a9489723 | 506 | /* We use this value to detect whether this state is in use. |
9bd1a882 RG |
507 | For performance reasons we don't want to use a lock here, but that means |
508 | we need to be very careful when modifying this value. Modifications happen | |
509 | from: | |
510 | - one of the UDP or DoH 'client' threads receiving a query, selecting a backend | |
511 | then picking one of the states associated to this backend (via the idOffset). | |
a9489723 | 512 | Most of the time this state should not be in use and usageIndicator is -1, but we |
9bd1a882 RG |
513 | might not yet have received a response for the query previously associated to this |
514 | state, meaning that we will 'reuse' this state and erase the existing state. | |
515 | If we ever receive a response for this state, it will be discarded. This is | |
516 | mostly fine for UDP except that we still need to be careful in order to miss | |
517 | the 'outstanding' counters, which should only be increased when we are picking | |
518 | an empty state, and not when reusing ; | |
519 | For DoH, though, we have dynamically allocated a DOHUnit object that needs to | |
520 | be freed, as well as internal objects internals to libh2o. | |
521 | - one of the UDP receiver threads receiving a response from a backend, picking | |
522 | the corresponding state and sending the response to the client ; | |
523 | - the 'healthcheck' thread scanning the states to actively discover timeouts, | |
524 | mostly to keep some counters like the 'outstanding' one sane. | |
a9489723 RG |
525 | We previously based that logic on the origFD (FD on which the query was received, |
526 | and therefore from where the response should be sent) but this suffered from an | |
527 | ABA problem since it was quite likely that a UDP 'client thread' would reset it to the | |
528 | same value since we only have so much incoming sockets: | |
529 | - 1/ 'client' thread gets a query and set origFD to its FD, say 5 ; | |
530 | - 2/ 'receiver' thread gets a response, read the value of origFD to 5, check that the qname, | |
531 | qtype and qclass match | |
532 | - 3/ during that time the 'client' thread reuses the state, setting again origFD to 5 ; | |
533 | - 4/ the 'receiver' thread uses compare_exchange_strong() to only replace the value if it's still | |
534 | 5, except it's not the same 5 anymore and it overrides a fresh state. | |
535 | We now use a 32-bit unsigned counter instead, which is incremented every time the state is set, | |
536 | wrapping around if necessary, and we set an atomic signed 64-bit value, so that we still have -1 | |
537 | when the state is unused and the value of our counter otherwise. | |
9bd1a882 | 538 | */ |
311f19d5 RG |
539 | std::atomic<int64_t> usageIndicator{unusedIndicator}; // set to unusedIndicator to indicate this state is empty // 8 |
540 | std::atomic<uint32_t> generation{0}; // increased every time a state is used, to be able to detect an ABA issue // 4 | |
2bf26975 | 541 | ComboAddress origRemote; // 28 |
549d63c9 | 542 | ComboAddress origDest; // 28 |
2bf26975 | 543 | StopWatch sentTime; // 16 |
544 | DNSName qname; // 80 | |
43234e76 | 545 | std::shared_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; |
d8c19b98 | 546 | #ifdef HAVE_PROTOBUF |
ec48a28d | 547 | boost::optional<boost::uuids::uuid> uniqueId; |
11e1e08b | 548 | #endif |
78e3ac9e | 549 | boost::optional<Netmask> subnet{boost::none}; |
886e2cf2 | 550 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; |
a76b0d63 | 551 | std::shared_ptr<QTag> qTag{nullptr}; |
b5b93e0b | 552 | const ClientState* cs{nullptr}; |
fbf14b03 | 553 | DOHUnit* du{nullptr}; |
9837850d | 554 | uint32_t cacheKey; // 4 |
555 | uint32_t cacheKeyNoECS; // 4 | |
71b86bd8 | 556 | uint16_t age; // 4 |
2bf26975 | 557 | uint16_t qtype; // 2 |
886e2cf2 | 558 | uint16_t qclass; // 2 |
2bf26975 | 559 | uint16_t origID; // 2 |
aeb36780 | 560 | uint16_t origFlags; // 2 |
a9489723 | 561 | int origFD{-1}; |
7b3865cd | 562 | int delayMsec; |
acb8f5d5 | 563 | boost::optional<uint32_t> tempFailureTTL; |
ca404e94 | 564 | bool ednsAdded{false}; |
ff73f02b | 565 | bool ecsAdded{false}; |
886e2cf2 | 566 | bool skipCache{false}; |
7cea4e39 | 567 | bool destHarvested{false}; // if true, origDest holds the original dest addr, otherwise the listening addr |
d7728daf | 568 | bool dnssecOK{false}; |
389d903a | 569 | bool useZeroScope; |
df111b53 | 570 | }; |
571 | ||
786e4d8c | 572 | typedef std::unordered_map<string, unsigned int> QueryCountRecords; |
dd1a3034 | 573 | typedef std::function<std::tuple<bool, string>(const DNSQuestion* dq)> QueryCountFilter; |
786e4d8c RS |
574 | struct QueryCount { |
575 | QueryCount() | |
576 | { | |
43234e76 | 577 | pthread_rwlock_init(&queryLock, nullptr); |
786e4d8c | 578 | } |
040793d4 OM |
579 | ~QueryCount() |
580 | { | |
581 | pthread_rwlock_destroy(&queryLock); | |
582 | } | |
786e4d8c RS |
583 | QueryCountRecords records; |
584 | QueryCountFilter filter; | |
585 | pthread_rwlock_t queryLock; | |
586 | bool enabled{false}; | |
587 | }; | |
588 | ||
589 | extern QueryCount g_qcount; | |
590 | ||
8a5d5053 | 591 | struct ClientState |
592 | { | |
8274967b | 593 | ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set<int>& cpus_): cpus(cpus_), local(local_), interface(itfName), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort) |
6e9fd124 RG |
594 | { |
595 | } | |
596 | ||
f0e4dcba | 597 | std::set<int> cpus; |
8a5d5053 | 598 | ComboAddress local; |
43234e76 | 599 | std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr}; |
6e9fd124 | 600 | std::shared_ptr<TLSFrontend> tlsFrontend{nullptr}; |
fbf14b03 | 601 | std::shared_ptr<DOHFrontend> dohFrontend{nullptr}; |
6e9fd124 | 602 | std::string interface; |
963bef8d | 603 | std::atomic<uint64_t> queries{0}; |
7fc95193 | 604 | mutable std::atomic<uint64_t> responses{0}; |
a6e9e107 RG |
605 | std::atomic<uint64_t> tcpDiedReadingQuery{0}; |
606 | std::atomic<uint64_t> tcpDiedSendingResponse{0}; | |
607 | std::atomic<uint64_t> tcpGaveUp{0}; | |
608 | std::atomic<uint64_t> tcpClientTimeouts{0}; | |
609 | std::atomic<uint64_t> tcpDownstreamTimeouts{0}; | |
cff9aa03 | 610 | std::atomic<uint64_t> tcpCurrentConnections{0}; |
846b63bb RG |
611 | std::atomic<uint64_t> tlsNewSessions{0}; // A new TLS session has been negotiated, no resumption |
612 | std::atomic<uint64_t> tlsResumptions{0}; // A TLS session has been resumed, either via session id or via a TLS ticket | |
b608e6c6 RG |
613 | std::atomic<uint64_t> tlsUnknownTicketKey{0}; // A TLS ticket has been presented but we don't have the associated key (might have expired) |
614 | std::atomic<uint64_t> tlsInactiveTicketKey{0}; // A TLS ticket has been successfully resumed but the key is no longer active, we should issue a new one | |
bb3954f0 RG |
615 | std::atomic<uint64_t> tls10queries{0}; // valid DNS queries received via TLSv1.0 |
616 | std::atomic<uint64_t> tls11queries{0}; // valid DNS queries received via TLSv1.1 | |
617 | std::atomic<uint64_t> tls12queries{0}; // valid DNS queries received via TLSv1.2 | |
618 | std::atomic<uint64_t> tls13queries{0}; // valid DNS queries received via TLSv1.3 | |
619 | std::atomic<uint64_t> tlsUnknownqueries{0}; // valid DNS queries received via unknown TLS version | |
cff9aa03 RG |
620 | std::atomic<double> tcpAvgQueriesPerConnection{0.0}; |
621 | /* in ms */ | |
622 | std::atomic<double> tcpAvgConnectionDuration{0.0}; | |
a36ce055 RG |
623 | int udpFD{-1}; |
624 | int tcpFD{-1}; | |
6e9fd124 | 625 | int fastOpenQueueSize{0}; |
b5b93e0b | 626 | bool muted{false}; |
6e9fd124 RG |
627 | bool tcp; |
628 | bool reuseport; | |
629 | bool ready{false}; | |
8429ad04 RG |
630 | |
631 | int getSocket() const | |
632 | { | |
633 | return udpFD != -1 ? udpFD : tcpFD; | |
634 | } | |
635 | ||
3a2ca389 RG |
636 | bool isUDP() const |
637 | { | |
638 | return udpFD != -1; | |
639 | } | |
640 | ||
641 | bool isTCP() const | |
642 | { | |
643 | return udpFD == -1; | |
644 | } | |
645 | ||
f34fdcc5 RG |
646 | bool hasTLS() const |
647 | { | |
648 | return tlsFrontend != nullptr || dohFrontend != nullptr; | |
649 | } | |
650 | ||
ba7ec340 RG |
651 | std::string getType() const |
652 | { | |
653 | std::string result = udpFD != -1 ? "UDP" : "TCP"; | |
654 | ||
fbf14b03 RG |
655 | if (dohFrontend) { |
656 | result += " (DNS over HTTPS)"; | |
657 | } | |
658 | else if (tlsFrontend) { | |
ba7ec340 RG |
659 | result += " (DNS over TLS)"; |
660 | } | |
661 | else if (dnscryptCtx) { | |
662 | result += " (DNSCrypt)"; | |
663 | } | |
664 | ||
665 | return result; | |
666 | } | |
667 | ||
8429ad04 RG |
668 | #ifdef HAVE_EBPF |
669 | shared_ptr<BPFFilter> d_filter; | |
670 | ||
671 | void detachFilter() | |
672 | { | |
673 | if (d_filter) { | |
674 | d_filter->removeSocket(getSocket()); | |
675 | d_filter = nullptr; | |
676 | } | |
677 | } | |
678 | ||
679 | void attachFilter(shared_ptr<BPFFilter> bpf) | |
680 | { | |
681 | detachFilter(); | |
682 | ||
683 | bpf->addSocket(getSocket()); | |
684 | d_filter = bpf; | |
685 | } | |
686 | #endif /* HAVE_EBPF */ | |
cff9aa03 | 687 | |
dd026b9c | 688 | void updateTCPMetrics(size_t nbQueries, uint64_t durationMs) |
cff9aa03 | 689 | { |
dd026b9c | 690 | tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0); |
cff9aa03 RG |
691 | tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0); |
692 | } | |
8a5d5053 | 693 | }; |
694 | ||
695 | class TCPClientCollection { | |
696 | std::vector<int> d_tcpclientthreads; | |
ded1985a | 697 | std::atomic<uint64_t> d_numthreads{0}; |
a9bf3ec4 | 698 | std::atomic<uint64_t> d_pos{0}; |
ded1985a | 699 | std::atomic<uint64_t> d_queued{0}; |
73402775 | 700 | const uint64_t d_maxthreads{0}; |
ded1985a | 701 | std::mutex d_mutex; |
edbda1ad | 702 | int d_singlePipe[2]; |
73402775 | 703 | const bool d_useSinglePipe; |
ded1985a | 704 | public: |
8a5d5053 | 705 | |
b79e4996 RG |
706 | TCPClientCollection(size_t maxThreads, bool useSinglePipe=false): d_maxthreads(maxThreads), d_singlePipe{-1,-1}, d_useSinglePipe(useSinglePipe) |
707 | ||
8a5d5053 | 708 | { |
a9bf3ec4 | 709 | d_tcpclientthreads.reserve(maxThreads); |
edbda1ad RG |
710 | |
711 | if (d_useSinglePipe) { | |
712 | if (pipe(d_singlePipe) < 0) { | |
c52b8cb6 OM |
713 | int err = errno; |
714 | throw std::runtime_error("Error creating the TCP single communication pipe: " + stringerror(err)); | |
edbda1ad | 715 | } |
3b07fd1b RG |
716 | |
717 | if (!setNonBlocking(d_singlePipe[0])) { | |
718 | int err = errno; | |
719 | close(d_singlePipe[0]); | |
720 | close(d_singlePipe[1]); | |
c52b8cb6 | 721 | throw std::runtime_error("Error setting the TCP single communication pipe non-blocking: " + stringerror(err)); |
3b07fd1b RG |
722 | } |
723 | ||
edbda1ad RG |
724 | if (!setNonBlocking(d_singlePipe[1])) { |
725 | int err = errno; | |
726 | close(d_singlePipe[0]); | |
727 | close(d_singlePipe[1]); | |
c52b8cb6 | 728 | throw std::runtime_error("Error setting the TCP single communication pipe non-blocking: " + stringerror(err)); |
edbda1ad RG |
729 | } |
730 | } | |
8a5d5053 | 731 | } |
a9bf3ec4 | 732 | int getThread() |
8a5d5053 | 733 | { |
6c1ca990 | 734 | uint64_t pos = d_pos++; |
8a5d5053 | 735 | ++d_queued; |
736 | return d_tcpclientthreads[pos % d_numthreads]; | |
737 | } | |
ded1985a RG |
738 | bool hasReachedMaxThreads() const |
739 | { | |
740 | return d_numthreads >= d_maxthreads; | |
741 | } | |
742 | uint64_t getThreadsCount() const | |
743 | { | |
744 | return d_numthreads; | |
745 | } | |
746 | uint64_t getQueuedCount() const | |
747 | { | |
748 | return d_queued; | |
749 | } | |
750 | void decrementQueuedCount() | |
751 | { | |
752 | --d_queued; | |
753 | } | |
8a5d5053 | 754 | void addTCPClientThread(); |
755 | }; | |
756 | ||
1f7646c2 | 757 | extern std::unique_ptr<TCPClientCollection> g_tcpclientthreads; |
8a5d5053 | 758 | |
df111b53 | 759 | struct DownstreamState |
760 | { | |
1720247e | 761 | typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t; |
98650fde | 762 | |
203b5348 RG |
763 | DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf, const std::string& sourceItfName, size_t numberOfSockets, bool connect); |
764 | DownstreamState(const ComboAddress& remote_): DownstreamState(remote_, ComboAddress(), 0, std::string(), 1, true) {} | |
6a62c0e3 RG |
765 | ~DownstreamState() |
766 | { | |
5bdbb83d | 767 | for (auto& fd : sockets) { |
150105a2 RG |
768 | if (fd >= 0) { |
769 | close(fd); | |
770 | fd = -1; | |
771 | } | |
772 | } | |
040793d4 | 773 | pthread_rwlock_destroy(&d_lock); |
6a62c0e3 | 774 | } |
1720247e | 775 | boost::uuids::uuid id; |
50033a8e | 776 | std::vector<unsigned int> hashes; |
d58e616a | 777 | mutable pthread_rwlock_t d_lock; |
5bdbb83d | 778 | std::vector<int> sockets; |
70b0d0e2 | 779 | const std::string sourceItfName; |
5bdbb83d | 780 | std::mutex socketsLock; |
5d7e6765 | 781 | std::mutex connectLock; |
5bdbb83d | 782 | std::unique_ptr<FDMultiplexer> mplexer{nullptr}; |
df111b53 | 783 | std::thread tid; |
a2353842 | 784 | const ComboAddress remote; |
df111b53 | 785 | QPSLimiter qps; |
786 | vector<IDState> idStates; | |
73402775 | 787 | const ComboAddress sourceAddr; |
98650fde | 788 | checkfunc_t checkFunction; |
fbe2a2e0 RG |
789 | DNSName checkName{"a.root-servers.net."}; |
790 | QType checkType{QType::A}; | |
de9f7157 | 791 | uint16_t checkClass{QClass::IN}; |
df111b53 | 792 | std::atomic<uint64_t> idOffset{0}; |
793 | std::atomic<uint64_t> sendErrors{0}; | |
794 | std::atomic<uint64_t> outstanding{0}; | |
795 | std::atomic<uint64_t> reuseds{0}; | |
796 | std::atomic<uint64_t> queries{0}; | |
7fc95193 | 797 | std::atomic<uint64_t> responses{0}; |
df111b53 | 798 | struct { |
799 | std::atomic<uint64_t> sendErrors{0}; | |
800 | std::atomic<uint64_t> reuseds{0}; | |
801 | std::atomic<uint64_t> queries{0}; | |
802 | } prev; | |
a6e9e107 RG |
803 | std::atomic<uint64_t> tcpDiedSendingQuery{0}; |
804 | std::atomic<uint64_t> tcpDiedReadingResponse{0}; | |
805 | std::atomic<uint64_t> tcpGaveUp{0}; | |
806 | std::atomic<uint64_t> tcpReadTimeouts{0}; | |
807 | std::atomic<uint64_t> tcpWriteTimeouts{0}; | |
cff9aa03 RG |
808 | std::atomic<uint64_t> tcpCurrentConnections{0}; |
809 | std::atomic<double> tcpAvgQueriesPerConnection{0.0}; | |
810 | /* in ms */ | |
811 | std::atomic<double> tcpAvgConnectionDuration{0.0}; | |
5bdbb83d | 812 | size_t socketsOffset{0}; |
df111b53 | 813 | double queryLoad{0.0}; |
814 | double dropRate{0.0}; | |
815 | double latencyUsec{0.0}; | |
816 | int order{1}; | |
817 | int weight{1}; | |
b40cffe7 | 818 | int tcpConnectTimeout{5}; |
3f6d07a4 RG |
819 | int tcpRecvTimeout{30}; |
820 | int tcpSendTimeout{30}; | |
7c9bf18d | 821 | unsigned int checkInterval{1}; |
822 | unsigned int lastCheck{0}; | |
73402775 | 823 | const unsigned int sourceItf{0}; |
3f6d07a4 | 824 | uint16_t retries{5}; |
c85f69a8 | 825 | uint16_t xpfRRCode{0}; |
b7e6f4a1 | 826 | uint16_t checkTimeout{1000}; /* in milliseconds */ |
9e87dcb8 | 827 | uint8_t currentCheckFailures{0}; |
853faf61 | 828 | uint8_t consecutiveSuccessfulChecks{0}; |
9e87dcb8 | 829 | uint8_t maxCheckFailures{1}; |
1b633bec | 830 | uint8_t minRiseSuccesses{1}; |
df111b53 | 831 | StopWatch sw; |
832 | set<string> pools; | |
833 | enum class Availability { Up, Down, Auto} availability{Availability::Auto}; | |
fbe2a2e0 | 834 | bool mustResolve{false}; |
df111b53 | 835 | bool upStatus{false}; |
ca404e94 | 836 | bool useECS{false}; |
bde73d5b | 837 | bool useProxyProtocol{false}; |
21830638 | 838 | bool setCD{false}; |
49c33a6c | 839 | bool disableZeroScope{false}; |
7565f4e6 | 840 | std::atomic<bool> connected{false}; |
5d7e6765 | 841 | std::atomic_flag threadStarted; |
284d460c | 842 | bool tcpFastOpen{false}; |
5602f131 | 843 | bool ipBindAddrNoPort{true}; |
5d7e6765 | 844 | |
df111b53 | 845 | bool isUp() const |
846 | { | |
847 | if(availability == Availability::Down) | |
848 | return false; | |
849 | if(availability == Availability::Up) | |
850 | return true; | |
851 | return upStatus; | |
852 | } | |
853 | void setUp() { availability = Availability::Up; } | |
854 | void setDown() { availability = Availability::Down; } | |
855 | void setAuto() { availability = Availability::Auto; } | |
508543b4 | 856 | const string& getName() const { |
18eeccc9 RG |
857 | return name; |
858 | } | |
508543b4 | 859 | const string& getNameWithAddr() const { |
be05aa91 RG |
860 | return nameWithAddr; |
861 | } | |
862 | void setName(const std::string& newName) | |
863 | { | |
864 | name = newName; | |
77c369f8 | 865 | nameWithAddr = newName.empty() ? remote.toStringWithPort() : (name + " (" + remote.toStringWithPort()+ ")"); |
a7940c06 | 866 | } |
be05aa91 | 867 | |
9f4eb5cc RG |
868 | string getStatus() const |
869 | { | |
870 | string status; | |
871 | if(availability == DownstreamState::Availability::Up) | |
872 | status = "UP"; | |
873 | else if(availability == DownstreamState::Availability::Down) | |
874 | status = "DOWN"; | |
875 | else | |
876 | status = (upStatus ? "up" : "down"); | |
877 | return status; | |
878 | } | |
5d7e6765 | 879 | bool reconnect(); |
f2caf657 CHB |
880 | void hash(); |
881 | void setId(const boost::uuids::uuid& newId); | |
882 | void setWeight(int newWeight); | |
cff9aa03 | 883 | |
dd026b9c | 884 | void updateTCPMetrics(size_t nbQueries, uint64_t durationMs) |
cff9aa03 | 885 | { |
dd026b9c | 886 | tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0); |
cff9aa03 RG |
887 | tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0); |
888 | } | |
be05aa91 RG |
889 | private: |
890 | std::string name; | |
891 | std::string nameWithAddr; | |
df111b53 | 892 | }; |
893 | using servers_t =vector<std::shared_ptr<DownstreamState>>; | |
df111b53 | 894 | |
9b73b71c | 895 | void responderThread(std::shared_ptr<DownstreamState> state); |
da4e7813 | 896 | extern std::mutex g_luamutex; |
897 | extern LuaContext g_lua; | |
898 | extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex | |
899 | ||
0940e4eb | 900 | class DNSRule |
901 | { | |
902 | public: | |
205f2081 RG |
903 | virtual ~DNSRule () |
904 | { | |
905 | } | |
497a6e3a | 906 | virtual bool matches(const DNSQuestion* dq) const =0; |
0940e4eb | 907 | virtual string toString() const = 0; |
908 | mutable std::atomic<uint64_t> d_matches{0}; | |
909 | }; | |
910 | ||
886e2cf2 RG |
911 | struct ServerPool |
912 | { | |
a1b1a29d RG |
913 | ServerPool() |
914 | { | |
915 | pthread_rwlock_init(&d_lock, nullptr); | |
916 | } | |
040793d4 OM |
917 | ~ServerPool() |
918 | { | |
919 | pthread_rwlock_destroy(&d_lock); | |
920 | } | |
a1b1a29d | 921 | |
886e2cf2 RG |
922 | const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; }; |
923 | ||
7e687744 RG |
924 | bool getECS() const |
925 | { | |
926 | return d_useECS; | |
927 | } | |
928 | ||
929 | void setECS(bool useECS) | |
930 | { | |
931 | d_useECS = useECS; | |
932 | } | |
933 | ||
886e2cf2 | 934 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; |
b9f8a6c8 | 935 | std::shared_ptr<ServerPolicy> policy{nullptr}; |
5c30ec69 | 936 | |
a1b1a29d RG |
937 | size_t countServers(bool upOnly) |
938 | { | |
939 | size_t count = 0; | |
940 | ReadLock rl(&d_lock); | |
941 | for (const auto& server : d_servers) { | |
942 | if (!upOnly || std::get<1>(server)->isUp() ) { | |
943 | count++; | |
c1b81381 RG |
944 | } |
945 | } | |
a1b1a29d RG |
946 | return count; |
947 | } | |
948 | ||
be05aa91 | 949 | ServerPolicy::NumberedServerVector getServers() |
a1b1a29d | 950 | { |
be05aa91 | 951 | ServerPolicy::NumberedServerVector result; |
a1b1a29d RG |
952 | { |
953 | ReadLock rl(&d_lock); | |
954 | result = d_servers; | |
955 | } | |
956 | return result; | |
957 | } | |
958 | ||
959 | void addServer(shared_ptr<DownstreamState>& server) | |
960 | { | |
961 | WriteLock wl(&d_lock); | |
962 | unsigned int count = (unsigned int) d_servers.size(); | |
963 | d_servers.push_back(make_pair(++count, server)); | |
964 | /* we need to reorder based on the server 'order' */ | |
965 | std::stable_sort(d_servers.begin(), d_servers.end(), [](const std::pair<unsigned int,std::shared_ptr<DownstreamState> >& a, const std::pair<unsigned int,std::shared_ptr<DownstreamState> >& b) { | |
966 | return a.second->order < b.second->order; | |
967 | }); | |
968 | /* and now we need to renumber for Lua (custom policies) */ | |
969 | size_t idx = 1; | |
970 | for (auto& serv : d_servers) { | |
971 | serv.first = idx++; | |
972 | } | |
973 | } | |
974 | ||
975 | void removeServer(shared_ptr<DownstreamState>& server) | |
976 | { | |
977 | WriteLock wl(&d_lock); | |
978 | size_t idx = 1; | |
979 | bool found = false; | |
980 | for (auto it = d_servers.begin(); it != d_servers.end();) { | |
981 | if (found) { | |
982 | /* we need to renumber the servers placed | |
983 | after the removed one, for Lua (custom policies) */ | |
984 | it->first = idx++; | |
985 | it++; | |
986 | } | |
987 | else if (it->second == server) { | |
988 | it = d_servers.erase(it); | |
989 | found = true; | |
990 | } else { | |
991 | idx++; | |
992 | it++; | |
993 | } | |
994 | } | |
995 | } | |
996 | ||
997 | private: | |
be05aa91 | 998 | ServerPolicy::NumberedServerVector d_servers; |
a1b1a29d | 999 | pthread_rwlock_t d_lock; |
7e687744 | 1000 | bool d_useECS{false}; |
886e2cf2 | 1001 | }; |
886e2cf2 | 1002 | |
42fae326 | 1003 | struct CarbonConfig |
1004 | { | |
d617b22c | 1005 | ComboAddress server; |
813b0ba9 | 1006 | std::string namespace_name; |
42fae326 | 1007 | std::string ourname; |
813b0ba9 | 1008 | std::string instance_name; |
d617b22c | 1009 | unsigned int interval; |
42fae326 | 1010 | }; |
1011 | ||
ca404e94 RG |
1012 | enum ednsHeaderFlags { |
1013 | EDNS_HEADER_FLAG_NONE = 0, | |
1014 | EDNS_HEADER_FLAG_DO = 32768 | |
1015 | }; | |
1016 | ||
4d5959e6 RG |
1017 | struct DNSDistRuleAction |
1018 | { | |
1019 | std::shared_ptr<DNSRule> d_rule; | |
1020 | std::shared_ptr<DNSAction> d_action; | |
1021 | boost::uuids::uuid d_id; | |
f8a222ac | 1022 | uint64_t d_creationOrder; |
4d5959e6 RG |
1023 | }; |
1024 | ||
1025 | struct DNSDistResponseRuleAction | |
1026 | { | |
1027 | std::shared_ptr<DNSRule> d_rule; | |
1028 | std::shared_ptr<DNSResponseAction> d_action; | |
1029 | boost::uuids::uuid d_id; | |
f8a222ac | 1030 | uint64_t d_creationOrder; |
4d5959e6 RG |
1031 | }; |
1032 | ||
71c94675 | 1033 | extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT; |
dd46e5e3 | 1034 | extern DNSAction::Action g_dynBlockAction; |
71c94675 | 1035 | |
d617b22c | 1036 | extern GlobalStateHolder<vector<CarbonConfig> > g_carbon; |
ecbe9133 | 1037 | extern GlobalStateHolder<ServerPolicy> g_policy; |
1038 | extern GlobalStateHolder<servers_t> g_dstates; | |
886e2cf2 | 1039 | extern GlobalStateHolder<pools_t> g_pools; |
4d5959e6 RG |
1040 | extern GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions; |
1041 | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions; | |
1042 | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions; | |
2d4783a8 | 1043 | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredresprulactions; |
638184e9 | 1044 | extern GlobalStateHolder<NetmaskGroup> g_ACL; |
2e72cc0e | 1045 | |
ecbe9133 | 1046 | extern ComboAddress g_serverControl; // not changed during runtime |
1047 | ||
a227f47d | 1048 | extern std::vector<shared_ptr<TLSFrontend>> g_tlslocals; |
fbf14b03 | 1049 | extern std::vector<shared_ptr<DOHFrontend>> g_dohlocals; |
6e9fd124 | 1050 | extern std::vector<std::unique_ptr<ClientState>> g_frontends; |
6ad8b29a | 1051 | extern bool g_truncateTC; |
b29edbee | 1052 | extern bool g_fixupCase; |
3f6d07a4 RG |
1053 | extern int g_tcpRecvTimeout; |
1054 | extern int g_tcpSendTimeout; | |
e0b5e49d | 1055 | extern int g_udpTimeout; |
e41f8165 RG |
1056 | extern uint16_t g_maxOutstanding; |
1057 | extern std::atomic<bool> g_configurationDone; | |
6c1ca990 RG |
1058 | extern uint64_t g_maxTCPClientThreads; |
1059 | extern uint64_t g_maxTCPQueuedConnections; | |
9396d955 RG |
1060 | extern size_t g_maxTCPQueriesPerConn; |
1061 | extern size_t g_maxTCPConnectionDuration; | |
1062 | extern size_t g_maxTCPConnectionsPerClient; | |
886e2cf2 | 1063 | extern std::atomic<uint16_t> g_cacheCleaningDelay; |
f65ea0c2 | 1064 | extern std::atomic<uint16_t> g_cacheCleaningPercentage; |
1ea747c0 | 1065 | extern uint32_t g_staleCacheEntriesTTL; |
56d68fad RG |
1066 | extern bool g_apiReadWrite; |
1067 | extern std::string g_apiConfigDirectory; | |
26a3cdb7 | 1068 | extern bool g_servFailOnNoPolicy; |
edbda1ad | 1069 | extern bool g_useTCPSinglePipe; |
cff9aa03 | 1070 | extern uint16_t g_downstreamTCPCleanupInterval; |
0beaa5c8 | 1071 | extern size_t g_udpVectorSize; |
53c57da7 | 1072 | extern bool g_preserveTrailingData; |
0dffe9e3 | 1073 | extern bool g_allowEmptyResponse; |
ca404e94 | 1074 | |
87b515ed RG |
1075 | #ifdef HAVE_EBPF |
1076 | extern shared_ptr<BPFFilter> g_defaultBPFFilter; | |
8429ad04 | 1077 | extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters; |
87b515ed RG |
1078 | #endif /* HAVE_EBPF */ |
1079 | ||
0beaa5c8 RG |
1080 | struct LocalHolders |
1081 | { | |
2d4783a8 | 1082 | LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), rulactions(g_rulactions.getLocal()), cacheHitRespRulactions(g_cachehitresprulactions.getLocal()), selfAnsweredRespRulactions(g_selfansweredresprulactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal()) |
0beaa5c8 RG |
1083 | { |
1084 | } | |
1085 | ||
1086 | LocalStateHolder<NetmaskGroup> acl; | |
1087 | LocalStateHolder<ServerPolicy> policy; | |
4d5959e6 RG |
1088 | LocalStateHolder<vector<DNSDistRuleAction> > rulactions; |
1089 | LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRulactions; | |
2d4783a8 | 1090 | LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRulactions; |
0beaa5c8 RG |
1091 | LocalStateHolder<servers_t> servers; |
1092 | LocalStateHolder<NetmaskTree<DynBlock> > dynNMGBlock; | |
1093 | LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock; | |
1094 | LocalStateHolder<pools_t> pools; | |
1095 | }; | |
1096 | ||
ecbe9133 | 1097 | struct dnsheader; |
1098 | ||
1099 | void controlThread(int fd, ComboAddress local); | |
b6e26895 | 1100 | vector<std::function<void(void)>> setupLua(bool client, const std::string& config); |
e7c732b8 | 1101 | |
80dbd7d2 CHB |
1102 | struct WebserverConfig |
1103 | { | |
1104 | std::string password; | |
1105 | std::string apiKey; | |
1106 | boost::optional<std::map<std::string, std::string> > customHeaders; | |
1107 | std::mutex lock; | |
1108 | }; | |
1109 | ||
32c97b56 CHB |
1110 | void setWebserverAPIKey(const boost::optional<std::string> apiKey); |
1111 | void setWebserverPassword(const std::string& password); | |
1112 | void setWebserverCustomHeaders(const boost::optional<std::map<std::string, std::string> > customHeaders); | |
1113 | ||
80dbd7d2 | 1114 | void dnsdistWebserverThread(int sock, const ComboAddress& local); |
9b73b71c | 1115 | void tcpAcceptorThread(void* p); |
fbf14b03 RG |
1116 | #ifdef HAVE_DNS_OVER_HTTPS |
1117 | void dohThread(ClientState* cs); | |
1118 | #endif /* HAVE_DNS_OVER_HTTPS */ | |
80a216c9 | 1119 | |
f758857a | 1120 | void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects |
1121 | void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls | |
1122 | bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect | |
1123 | void resetLuaSideEffect(); // reset to indeterminate state | |
11e1e08b | 1124 | |
e7c732b8 | 1125 | bool responseContentMatches(const char* response, const uint16_t responseLen, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& remote, unsigned int& consumed); |
3e425868 | 1126 | bool processResponse(char** response, uint16_t* responseLen, size_t* responseSize, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRulactions, DNSResponse& dr, size_t addRoom, std::vector<uint8_t>& rewrittenResponse, bool muted); |
2a28db86 | 1127 | bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::string& ruleresult, bool& drop); |
4ab01344 | 1128 | |
0beaa5c8 | 1129 | bool checkQueryHeaders(const struct dnsheader* dh); |
fcffc585 | 1130 | |
6e9fd124 | 1131 | extern std::vector<std::shared_ptr<DNSCryptContext>> g_dnsCryptLocals; |
43234e76 | 1132 | int handleDNSCryptQuery(char* packet, uint16_t len, std::shared_ptr<DNSCryptQuery> query, uint16_t* decryptedQueryLen, bool tcp, time_t now, std::vector<uint8_t>& response); |
4ab01344 | 1133 | boost::optional<std::vector<uint8_t>> checkDNSCryptQuery(const ClientState& cs, const char* query, uint16_t& len, std::shared_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp); |
9f4eb5cc | 1134 | |
18f707fa | 1135 | bool addXPF(DNSQuestion& dq, uint16_t optionCode); |
5cc8371b | 1136 | |
555970c9 RG |
1137 | uint16_t getRandomDNSID(); |
1138 | ||
9f4eb5cc RG |
1139 | #include "dnsdist-snmp.hh" |
1140 | ||
1141 | extern bool g_snmpEnabled; | |
1142 | extern bool g_snmpTrapsEnabled; | |
1143 | extern DNSDistSNMPAgent* g_snmpAgent; | |
e7c732b8 RG |
1144 | extern bool g_addEDNSToSelfGeneratedResponses; |
1145 | ||
83fe2c55 | 1146 | extern std::set<std::string> g_capabilitiesToRetain; |
8179b6d6 RG |
1147 | static const uint16_t s_udpIncomingBufferSize{1500}; // don't accept UDP queries larger than this value |
1148 | static const size_t s_maxPacketCacheEntrySize{4096}; // don't cache responses larger than this value | |
4ab01344 | 1149 | |
3e425868 RG |
1150 | enum class ProcessQueryResult { Drop, SendAnswer, PassToBackend }; |
1151 | ProcessQueryResult processQuery(DNSQuestion& dq, ClientState& cs, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend); | |
4ab01344 | 1152 | |
d0ae6360 RG |
1153 | DNSResponse makeDNSResponseFromIDState(IDState& ids, struct dnsheader* dh, size_t bufferSize, uint16_t responseLen, bool isTCP); |
1154 | void setIDStateFromDNSQuestion(IDState& ids, DNSQuestion& dq, DNSName&& qname); | |
fbf14b03 RG |
1155 | |
1156 | int pickBackendSocketForSending(std::shared_ptr<DownstreamState>& state); | |
1157 | ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const char* request, const size_t requestLen, bool healthCheck=false); |