]>
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" |
25 | #include <time.h> | |
26 | #include "misc.hh" | |
5bdbb83d | 27 | #include "mplexer.hh" |
df111b53 | 28 | #include "iputils.hh" |
29 | #include "dnsname.hh" | |
30 | #include <atomic> | |
e16fd59c | 31 | #include <boost/variant.hpp> |
df111b53 | 32 | #include <mutex> |
33 | #include <thread> | |
bffca8b9 | 34 | #include <unistd.h> |
ecbe9133 | 35 | #include "sholder.hh" |
11e1e08b | 36 | #include "dnscrypt.hh" |
886e2cf2 | 37 | #include "dnsdist-cache.hh" |
85c7ca75 | 38 | #include "gettime.hh" |
87b515ed RG |
39 | #include "dnsdist-dynbpf.hh" |
40 | #include "bpf-filter.hh" | |
26a6373d SO |
41 | #include <string> |
42 | #include <unordered_map> | |
a227f47d | 43 | #include "tcpiohandler.hh" |
26a6373d | 44 | |
d8c19b98 RG |
45 | #include <boost/uuid/uuid.hpp> |
46 | #include <boost/uuid/uuid_generators.hpp> | |
4d5959e6 | 47 | #include <boost/uuid/uuid_io.hpp> |
d8c19b98 | 48 | |
42fae326 | 49 | void* carbonDumpThread(); |
61d1b966 | 50 | uint64_t uptimeOfProcess(const std::string& str); |
bd1c631b | 51 | |
7b925432 RG |
52 | extern uint16_t g_ECSSourcePrefixV4; |
53 | extern uint16_t g_ECSSourcePrefixV6; | |
54 | extern bool g_ECSOverride; | |
55 | ||
0beaa5c8 | 56 | extern thread_local boost::uuids::random_generator t_uuidGenerator; |
26a6373d | 57 | |
15fac047 CH |
58 | typedef std::unordered_map<string, string> QTag; |
59 | ||
7b925432 RG |
60 | struct DNSQuestion |
61 | { | |
5ffb2f83 CH |
62 | DNSQuestion(const DNSName* name, uint16_t type, uint16_t class_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t queryLen, bool isTcp, const struct timespec* queryTime_): |
63 | qname(name), qtype(type), qclass(class_), local(lc), remote(rem), dh(header), size(bufferSize), len(queryLen), ecsPrefixLength(rem->sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), tempFailureTTL(boost::none), tcp(isTcp), queryTime(queryTime_), ecsOverride(g_ECSOverride) { } | |
7b925432 RG |
64 | |
65 | #ifdef HAVE_PROTOBUF | |
ec48a28d | 66 | boost::optional<boost::uuids::uuid> uniqueId; |
7b925432 RG |
67 | #endif |
68 | const DNSName* qname; | |
69 | const uint16_t qtype; | |
70 | const uint16_t qclass; | |
71 | const ComboAddress* local; | |
72 | const ComboAddress* remote; | |
15fac047 | 73 | std::shared_ptr<QTag> qTag{nullptr}; |
7b925432 RG |
74 | struct dnsheader* dh; |
75 | size_t size; | |
76 | uint16_t len; | |
77 | uint16_t ecsPrefixLength; | |
acb8f5d5 | 78 | boost::optional<uint32_t> tempFailureTTL; |
7b925432 | 79 | const bool tcp; |
5ffb2f83 | 80 | const struct timespec* queryTime; |
7b925432 RG |
81 | bool skipCache{false}; |
82 | bool ecsOverride; | |
5b8255ba | 83 | bool useECS{true}; |
5cc8371b | 84 | bool addXPF{true}; |
7b925432 RG |
85 | }; |
86 | ||
87 | struct DNSResponse : DNSQuestion | |
88 | { | |
5ffb2f83 CH |
89 | DNSResponse(const DNSName* name, uint16_t type, uint16_t class_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t responseLen, bool isTcp, const struct timespec* queryTime_): |
90 | DNSQuestion(name, type, class_, lc, rem, header, bufferSize, responseLen, isTcp, queryTime_) { } | |
7b925432 RG |
91 | }; |
92 | ||
5c30ec69 LM |
93 | /* so what could you do: |
94 | drop, | |
95 | fake up nxdomain, | |
96 | provide actual answer, | |
97 | allow & and stop processing, | |
98 | continue processing, | |
7b925432 RG |
99 | modify header: (servfail|refused|notimp), set TC=1, |
100 | send to pool */ | |
101 | ||
102 | class DNSAction | |
103 | { | |
104 | public: | |
5f23eb98 | 105 | enum class Action { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, ServFail, None}; |
7b925432 | 106 | virtual Action operator()(DNSQuestion*, string* ruleresult) const =0; |
205f2081 RG |
107 | virtual ~DNSAction() |
108 | { | |
109 | } | |
7b925432 | 110 | virtual string toString() const = 0; |
b8019cf7 | 111 | virtual std::map<string, double> getStats() const |
7b925432 RG |
112 | { |
113 | return {{}}; | |
114 | } | |
115 | }; | |
116 | ||
117 | class DNSResponseAction | |
118 | { | |
119 | public: | |
5f23eb98 | 120 | enum class Action { Allow, Delay, Drop, HeaderModify, ServFail, None }; |
7b925432 | 121 | virtual Action operator()(DNSResponse*, string* ruleresult) const =0; |
205f2081 RG |
122 | virtual ~DNSResponseAction() |
123 | { | |
124 | } | |
7b925432 RG |
125 | virtual string toString() const = 0; |
126 | }; | |
127 | ||
78ffa782 | 128 | struct DynBlock |
129 | { | |
5708a729 RG |
130 | DynBlock(): action(DNSAction::Action::None) |
131 | { | |
132 | } | |
133 | ||
134 | DynBlock(const std::string& reason_, const struct timespec& until_, const DNSName& domain_, DNSAction::Action action_): reason(reason_), until(until_), domain(domain_), action(action_) | |
135 | { | |
136 | } | |
137 | ||
138 | DynBlock(const DynBlock& rhs): reason(rhs.reason), until(rhs.until), domain(rhs.domain), action(rhs.action) | |
139 | { | |
140 | blocks.store(rhs.blocks); | |
141 | } | |
142 | ||
78ffa782 | 143 | DynBlock& operator=(const DynBlock& rhs) |
144 | { | |
145 | reason=rhs.reason; | |
146 | until=rhs.until; | |
71c94675 | 147 | domain=rhs.domain; |
7b925432 | 148 | action=rhs.action; |
78ffa782 | 149 | blocks.store(rhs.blocks); |
150 | return *this; | |
151 | } | |
71c94675 | 152 | |
78ffa782 | 153 | string reason; |
154 | struct timespec until; | |
71c94675 | 155 | DNSName domain; |
7b925432 | 156 | DNSAction::Action action; |
78ffa782 | 157 | mutable std::atomic<unsigned int> blocks; |
158 | }; | |
159 | ||
160 | extern GlobalStateHolder<NetmaskTree<DynBlock>> g_dynblockNMG; | |
f758857a | 161 | |
162 | extern vector<pair<struct timeval, std::string> > g_confDelta; | |
163 | ||
e48090d1 | 164 | struct DNSDistStats |
165 | { | |
6ad8b29a | 166 | using stat_t=std::atomic<uint64_t>; // aww yiss ;-) |
e48090d1 | 167 | stat_t responses{0}; |
168 | stat_t servfailResponses{0}; | |
169 | stat_t queries{0}; | |
e73ec7d3 | 170 | stat_t nonCompliantQueries{0}; |
d08b1cdf | 171 | stat_t nonCompliantResponses{0}; |
643a182a | 172 | stat_t rdQueries{0}; |
2efd427d | 173 | stat_t emptyQueries{0}; |
e48090d1 | 174 | stat_t aclDrops{0}; |
bd1c631b | 175 | stat_t dynBlocked{0}; |
e48090d1 | 176 | stat_t ruleDrop{0}; |
177 | stat_t ruleNXDomain{0}; | |
dd46e5e3 | 178 | stat_t ruleRefused{0}; |
5f23eb98 | 179 | stat_t ruleServFail{0}; |
e48090d1 | 180 | stat_t selfAnswered{0}; |
181 | stat_t downstreamTimeouts{0}; | |
182 | stat_t downstreamSendErrors{0}; | |
6ad8b29a | 183 | stat_t truncFail{0}; |
b8bc7e61 | 184 | stat_t noPolicy{0}; |
886e2cf2 RG |
185 | stat_t cacheHits{0}; |
186 | stat_t cacheMisses{0}; | |
42fae326 | 187 | stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}; |
5c30ec69 | 188 | |
e16fd59c | 189 | double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0}; |
a1a787dc | 190 | typedef std::function<uint64_t(const std::string&)> statfunction_t; |
191 | typedef boost::variant<stat_t*, double*, statfunction_t> entry_t; | |
e16fd59c | 192 | std::vector<std::pair<std::string, entry_t>> entries{ |
dd46e5e3 RG |
193 | {"responses", &responses}, |
194 | {"servfail-responses", &servfailResponses}, | |
195 | {"queries", &queries}, | |
196 | {"acl-drops", &aclDrops}, | |
dd46e5e3 RG |
197 | {"rule-drop", &ruleDrop}, |
198 | {"rule-nxdomain", &ruleNXDomain}, | |
199 | {"rule-refused", &ruleRefused}, | |
5f23eb98 | 200 | {"rule-servfail", &ruleServFail}, |
dd46e5e3 RG |
201 | {"self-answered", &selfAnswered}, |
202 | {"downstream-timeouts", &downstreamTimeouts}, | |
5c30ec69 | 203 | {"downstream-send-errors", &downstreamSendErrors}, |
dd46e5e3 RG |
204 | {"trunc-failures", &truncFail}, |
205 | {"no-policy", &noPolicy}, | |
206 | {"latency0-1", &latency0_1}, | |
207 | {"latency1-10", &latency1_10}, | |
208 | {"latency10-50", &latency10_50}, | |
209 | {"latency50-100", &latency50_100}, | |
210 | {"latency100-1000", &latency100_1000}, | |
211 | {"latency-slow", &latencySlow}, | |
212 | {"latency-avg100", &latencyAvg100}, | |
213 | {"latency-avg1000", &latencyAvg1000}, | |
214 | {"latency-avg10000", &latencyAvg10000}, | |
215 | {"latency-avg1000000", &latencyAvg1000000}, | |
61d1b966 | 216 | {"uptime", uptimeOfProcess}, |
a9b6db56 | 217 | {"real-memory-usage", getRealMemoryUsage}, |
a2aa00ed | 218 | {"noncompliant-queries", &nonCompliantQueries}, |
d08b1cdf | 219 | {"noncompliant-responses", &nonCompliantResponses}, |
643a182a | 220 | {"rdqueries", &rdQueries}, |
2efd427d | 221 | {"empty-queries", &emptyQueries}, |
886e2cf2 RG |
222 | {"cache-hits", &cacheHits}, |
223 | {"cache-misses", &cacheMisses}, | |
4f99f3d3 RG |
224 | {"cpu-user-msec", getCPUTimeUser}, |
225 | {"cpu-sys-msec", getCPUTimeSystem}, | |
dd46e5e3 | 226 | {"fd-usage", getOpenFileDescriptors}, |
5c30ec69 | 227 | {"dyn-blocked", &dynBlocked}, |
bd1c631b | 228 | {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }} |
42fae326 | 229 | }; |
e48090d1 | 230 | }; |
231 | ||
e16fd59c | 232 | |
e48090d1 | 233 | extern struct DNSDistStats g_stats; |
f653b8df | 234 | void doLatencyStats(double udiff); |
e48090d1 | 235 | |
638184e9 | 236 | |
df111b53 | 237 | struct StopWatch |
238 | { | |
58307a85 RG |
239 | StopWatch(bool realTime=false): d_needRealTime(realTime) |
240 | { | |
241 | } | |
df111b53 | 242 | struct timespec d_start{0,0}; |
58307a85 RG |
243 | bool d_needRealTime{false}; |
244 | ||
5c30ec69 | 245 | void start() { |
58307a85 | 246 | if(gettime(&d_start, d_needRealTime) < 0) |
df111b53 | 247 | unixDie("Getting timestamp"); |
5c30ec69 | 248 | |
df111b53 | 249 | } |
cf48b0ce RG |
250 | |
251 | void set(const struct timespec& from) { | |
252 | d_start = from; | |
253 | } | |
5c30ec69 | 254 | |
df111b53 | 255 | double udiff() const { |
256 | struct timespec now; | |
58307a85 | 257 | if(gettime(&now, d_needRealTime) < 0) |
df111b53 | 258 | unixDie("Getting timestamp"); |
5c30ec69 | 259 | |
df111b53 | 260 | return 1000000.0*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec)/1000.0; |
261 | } | |
262 | ||
263 | double udiffAndSet() { | |
264 | struct timespec now; | |
58307a85 | 265 | if(gettime(&now, d_needRealTime) < 0) |
df111b53 | 266 | unixDie("Getting timestamp"); |
5c30ec69 | 267 | |
df111b53 | 268 | auto ret= 1000000.0*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec)/1000.0; |
269 | d_start = now; | |
270 | return ret; | |
271 | } | |
272 | ||
273 | }; | |
274 | ||
275 | class QPSLimiter | |
276 | { | |
277 | public: | |
278 | QPSLimiter() | |
279 | { | |
280 | } | |
281 | ||
282 | QPSLimiter(unsigned int rate, unsigned int burst) : d_rate(rate), d_burst(burst), d_tokens(burst) | |
283 | { | |
284 | d_passthrough=false; | |
285 | d_prev.start(); | |
286 | } | |
287 | ||
288 | unsigned int getRate() const | |
289 | { | |
290 | return d_passthrough? 0 : d_rate; | |
291 | } | |
292 | ||
293 | int getPassed() const | |
294 | { | |
295 | return d_passed; | |
296 | } | |
297 | int getBlocked() const | |
298 | { | |
299 | return d_blocked; | |
300 | } | |
301 | ||
ecbe9133 | 302 | bool check() const // this is not quite fair |
df111b53 | 303 | { |
304 | if(d_passthrough) | |
305 | return true; | |
306 | auto delta = d_prev.udiffAndSet(); | |
5c30ec69 | 307 | |
df111b53 | 308 | d_tokens += 1.0*d_rate * (delta/1000000.0); |
309 | ||
310 | if(d_tokens > d_burst) | |
311 | d_tokens = d_burst; | |
312 | ||
313 | bool ret=false; | |
314 | if(d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise | |
315 | ret=true; | |
316 | --d_tokens; | |
317 | d_passed++; | |
318 | } | |
319 | else | |
320 | d_blocked++; | |
321 | ||
5c30ec69 | 322 | return ret; |
df111b53 | 323 | } |
324 | private: | |
325 | bool d_passthrough{true}; | |
326 | unsigned int d_rate; | |
327 | unsigned int d_burst; | |
ecbe9133 | 328 | mutable double d_tokens; |
329 | mutable StopWatch d_prev; | |
330 | mutable unsigned int d_passed{0}; | |
331 | mutable unsigned int d_blocked{0}; | |
df111b53 | 332 | }; |
333 | ||
b5b93e0b RG |
334 | struct ClientState; |
335 | ||
df111b53 | 336 | struct IDState |
337 | { | |
acb8f5d5 | 338 | IDState() : origFD(-1), sentTime(true), delayMsec(0), tempFailureTTL(boost::none) { origDest.sin4.sin_family = 0;} |
5708a729 | 339 | IDState(const IDState& orig): origRemote(orig.origRemote), origDest(orig.origDest) |
df111b53 | 340 | { |
341 | origFD = orig.origFD; | |
342 | origID = orig.origID; | |
7b3865cd | 343 | delayMsec = orig.delayMsec; |
acb8f5d5 | 344 | tempFailureTTL = orig.tempFailureTTL; |
df111b53 | 345 | age.store(orig.age.load()); |
346 | } | |
347 | ||
2bf26975 | 348 | int origFD; // set to <0 to indicate this state is empty // 4 |
349 | ||
350 | ComboAddress origRemote; // 28 | |
549d63c9 | 351 | ComboAddress origDest; // 28 |
2bf26975 | 352 | StopWatch sentTime; // 16 |
353 | DNSName qname; // 80 | |
11e1e08b | 354 | #ifdef HAVE_DNSCRYPT |
43234e76 | 355 | std::shared_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; |
d8c19b98 RG |
356 | #endif |
357 | #ifdef HAVE_PROTOBUF | |
ec48a28d | 358 | boost::optional<boost::uuids::uuid> uniqueId; |
11e1e08b | 359 | #endif |
886e2cf2 | 360 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; |
a76b0d63 | 361 | std::shared_ptr<QTag> qTag{nullptr}; |
b5b93e0b | 362 | const ClientState* cs{nullptr}; |
886e2cf2 | 363 | uint32_t cacheKey; // 8 |
2bf26975 | 364 | std::atomic<uint16_t> age; // 4 |
365 | uint16_t qtype; // 2 | |
886e2cf2 | 366 | uint16_t qclass; // 2 |
2bf26975 | 367 | uint16_t origID; // 2 |
aeb36780 | 368 | uint16_t origFlags; // 2 |
7b3865cd | 369 | int delayMsec; |
acb8f5d5 | 370 | boost::optional<uint32_t> tempFailureTTL; |
ca404e94 | 371 | bool ednsAdded{false}; |
ff73f02b | 372 | bool ecsAdded{false}; |
886e2cf2 | 373 | bool skipCache{false}; |
7cea4e39 | 374 | bool destHarvested{false}; // if true, origDest holds the original dest addr, otherwise the listening addr |
df111b53 | 375 | }; |
376 | ||
786e4d8c RS |
377 | typedef std::unordered_map<string, unsigned int> QueryCountRecords; |
378 | typedef std::function<std::tuple<bool, string>(DNSQuestion dq)> QueryCountFilter; | |
379 | struct QueryCount { | |
380 | QueryCount() | |
381 | { | |
43234e76 | 382 | pthread_rwlock_init(&queryLock, nullptr); |
786e4d8c RS |
383 | } |
384 | QueryCountRecords records; | |
385 | QueryCountFilter filter; | |
386 | pthread_rwlock_t queryLock; | |
387 | bool enabled{false}; | |
388 | }; | |
389 | ||
390 | extern QueryCount g_qcount; | |
391 | ||
8a5d5053 | 392 | struct ClientState |
393 | { | |
f0e4dcba | 394 | std::set<int> cpus; |
8a5d5053 | 395 | ComboAddress local; |
11e1e08b | 396 | #ifdef HAVE_DNSCRYPT |
43234e76 | 397 | std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr}; |
11e1e08b | 398 | #endif |
a227f47d | 399 | shared_ptr<TLSFrontend> tlsFrontend; |
963bef8d | 400 | std::atomic<uint64_t> queries{0}; |
a36ce055 RG |
401 | int udpFD{-1}; |
402 | int tcpFD{-1}; | |
b5b93e0b | 403 | bool muted{false}; |
8429ad04 RG |
404 | |
405 | int getSocket() const | |
406 | { | |
407 | return udpFD != -1 ? udpFD : tcpFD; | |
408 | } | |
409 | ||
410 | #ifdef HAVE_EBPF | |
411 | shared_ptr<BPFFilter> d_filter; | |
412 | ||
413 | void detachFilter() | |
414 | { | |
415 | if (d_filter) { | |
416 | d_filter->removeSocket(getSocket()); | |
417 | d_filter = nullptr; | |
418 | } | |
419 | } | |
420 | ||
421 | void attachFilter(shared_ptr<BPFFilter> bpf) | |
422 | { | |
423 | detachFilter(); | |
424 | ||
425 | bpf->addSocket(getSocket()); | |
426 | d_filter = bpf; | |
427 | } | |
428 | #endif /* HAVE_EBPF */ | |
8a5d5053 | 429 | }; |
430 | ||
431 | class TCPClientCollection { | |
432 | std::vector<int> d_tcpclientthreads; | |
ded1985a | 433 | std::atomic<uint64_t> d_numthreads{0}; |
a9bf3ec4 | 434 | std::atomic<uint64_t> d_pos{0}; |
ded1985a | 435 | std::atomic<uint64_t> d_queued{0}; |
6c1ca990 | 436 | uint64_t d_maxthreads{0}; |
ded1985a | 437 | std::mutex d_mutex; |
edbda1ad RG |
438 | int d_singlePipe[2]; |
439 | bool d_useSinglePipe; | |
ded1985a | 440 | public: |
8a5d5053 | 441 | |
b79e4996 RG |
442 | TCPClientCollection(size_t maxThreads, bool useSinglePipe=false): d_maxthreads(maxThreads), d_singlePipe{-1,-1}, d_useSinglePipe(useSinglePipe) |
443 | ||
8a5d5053 | 444 | { |
a9bf3ec4 | 445 | d_tcpclientthreads.reserve(maxThreads); |
edbda1ad RG |
446 | |
447 | if (d_useSinglePipe) { | |
448 | if (pipe(d_singlePipe) < 0) { | |
449 | throw std::runtime_error("Error creating the TCP single communication pipe: " + string(strerror(errno))); | |
450 | } | |
451 | if (!setNonBlocking(d_singlePipe[1])) { | |
452 | int err = errno; | |
453 | close(d_singlePipe[0]); | |
454 | close(d_singlePipe[1]); | |
455 | throw std::runtime_error("Error setting the TCP single communication pipe non-blocking: " + string(strerror(err))); | |
456 | } | |
457 | } | |
8a5d5053 | 458 | } |
a9bf3ec4 | 459 | int getThread() |
8a5d5053 | 460 | { |
6c1ca990 | 461 | uint64_t pos = d_pos++; |
8a5d5053 | 462 | ++d_queued; |
463 | return d_tcpclientthreads[pos % d_numthreads]; | |
464 | } | |
ded1985a RG |
465 | bool hasReachedMaxThreads() const |
466 | { | |
467 | return d_numthreads >= d_maxthreads; | |
468 | } | |
469 | uint64_t getThreadsCount() const | |
470 | { | |
471 | return d_numthreads; | |
472 | } | |
473 | uint64_t getQueuedCount() const | |
474 | { | |
475 | return d_queued; | |
476 | } | |
477 | void decrementQueuedCount() | |
478 | { | |
479 | --d_queued; | |
480 | } | |
8a5d5053 | 481 | void addTCPClientThread(); |
482 | }; | |
483 | ||
a9bf3ec4 | 484 | extern std::shared_ptr<TCPClientCollection> g_tcpclientthreads; |
8a5d5053 | 485 | |
df111b53 | 486 | struct DownstreamState |
487 | { | |
98650fde RG |
488 | typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t; |
489 | ||
150105a2 RG |
490 | DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf, size_t numberOfSockets); |
491 | DownstreamState(const ComboAddress& remote_): DownstreamState(remote_, ComboAddress(), 0, 1) {} | |
6a62c0e3 RG |
492 | ~DownstreamState() |
493 | { | |
5bdbb83d | 494 | for (auto& fd : sockets) { |
150105a2 RG |
495 | if (fd >= 0) { |
496 | close(fd); | |
497 | fd = -1; | |
498 | } | |
499 | } | |
6a62c0e3 | 500 | } |
df111b53 | 501 | |
5bdbb83d RG |
502 | std::vector<int> sockets; |
503 | std::mutex socketsLock; | |
504 | std::unique_ptr<FDMultiplexer> mplexer{nullptr}; | |
df111b53 | 505 | std::thread tid; |
506 | ComboAddress remote; | |
507 | QPSLimiter qps; | |
508 | vector<IDState> idStates; | |
fbe2a2e0 | 509 | ComboAddress sourceAddr; |
98650fde | 510 | checkfunc_t checkFunction; |
fbe2a2e0 RG |
511 | DNSName checkName{"a.root-servers.net."}; |
512 | QType checkType{QType::A}; | |
de9f7157 | 513 | uint16_t checkClass{QClass::IN}; |
df111b53 | 514 | std::atomic<uint64_t> idOffset{0}; |
515 | std::atomic<uint64_t> sendErrors{0}; | |
516 | std::atomic<uint64_t> outstanding{0}; | |
517 | std::atomic<uint64_t> reuseds{0}; | |
518 | std::atomic<uint64_t> queries{0}; | |
519 | struct { | |
520 | std::atomic<uint64_t> sendErrors{0}; | |
521 | std::atomic<uint64_t> reuseds{0}; | |
522 | std::atomic<uint64_t> queries{0}; | |
523 | } prev; | |
18eeccc9 | 524 | string name; |
5bdbb83d | 525 | size_t socketsOffset{0}; |
df111b53 | 526 | double queryLoad{0.0}; |
527 | double dropRate{0.0}; | |
528 | double latencyUsec{0.0}; | |
529 | int order{1}; | |
530 | int weight{1}; | |
b40cffe7 | 531 | int tcpConnectTimeout{5}; |
3f6d07a4 RG |
532 | int tcpRecvTimeout{30}; |
533 | int tcpSendTimeout{30}; | |
fbe2a2e0 | 534 | unsigned int sourceItf{0}; |
3f6d07a4 | 535 | uint16_t retries{5}; |
c85f69a8 | 536 | uint16_t xpfRRCode{0}; |
9e87dcb8 RG |
537 | uint8_t currentCheckFailures{0}; |
538 | uint8_t maxCheckFailures{1}; | |
df111b53 | 539 | StopWatch sw; |
540 | set<string> pools; | |
541 | enum class Availability { Up, Down, Auto} availability{Availability::Auto}; | |
fbe2a2e0 | 542 | bool mustResolve{false}; |
df111b53 | 543 | bool upStatus{false}; |
ca404e94 | 544 | bool useECS{false}; |
21830638 | 545 | bool setCD{false}; |
7565f4e6 | 546 | std::atomic<bool> connected{false}; |
284d460c | 547 | bool tcpFastOpen{false}; |
5602f131 | 548 | bool ipBindAddrNoPort{true}; |
df111b53 | 549 | bool isUp() const |
550 | { | |
551 | if(availability == Availability::Down) | |
552 | return false; | |
553 | if(availability == Availability::Up) | |
554 | return true; | |
555 | return upStatus; | |
556 | } | |
557 | void setUp() { availability = Availability::Up; } | |
558 | void setDown() { availability = Availability::Down; } | |
559 | void setAuto() { availability = Availability::Auto; } | |
18eeccc9 RG |
560 | string getName() const { |
561 | if (name.empty()) { | |
562 | return remote.toStringWithPort(); | |
563 | } | |
564 | return name; | |
565 | } | |
a7940c06 | 566 | string getNameWithAddr() const { |
567 | if (name.empty()) { | |
568 | return remote.toStringWithPort(); | |
569 | } | |
570 | return name + " (" + remote.toStringWithPort()+ ")"; | |
571 | } | |
9f4eb5cc RG |
572 | string getStatus() const |
573 | { | |
574 | string status; | |
575 | if(availability == DownstreamState::Availability::Up) | |
576 | status = "UP"; | |
577 | else if(availability == DownstreamState::Availability::Down) | |
578 | status = "DOWN"; | |
579 | else | |
580 | status = (upStatus ? "up" : "down"); | |
581 | return status; | |
582 | } | |
b58f08e5 | 583 | void reconnect(); |
df111b53 | 584 | }; |
585 | using servers_t =vector<std::shared_ptr<DownstreamState>>; | |
df111b53 | 586 | |
da4e7813 | 587 | template <class T> using NumberedVector = std::vector<std::pair<unsigned int, T> >; |
588 | ||
589 | void* responderThread(std::shared_ptr<DownstreamState> state); | |
590 | extern std::mutex g_luamutex; | |
591 | extern LuaContext g_lua; | |
592 | extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex | |
593 | ||
0940e4eb | 594 | class DNSRule |
595 | { | |
596 | public: | |
205f2081 RG |
597 | virtual ~DNSRule () |
598 | { | |
599 | } | |
497a6e3a | 600 | virtual bool matches(const DNSQuestion* dq) const =0; |
0940e4eb | 601 | virtual string toString() const = 0; |
602 | mutable std::atomic<uint64_t> d_matches{0}; | |
603 | }; | |
604 | ||
da4e7813 | 605 | using NumberedServerVector = NumberedVector<shared_ptr<DownstreamState>>; |
497a6e3a | 606 | typedef std::function<shared_ptr<DownstreamState>(const NumberedServerVector& servers, const DNSQuestion*)> policyfunc_t; |
df111b53 | 607 | |
608 | struct ServerPolicy | |
609 | { | |
610 | string name; | |
70a57b05 | 611 | policyfunc_t policy; |
a1b1a29d | 612 | bool isLua; |
df111b53 | 613 | }; |
614 | ||
886e2cf2 RG |
615 | struct ServerPool |
616 | { | |
a1b1a29d RG |
617 | ServerPool() |
618 | { | |
619 | pthread_rwlock_init(&d_lock, nullptr); | |
620 | } | |
621 | ||
886e2cf2 RG |
622 | const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; }; |
623 | ||
7e687744 RG |
624 | bool getECS() const |
625 | { | |
626 | return d_useECS; | |
627 | } | |
628 | ||
629 | void setECS(bool useECS) | |
630 | { | |
631 | d_useECS = useECS; | |
632 | } | |
633 | ||
886e2cf2 | 634 | std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; |
b9f8a6c8 | 635 | std::shared_ptr<ServerPolicy> policy{nullptr}; |
5c30ec69 | 636 | |
a1b1a29d RG |
637 | size_t countServers(bool upOnly) |
638 | { | |
639 | size_t count = 0; | |
640 | ReadLock rl(&d_lock); | |
641 | for (const auto& server : d_servers) { | |
642 | if (!upOnly || std::get<1>(server)->isUp() ) { | |
643 | count++; | |
216f5b41 LM |
644 | }; |
645 | }; | |
a1b1a29d RG |
646 | return count; |
647 | } | |
648 | ||
649 | NumberedVector<shared_ptr<DownstreamState>> getServers() | |
650 | { | |
651 | NumberedVector<shared_ptr<DownstreamState>> result; | |
652 | { | |
653 | ReadLock rl(&d_lock); | |
654 | result = d_servers; | |
655 | } | |
656 | return result; | |
657 | } | |
658 | ||
659 | void addServer(shared_ptr<DownstreamState>& server) | |
660 | { | |
661 | WriteLock wl(&d_lock); | |
662 | unsigned int count = (unsigned int) d_servers.size(); | |
663 | d_servers.push_back(make_pair(++count, server)); | |
664 | /* we need to reorder based on the server 'order' */ | |
665 | 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) { | |
666 | return a.second->order < b.second->order; | |
667 | }); | |
668 | /* and now we need to renumber for Lua (custom policies) */ | |
669 | size_t idx = 1; | |
670 | for (auto& serv : d_servers) { | |
671 | serv.first = idx++; | |
672 | } | |
673 | } | |
674 | ||
675 | void removeServer(shared_ptr<DownstreamState>& server) | |
676 | { | |
677 | WriteLock wl(&d_lock); | |
678 | size_t idx = 1; | |
679 | bool found = false; | |
680 | for (auto it = d_servers.begin(); it != d_servers.end();) { | |
681 | if (found) { | |
682 | /* we need to renumber the servers placed | |
683 | after the removed one, for Lua (custom policies) */ | |
684 | it->first = idx++; | |
685 | it++; | |
686 | } | |
687 | else if (it->second == server) { | |
688 | it = d_servers.erase(it); | |
689 | found = true; | |
690 | } else { | |
691 | idx++; | |
692 | it++; | |
693 | } | |
694 | } | |
695 | } | |
696 | ||
697 | private: | |
698 | NumberedVector<shared_ptr<DownstreamState>> d_servers; | |
699 | pthread_rwlock_t d_lock; | |
7e687744 | 700 | bool d_useECS{false}; |
886e2cf2 RG |
701 | }; |
702 | using pools_t=map<std::string,std::shared_ptr<ServerPool>>; | |
742c079a | 703 | void setPoolPolicy(pools_t& pools, const string& poolName, std::shared_ptr<ServerPolicy> policy); |
886e2cf2 RG |
704 | void addServerToPool(pools_t& pools, const string& poolName, std::shared_ptr<DownstreamState> server); |
705 | void removeServerFromPool(pools_t& pools, const string& poolName, std::shared_ptr<DownstreamState> server); | |
706 | ||
42fae326 | 707 | struct CarbonConfig |
708 | { | |
d617b22c | 709 | ComboAddress server; |
42fae326 | 710 | std::string ourname; |
d617b22c | 711 | unsigned int interval; |
42fae326 | 712 | }; |
713 | ||
ca404e94 RG |
714 | enum ednsHeaderFlags { |
715 | EDNS_HEADER_FLAG_NONE = 0, | |
716 | EDNS_HEADER_FLAG_DO = 32768 | |
717 | }; | |
718 | ||
4d5959e6 RG |
719 | struct DNSDistRuleAction |
720 | { | |
721 | std::shared_ptr<DNSRule> d_rule; | |
722 | std::shared_ptr<DNSAction> d_action; | |
723 | boost::uuids::uuid d_id; | |
724 | }; | |
725 | ||
726 | struct DNSDistResponseRuleAction | |
727 | { | |
728 | std::shared_ptr<DNSRule> d_rule; | |
729 | std::shared_ptr<DNSResponseAction> d_action; | |
730 | boost::uuids::uuid d_id; | |
731 | }; | |
732 | ||
71c94675 | 733 | extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT; |
dd46e5e3 | 734 | extern DNSAction::Action g_dynBlockAction; |
71c94675 | 735 | |
d617b22c | 736 | extern GlobalStateHolder<vector<CarbonConfig> > g_carbon; |
ecbe9133 | 737 | extern GlobalStateHolder<ServerPolicy> g_policy; |
738 | extern GlobalStateHolder<servers_t> g_dstates; | |
886e2cf2 | 739 | extern GlobalStateHolder<pools_t> g_pools; |
4d5959e6 RG |
740 | extern GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions; |
741 | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions; | |
742 | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions; | |
2d4783a8 | 743 | extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredresprulactions; |
638184e9 | 744 | extern GlobalStateHolder<NetmaskGroup> g_ACL; |
2e72cc0e | 745 | |
ecbe9133 | 746 | extern ComboAddress g_serverControl; // not changed during runtime |
747 | ||
f0e4dcba | 748 | extern std::vector<std::tuple<ComboAddress, bool, bool, int, std::string, std::set<int>>> g_locals; // not changed at runtime (we hope XXX) |
a227f47d | 749 | extern std::vector<shared_ptr<TLSFrontend>> g_tlslocals; |
963bef8d | 750 | extern vector<ClientState*> g_frontends; |
6ad8b29a | 751 | extern bool g_truncateTC; |
b29edbee | 752 | extern bool g_fixupCase; |
3f6d07a4 RG |
753 | extern int g_tcpRecvTimeout; |
754 | extern int g_tcpSendTimeout; | |
e0b5e49d | 755 | extern int g_udpTimeout; |
e41f8165 RG |
756 | extern uint16_t g_maxOutstanding; |
757 | extern std::atomic<bool> g_configurationDone; | |
6c1ca990 RG |
758 | extern uint64_t g_maxTCPClientThreads; |
759 | extern uint64_t g_maxTCPQueuedConnections; | |
9396d955 RG |
760 | extern size_t g_maxTCPQueriesPerConn; |
761 | extern size_t g_maxTCPConnectionDuration; | |
762 | extern size_t g_maxTCPConnectionsPerClient; | |
886e2cf2 | 763 | extern std::atomic<uint16_t> g_cacheCleaningDelay; |
f65ea0c2 | 764 | extern std::atomic<uint16_t> g_cacheCleaningPercentage; |
9e87dcb8 | 765 | extern bool g_verboseHealthChecks; |
1ea747c0 | 766 | extern uint32_t g_staleCacheEntriesTTL; |
56d68fad RG |
767 | extern bool g_apiReadWrite; |
768 | extern std::string g_apiConfigDirectory; | |
26a3cdb7 | 769 | extern bool g_servFailOnNoPolicy; |
36e763fa | 770 | extern uint32_t g_hashperturb; |
edbda1ad | 771 | extern bool g_useTCPSinglePipe; |
840ed663 | 772 | extern std::atomic<uint16_t> g_downstreamTCPCleanupInterval; |
0beaa5c8 | 773 | extern size_t g_udpVectorSize; |
ca404e94 | 774 | |
87b515ed RG |
775 | #ifdef HAVE_EBPF |
776 | extern shared_ptr<BPFFilter> g_defaultBPFFilter; | |
8429ad04 | 777 | extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters; |
87b515ed RG |
778 | #endif /* HAVE_EBPF */ |
779 | ||
0beaa5c8 RG |
780 | struct LocalHolders |
781 | { | |
2d4783a8 | 782 | 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 |
783 | { |
784 | } | |
785 | ||
786 | LocalStateHolder<NetmaskGroup> acl; | |
787 | LocalStateHolder<ServerPolicy> policy; | |
4d5959e6 RG |
788 | LocalStateHolder<vector<DNSDistRuleAction> > rulactions; |
789 | LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRulactions; | |
2d4783a8 | 790 | LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRulactions; |
0beaa5c8 RG |
791 | LocalStateHolder<servers_t> servers; |
792 | LocalStateHolder<NetmaskTree<DynBlock> > dynNMGBlock; | |
793 | LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock; | |
794 | LocalStateHolder<pools_t> pools; | |
795 | }; | |
796 | ||
ecbe9133 | 797 | struct dnsheader; |
798 | ||
799 | void controlThread(int fd, ComboAddress local); | |
839f3021 | 800 | vector<std::function<void(void)>> setupLua(bool client, const std::string& config); |
886e2cf2 RG |
801 | std::shared_ptr<ServerPool> getPool(const pools_t& pools, const std::string& poolName); |
802 | std::shared_ptr<ServerPool> createPoolIfNotExists(pools_t& pools, const string& poolName); | |
a1b1a29d | 803 | NumberedServerVector getDownstreamCandidates(const pools_t& pools, const std::string& poolName); |
da4e7813 | 804 | |
497a6e3a | 805 | std::shared_ptr<DownstreamState> firstAvailable(const NumberedServerVector& servers, const DNSQuestion* dq); |
ecbe9133 | 806 | |
497a6e3a RG |
807 | std::shared_ptr<DownstreamState> leastOutstanding(const NumberedServerVector& servers, const DNSQuestion* dq); |
808 | std::shared_ptr<DownstreamState> wrandom(const NumberedServerVector& servers, const DNSQuestion* dq); | |
809 | std::shared_ptr<DownstreamState> whashed(const NumberedServerVector& servers, const DNSQuestion* dq); | |
810 | std::shared_ptr<DownstreamState> roundrobin(const NumberedServerVector& servers, const DNSQuestion* dq); | |
520eb5a0 | 811 | int getEDNSZ(const char* packet, unsigned int len); |
ca404e94 | 812 | uint16_t getEDNSOptionCode(const char * packet, size_t len); |
002decab | 813 | void dnsdistWebserverThread(int sock, const ComboAddress& local, const string& password, const string& apiKey, const boost::optional<std::map<std::string, std::string> >&); |
6885d4bf | 814 | bool getMsgLen32(int fd, uint32_t* len); |
815 | bool putMsgLen32(int fd, uint32_t len); | |
d8d85a30 | 816 | void* tcpAcceptorThread(void* p); |
80a216c9 | 817 | |
f758857a | 818 | void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects |
819 | void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls | |
820 | bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect | |
821 | void resetLuaSideEffect(); // reset to indeterminate state | |
11e1e08b | 822 | |
fcffc585 | 823 | bool responseContentMatches(const char* response, const uint16_t responseLen, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& remote); |
0beaa5c8 | 824 | bool processQuery(LocalHolders& holders, DNSQuestion& dq, string& poolname, int* delayMsec, const struct timespec& now); |
4d5959e6 | 825 | bool processResponse(LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRulactions, DNSResponse& dr, int* delayMsec); |
ff73f02b | 826 | bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom); |
0f72fd5c | 827 | void restoreFlags(struct dnsheader* dh, uint16_t origFlags); |
0beaa5c8 | 828 | bool checkQueryHeaders(const struct dnsheader* dh); |
fcffc585 | 829 | |
11e1e08b | 830 | #ifdef HAVE_DNSCRYPT |
43234e76 | 831 | extern std::vector<std::tuple<ComboAddress, std::shared_ptr<DNSCryptContext>, bool, int, std::string, std::set<int> > > g_dnsCryptLocals; |
11e1e08b | 832 | |
43234e76 RG |
833 | bool encryptResponse(char* response, uint16_t* responseLen, size_t responseSize, bool tcp, std::shared_ptr<DNSCryptQuery> dnsCryptQuery, dnsheader** dh, dnsheader* dhCopy); |
834 | 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); | |
11e1e08b | 835 | #endif |
9f4eb5cc | 836 | |
18f707fa | 837 | bool addXPF(DNSQuestion& dq, uint16_t optionCode); |
5cc8371b | 838 | |
9f4eb5cc RG |
839 | #include "dnsdist-snmp.hh" |
840 | ||
841 | extern bool g_snmpEnabled; | |
842 | extern bool g_snmpTrapsEnabled; | |
843 | extern DNSDistSNMPAgent* g_snmpAgent; |