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