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