]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua-actions.cc
TeeAction: make getStats() order deterministic
[thirdparty/pdns.git] / pdns / dnsdist-lua-actions.cc
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 #include "dnsdist.hh"
23 #include "dnsdist-ecs.hh"
24 #include "dnsdist-lua.hh"
25 #include "dnsdist-protobuf.hh"
26
27 #include "dolog.hh"
28 #include "dnstap.hh"
29 #include "ednsoptions.hh"
30 #include "fstrm_logger.hh"
31 #include "remote_logger.hh"
32
33 class DropAction : public DNSAction
34 {
35 public:
36 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
37 {
38 return Action::Drop;
39 }
40 string toString() const override
41 {
42 return "drop";
43 }
44 };
45
46 class AllowAction : public DNSAction
47 {
48 public:
49 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
50 {
51 return Action::Allow;
52 }
53 string toString() const override
54 {
55 return "allow";
56 }
57 };
58
59
60 class QPSAction : public DNSAction
61 {
62 public:
63 QPSAction(int limit) : d_qps(limit, limit)
64 {}
65 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
66 {
67 if(d_qps.check())
68 return Action::None;
69 else
70 return Action::Drop;
71 }
72 string toString() const override
73 {
74 return "qps limit to "+std::to_string(d_qps.getRate());
75 }
76 private:
77 QPSLimiter d_qps;
78 };
79
80 class DelayAction : public DNSAction
81 {
82 public:
83 DelayAction(int msec) : d_msec(msec)
84 {}
85 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
86 {
87 *ruleresult=std::to_string(d_msec);
88 return Action::Delay;
89 }
90 string toString() const override
91 {
92 return "delay by "+std::to_string(d_msec)+ " msec";
93 }
94 private:
95 int d_msec;
96 };
97
98
99 class TeeAction : public DNSAction
100 {
101 public:
102 TeeAction(const ComboAddress& ca, bool addECS=false);
103 ~TeeAction() override;
104 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override;
105 string toString() const override;
106 std::map<string, double> getStats() const override;
107
108 private:
109 ComboAddress d_remote;
110 std::thread d_worker;
111 void worker();
112
113 int d_fd;
114 mutable std::atomic<unsigned long> d_senderrors{0};
115 unsigned long d_recverrors{0};
116 mutable std::atomic<unsigned long> d_queries{0};
117 unsigned long d_responses{0};
118 unsigned long d_nxdomains{0};
119 unsigned long d_servfails{0};
120 unsigned long d_refuseds{0};
121 unsigned long d_formerrs{0};
122 unsigned long d_notimps{0};
123 unsigned long d_noerrors{0};
124 mutable unsigned long d_tcpdrops{0};
125 unsigned long d_otherrcode{0};
126 std::atomic<bool> d_pleaseQuit{false};
127 bool d_addECS{false};
128 };
129
130 TeeAction::TeeAction(const ComboAddress& ca, bool addECS) : d_remote(ca), d_addECS(addECS)
131 {
132 d_fd=SSocket(d_remote.sin4.sin_family, SOCK_DGRAM, 0);
133 SConnect(d_fd, d_remote);
134 setNonBlocking(d_fd);
135 d_worker=std::thread(std::bind(&TeeAction::worker, this));
136 }
137
138 TeeAction::~TeeAction()
139 {
140 d_pleaseQuit=true;
141 close(d_fd);
142 d_worker.join();
143 }
144
145
146 DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) const
147 {
148 if(dq->tcp) {
149 d_tcpdrops++;
150 }
151 else {
152 ssize_t res;
153 d_queries++;
154
155 if(d_addECS) {
156 std::string query;
157 uint16_t len = dq->len;
158 bool ednsAdded = false;
159 bool ecsAdded = false;
160 query.reserve(dq->size);
161 query.assign((char*) dq->dh, len);
162
163 if (!handleEDNSClientSubnet((char*) query.c_str(), query.capacity(), dq->qname->wirelength(), &len, &ednsAdded, &ecsAdded, *dq->remote, dq->ecsOverride, dq->ecsPrefixLength)) {
164 return DNSAction::Action::None;
165 }
166
167 res = send(d_fd, query.c_str(), len, 0);
168 }
169 else {
170 res = send(d_fd, (char*)dq->dh, dq->len, 0);
171 }
172
173 if (res <= 0)
174 d_senderrors++;
175 }
176 return DNSAction::Action::None;
177 }
178
179 string TeeAction::toString() const
180 {
181 return "tee to "+d_remote.toStringWithPort();
182 }
183
184 std::map<string,double> TeeAction::getStats() const
185 {
186 return {{"queries", d_queries},
187 {"responses", d_responses},
188 {"recv-errors", d_recverrors},
189 {"send-errors", d_senderrors},
190 {"noerrors", d_noerrors},
191 {"nxdomains", d_nxdomains},
192 {"refuseds", d_refuseds},
193 {"servfails", d_servfails},
194 {"other-rcode", d_otherrcode},
195 {"tcp-drops", d_tcpdrops}
196 };
197 }
198
199 void TeeAction::worker()
200 {
201 char packet[1500];
202 int res=0;
203 struct dnsheader* dh=(struct dnsheader*)packet;
204 for(;;) {
205 res=waitForData(d_fd, 0, 250000);
206 if(d_pleaseQuit)
207 break;
208 if(res < 0) {
209 usleep(250000);
210 continue;
211 }
212 if(res==0)
213 continue;
214 res=recv(d_fd, packet, sizeof(packet), 0);
215 if(res <= (int)sizeof(struct dnsheader))
216 d_recverrors++;
217 else if(res > 0)
218 d_responses++;
219
220 if(dh->rcode == RCode::NoError)
221 d_noerrors++;
222 else if(dh->rcode == RCode::ServFail)
223 d_servfails++;
224 else if(dh->rcode == RCode::NXDomain)
225 d_nxdomains++;
226 else if(dh->rcode == RCode::Refused)
227 d_refuseds++;
228 else if(dh->rcode == RCode::FormErr)
229 d_formerrs++;
230 else if(dh->rcode == RCode::NotImp)
231 d_notimps++;
232 }
233 }
234
235 class PoolAction : public DNSAction
236 {
237 public:
238 PoolAction(const std::string& pool) : d_pool(pool) {}
239 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
240 {
241 *ruleresult=d_pool;
242 return Action::Pool;
243 }
244 string toString() const override
245 {
246 return "to pool "+d_pool;
247 }
248
249 private:
250 string d_pool;
251 };
252
253
254 class QPSPoolAction : public DNSAction
255 {
256 public:
257 QPSPoolAction(unsigned int limit, const std::string& pool) : d_qps(limit, limit), d_pool(pool) {}
258 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
259 {
260 if(d_qps.check()) {
261 *ruleresult=d_pool;
262 return Action::Pool;
263 }
264 else
265 return Action::None;
266 }
267 string toString() const override
268 {
269 return "max " +std::to_string(d_qps.getRate())+" to pool "+d_pool;
270 }
271
272 private:
273 QPSLimiter d_qps;
274 string d_pool;
275 };
276
277 class RCodeAction : public DNSAction
278 {
279 public:
280 RCodeAction(uint8_t rcode) : d_rcode(rcode) {}
281 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
282 {
283 dq->dh->rcode = d_rcode;
284 dq->dh->qr = true; // for good measure
285 return Action::HeaderModify;
286 }
287 string toString() const override
288 {
289 return "set rcode "+std::to_string(d_rcode);
290 }
291
292 private:
293 uint8_t d_rcode;
294 };
295
296 class TCAction : public DNSAction
297 {
298 public:
299 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
300 {
301 return Action::Truncate;
302 }
303 string toString() const override
304 {
305 return "tc=1 answer";
306 }
307 };
308
309 DNSAction::Action LuaAction::operator()(DNSQuestion* dq, string* ruleresult) const
310 {
311 std::lock_guard<std::mutex> lock(g_luamutex);
312 try {
313 auto ret = d_func(dq);
314 if(ruleresult)
315 *ruleresult=std::get<1>(ret);
316 return (Action)std::get<0>(ret);
317 } catch (std::exception &e) {
318 warnlog("LuaAction failed inside lua, returning ServFail: %s", e.what());
319 } catch (...) {
320 warnlog("LuaAction failed inside lua, returning ServFail: [unknown exception]");
321 }
322 return DNSAction::Action::ServFail;
323 }
324
325 DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, string* ruleresult) const
326 {
327 std::lock_guard<std::mutex> lock(g_luamutex);
328 try {
329 auto ret = d_func(dr);
330 if(ruleresult)
331 *ruleresult=std::get<1>(ret);
332 return (Action)std::get<0>(ret);
333 } catch (std::exception &e) {
334 warnlog("LuaResponseAction failed inside lua, returning ServFail: %s", e.what());
335 } catch (...) {
336 warnlog("LuaResponseAction failed inside lua, returning ServFail: [unknown exception]");
337 }
338 return DNSResponseAction::Action::ServFail;
339 }
340
341 DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, string* ruleresult) const
342 {
343 uint16_t qtype = dq->qtype;
344 // do we even have a response?
345 if(d_cname.empty() && !std::count_if(d_addrs.begin(), d_addrs.end(), [qtype](const ComboAddress& a)
346 {
347 return (qtype == QType::ANY || ((a.sin4.sin_family == AF_INET && qtype == QType::A) ||
348 (a.sin4.sin_family == AF_INET6 && qtype == QType::AAAA)));
349 }))
350 return Action::None;
351
352 vector<ComboAddress> addrs;
353 unsigned int totrdatalen=0;
354 if (!d_cname.empty()) {
355 qtype = QType::CNAME;
356 totrdatalen += d_cname.toDNSString().size();
357 } else {
358 for(const auto& addr : d_addrs) {
359 if(qtype != QType::ANY && ((addr.sin4.sin_family == AF_INET && qtype != QType::A) ||
360 (addr.sin4.sin_family == AF_INET6 && qtype != QType::AAAA))) {
361 continue;
362 }
363 totrdatalen += addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
364 addrs.push_back(addr);
365 }
366 }
367
368 if(addrs.size() > 1)
369 random_shuffle(addrs.begin(), addrs.end());
370
371 unsigned int consumed=0;
372 DNSName ignore((char*)dq->dh, dq->len, sizeof(dnsheader), false, 0, 0, &consumed);
373
374 if (dq->size < (sizeof(dnsheader) + consumed + 4 + ((d_cname.empty() ? 0 : 1) + addrs.size())*12 /* recordstart */ + totrdatalen)) {
375 return Action::None;
376 }
377
378 dq->len = sizeof(dnsheader) + consumed + 4; // there goes your EDNS
379 char* dest = ((char*)dq->dh) + dq->len;
380
381 dq->dh->qr = true; // for good measure
382 dq->dh->ra = dq->dh->rd; // for good measure
383 dq->dh->ad = false;
384 dq->dh->ancount = 0;
385 dq->dh->arcount = 0; // for now, forget about your EDNS, we're marching over it
386
387 if(qtype == QType::CNAME) {
388 string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
389 const unsigned char recordstart[]={0xc0, 0x0c, // compressed name
390 0, (unsigned char) qtype,
391 0, QClass::IN, // IN
392 0, 0, 0, 60, // TTL
393 0, (unsigned char)wireData.length()};
394 static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
395
396 memcpy(dest, recordstart, sizeof(recordstart));
397 dest += sizeof(recordstart);
398 memcpy(dest, wireData.c_str(), wireData.length());
399 dq->len += wireData.length() + sizeof(recordstart);
400 dq->dh->ancount++;
401 }
402 else {
403 for(const auto& addr : addrs) {
404 unsigned char rdatalen = addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
405 const unsigned char recordstart[]={0xc0, 0x0c, // compressed name
406 0, (unsigned char) (addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA),
407 0, QClass::IN, // IN
408 0, 0, 0, 60, // TTL
409 0, rdatalen};
410 static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
411
412 memcpy(dest, recordstart, sizeof(recordstart));
413 dest += sizeof(recordstart);
414
415 memcpy(dest,
416 addr.sin4.sin_family == AF_INET ? (void*)&addr.sin4.sin_addr.s_addr : (void*)&addr.sin6.sin6_addr.s6_addr,
417 rdatalen);
418 dest += rdatalen;
419 dq->len += rdatalen + sizeof(recordstart);
420 dq->dh->ancount++;
421 }
422 }
423
424 dq->dh->ancount = htons(dq->dh->ancount);
425
426 return Action::HeaderModify;
427 }
428
429 class MacAddrAction : public DNSAction
430 {
431 public:
432 MacAddrAction(uint16_t code) : d_code(code)
433 {}
434 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
435 {
436 if(dq->dh->arcount)
437 return Action::None;
438
439 string mac = getMACAddress(*dq->remote);
440 if(mac.empty())
441 return Action::None;
442
443 string optRData;
444 generateEDNSOption(d_code, mac, optRData);
445
446 string res;
447 generateOptRR(optRData, res);
448
449 if ((dq->size - dq->len) < res.length())
450 return Action::None;
451
452 dq->dh->arcount = htons(1);
453 char* dest = ((char*)dq->dh) + dq->len;
454 memcpy(dest, res.c_str(), res.length());
455 dq->len += res.length();
456
457 return Action::None;
458 }
459 string toString() const override
460 {
461 return "add EDNS MAC (code="+std::to_string(d_code)+")";
462 }
463 private:
464 uint16_t d_code{3};
465 };
466
467 class NoRecurseAction : public DNSAction
468 {
469 public:
470 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
471 {
472 dq->dh->rd = false;
473 return Action::None;
474 }
475 string toString() const override
476 {
477 return "set rd=0";
478 }
479 };
480
481 class LogAction : public DNSAction, public boost::noncopyable
482 {
483 public:
484 LogAction() : d_fp(0)
485 {
486 }
487 LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true) : d_fname(str), d_binary(binary)
488 {
489 if(str.empty())
490 return;
491 if(append)
492 d_fp = fopen(str.c_str(), "a+");
493 else
494 d_fp = fopen(str.c_str(), "w");
495 if(!d_fp)
496 throw std::runtime_error("Unable to open file '"+str+"' for logging: "+string(strerror(errno)));
497 if(!buffered)
498 setbuf(d_fp, 0);
499 }
500 ~LogAction() override
501 {
502 if(d_fp)
503 fclose(d_fp);
504 }
505 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
506 {
507 if(!d_fp) {
508 vinfolog("Packet from %s for %s %s with id %d", dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id);
509 }
510 else {
511 if(d_binary) {
512 string out = dq->qname->toDNSString();
513 fwrite(out.c_str(), 1, out.size(), d_fp);
514 fwrite((void*)&dq->qtype, 1, 2, d_fp);
515 }
516 else {
517 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);
518 }
519 }
520 return Action::None;
521 }
522 string toString() const override
523 {
524 if (!d_fname.empty()) {
525 return "log to " + d_fname;
526 }
527 return "log";
528 }
529 private:
530 string d_fname;
531 FILE* d_fp{0};
532 bool d_binary{true};
533 };
534
535
536 class DisableValidationAction : public DNSAction
537 {
538 public:
539 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
540 {
541 dq->dh->cd = true;
542 return Action::None;
543 }
544 string toString() const override
545 {
546 return "set cd=1";
547 }
548 };
549
550 class SkipCacheAction : public DNSAction
551 {
552 public:
553 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
554 {
555 dq->skipCache = true;
556 return Action::None;
557 }
558 string toString() const override
559 {
560 return "skip cache";
561 }
562 };
563
564 class TempFailureCacheTTLAction : public DNSAction
565 {
566 public:
567 TempFailureCacheTTLAction(uint32_t ttl) : d_ttl(ttl)
568 {}
569 TempFailureCacheTTLAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
570 {
571 dq->tempFailureTTL = d_ttl;
572 return Action::None;
573 }
574 string toString() const override
575 {
576 return "set tempfailure cache ttl to "+std::to_string(d_ttl);
577 }
578 private:
579 uint32_t d_ttl;
580 };
581
582 class ECSPrefixLengthAction : public DNSAction
583 {
584 public:
585 ECSPrefixLengthAction(uint16_t v4Length, uint16_t v6Length) : d_v4PrefixLength(v4Length), d_v6PrefixLength(v6Length)
586 {
587 }
588 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
589 {
590 dq->ecsPrefixLength = dq->remote->sin4.sin_family == AF_INET ? d_v4PrefixLength : d_v6PrefixLength;
591 return Action::None;
592 }
593 string toString() const override
594 {
595 return "set ECS prefix length to " + std::to_string(d_v4PrefixLength) + "/" + std::to_string(d_v6PrefixLength);
596 }
597 private:
598 uint16_t d_v4PrefixLength;
599 uint16_t d_v6PrefixLength;
600 };
601
602 class ECSOverrideAction : public DNSAction
603 {
604 public:
605 ECSOverrideAction(bool ecsOverride) : d_ecsOverride(ecsOverride)
606 {
607 }
608 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
609 {
610 dq->ecsOverride = d_ecsOverride;
611 return Action::None;
612 }
613 string toString() const override
614 {
615 return "set ECS override to " + std::to_string(d_ecsOverride);
616 }
617 private:
618 bool d_ecsOverride;
619 };
620
621
622 class DisableECSAction : public DNSAction
623 {
624 public:
625 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
626 {
627 dq->useECS = false;
628 return Action::None;
629 }
630 string toString() const override
631 {
632 return "disable ECS";
633 }
634 };
635
636 class DnstapLogAction : public DNSAction, public boost::noncopyable
637 {
638 public:
639 DnstapLogAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DnstapMessage*)> > alterFunc): d_identity(identity), d_logger(logger), d_alterFunc(alterFunc)
640 {
641 }
642 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
643 {
644 #ifdef HAVE_PROTOBUF
645 DnstapMessage message(d_identity, dq->remote, dq->local, dq->tcp, reinterpret_cast<const char*>(dq->dh), dq->len, dq->queryTime, nullptr);
646 {
647 if (d_alterFunc) {
648 std::lock_guard<std::mutex> lock(g_luamutex);
649 (*d_alterFunc)(*dq, &message);
650 }
651 }
652 std::string data;
653 message.serialize(data);
654 d_logger->queueData(data);
655 #endif /* HAVE_PROTOBUF */
656 return Action::None;
657 }
658 string toString() const override
659 {
660 return "remote log as dnstap to " + (d_logger ? d_logger->toString() : "");
661 }
662 private:
663 std::string d_identity;
664 std::shared_ptr<RemoteLoggerInterface> d_logger;
665 boost::optional<std::function<void(const DNSQuestion&, DnstapMessage*)> > d_alterFunc;
666 };
667
668 class RemoteLogAction : public DNSAction, public boost::noncopyable
669 {
670 public:
671 RemoteLogAction(std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
672 {
673 }
674 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
675 {
676 #ifdef HAVE_PROTOBUF
677 if (!dq->uniqueId) {
678 dq->uniqueId = t_uuidGenerator();
679 }
680
681 DNSDistProtoBufMessage message(*dq);
682 {
683 if (d_alterFunc) {
684 std::lock_guard<std::mutex> lock(g_luamutex);
685 (*d_alterFunc)(*dq, &message);
686 }
687 }
688 std::string data;
689 message.serialize(data);
690 d_logger->queueData(data);
691 #endif /* HAVE_PROTOBUF */
692 return Action::None;
693 }
694 string toString() const override
695 {
696 return "remote log to " + (d_logger ? d_logger->toString() : "");
697 }
698 private:
699 std::shared_ptr<RemoteLoggerInterface> d_logger;
700 boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > d_alterFunc;
701 };
702
703 class SNMPTrapAction : public DNSAction
704 {
705 public:
706 SNMPTrapAction(const std::string& reason): d_reason(reason)
707 {
708 }
709 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
710 {
711 if (g_snmpAgent && g_snmpTrapsEnabled) {
712 g_snmpAgent->sendDNSTrap(*dq, d_reason);
713 }
714
715 return Action::None;
716 }
717 string toString() const override
718 {
719 return "send SNMP trap";
720 }
721 private:
722 std::string d_reason;
723 };
724
725 class TagAction : public DNSAction
726 {
727 public:
728 TagAction(const std::string tag, const std::string value): d_tag(tag), d_value(value)
729 {
730 }
731 DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
732 {
733 if (!dq->qTag) {
734 dq->qTag = std::make_shared<QTag>();
735 }
736
737 dq->qTag->insert({d_tag, d_value});
738
739 return Action::None;
740 }
741 string toString() const override
742 {
743 return "set tag '" + d_tag + "' to value '" + d_value + "'";
744 }
745 private:
746 std::string d_tag;
747 std::string d_value;
748 };
749
750 class DnstapLogResponseAction : public DNSResponseAction, public boost::noncopyable
751 {
752 public:
753 DnstapLogResponseAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DnstapMessage*)> > alterFunc): d_identity(identity), d_logger(logger), d_alterFunc(alterFunc)
754 {
755 }
756 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
757 {
758 #ifdef HAVE_PROTOBUF
759 struct timespec now;
760 gettime(&now, true);
761 DnstapMessage message(d_identity, dr->remote, dr->local, dr->tcp, reinterpret_cast<const char*>(dr->dh), dr->len, dr->queryTime, &now);
762 {
763 if (d_alterFunc) {
764 std::lock_guard<std::mutex> lock(g_luamutex);
765 (*d_alterFunc)(*dr, &message);
766 }
767 }
768 std::string data;
769 message.serialize(data);
770 d_logger->queueData(data);
771 #endif /* HAVE_PROTOBUF */
772 return Action::None;
773 }
774 string toString() const override
775 {
776 return "log response as dnstap to " + (d_logger ? d_logger->toString() : "");
777 }
778 private:
779 std::string d_identity;
780 std::shared_ptr<RemoteLoggerInterface> d_logger;
781 boost::optional<std::function<void(const DNSResponse&, DnstapMessage*)> > d_alterFunc;
782 };
783
784 class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
785 {
786 public:
787 RemoteLogResponseAction(std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
788 {
789 }
790 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
791 {
792 #ifdef HAVE_PROTOBUF
793 if (!dr->uniqueId) {
794 dr->uniqueId = t_uuidGenerator();
795 }
796
797 DNSDistProtoBufMessage message(*dr, d_includeCNAME);
798 {
799 if (d_alterFunc) {
800 std::lock_guard<std::mutex> lock(g_luamutex);
801 (*d_alterFunc)(*dr, &message);
802 }
803 }
804 std::string data;
805 message.serialize(data);
806 d_logger->queueData(data);
807 #endif /* HAVE_PROTOBUF */
808 return Action::None;
809 }
810 string toString() const override
811 {
812 return "remote log response to " + (d_logger ? d_logger->toString() : "");
813 }
814 private:
815 std::shared_ptr<RemoteLoggerInterface> d_logger;
816 boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
817 bool d_includeCNAME;
818 };
819
820 class DropResponseAction : public DNSResponseAction
821 {
822 public:
823 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
824 {
825 return Action::Drop;
826 }
827 string toString() const override
828 {
829 return "drop";
830 }
831 };
832
833 class AllowResponseAction : public DNSResponseAction
834 {
835 public:
836 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
837 {
838 return Action::Allow;
839 }
840 string toString() const override
841 {
842 return "allow";
843 }
844 };
845
846 class DelayResponseAction : public DNSResponseAction
847 {
848 public:
849 DelayResponseAction(int msec) : d_msec(msec)
850 {}
851 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
852 {
853 *ruleresult=std::to_string(d_msec);
854 return Action::Delay;
855 }
856 string toString() const override
857 {
858 return "delay by "+std::to_string(d_msec)+ " msec";
859 }
860 private:
861 int d_msec;
862 };
863
864 class SNMPTrapResponseAction : public DNSResponseAction
865 {
866 public:
867 SNMPTrapResponseAction(const std::string& reason): d_reason(reason)
868 {
869 }
870 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
871 {
872 if (g_snmpAgent && g_snmpTrapsEnabled) {
873 g_snmpAgent->sendDNSTrap(*dr, d_reason);
874 }
875
876 return Action::None;
877 }
878 string toString() const override
879 {
880 return "send SNMP trap";
881 }
882 private:
883 std::string d_reason;
884 };
885
886 class TagResponseAction : public DNSResponseAction
887 {
888 public:
889 TagResponseAction(const std::string tag, const std::string value): d_tag(tag), d_value(value)
890 {
891 }
892 DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
893 {
894 if (!dr->qTag) {
895 dr->qTag = std::make_shared<QTag>();
896 }
897
898 dr->qTag->insert({d_tag, d_value});
899
900 return Action::None;
901 }
902 string toString() const override
903 {
904 return "set tag '" + d_tag + "' to value '" + d_value + "'";
905 }
906 private:
907 std::string d_tag;
908 std::string d_value;
909 };
910
911 template<typename T, typename ActionT>
912 static void addAction(GlobalStateHolder<vector<T> > *someRulActions, luadnsrule_t var, std::shared_ptr<ActionT> action, boost::optional<luaruleparams_t> params) {
913 setLuaSideEffect();
914
915 boost::uuids::uuid uuid;
916 parseRuleParams(params, uuid);
917
918 auto rule=makeRule(var);
919 someRulActions->modify([rule, action, uuid](vector<T>& rulactions){
920 rulactions.push_back({rule, action, uuid});
921 });
922 }
923
924 void setupLuaActions()
925 {
926 g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action, boost::optional<luaruleparams_t> params) {
927 boost::uuids::uuid uuid;
928 parseRuleParams(params, uuid);
929
930 auto rule=makeRule(dnsrule);
931 DNSDistRuleAction ra({rule, action, uuid});
932 return std::make_shared<DNSDistRuleAction>(ra);
933 });
934
935 g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
936 if (era.type() != typeid(std::shared_ptr<DNSAction>)) {
937 throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
938 }
939
940 addAction(&g_rulactions, var, boost::get<std::shared_ptr<DNSAction> >(era), params);
941 });
942
943 g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func, boost::optional<luaruleparams_t> params) {
944 addAction(&g_rulactions, var, std::make_shared<LuaAction>(func), params);
945 });
946
947 g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func, boost::optional<luaruleparams_t> params) {
948 addAction(&g_resprulactions, var, std::make_shared<LuaResponseAction>(func), params);
949 });
950
951 g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
952 if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
953 throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
954 }
955
956 addAction(&g_resprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
957 });
958
959 g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
960 if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
961 throw std::runtime_error("addCacheHitResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
962 }
963
964 addAction(&g_cachehitresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
965 });
966
967 g_lua.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
968 if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
969 throw std::runtime_error("addSelfAnsweredResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
970 }
971
972 addAction(&g_selfansweredresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
973 });
974
975 g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
976 setLuaNoSideEffect();
977 auto stats = ta.getStats();
978 for(const auto& s : stats) {
979 g_outputBuffer+=s.first+"\t";
980 if((uint64_t)s.second == s.second)
981 g_outputBuffer += std::to_string((uint64_t)s.second)+"\n";
982 else
983 g_outputBuffer += std::to_string(s.second)+"\n";
984 }
985 });
986
987 g_lua.writeFunction("getAction", [](unsigned int num) {
988 setLuaNoSideEffect();
989 boost::optional<std::shared_ptr<DNSAction>> ret;
990 auto rulactions = g_rulactions.getCopy();
991 if(num < rulactions.size())
992 ret=rulactions[num].d_action;
993 return ret;
994 });
995
996 g_lua.registerFunction("getStats", &DNSAction::getStats);
997
998 g_lua.writeFunction("LuaAction", [](LuaAction::func_t func) {
999 setLuaSideEffect();
1000 return std::shared_ptr<DNSAction>(new LuaAction(func));
1001 });
1002
1003 g_lua.writeFunction("NoRecurseAction", []() {
1004 return std::shared_ptr<DNSAction>(new NoRecurseAction);
1005 });
1006
1007 g_lua.writeFunction("MacAddrAction", [](int code) {
1008 return std::shared_ptr<DNSAction>(new MacAddrAction(code));
1009 });
1010
1011 g_lua.writeFunction("PoolAction", [](const string& a) {
1012 return std::shared_ptr<DNSAction>(new PoolAction(a));
1013 });
1014
1015 g_lua.writeFunction("QPSAction", [](int limit) {
1016 return std::shared_ptr<DNSAction>(new QPSAction(limit));
1017 });
1018
1019 g_lua.writeFunction("QPSPoolAction", [](int limit, const string& a) {
1020 return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
1021 });
1022
1023 g_lua.writeFunction("SpoofAction", [](boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b ) {
1024 vector<ComboAddress> addrs;
1025 if(auto s = boost::get<string>(&inp))
1026 addrs.push_back(ComboAddress(*s));
1027 else {
1028 const auto& v = boost::get<vector<pair<int,string>>>(inp);
1029 for(const auto& a: v)
1030 addrs.push_back(ComboAddress(a.second));
1031 }
1032 if(b)
1033 addrs.push_back(ComboAddress(*b));
1034 return std::shared_ptr<DNSAction>(new SpoofAction(addrs));
1035 });
1036
1037 g_lua.writeFunction("SpoofCNAMEAction", [](const string& a) {
1038 return std::shared_ptr<DNSAction>(new SpoofAction(a));
1039 });
1040
1041 g_lua.writeFunction("DropAction", []() {
1042 return std::shared_ptr<DNSAction>(new DropAction);
1043 });
1044
1045 g_lua.writeFunction("AllowAction", []() {
1046 return std::shared_ptr<DNSAction>(new AllowAction);
1047 });
1048
1049 g_lua.writeFunction("DelayAction", [](int msec) {
1050 return std::shared_ptr<DNSAction>(new DelayAction(msec));
1051 });
1052
1053 g_lua.writeFunction("TCAction", []() {
1054 return std::shared_ptr<DNSAction>(new TCAction);
1055 });
1056
1057 g_lua.writeFunction("DisableValidationAction", []() {
1058 return std::shared_ptr<DNSAction>(new DisableValidationAction);
1059 });
1060
1061 g_lua.writeFunction("LogAction", [](const std::string& fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered) {
1062 return std::shared_ptr<DNSAction>(new LogAction(fname, binary ? *binary : true, append ? *append : false, buffered ? *buffered : false));
1063 });
1064
1065 g_lua.writeFunction("RCodeAction", [](uint8_t rcode) {
1066 return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
1067 });
1068
1069 g_lua.writeFunction("SkipCacheAction", []() {
1070 return std::shared_ptr<DNSAction>(new SkipCacheAction);
1071 });
1072
1073 g_lua.writeFunction("TempFailureCacheTTLAction", [](int maxTTL) {
1074 return std::shared_ptr<DNSAction>(new TempFailureCacheTTLAction(maxTTL));
1075 });
1076
1077 g_lua.writeFunction("DropResponseAction", []() {
1078 return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
1079 });
1080
1081 g_lua.writeFunction("AllowResponseAction", []() {
1082 return std::shared_ptr<DNSResponseAction>(new AllowResponseAction);
1083 });
1084
1085 g_lua.writeFunction("DelayResponseAction", [](int msec) {
1086 return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
1087 });
1088
1089 g_lua.writeFunction("LuaResponseAction", [](LuaResponseAction::func_t func) {
1090 setLuaSideEffect();
1091 return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
1092 });
1093
1094 g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
1095 // avoids potentially-evaluated-expression warning with clang.
1096 RemoteLoggerInterface& rl = *logger.get();
1097 if (typeid(rl) != typeid(RemoteLogger)) {
1098 // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
1099 throw std::runtime_error(std::string("RemoteLogAction only takes RemoteLogger. For other types, please look at DnstapLogAction."));
1100 }
1101 #ifdef HAVE_PROTOBUF
1102 return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
1103 #else
1104 throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
1105 #endif
1106 });
1107
1108 g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
1109 // avoids potentially-evaluated-expression warning with clang.
1110 RemoteLoggerInterface& rl = *logger.get();
1111 if (typeid(rl) != typeid(RemoteLogger)) {
1112 // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
1113 throw std::runtime_error("RemoteLogResponseAction only takes RemoteLogger. For other types, please look at DnstapLogResponseAction.");
1114 }
1115 #ifdef HAVE_PROTOBUF
1116 return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
1117 #else
1118 throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
1119 #endif
1120 });
1121
1122 g_lua.writeFunction("DnstapLogAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DnstapMessage*)> > alterFunc) {
1123 #ifdef HAVE_PROTOBUF
1124 return std::shared_ptr<DNSAction>(new DnstapLogAction(identity, logger, alterFunc));
1125 #else
1126 throw std::runtime_error("Protobuf support is required to use DnstapLogAction");
1127 #endif
1128 });
1129
1130 g_lua.writeFunction("DnstapLogResponseAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DnstapMessage*)> > alterFunc) {
1131 #ifdef HAVE_PROTOBUF
1132 return std::shared_ptr<DNSResponseAction>(new DnstapLogResponseAction(identity, logger, alterFunc));
1133 #else
1134 throw std::runtime_error("Protobuf support is required to use DnstapLogResponseAction");
1135 #endif
1136 });
1137
1138 g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
1139 return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
1140 });
1141
1142 g_lua.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
1143 return std::shared_ptr<DNSAction>(new ECSPrefixLengthAction(v4PrefixLength, v6PrefixLength));
1144 });
1145
1146 g_lua.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
1147 return std::shared_ptr<DNSAction>(new ECSOverrideAction(ecsOverride));
1148 });
1149
1150 g_lua.writeFunction("DisableECSAction", []() {
1151 return std::shared_ptr<DNSAction>(new DisableECSAction());
1152 });
1153
1154 g_lua.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
1155 #ifdef HAVE_NET_SNMP
1156 return std::shared_ptr<DNSAction>(new SNMPTrapAction(reason ? *reason : ""));
1157 #else
1158 throw std::runtime_error("NET SNMP support is required to use SNMPTrapAction()");
1159 #endif /* HAVE_NET_SNMP */
1160 });
1161
1162 g_lua.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
1163 #ifdef HAVE_NET_SNMP
1164 return std::shared_ptr<DNSResponseAction>(new SNMPTrapResponseAction(reason ? *reason : ""));
1165 #else
1166 throw std::runtime_error("NET SNMP support is required to use SNMPTrapResponseAction()");
1167 #endif /* HAVE_NET_SNMP */
1168 });
1169
1170 g_lua.writeFunction("TagAction", [](std::string tag, std::string value) {
1171 return std::shared_ptr<DNSAction>(new TagAction(tag, value));
1172 });
1173
1174 g_lua.writeFunction("TagResponseAction", [](std::string tag, std::string value) {
1175 return std::shared_ptr<DNSResponseAction>(new TagResponseAction(tag, value));
1176 });
1177 }