]>
Commit | Line | Data |
---|---|---|
dc2fd93a RG |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
22 | #pragma once | |
23 | ||
23adffab RG |
24 | #include <unordered_set> |
25 | ||
dc2fd93a | 26 | #include "dolog.hh" |
03b00917 | 27 | #include "dnsdist-rings.hh" |
23adffab | 28 | #include "statnode.hh" |
dc2fd93a | 29 | |
861ce85b RG |
30 | #include "dnsdist-lua-inspection-ffi.hh" |
31 | ||
32 | // dnsdist_ffi_stat_node_t is a lightuserdata | |
33 | template<> | |
34 | struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> { | |
35 | static const int minSize = 1; | |
36 | static const int maxSize = 1; | |
37 | ||
38 | static PushedObject push(lua_State* state, dnsdist_ffi_stat_node_t* ptr) noexcept { | |
39 | lua_pushlightuserdata(state, ptr); | |
40 | return PushedObject{state, 1}; | |
41 | } | |
42 | }; | |
43 | ||
44 | typedef std::function<bool(dnsdist_ffi_stat_node_t*)> dnsdist_ffi_stat_node_visitor_t; | |
45 | ||
46 | struct dnsdist_ffi_stat_node_t | |
47 | { | |
48 | dnsdist_ffi_stat_node_t(const StatNode& node_, const StatNode::Stat& self_, const StatNode::Stat& children_): node(node_), self(self_), children(children_) | |
49 | { | |
50 | } | |
51 | ||
52 | const StatNode& node; | |
53 | const StatNode::Stat& self; | |
54 | const StatNode::Stat& children; | |
55 | }; | |
56 | ||
dc2fd93a RG |
57 | class DynBlockRulesGroup |
58 | { | |
59 | private: | |
60 | ||
61 | struct Counts | |
62 | { | |
63 | std::map<uint8_t, uint64_t> d_rcodeCounts; | |
64 | std::map<uint16_t, uint64_t> d_qtypeCounts; | |
65 | uint64_t queries{0}; | |
66 | uint64_t respBytes{0}; | |
67 | }; | |
68 | ||
69 | struct DynBlockRule | |
70 | { | |
71 | DynBlockRule(): d_enabled(false) | |
72 | { | |
73 | } | |
74 | ||
1d3ba133 | 75 | DynBlockRule(const std::string& blockReason, unsigned int blockDuration, unsigned int rate, unsigned int warningRate, unsigned int seconds, DNSAction::Action action): d_blockReason(blockReason), d_blockDuration(blockDuration), d_rate(rate), d_warningRate(warningRate), d_seconds(seconds), d_action(action), d_enabled(true) |
dc2fd93a RG |
76 | { |
77 | } | |
78 | ||
79 | bool matches(const struct timespec& when) | |
80 | { | |
81 | if (!d_enabled) { | |
82 | return false; | |
83 | } | |
84 | ||
85 | if (d_seconds && when < d_cutOff) { | |
86 | return false; | |
87 | } | |
88 | ||
89 | if (when < d_minTime) { | |
90 | d_minTime = when; | |
91 | } | |
92 | ||
93 | return true; | |
94 | } | |
95 | ||
96 | bool rateExceeded(unsigned int count, const struct timespec& now) const | |
97 | { | |
98 | if (!d_enabled) { | |
99 | return false; | |
100 | } | |
101 | ||
102 | double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime); | |
103 | double limit = delta * d_rate; | |
104 | return (count > limit); | |
105 | } | |
106 | ||
1d3ba133 RG |
107 | bool warningRateExceeded(unsigned int count, const struct timespec& now) const |
108 | { | |
15e60f9f | 109 | if (d_warningRate == 0) { |
1d3ba133 RG |
110 | return false; |
111 | } | |
112 | ||
113 | double delta = d_seconds ? d_seconds : DiffTime(now, d_minTime); | |
114 | double limit = delta * d_warningRate; | |
115 | return (count > limit); | |
116 | } | |
117 | ||
dc2fd93a RG |
118 | bool isEnabled() const |
119 | { | |
15e60f9f | 120 | return d_enabled; |
dc2fd93a RG |
121 | } |
122 | ||
b718792f RG |
123 | std::string toString() const |
124 | { | |
125 | if (!isEnabled()) { | |
126 | return ""; | |
127 | } | |
128 | ||
129 | std::stringstream result; | |
130 | if (d_action != DNSAction::Action::None) { | |
131 | result << DNSAction::typeToString(d_action) << " "; | |
132 | } | |
133 | else { | |
134 | result << "Apply the global DynBlock action "; | |
135 | } | |
136 | result << "for " << std::to_string(d_blockDuration) << " seconds when over " << std::to_string(d_rate) << " during the last " << d_seconds << " seconds, reason: '" << d_blockReason << "'"; | |
137 | ||
138 | return result.str(); | |
139 | } | |
140 | ||
dc2fd93a RG |
141 | std::string d_blockReason; |
142 | struct timespec d_cutOff; | |
143 | struct timespec d_minTime; | |
144 | unsigned int d_blockDuration{0}; | |
145 | unsigned int d_rate{0}; | |
1d3ba133 | 146 | unsigned int d_warningRate{0}; |
dc2fd93a RG |
147 | unsigned int d_seconds{0}; |
148 | DNSAction::Action d_action{DNSAction::Action::None}; | |
149 | bool d_enabled{false}; | |
150 | }; | |
151 | ||
23adffab | 152 | typedef std::unordered_map<ComboAddress, Counts, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual> counts_t; |
dc2fd93a RG |
153 | |
154 | public: | |
155 | DynBlockRulesGroup() | |
156 | { | |
157 | } | |
158 | ||
1d3ba133 | 159 | void setQueryRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action) |
dc2fd93a | 160 | { |
1d3ba133 | 161 | d_queryRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); |
dc2fd93a RG |
162 | } |
163 | ||
1d3ba133 RG |
164 | /* rate is in bytes per second */ |
165 | void setResponseByteRate(unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action) | |
dc2fd93a | 166 | { |
1d3ba133 | 167 | d_respRateRule = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); |
dc2fd93a RG |
168 | } |
169 | ||
1d3ba133 | 170 | void setRCodeRate(uint8_t rcode, unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action) |
dc2fd93a RG |
171 | { |
172 | auto& entry = d_rcodeRules[rcode]; | |
1d3ba133 | 173 | entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); |
dc2fd93a RG |
174 | } |
175 | ||
1d3ba133 | 176 | void setQTypeRate(uint16_t qtype, unsigned int rate, unsigned int warningRate, unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action) |
dc2fd93a RG |
177 | { |
178 | auto& entry = d_qtypeRules[qtype]; | |
1d3ba133 | 179 | entry = DynBlockRule(reason, blockDuration, rate, warningRate, seconds, action); |
dc2fd93a RG |
180 | } |
181 | ||
23adffab RG |
182 | typedef std::function<bool(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> smtVisitor_t; |
183 | ||
184 | void setSuffixMatchRule(unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action, smtVisitor_t visitor) | |
185 | { | |
186 | d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action); | |
187 | d_smtVisitor = visitor; | |
188 | } | |
189 | ||
861ce85b RG |
190 | void setSuffixMatchRuleFFI(unsigned int seconds, std::string reason, unsigned int blockDuration, DNSAction::Action action, dnsdist_ffi_stat_node_visitor_t visitor) |
191 | { | |
192 | d_suffixMatchRule = DynBlockRule(reason, blockDuration, 0, 0, seconds, action); | |
193 | d_smtVisitorFFI = visitor; | |
194 | } | |
195 | ||
dc2fd93a | 196 | void apply() |
1d3ba133 RG |
197 | { |
198 | struct timespec now; | |
199 | gettime(&now); | |
200 | ||
201 | apply(now); | |
202 | } | |
203 | ||
204 | void apply(const struct timespec& now) | |
dc2fd93a RG |
205 | { |
206 | counts_t counts; | |
23adffab | 207 | StatNode statNodeRoot; |
dc2fd93a RG |
208 | |
209 | size_t entriesCount = 0; | |
210 | if (hasQueryRules()) { | |
cfe4b655 | 211 | entriesCount += g_rings.getNumberOfQueryEntries(); |
dc2fd93a RG |
212 | } |
213 | if (hasResponseRules()) { | |
cfe4b655 | 214 | entriesCount += g_rings.getNumberOfResponseEntries(); |
dc2fd93a RG |
215 | } |
216 | counts.reserve(entriesCount); | |
217 | ||
1d3ba133 | 218 | processQueryRules(counts, now); |
23adffab | 219 | processResponseRules(counts, statNodeRoot, now); |
dc2fd93a | 220 | |
23adffab | 221 | if (counts.empty() && statNodeRoot.empty()) { |
dc2fd93a RG |
222 | return; |
223 | } | |
224 | ||
225 | boost::optional<NetmaskTree<DynBlock> > blocks; | |
226 | bool updated = false; | |
dc2fd93a RG |
227 | |
228 | for (const auto& entry : counts) { | |
1d3ba133 RG |
229 | const auto& requestor = entry.first; |
230 | const auto& counters = entry.second; | |
231 | ||
232 | if (d_queryRateRule.warningRateExceeded(counters.queries, now)) { | |
233 | handleWarning(blocks, now, requestor, d_queryRateRule, updated); | |
234 | } | |
235 | ||
236 | if (d_queryRateRule.rateExceeded(counters.queries, now)) { | |
237 | addBlock(blocks, now, requestor, d_queryRateRule, updated); | |
dc2fd93a RG |
238 | continue; |
239 | } | |
240 | ||
1d3ba133 RG |
241 | if (d_respRateRule.warningRateExceeded(counters.respBytes, now)) { |
242 | handleWarning(blocks, now, requestor, d_respRateRule, updated); | |
243 | } | |
244 | ||
245 | if (d_respRateRule.rateExceeded(counters.respBytes, now)) { | |
246 | addBlock(blocks, now, requestor, d_respRateRule, updated); | |
dc2fd93a RG |
247 | continue; |
248 | } | |
249 | ||
1d3ba133 RG |
250 | for (const auto& pair : d_qtypeRules) { |
251 | const auto qtype = pair.first; | |
252 | ||
253 | const auto& typeIt = counters.d_qtypeCounts.find(qtype); | |
254 | if (typeIt != counters.d_qtypeCounts.cend()) { | |
255 | ||
256 | if (pair.second.warningRateExceeded(typeIt->second, now)) { | |
257 | handleWarning(blocks, now, requestor, pair.second, updated); | |
258 | } | |
259 | ||
260 | if (pair.second.rateExceeded(typeIt->second, now)) { | |
261 | addBlock(blocks, now, requestor, pair.second, updated); | |
262 | break; | |
263 | } | |
dc2fd93a RG |
264 | } |
265 | } | |
266 | ||
1d3ba133 RG |
267 | for (const auto& pair : d_rcodeRules) { |
268 | const auto rcode = pair.first; | |
269 | ||
270 | const auto& rcodeIt = counters.d_rcodeCounts.find(rcode); | |
271 | if (rcodeIt != counters.d_rcodeCounts.cend()) { | |
272 | if (pair.second.warningRateExceeded(rcodeIt->second, now)) { | |
273 | handleWarning(blocks, now, requestor, pair.second, updated); | |
274 | } | |
275 | ||
276 | if (pair.second.rateExceeded(rcodeIt->second, now)) { | |
277 | addBlock(blocks, now, requestor, pair.second, updated); | |
278 | break; | |
279 | } | |
dc2fd93a RG |
280 | } |
281 | } | |
282 | } | |
283 | ||
284 | if (updated && blocks) { | |
285 | g_dynblockNMG.setState(*blocks); | |
286 | } | |
23adffab RG |
287 | |
288 | if (!statNodeRoot.empty()) { | |
289 | StatNode::Stat node; | |
290 | std::unordered_set<DNSName> namesToBlock; | |
291 | statNodeRoot.visit([this,&namesToBlock](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) { | |
861ce85b RG |
292 | bool block = false; |
293 | ||
294 | if (d_smtVisitorFFI) { | |
295 | dnsdist_ffi_stat_node_t tmp(*node_, self, children); | |
296 | block = d_smtVisitorFFI(&tmp); | |
297 | } | |
298 | else { | |
299 | block = d_smtVisitor(*node_, self, children); | |
300 | } | |
301 | ||
23adffab RG |
302 | if (block) { |
303 | namesToBlock.insert(DNSName(node_->fullname)); | |
304 | } | |
305 | }, | |
306 | node); | |
307 | ||
308 | if (!namesToBlock.empty()) { | |
309 | updated = false; | |
310 | SuffixMatchTree<DynBlock> smtBlocks = g_dynblockSMT.getCopy(); | |
311 | for (const auto& name : namesToBlock) { | |
312 | addOrRefreshBlockSMT(smtBlocks, now, name, d_suffixMatchRule, updated); | |
313 | } | |
314 | if (updated) { | |
315 | g_dynblockSMT.setState(smtBlocks); | |
316 | } | |
317 | } | |
318 | } | |
dc2fd93a RG |
319 | } |
320 | ||
b718792f RG |
321 | void excludeRange(const Netmask& range) |
322 | { | |
323 | d_excludedSubnets.addMask(range); | |
324 | } | |
325 | ||
326 | void includeRange(const Netmask& range) | |
327 | { | |
328 | d_excludedSubnets.addMask(range, false); | |
329 | } | |
330 | ||
23adffab RG |
331 | void excludeDomain(const DNSName& domain) |
332 | { | |
333 | d_excludedDomains.add(domain); | |
334 | } | |
335 | ||
b718792f RG |
336 | std::string toString() const |
337 | { | |
338 | std::stringstream result; | |
339 | ||
340 | result << "Query rate rule: " << d_queryRateRule.toString() << std::endl; | |
341 | result << "Response rate rule: " << d_respRateRule.toString() << std::endl; | |
23adffab | 342 | result << "SuffixMatch rule: " << d_suffixMatchRule.toString() << std::endl; |
b718792f RG |
343 | result << "RCode rules: " << std::endl; |
344 | for (const auto& rule : d_rcodeRules) { | |
345 | result << "- " << RCode::to_s(rule.first) << ": " << rule.second.toString() << std::endl; | |
346 | } | |
347 | result << "QType rules: " << std::endl; | |
348 | for (const auto& rule : d_qtypeRules) { | |
349 | result << "- " << QType(rule.first).getName() << ": " << rule.second.toString() << std::endl; | |
350 | } | |
351 | result << "Excluded Subnets: " << d_excludedSubnets.toString() << std::endl; | |
23adffab | 352 | result << "Excluded Domains: " << d_excludedDomains.toString() << std::endl; |
b718792f RG |
353 | |
354 | return result.str(); | |
355 | } | |
356 | ||
1d3ba133 RG |
357 | void setQuiet(bool quiet) |
358 | { | |
359 | d_beQuiet = quiet; | |
360 | } | |
361 | ||
dc2fd93a RG |
362 | private: |
363 | bool checkIfQueryTypeMatches(const Rings::Query& query) | |
364 | { | |
365 | auto rule = d_qtypeRules.find(query.qtype); | |
366 | if (rule == d_qtypeRules.end()) { | |
367 | return false; | |
368 | } | |
369 | ||
370 | return rule->second.matches(query.when); | |
371 | } | |
372 | ||
373 | bool checkIfResponseCodeMatches(const Rings::Response& response) | |
374 | { | |
375 | auto rule = d_rcodeRules.find(response.dh.rcode); | |
376 | if (rule == d_rcodeRules.end()) { | |
377 | return false; | |
378 | } | |
379 | ||
380 | return rule->second.matches(response.when); | |
381 | } | |
382 | ||
1d3ba133 | 383 | void addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock> >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated, bool warning) |
dc2fd93a | 384 | { |
b718792f RG |
385 | if (d_excludedSubnets.match(requestor)) { |
386 | /* do not add a block for excluded subnets */ | |
387 | return; | |
388 | } | |
389 | ||
dc2fd93a RG |
390 | if (!blocks) { |
391 | blocks = g_dynblockNMG.getCopy(); | |
392 | } | |
393 | struct timespec until = now; | |
e4dfb2eb | 394 | until.tv_sec += rule.d_blockDuration; |
dc2fd93a RG |
395 | unsigned int count = 0; |
396 | const auto& got = blocks->lookup(Netmask(requestor)); | |
397 | bool expired = false; | |
1d3ba133 RG |
398 | bool wasWarning = false; |
399 | ||
dc2fd93a | 400 | if (got) { |
1d3ba133 RG |
401 | if (warning && !got->second.warning) { |
402 | /* we have an existing entry which is not a warning, | |
403 | don't override it */ | |
dc2fd93a RG |
404 | return; |
405 | } | |
1d3ba133 RG |
406 | else if (!warning && got->second.warning) { |
407 | wasWarning = true; | |
408 | } | |
409 | else { | |
410 | if (until < got->second.until) { | |
411 | // had a longer policy | |
412 | return; | |
413 | } | |
414 | } | |
dc2fd93a RG |
415 | |
416 | if (now < got->second.until) { | |
417 | // only inherit count on fresh query we are extending | |
418 | count = got->second.blocks; | |
419 | } | |
420 | else { | |
421 | expired = true; | |
422 | } | |
423 | } | |
424 | ||
1d3ba133 | 425 | DynBlock db{rule.d_blockReason, until, DNSName(), warning ? DNSAction::Action::NoOp : rule.d_action}; |
dc2fd93a | 426 | db.blocks = count; |
1d3ba133 RG |
427 | db.warning = warning; |
428 | if (!d_beQuiet && (!got || expired || wasWarning)) { | |
429 | warnlog("Inserting %sdynamic block for %s for %d seconds: %s", warning ? "(warning) " :"", requestor.toString(), rule.d_blockDuration, rule.d_blockReason); | |
dc2fd93a RG |
430 | } |
431 | blocks->insert(Netmask(requestor)).second = db; | |
432 | updated = true; | |
433 | } | |
434 | ||
23adffab RG |
435 | void addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated) |
436 | { | |
437 | if (d_excludedDomains.check(name)) { | |
438 | /* do not add a block for excluded domains */ | |
439 | return; | |
440 | } | |
441 | ||
442 | struct timespec until = now; | |
443 | until.tv_sec += rule.d_blockDuration; | |
444 | unsigned int count = 0; | |
445 | const auto& got = blocks.lookup(name); | |
446 | bool expired = false; | |
447 | ||
448 | if (got) { | |
449 | if (until < got->until) { | |
450 | // had a longer policy | |
451 | return; | |
452 | } | |
453 | ||
454 | if (now < got->until) { | |
455 | // only inherit count on fresh query we are extending | |
456 | count = got->blocks; | |
457 | } | |
458 | else { | |
459 | expired = true; | |
460 | } | |
461 | } | |
462 | ||
463 | DynBlock db{rule.d_blockReason, until, name, rule.d_action}; | |
464 | db.blocks = count; | |
465 | ||
466 | if (!d_beQuiet && (!got || expired)) { | |
467 | warnlog("Inserting dynamic block for %s for %d seconds: %s", name, rule.d_blockDuration, rule.d_blockReason); | |
468 | } | |
469 | blocks.add(name, db); | |
470 | updated = true; | |
471 | } | |
472 | ||
1d3ba133 RG |
473 | void addBlock(boost::optional<NetmaskTree<DynBlock> >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated) |
474 | { | |
475 | addOrRefreshBlock(blocks, now, requestor, rule, updated, false); | |
476 | } | |
477 | ||
478 | void handleWarning(boost::optional<NetmaskTree<DynBlock> >& blocks, const struct timespec& now, const ComboAddress& requestor, const DynBlockRule& rule, bool& updated) | |
479 | { | |
480 | addOrRefreshBlock(blocks, now, requestor, rule, updated, true); | |
481 | } | |
482 | ||
dc2fd93a RG |
483 | bool hasQueryRules() const |
484 | { | |
485 | return d_queryRateRule.isEnabled() || !d_qtypeRules.empty(); | |
486 | } | |
487 | ||
488 | bool hasResponseRules() const | |
489 | { | |
490 | return d_respRateRule.isEnabled() || !d_rcodeRules.empty(); | |
491 | } | |
492 | ||
23adffab RG |
493 | bool hasSuffixMatchRules() const |
494 | { | |
495 | return d_suffixMatchRule.isEnabled(); | |
496 | } | |
497 | ||
dc2fd93a RG |
498 | bool hasRules() const |
499 | { | |
500 | return hasQueryRules() || hasResponseRules(); | |
501 | } | |
502 | ||
1d3ba133 | 503 | void processQueryRules(counts_t& counts, const struct timespec& now) |
dc2fd93a RG |
504 | { |
505 | if (!hasQueryRules()) { | |
506 | return; | |
507 | } | |
508 | ||
dc2fd93a RG |
509 | d_queryRateRule.d_cutOff = d_queryRateRule.d_minTime = now; |
510 | d_queryRateRule.d_cutOff.tv_sec -= d_queryRateRule.d_seconds; | |
511 | ||
512 | for (auto& rule : d_qtypeRules) { | |
513 | rule.second.d_cutOff = rule.second.d_minTime = now; | |
514 | rule.second.d_cutOff.tv_sec -= rule.second.d_seconds; | |
515 | } | |
516 | ||
03b00917 RG |
517 | for (const auto& shard : g_rings.d_shards) { |
518 | std::lock_guard<std::mutex> rl(shard->queryLock); | |
519 | for(const auto& c : shard->queryRing) { | |
dc2fd93a RG |
520 | if (now < c.when) { |
521 | continue; | |
522 | } | |
523 | ||
524 | bool qRateMatches = d_queryRateRule.matches(c.when); | |
525 | bool typeRuleMatches = checkIfQueryTypeMatches(c); | |
526 | ||
527 | if (qRateMatches || typeRuleMatches) { | |
528 | auto& entry = counts[c.requestor]; | |
529 | if (qRateMatches) { | |
530 | entry.queries++; | |
531 | } | |
532 | if (typeRuleMatches) { | |
533 | entry.d_qtypeCounts[c.qtype]++; | |
534 | } | |
535 | } | |
536 | } | |
537 | } | |
538 | } | |
539 | ||
23adffab | 540 | void processResponseRules(counts_t& counts, StatNode& root, const struct timespec& now) |
dc2fd93a | 541 | { |
23adffab | 542 | if (!hasResponseRules() && !hasSuffixMatchRules()) { |
dc2fd93a RG |
543 | return; |
544 | } | |
545 | ||
dc2fd93a RG |
546 | d_respRateRule.d_cutOff = d_respRateRule.d_minTime = now; |
547 | d_respRateRule.d_cutOff.tv_sec -= d_respRateRule.d_seconds; | |
548 | ||
23adffab RG |
549 | d_suffixMatchRule.d_cutOff = d_suffixMatchRule.d_minTime = now; |
550 | d_suffixMatchRule.d_cutOff.tv_sec -= d_suffixMatchRule.d_seconds; | |
551 | ||
dc2fd93a RG |
552 | for (auto& rule : d_rcodeRules) { |
553 | rule.second.d_cutOff = rule.second.d_minTime = now; | |
554 | rule.second.d_cutOff.tv_sec -= rule.second.d_seconds; | |
555 | } | |
556 | ||
03b00917 RG |
557 | for (const auto& shard : g_rings.d_shards) { |
558 | std::lock_guard<std::mutex> rl(shard->respLock); | |
559 | for(const auto& c : shard->respRing) { | |
dc2fd93a RG |
560 | if (now < c.when) { |
561 | continue; | |
562 | } | |
563 | ||
564 | bool respRateMatches = d_respRateRule.matches(c.when); | |
23adffab | 565 | bool suffixMatchRuleMatches = d_suffixMatchRule.matches(c.when); |
dc2fd93a RG |
566 | bool rcodeRuleMatches = checkIfResponseCodeMatches(c); |
567 | ||
568 | if (respRateMatches || rcodeRuleMatches) { | |
569 | auto& entry = counts[c.requestor]; | |
570 | if (respRateMatches) { | |
571 | entry.respBytes += c.size; | |
572 | } | |
573 | if (rcodeRuleMatches) { | |
574 | entry.d_rcodeCounts[c.dh.rcode]++; | |
575 | } | |
576 | } | |
23adffab RG |
577 | |
578 | if (suffixMatchRuleMatches) { | |
579 | root.submit(c.name, c.dh.rcode, boost::none); | |
580 | } | |
dc2fd93a RG |
581 | } |
582 | } | |
583 | } | |
584 | ||
585 | std::map<uint8_t, DynBlockRule> d_rcodeRules; | |
586 | std::map<uint16_t, DynBlockRule> d_qtypeRules; | |
587 | DynBlockRule d_queryRateRule; | |
588 | DynBlockRule d_respRateRule; | |
23adffab | 589 | DynBlockRule d_suffixMatchRule; |
b718792f | 590 | NetmaskGroup d_excludedSubnets; |
23adffab RG |
591 | SuffixMatchNode d_excludedDomains; |
592 | smtVisitor_t d_smtVisitor; | |
861ce85b | 593 | dnsdist_ffi_stat_node_visitor_t d_smtVisitorFFI; |
1d3ba133 | 594 | bool d_beQuiet{false}; |
dc2fd93a | 595 | }; |