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