]>
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 | */ | |
0940e4eb | 22 | #include "dnsdist.hh" |
01520028 | 23 | #include "dnsdist-ecs.hh" |
0940e4eb | 24 | #include "dnsname.hh" |
808c5ef7 | 25 | #include "dolog.hh" |
5c3b5e7f | 26 | #include "ednsoptions.hh" |
f0acf608 | 27 | #include "lock.hh" |
ec469dd7 RG |
28 | #include "remote_logger.hh" |
29 | #include "dnsdist-protobuf.hh" | |
55baa1f2 | 30 | #include "dnsparser.hh" |
0940e4eb | 31 | |
6bba426c | 32 | class MaxQPSIPRule : public DNSRule |
33 | { | |
34 | public: | |
1b726acf | 35 | MaxQPSIPRule(unsigned int qps, unsigned int ipv4trunc=32, unsigned int ipv6trunc=64) : |
36 | d_qps(qps), d_ipv4trunc(ipv4trunc), d_ipv6trunc(ipv6trunc) | |
f0acf608 RG |
37 | { |
38 | pthread_rwlock_init(&d_lock, 0); | |
39 | } | |
6bba426c | 40 | |
497a6e3a | 41 | bool matches(const DNSQuestion* dq) const override |
6bba426c | 42 | { |
497a6e3a | 43 | ComboAddress zeroport(*dq->remote); |
6bba426c | 44 | zeroport.sin4.sin_port=0; |
1b726acf | 45 | zeroport.truncate(zeroport.sin4.sin_family == AF_INET ? d_ipv4trunc : d_ipv6trunc); |
f0acf608 RG |
46 | { |
47 | ReadLock r(&d_lock); | |
48 | const auto iter = d_limits.find(zeroport); | |
49 | if (iter != d_limits.end()) { | |
50 | return !iter->second.check(); | |
51 | } | |
52 | } | |
53 | { | |
54 | WriteLock w(&d_lock); | |
55 | auto iter = d_limits.find(zeroport); | |
56 | if(iter == d_limits.end()) { | |
57 | iter=d_limits.insert({zeroport,QPSLimiter(d_qps, d_qps)}).first; | |
58 | } | |
59 | return !iter->second.check(); | |
6bba426c | 60 | } |
6bba426c | 61 | } |
62 | ||
63 | string toString() const override | |
64 | { | |
1b726acf | 65 | return "IP (/"+std::to_string(d_ipv4trunc)+", /"+std::to_string(d_ipv6trunc)+") match for QPS over " + std::to_string(d_qps); |
6bba426c | 66 | } |
67 | ||
68 | ||
69 | private: | |
f0acf608 | 70 | mutable pthread_rwlock_t d_lock; |
6bba426c | 71 | mutable std::map<ComboAddress, QPSLimiter> d_limits; |
1b726acf | 72 | unsigned int d_qps, d_ipv4trunc, d_ipv6trunc; |
6bba426c | 73 | |
74 | }; | |
75 | ||
2332b03c | 76 | class MaxQPSRule : public DNSRule |
77 | { | |
78 | public: | |
79 | MaxQPSRule(unsigned int qps) | |
80 | : d_qps(qps, qps) | |
81 | {} | |
82 | ||
83 | MaxQPSRule(unsigned int qps, unsigned int burst) | |
84 | : d_qps(qps, burst) | |
85 | {} | |
86 | ||
87 | ||
497a6e3a | 88 | bool matches(const DNSQuestion* qd) const override |
2332b03c | 89 | { |
90 | return d_qps.check(); | |
91 | } | |
92 | ||
93 | string toString() const override | |
94 | { | |
95 | return "Max " + std::to_string(d_qps.getRate()) + " qps"; | |
96 | } | |
97 | ||
98 | ||
99 | private: | |
100 | mutable QPSLimiter d_qps; | |
101 | }; | |
102 | ||
103 | ||
6bba426c | 104 | |
0940e4eb | 105 | class NetmaskGroupRule : public DNSRule |
106 | { | |
107 | public: | |
108 | NetmaskGroupRule(const NetmaskGroup& nmg) : d_nmg(nmg) | |
109 | { | |
110 | ||
111 | } | |
497a6e3a | 112 | bool matches(const DNSQuestion* dq) const override |
0940e4eb | 113 | { |
497a6e3a | 114 | return d_nmg.match(*dq->remote); |
0940e4eb | 115 | } |
116 | ||
117 | string toString() const override | |
118 | { | |
a95d4eeb | 119 | return "Src: "+d_nmg.toString(); |
0940e4eb | 120 | } |
121 | private: | |
122 | NetmaskGroup d_nmg; | |
123 | }; | |
124 | ||
89cb6f9a | 125 | class AllRule : public DNSRule |
126 | { | |
127 | public: | |
128 | AllRule() {} | |
497a6e3a | 129 | bool matches(const DNSQuestion* dq) const override |
89cb6f9a | 130 | { |
131 | return true; | |
132 | } | |
133 | ||
134 | string toString() const override | |
135 | { | |
136 | return "All"; | |
137 | } | |
138 | ||
139 | }; | |
140 | ||
141 | ||
520eb5a0 | 142 | class DNSSECRule : public DNSRule |
143 | { | |
144 | public: | |
145 | DNSSECRule() | |
146 | { | |
147 | ||
148 | } | |
497a6e3a | 149 | bool matches(const DNSQuestion* dq) const override |
520eb5a0 | 150 | { |
497a6e3a | 151 | return dq->dh->cd || (getEDNSZ((const char*)dq->dh, dq->len) & EDNS_HEADER_FLAG_DO); // turns out dig sets ad by default.. |
520eb5a0 | 152 | } |
153 | ||
154 | string toString() const override | |
155 | { | |
156 | return "DNSSEC"; | |
157 | } | |
158 | }; | |
159 | ||
b7860997 | 160 | class AndRule : public DNSRule |
161 | { | |
162 | public: | |
163 | AndRule(const vector<pair<int, shared_ptr<DNSRule> > >& rules) | |
164 | { | |
165 | for(const auto& r : rules) | |
166 | d_rules.push_back(r.second); | |
167 | } | |
168 | ||
497a6e3a | 169 | bool matches(const DNSQuestion* dq) const override |
b7860997 | 170 | { |
171 | auto iter = d_rules.begin(); | |
172 | for(; iter != d_rules.end(); ++iter) | |
497a6e3a | 173 | if(!(*iter)->matches(dq)) |
b7860997 | 174 | break; |
175 | return iter == d_rules.end(); | |
176 | } | |
177 | ||
178 | string toString() const override | |
179 | { | |
180 | string ret; | |
181 | for(const auto& rule : d_rules) { | |
182 | if(!ret.empty()) | |
183 | ret+= " && "; | |
184 | ret += "("+ rule->toString()+")"; | |
185 | } | |
186 | return ret; | |
187 | } | |
188 | private: | |
189 | ||
190 | vector<std::shared_ptr<DNSRule> > d_rules; | |
191 | ||
192 | }; | |
193 | ||
194 | ||
e7a1029c RG |
195 | class OrRule : public DNSRule |
196 | { | |
197 | public: | |
198 | OrRule(const vector<pair<int, shared_ptr<DNSRule> > >& rules) | |
199 | { | |
200 | for(const auto& r : rules) | |
201 | d_rules.push_back(r.second); | |
202 | } | |
203 | ||
204 | bool matches(const DNSQuestion* dq) const override | |
205 | { | |
206 | auto iter = d_rules.begin(); | |
207 | for(; iter != d_rules.end(); ++iter) | |
208 | if((*iter)->matches(dq)) | |
209 | return true; | |
210 | return false; | |
211 | } | |
212 | ||
213 | string toString() const override | |
214 | { | |
215 | string ret; | |
216 | for(const auto& rule : d_rules) { | |
217 | if(!ret.empty()) | |
218 | ret+= " || "; | |
219 | ret += "("+ rule->toString()+")"; | |
220 | } | |
221 | return ret; | |
222 | } | |
223 | private: | |
224 | ||
225 | vector<std::shared_ptr<DNSRule> > d_rules; | |
226 | ||
227 | }; | |
228 | ||
229 | ||
6eecd4c2 | 230 | class RegexRule : public DNSRule |
231 | { | |
232 | public: | |
233 | RegexRule(const std::string& regex) : d_regex(regex), d_visual(regex) | |
234 | { | |
235 | ||
236 | } | |
497a6e3a | 237 | bool matches(const DNSQuestion* dq) const override |
6eecd4c2 | 238 | { |
497a6e3a | 239 | return d_regex.match(dq->qname->toStringNoDot()); |
6eecd4c2 | 240 | } |
241 | ||
242 | string toString() const override | |
243 | { | |
4ed8dfeb | 244 | return "Regex: "+d_visual; |
6eecd4c2 | 245 | } |
246 | private: | |
247 | Regex d_regex; | |
248 | string d_visual; | |
249 | }; | |
250 | ||
4ed8dfeb | 251 | #ifdef HAVE_RE2 |
252 | #include <re2/re2.h> | |
253 | class RE2Rule : public DNSRule | |
254 | { | |
255 | public: | |
256 | RE2Rule(const std::string& re2) : d_re2(re2, RE2::Latin1), d_visual(re2) | |
257 | { | |
258 | ||
259 | } | |
260 | bool matches(const DNSQuestion* dq) const override | |
261 | { | |
262 | return RE2::FullMatch(dq->qname->toStringNoDot(), d_re2); | |
263 | } | |
264 | ||
265 | string toString() const override | |
266 | { | |
267 | return "RE2 match: "+d_visual; | |
268 | } | |
269 | private: | |
270 | RE2 d_re2; | |
271 | string d_visual; | |
272 | }; | |
273 | #endif | |
274 | ||
520eb5a0 | 275 | |
0940e4eb | 276 | class SuffixMatchNodeRule : public DNSRule |
277 | { | |
278 | public: | |
291729f3 | 279 | SuffixMatchNodeRule(const SuffixMatchNode& smn, bool quiet=false) : d_smn(smn), d_quiet(quiet) |
0940e4eb | 280 | { |
281 | } | |
497a6e3a | 282 | bool matches(const DNSQuestion* dq) const override |
0940e4eb | 283 | { |
497a6e3a | 284 | return d_smn.check(*dq->qname); |
0940e4eb | 285 | } |
286 | string toString() const override | |
287 | { | |
291729f3 | 288 | if(d_quiet) |
289 | return "qname==in-set"; | |
290 | else | |
291 | return "qname=="+d_smn.toString(); | |
0940e4eb | 292 | } |
293 | private: | |
294 | SuffixMatchNode d_smn; | |
291729f3 | 295 | bool d_quiet; |
0940e4eb | 296 | }; |
297 | ||
298 | class QTypeRule : public DNSRule | |
299 | { | |
300 | public: | |
301 | QTypeRule(uint16_t qtype) : d_qtype(qtype) | |
302 | { | |
303 | } | |
497a6e3a | 304 | bool matches(const DNSQuestion* dq) const override |
0940e4eb | 305 | { |
497a6e3a | 306 | return d_qtype == dq->qtype; |
0940e4eb | 307 | } |
308 | string toString() const override | |
309 | { | |
310 | QType qt(d_qtype); | |
311 | return "qtype=="+qt.getName(); | |
312 | } | |
313 | private: | |
314 | uint16_t d_qtype; | |
315 | }; | |
316 | ||
3b069df2 | 317 | class QClassRule : public DNSRule |
318 | { | |
319 | public: | |
320 | QClassRule(uint16_t qclass) : d_qclass(qclass) | |
321 | { | |
322 | } | |
323 | bool matches(const DNSQuestion* dq) const override | |
324 | { | |
325 | return d_qclass == dq->qclass; | |
326 | } | |
327 | string toString() const override | |
328 | { | |
329 | return "qclass=="+std::to_string(d_qclass); | |
330 | } | |
331 | private: | |
332 | uint16_t d_qclass; | |
333 | }; | |
334 | ||
55baa1f2 RG |
335 | class OpcodeRule : public DNSRule |
336 | { | |
337 | public: | |
338 | OpcodeRule(uint8_t opcode) : d_opcode(opcode) | |
339 | { | |
340 | } | |
341 | bool matches(const DNSQuestion* dq) const override | |
342 | { | |
343 | return d_opcode == dq->dh->opcode; | |
344 | } | |
345 | string toString() const override | |
346 | { | |
408ede11 | 347 | return "opcode=="+std::to_string(d_opcode); |
55baa1f2 RG |
348 | } |
349 | private: | |
350 | uint8_t d_opcode; | |
351 | }; | |
3b069df2 | 352 | |
490a29bb RG |
353 | class TCPRule : public DNSRule |
354 | { | |
355 | public: | |
356 | TCPRule(bool tcp): d_tcp(tcp) | |
357 | { | |
358 | } | |
359 | bool matches(const DNSQuestion* dq) const override | |
360 | { | |
361 | return dq->tcp == d_tcp; | |
362 | } | |
363 | string toString() const override | |
364 | { | |
365 | return (d_tcp ? "TCP" : "UDP"); | |
366 | } | |
367 | private: | |
368 | bool d_tcp; | |
369 | }; | |
370 | ||
e7a1029c RG |
371 | |
372 | class NotRule : public DNSRule | |
373 | { | |
374 | public: | |
375 | NotRule(shared_ptr<DNSRule>& rule): d_rule(rule) | |
376 | { | |
377 | } | |
378 | bool matches(const DNSQuestion* dq) const override | |
379 | { | |
380 | return !d_rule->matches(dq); | |
381 | } | |
382 | string toString() const override | |
383 | { | |
3b069df2 | 384 | return "!("+ d_rule->toString()+")"; |
e7a1029c RG |
385 | } |
386 | private: | |
387 | shared_ptr<DNSRule> d_rule; | |
388 | }; | |
389 | ||
55baa1f2 RG |
390 | class RecordsCountRule : public DNSRule |
391 | { | |
392 | public: | |
393 | RecordsCountRule(uint8_t section, uint16_t minCount, uint16_t maxCount): d_minCount(minCount), d_maxCount(maxCount), d_section(section) | |
394 | { | |
395 | } | |
396 | bool matches(const DNSQuestion* dq) const override | |
397 | { | |
398 | uint16_t count = 0; | |
399 | switch(d_section) { | |
400 | case 0: | |
401 | count = ntohs(dq->dh->qdcount); | |
402 | break; | |
403 | case 1: | |
404 | count = ntohs(dq->dh->ancount); | |
405 | break; | |
406 | case 2: | |
407 | count = ntohs(dq->dh->nscount); | |
408 | break; | |
409 | case 3: | |
410 | count = ntohs(dq->dh->arcount); | |
411 | break; | |
412 | } | |
413 | return count >= d_minCount && count <= d_maxCount; | |
414 | } | |
415 | string toString() const override | |
416 | { | |
417 | string section; | |
418 | switch(d_section) { | |
419 | case 0: | |
420 | section = "QD"; | |
421 | break; | |
422 | case 1: | |
423 | section = "AN"; | |
424 | break; | |
425 | case 2: | |
426 | section = "NS"; | |
427 | break; | |
428 | case 3: | |
429 | section = "AR"; | |
430 | break; | |
431 | } | |
432 | return std::to_string(d_minCount) + " <= records in " + section + " <= "+ std::to_string(d_maxCount); | |
433 | } | |
434 | private: | |
435 | uint16_t d_minCount; | |
436 | uint16_t d_maxCount; | |
437 | uint8_t d_section; | |
438 | }; | |
439 | ||
440 | class RecordsTypeCountRule : public DNSRule | |
441 | { | |
442 | public: | |
443 | RecordsTypeCountRule(uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount): d_type(type), d_minCount(minCount), d_maxCount(maxCount), d_section(section) | |
444 | { | |
445 | } | |
446 | bool matches(const DNSQuestion* dq) const override | |
447 | { | |
448 | uint16_t count = 0; | |
449 | switch(d_section) { | |
450 | case 0: | |
451 | count = ntohs(dq->dh->qdcount); | |
452 | break; | |
453 | case 1: | |
454 | count = ntohs(dq->dh->ancount); | |
455 | break; | |
456 | case 2: | |
457 | count = ntohs(dq->dh->nscount); | |
458 | break; | |
459 | case 3: | |
460 | count = ntohs(dq->dh->arcount); | |
461 | break; | |
462 | } | |
463 | if (count < d_minCount || count > d_maxCount) { | |
464 | return false; | |
465 | } | |
466 | count = getRecordsOfTypeCount(reinterpret_cast<const char*>(dq->dh), dq->len, d_section, d_type); | |
467 | return count >= d_minCount && count <= d_maxCount; | |
468 | } | |
469 | string toString() const override | |
470 | { | |
471 | string section; | |
472 | switch(d_section) { | |
473 | case 0: | |
474 | section = "QD"; | |
475 | break; | |
476 | case 1: | |
477 | section = "AN"; | |
478 | break; | |
479 | case 2: | |
480 | section = "NS"; | |
481 | break; | |
482 | case 3: | |
483 | section = "AR"; | |
484 | break; | |
485 | } | |
486 | return std::to_string(d_minCount) + " <= " + QType(d_type).getName() + " records in " + section + " <= "+ std::to_string(d_maxCount); | |
487 | } | |
488 | private: | |
489 | uint16_t d_type; | |
490 | uint16_t d_minCount; | |
491 | uint16_t d_maxCount; | |
492 | uint8_t d_section; | |
493 | }; | |
494 | ||
495 | class TrailingDataRule : public DNSRule | |
496 | { | |
497 | public: | |
498 | TrailingDataRule() | |
499 | { | |
500 | } | |
501 | bool matches(const DNSQuestion* dq) const override | |
502 | { | |
503 | uint16_t length = getDNSPacketLength(reinterpret_cast<const char*>(dq->dh), dq->len); | |
504 | return length < dq->len; | |
505 | } | |
506 | string toString() const override | |
507 | { | |
508 | return "trailing data"; | |
509 | } | |
510 | }; | |
e7a1029c | 511 | |
57c61ce9 RG |
512 | class QNameLabelsCountRule : public DNSRule |
513 | { | |
514 | public: | |
515 | QNameLabelsCountRule(unsigned int minLabelsCount, unsigned int maxLabelsCount): d_min(minLabelsCount), d_max(maxLabelsCount) | |
516 | { | |
517 | } | |
518 | bool matches(const DNSQuestion* dq) const override | |
519 | { | |
520 | unsigned int count = dq->qname->countLabels(); | |
521 | return count < d_min || count > d_max; | |
522 | } | |
523 | string toString() const override | |
524 | { | |
525 | return "labels count < " + std::to_string(d_min) + " || labels count > " + std::to_string(d_max); | |
526 | } | |
527 | private: | |
528 | unsigned int d_min; | |
529 | unsigned int d_max; | |
530 | }; | |
531 | ||
532 | class QNameWireLengthRule : public DNSRule | |
533 | { | |
534 | public: | |
535 | QNameWireLengthRule(size_t min, size_t max): d_min(min), d_max(max) | |
536 | { | |
537 | } | |
538 | bool matches(const DNSQuestion* dq) const override | |
539 | { | |
540 | size_t const wirelength = dq->qname->wirelength(); | |
541 | return wirelength < d_min || wirelength > d_max; | |
542 | } | |
543 | string toString() const override | |
544 | { | |
545 | return "wire length < " + std::to_string(d_min) + " || wire length > " + std::to_string(d_max); | |
546 | } | |
547 | private: | |
548 | size_t d_min; | |
549 | size_t d_max; | |
550 | }; | |
551 | ||
0940e4eb | 552 | class DropAction : public DNSAction |
553 | { | |
554 | public: | |
497a6e3a | 555 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
0940e4eb | 556 | { |
557 | return Action::Drop; | |
558 | } | |
559 | string toString() const override | |
560 | { | |
561 | return "drop"; | |
562 | } | |
563 | }; | |
564 | ||
63beb26d G |
565 | class AllowAction : public DNSAction |
566 | { | |
567 | public: | |
497a6e3a | 568 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
63beb26d G |
569 | { |
570 | return Action::Allow; | |
571 | } | |
572 | string toString() const override | |
573 | { | |
574 | return "allow"; | |
575 | } | |
576 | }; | |
577 | ||
6bba426c | 578 | |
0940e4eb | 579 | class QPSAction : public DNSAction |
580 | { | |
581 | public: | |
582 | QPSAction(int limit) : d_qps(limit, limit) | |
583 | {} | |
497a6e3a | 584 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
0940e4eb | 585 | { |
586 | if(d_qps.check()) | |
856c35e3 | 587 | return Action::None; |
0940e4eb | 588 | else |
589 | return Action::Drop; | |
590 | } | |
591 | string toString() const override | |
592 | { | |
593 | return "qps limit to "+std::to_string(d_qps.getRate()); | |
594 | } | |
595 | private: | |
596 | QPSLimiter d_qps; | |
597 | }; | |
598 | ||
7b3865cd | 599 | class DelayAction : public DNSAction |
600 | { | |
601 | public: | |
602 | DelayAction(int msec) : d_msec(msec) | |
603 | {} | |
497a6e3a | 604 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
7b3865cd | 605 | { |
606 | *ruleresult=std::to_string(d_msec); | |
607 | return Action::Delay; | |
608 | } | |
609 | string toString() const override | |
610 | { | |
611 | return "delay by "+std::to_string(d_msec)+ " msec"; | |
612 | } | |
613 | private: | |
614 | int d_msec; | |
615 | }; | |
616 | ||
617 | ||
cf6874ba | 618 | class TeeAction : public DNSAction |
619 | { | |
620 | public: | |
384c2cb2 | 621 | TeeAction(const ComboAddress& ca, bool addECS=false); |
cf6874ba | 622 | ~TeeAction(); |
623 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override; | |
624 | string toString() const override; | |
625 | std::unordered_map<string, double> getStats() const override; | |
626 | private: | |
627 | ComboAddress d_remote; | |
628 | std::thread d_worker; | |
629 | void worker(); | |
630 | ||
631 | int d_fd; | |
fa6c1f0c | 632 | mutable std::atomic<unsigned long> d_senderrors{0}; |
3d8a6b1d | 633 | unsigned long d_recverrors{0}; |
fa6c1f0c | 634 | mutable std::atomic<unsigned long> d_queries{0}; |
cf6874ba | 635 | unsigned long d_responses{0}; |
3d8a6b1d | 636 | unsigned long d_nxdomains{0}; |
637 | unsigned long d_servfails{0}; | |
638 | unsigned long d_refuseds{0}; | |
639 | unsigned long d_formerrs{0}; | |
640 | unsigned long d_notimps{0}; | |
641 | unsigned long d_noerrors{0}; | |
642 | mutable unsigned long d_tcpdrops{0}; | |
643 | unsigned long d_otherrcode{0}; | |
cf6874ba | 644 | std::atomic<bool> d_pleaseQuit{false}; |
384c2cb2 | 645 | bool d_addECS{false}; |
cf6874ba | 646 | }; |
647 | ||
648 | ||
649 | ||
0940e4eb | 650 | class PoolAction : public DNSAction |
651 | { | |
652 | public: | |
653 | PoolAction(const std::string& pool) : d_pool(pool) {} | |
497a6e3a | 654 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
0940e4eb | 655 | { |
656 | *ruleresult=d_pool; | |
657 | return Action::Pool; | |
658 | } | |
659 | string toString() const override | |
660 | { | |
661 | return "to pool "+d_pool; | |
662 | } | |
663 | ||
664 | private: | |
665 | string d_pool; | |
666 | }; | |
667 | ||
fd010ca3 | 668 | |
669 | class QPSPoolAction : public DNSAction | |
670 | { | |
671 | public: | |
672 | QPSPoolAction(unsigned int limit, const std::string& pool) : d_qps(limit, limit), d_pool(pool) {} | |
497a6e3a | 673 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
fd010ca3 | 674 | { |
675 | if(d_qps.check()) { | |
676 | *ruleresult=d_pool; | |
677 | return Action::Pool; | |
678 | } | |
679 | else | |
680 | return Action::None; | |
681 | } | |
682 | string toString() const override | |
683 | { | |
684 | return "max " +std::to_string(d_qps.getRate())+" to pool "+d_pool; | |
685 | } | |
686 | ||
687 | private: | |
688 | QPSLimiter d_qps; | |
689 | string d_pool; | |
690 | }; | |
691 | ||
0940e4eb | 692 | class RCodeAction : public DNSAction |
693 | { | |
694 | public: | |
695 | RCodeAction(int rcode) : d_rcode(rcode) {} | |
497a6e3a | 696 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
0940e4eb | 697 | { |
497a6e3a RG |
698 | dq->dh->rcode = d_rcode; |
699 | dq->dh->qr = true; // for good measure | |
0940e4eb | 700 | return Action::HeaderModify; |
701 | } | |
702 | string toString() const override | |
703 | { | |
704 | return "set rcode "+std::to_string(d_rcode); | |
705 | } | |
706 | ||
707 | private: | |
708 | int d_rcode; | |
709 | }; | |
710 | ||
711 | class TCAction : public DNSAction | |
712 | { | |
713 | public: | |
497a6e3a | 714 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
0940e4eb | 715 | { |
497a6e3a RG |
716 | dq->dh->tc = true; |
717 | dq->dh->qr = true; // for good measure | |
0940e4eb | 718 | return Action::HeaderModify; |
719 | } | |
720 | string toString() const override | |
721 | { | |
722 | return "tc=1 answer"; | |
723 | } | |
724 | }; | |
0570f37c | 725 | |
731774a8 | 726 | class SpoofAction : public DNSAction |
727 | { | |
728 | public: | |
9ebc0e91 | 729 | SpoofAction(const vector<ComboAddress>& addrs) : d_addrs(addrs) |
7791f83a | 730 | { |
7791f83a | 731 | } |
9ebc0e91 | 732 | |
733 | SpoofAction(const string& cname): d_cname(cname) { } | |
734 | ||
497a6e3a | 735 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
731774a8 | 736 | { |
497a6e3a | 737 | uint16_t qtype = dq->qtype; |
9ebc0e91 | 738 | // do we even have a response? |
739 | if(d_cname.empty() && !std::count_if(d_addrs.begin(), d_addrs.end(), [qtype](const ComboAddress& a) | |
740 | { | |
741 | return (qtype == QType::ANY || ((a.sin4.sin_family == AF_INET && qtype == QType::A) || | |
742 | (a.sin4.sin_family == AF_INET6 && qtype == QType::AAAA))); | |
743 | })) | |
731774a8 | 744 | return Action::None; |
9ebc0e91 | 745 | |
746 | vector<ComboAddress> addrs; | |
747 | unsigned int totrdatalen=0; | |
87c605c4 RG |
748 | if (!d_cname.empty()) { |
749 | qtype = QType::CNAME; | |
9ebc0e91 | 750 | totrdatalen += d_cname.toDNSString().size(); |
87c605c4 | 751 | } else { |
9ebc0e91 | 752 | for(const auto& addr : d_addrs) { |
753 | if(qtype != QType::ANY && ((addr.sin4.sin_family == AF_INET && qtype != QType::A) || | |
754 | (addr.sin4.sin_family == AF_INET6 && qtype != QType::AAAA))) | |
755 | continue; | |
a683e8bd | 756 | totrdatalen += addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr); |
9ebc0e91 | 757 | addrs.push_back(addr); |
758 | } | |
87c605c4 RG |
759 | } |
760 | ||
9ebc0e91 | 761 | if(addrs.size() > 1) |
762 | random_shuffle(addrs.begin(), addrs.end()); | |
87c605c4 | 763 | |
9ebc0e91 | 764 | unsigned int consumed=0; |
497a6e3a | 765 | DNSName ignore((char*)dq->dh, dq->len, sizeof(dnsheader), false, 0, 0, &consumed); |
87c605c4 | 766 | |
9ebc0e91 | 767 | if (dq->size < (sizeof(dnsheader) + consumed + 4 + ((d_cname.empty() ? 0 : 1) + addrs.size())*12 /* recordstart */ + totrdatalen)) { |
87c605c4 RG |
768 | return Action::None; |
769 | } | |
770 | ||
9ebc0e91 | 771 | dq->len = sizeof(dnsheader) + consumed + 4; // there goes your EDNS |
772 | char* dest = ((char*)dq->dh) + dq->len; | |
773 | ||
497a6e3a RG |
774 | dq->dh->qr = true; // for good measure |
775 | dq->dh->ra = dq->dh->rd; // for good measure | |
776 | dq->dh->ad = false; | |
9ebc0e91 | 777 | dq->dh->ancount = 0; |
497a6e3a | 778 | dq->dh->arcount = 0; // for now, forget about your EDNS, we're marching over it |
731774a8 | 779 | |
9ebc0e91 | 780 | if(qtype == QType::CNAME) { |
781 | string wireData = d_cname.toDNSString(); // Note! This doesn't do compression! | |
782 | const unsigned char recordstart[]={0xc0, 0x0c, // compressed name | |
783 | 0, (unsigned char) qtype, | |
784 | 0, QClass::IN, // IN | |
785 | 0, 0, 0, 60, // TTL | |
786 | 0, (unsigned char)wireData.length()}; | |
a683e8bd | 787 | static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid"); |
9ebc0e91 | 788 | |
789 | memcpy(dest, recordstart, sizeof(recordstart)); | |
790 | dest += sizeof(recordstart); | |
791 | memcpy(dest, wireData.c_str(), wireData.length()); | |
792 | dq->len += wireData.length() + sizeof(recordstart); | |
793 | dq->dh->ancount++; | |
87c605c4 | 794 | } |
a683e8bd RG |
795 | else { |
796 | for(const auto& addr : addrs) { | |
797 | unsigned char rdatalen = addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr); | |
798 | const unsigned char recordstart[]={0xc0, 0x0c, // compressed name | |
799 | 0, (unsigned char) (addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA), | |
800 | 0, QClass::IN, // IN | |
801 | 0, 0, 0, 60, // TTL | |
802 | 0, rdatalen}; | |
803 | static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid"); | |
804 | ||
805 | memcpy(dest, recordstart, sizeof(recordstart)); | |
806 | dest += sizeof(recordstart); | |
807 | ||
808 | memcpy(dest, | |
809 | addr.sin4.sin_family == AF_INET ? (void*)&addr.sin4.sin_addr.s_addr : (void*)&addr.sin6.sin6_addr.s6_addr, | |
810 | rdatalen); | |
811 | dest += rdatalen; | |
812 | dq->len += rdatalen + sizeof(recordstart); | |
813 | dq->dh->ancount++; | |
814 | } | |
9ebc0e91 | 815 | } |
a683e8bd | 816 | |
9ebc0e91 | 817 | dq->dh->ancount = htons(dq->dh->ancount); |
818 | ||
731774a8 | 819 | return Action::HeaderModify; |
820 | } | |
9ebc0e91 | 821 | |
731774a8 | 822 | string toString() const override |
823 | { | |
9ebc0e91 | 824 | string ret = "spoof in "; |
87c605c4 | 825 | if(!d_cname.empty()) { |
9ebc0e91 | 826 | ret+=d_cname.toString()+ " "; |
87c605c4 | 827 | } else { |
9ebc0e91 | 828 | for(const auto& a : d_addrs) |
829 | ret += a.toString()+" "; | |
731774a8 | 830 | } |
831 | return ret; | |
832 | } | |
833 | private: | |
9ebc0e91 | 834 | std::vector<ComboAddress> d_addrs; |
87c605c4 | 835 | DNSName d_cname; |
731774a8 | 836 | }; |
837 | ||
6907f014 | 838 | class MacAddrAction : public DNSAction |
839 | { | |
840 | public: | |
841 | MacAddrAction(uint16_t code) : d_code(code) | |
842 | {} | |
843 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override | |
844 | { | |
845 | if(dq->dh->arcount) | |
846 | return Action::None; | |
847 | ||
01520028 RG |
848 | string mac = getMACAddress(*dq->remote); |
849 | if(mac.empty()) | |
850 | return Action::None; | |
851 | ||
6907f014 | 852 | string optRData; |
01520028 RG |
853 | generateEDNSOption(d_code, mac, optRData); |
854 | ||
855 | string res; | |
856 | generateOptRR(optRData, res); | |
857 | ||
858 | if ((dq->size - dq->len) < res.length()) | |
859 | return Action::None; | |
860 | ||
861 | dq->dh->arcount = htons(1); | |
862 | char* dest = ((char*)dq->dh) + dq->len; | |
863 | memcpy(dest, res.c_str(), res.length()); | |
864 | dq->len += res.length(); | |
865 | ||
6907f014 | 866 | return Action::None; |
867 | } | |
868 | string toString() const override | |
869 | { | |
870 | return "add EDNS MAC (code="+std::to_string(d_code)+")"; | |
871 | } | |
872 | private: | |
873 | uint16_t d_code{3}; | |
874 | }; | |
731774a8 | 875 | |
0570f37c | 876 | class NoRecurseAction : public DNSAction |
877 | { | |
878 | public: | |
497a6e3a | 879 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
0570f37c | 880 | { |
497a6e3a | 881 | dq->dh->rd = false; |
1a2a4e68 | 882 | return Action::None; |
0570f37c | 883 | } |
884 | string toString() const override | |
885 | { | |
6bba426c | 886 | return "set rd=0"; |
0570f37c | 887 | } |
888 | }; | |
f39b7598 | 889 | |
808c5ef7 | 890 | class LogAction : public DNSAction, public boost::noncopyable |
891 | { | |
892 | public: | |
893 | LogAction() : d_fp(0) | |
894 | { | |
895 | } | |
bb671e4a | 896 | LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true) : d_fname(str), d_binary(binary) |
808c5ef7 | 897 | { |
898 | if(str.empty()) | |
899 | return; | |
bb671e4a | 900 | if(append) |
456fc645 | 901 | d_fp = fopen(str.c_str(), "a+"); |
902 | else | |
903 | d_fp = fopen(str.c_str(), "w"); | |
808c5ef7 | 904 | if(!d_fp) |
905 | throw std::runtime_error("Unable to open file '"+str+"' for logging: "+string(strerror(errno))); | |
bb671e4a | 906 | if(!buffered) |
456fc645 | 907 | setbuf(d_fp, 0); |
808c5ef7 | 908 | } |
909 | ~LogAction() | |
910 | { | |
911 | if(d_fp) | |
912 | fclose(d_fp); | |
913 | } | |
497a6e3a | 914 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
808c5ef7 | 915 | { |
0a271e94 | 916 | if(!d_fp) { |
497a6e3a | 917 | vinfolog("Packet from %s for %s %s with id %d", dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id); |
0a271e94 | 918 | } |
808c5ef7 | 919 | else { |
18029431 RG |
920 | if(d_binary) { |
921 | string out = dq->qname->toDNSString(); | |
922 | fwrite(out.c_str(), 1, out.size(), d_fp); | |
923 | fwrite((void*)&dq->qtype, 1, 2, d_fp); | |
924 | } | |
925 | else { | |
926 | fprintf(d_fp, "Packet from %s for %s %s with id %d\n", dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).getName().c_str(), dq->dh->id); | |
927 | } | |
808c5ef7 | 928 | } |
929 | return Action::None; | |
930 | } | |
931 | string toString() const override | |
932 | { | |
18029431 RG |
933 | if (!d_fname.empty()) { |
934 | return "log to " + d_fname; | |
935 | } | |
808c5ef7 | 936 | return "log"; |
937 | } | |
938 | private: | |
939 | string d_fname; | |
8d06661a | 940 | FILE* d_fp{0}; |
18029431 | 941 | bool d_binary{true}; |
808c5ef7 | 942 | }; |
943 | ||
944 | ||
f39b7598 RG |
945 | class DisableValidationAction : public DNSAction |
946 | { | |
947 | public: | |
497a6e3a | 948 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override |
f39b7598 | 949 | { |
497a6e3a | 950 | dq->dh->cd = true; |
1a2a4e68 | 951 | return Action::None; |
f39b7598 RG |
952 | } |
953 | string toString() const override | |
954 | { | |
955 | return "set cd=1"; | |
956 | } | |
957 | }; | |
886e2cf2 RG |
958 | |
959 | class SkipCacheAction : public DNSAction | |
960 | { | |
961 | public: | |
962 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override | |
963 | { | |
964 | dq->skipCache = true; | |
965 | return Action::None; | |
966 | } | |
967 | string toString() const override | |
968 | { | |
969 | return "skip cache"; | |
970 | } | |
971 | }; | |
d8c19b98 RG |
972 | |
973 | class RemoteLogAction : public DNSAction, public boost::noncopyable | |
974 | { | |
975 | public: | |
976 | RemoteLogAction(std::shared_ptr<RemoteLogger> logger): d_logger(logger) | |
977 | { | |
978 | } | |
979 | DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override | |
980 | { | |
ec469dd7 | 981 | #ifdef HAVE_PROTOBUF |
d9d3f9c1 | 982 | DNSDistProtoBufMessage message(DNSDistProtoBufMessage::Query, *dq); |
ec469dd7 | 983 | std::string data; |
d9d3f9c1 | 984 | message.serialize(data); |
ec469dd7 RG |
985 | d_logger->queueData(data); |
986 | #endif /* HAVE_PROTOBUF */ | |
d8c19b98 RG |
987 | return Action::None; |
988 | } | |
989 | string toString() const override | |
990 | { | |
991 | return "remote log to " + d_logger->toString(); | |
992 | } | |
993 | private: | |
994 | std::shared_ptr<RemoteLogger> d_logger; | |
995 | }; | |
996 | ||
8146444b | 997 | class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable |
d8c19b98 RG |
998 | { |
999 | public: | |
1000 | RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger): d_logger(logger) | |
1001 | { | |
1002 | } | |
58307a85 | 1003 | DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override |
d8c19b98 | 1004 | { |
ec469dd7 | 1005 | #ifdef HAVE_PROTOBUF |
58307a85 | 1006 | DNSDistProtoBufMessage message(*dr); |
ec469dd7 | 1007 | std::string data; |
d9d3f9c1 | 1008 | message.serialize(data); |
ec469dd7 RG |
1009 | d_logger->queueData(data); |
1010 | #endif /* HAVE_PROTOBUF */ | |
d8c19b98 RG |
1011 | return Action::None; |
1012 | } | |
1013 | string toString() const override | |
1014 | { | |
1015 | return "remote log response to " + d_logger->toString(); | |
1016 | } | |
1017 | private: | |
1018 | std::shared_ptr<RemoteLogger> d_logger; | |
1019 | }; |