]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua-actions.cc
dnsdist: Add SetNegativeAndSOAAction() and its Lua binding
[thirdparty/pdns.git] / pdns / dnsdist-lua-actions.cc
CommitLineData
6bb38cd6
RG
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
af7afecf 22#include "config.h"
519f5484 23#include "threadname.hh"
6bb38cd6
RG
24#include "dnsdist.hh"
25#include "dnsdist-ecs.hh"
26#include "dnsdist-lua.hh"
27#include "dnsdist-protobuf.hh"
f441962a 28#include "dnsdist-kvs.hh"
6bb38cd6
RG
29
30#include "dolog.hh"
82a91ddf 31#include "dnstap.hh"
6bb38cd6 32#include "ednsoptions.hh"
82a91ddf 33#include "fstrm_logger.hh"
6bb38cd6 34#include "remote_logger.hh"
af7afecf
RG
35
36#include <boost/optional/optional_io.hpp>
37
38#ifdef HAVE_LIBCRYPTO
39#include "ipcipher.hh"
40#endif /* HAVE_LIBCRYPTO */
6bb38cd6
RG
41
42class DropAction : public DNSAction
43{
44public:
d67c1cbe 45 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
46 {
47 return Action::Drop;
48 }
d67c1cbe 49 std::string toString() const override
6bb38cd6
RG
50 {
51 return "drop";
52 }
53};
54
55class AllowAction : public DNSAction
56{
57public:
d67c1cbe 58 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
59 {
60 return Action::Allow;
61 }
d67c1cbe 62 std::string toString() const override
6bb38cd6
RG
63 {
64 return "allow";
65 }
66};
67
bc084a31
RG
68class NoneAction : public DNSAction
69{
70public:
d67c1cbe 71 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
bc084a31
RG
72 {
73 return Action::None;
74 }
d67c1cbe 75 std::string toString() const override
bc084a31
RG
76 {
77 return "no op";
78 }
79};
6bb38cd6
RG
80
81class QPSAction : public DNSAction
82{
83public:
84 QPSAction(int limit) : d_qps(limit, limit)
85 {}
d67c1cbe 86 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
87 {
88 if(d_qps.check())
89 return Action::None;
90 else
91 return Action::Drop;
92 }
d67c1cbe 93 std::string toString() const override
6bb38cd6
RG
94 {
95 return "qps limit to "+std::to_string(d_qps.getRate());
96 }
97private:
98 QPSLimiter d_qps;
99};
100
101class DelayAction : public DNSAction
102{
103public:
104 DelayAction(int msec) : d_msec(msec)
105 {}
d67c1cbe 106 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
107 {
108 *ruleresult=std::to_string(d_msec);
109 return Action::Delay;
110 }
d67c1cbe 111 std::string toString() const override
6bb38cd6
RG
112 {
113 return "delay by "+std::to_string(d_msec)+ " msec";
114 }
115private:
116 int d_msec;
117};
118
119
120class TeeAction : public DNSAction
121{
122public:
123 TeeAction(const ComboAddress& ca, bool addECS=false);
124 ~TeeAction() override;
d67c1cbe
SK
125 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override;
126 std::string toString() const override;
127 std::map<std::string, double> getStats() const override;
6bb38cd6
RG
128
129private:
130 ComboAddress d_remote;
131 std::thread d_worker;
132 void worker();
133
134 int d_fd;
135 mutable std::atomic<unsigned long> d_senderrors{0};
136 unsigned long d_recverrors{0};
137 mutable std::atomic<unsigned long> d_queries{0};
138 unsigned long d_responses{0};
139 unsigned long d_nxdomains{0};
140 unsigned long d_servfails{0};
141 unsigned long d_refuseds{0};
142 unsigned long d_formerrs{0};
143 unsigned long d_notimps{0};
144 unsigned long d_noerrors{0};
145 mutable unsigned long d_tcpdrops{0};
146 unsigned long d_otherrcode{0};
147 std::atomic<bool> d_pleaseQuit{false};
148 bool d_addECS{false};
149};
150
151TeeAction::TeeAction(const ComboAddress& ca, bool addECS) : d_remote(ca), d_addECS(addECS)
152{
153 d_fd=SSocket(d_remote.sin4.sin_family, SOCK_DGRAM, 0);
154 SConnect(d_fd, d_remote);
155 setNonBlocking(d_fd);
156 d_worker=std::thread(std::bind(&TeeAction::worker, this));
157}
158
159TeeAction::~TeeAction()
160{
161 d_pleaseQuit=true;
162 close(d_fd);
163 d_worker.join();
164}
165
d67c1cbe 166DNSAction::Action TeeAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
6bb38cd6
RG
167{
168 if(dq->tcp) {
169 d_tcpdrops++;
170 }
171 else {
172 ssize_t res;
173 d_queries++;
174
175 if(d_addECS) {
176 std::string query;
177 uint16_t len = dq->len;
178 bool ednsAdded = false;
179 bool ecsAdded = false;
180 query.reserve(dq->size);
181 query.assign((char*) dq->dh, len);
182
d67c1cbe 183 std::string newECSOption;
cbf4e13a
RG
184 generateECSOption(dq->ecsSet ? dq->ecs.getNetwork() : *dq->remote, newECSOption, dq->ecsSet ? dq->ecs.getBits() : dq->ecsPrefixLength);
185
be90d6bd 186 if (!handleEDNSClientSubnet(const_cast<char*>(query.c_str()), query.capacity(), dq->qname->wirelength(), &len, ednsAdded, ecsAdded, dq->ecsOverride, newECSOption, g_preserveTrailingData)) {
6bb38cd6
RG
187 return DNSAction::Action::None;
188 }
189
190 res = send(d_fd, query.c_str(), len, 0);
191 }
192 else {
193 res = send(d_fd, (char*)dq->dh, dq->len, 0);
194 }
195
196 if (res <= 0)
197 d_senderrors++;
198 }
199 return DNSAction::Action::None;
200}
201
d67c1cbe 202std::string TeeAction::toString() const
6bb38cd6
RG
203{
204 return "tee to "+d_remote.toStringWithPort();
205}
206
d67c1cbe 207std::map<std::string,double> TeeAction::getStats() const
6bb38cd6
RG
208{
209 return {{"queries", d_queries},
210 {"responses", d_responses},
211 {"recv-errors", d_recverrors},
212 {"send-errors", d_senderrors},
213 {"noerrors", d_noerrors},
214 {"nxdomains", d_nxdomains},
215 {"refuseds", d_refuseds},
216 {"servfails", d_servfails},
217 {"other-rcode", d_otherrcode},
218 {"tcp-drops", d_tcpdrops}
219 };
220}
221
222void TeeAction::worker()
223{
519f5484 224 setThreadName("dnsdist/TeeWork");
6bb38cd6
RG
225 char packet[1500];
226 int res=0;
227 struct dnsheader* dh=(struct dnsheader*)packet;
228 for(;;) {
229 res=waitForData(d_fd, 0, 250000);
230 if(d_pleaseQuit)
231 break;
232 if(res < 0) {
233 usleep(250000);
234 continue;
235 }
236 if(res==0)
237 continue;
238 res=recv(d_fd, packet, sizeof(packet), 0);
239 if(res <= (int)sizeof(struct dnsheader))
240 d_recverrors++;
241 else if(res > 0)
242 d_responses++;
243
244 if(dh->rcode == RCode::NoError)
245 d_noerrors++;
246 else if(dh->rcode == RCode::ServFail)
247 d_servfails++;
248 else if(dh->rcode == RCode::NXDomain)
249 d_nxdomains++;
250 else if(dh->rcode == RCode::Refused)
251 d_refuseds++;
252 else if(dh->rcode == RCode::FormErr)
253 d_formerrs++;
254 else if(dh->rcode == RCode::NotImp)
255 d_notimps++;
256 }
257}
258
259class PoolAction : public DNSAction
260{
261public:
262 PoolAction(const std::string& pool) : d_pool(pool) {}
d67c1cbe 263 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
264 {
265 *ruleresult=d_pool;
266 return Action::Pool;
267 }
d67c1cbe 268 std::string toString() const override
6bb38cd6
RG
269 {
270 return "to pool "+d_pool;
271 }
272
273private:
d67c1cbe 274 std::string d_pool;
6bb38cd6
RG
275};
276
277
278class QPSPoolAction : public DNSAction
279{
280public:
281 QPSPoolAction(unsigned int limit, const std::string& pool) : d_qps(limit, limit), d_pool(pool) {}
d67c1cbe 282 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
283 {
284 if(d_qps.check()) {
285 *ruleresult=d_pool;
286 return Action::Pool;
287 }
288 else
289 return Action::None;
290 }
d67c1cbe 291 std::string toString() const override
6bb38cd6
RG
292 {
293 return "max " +std::to_string(d_qps.getRate())+" to pool "+d_pool;
294 }
295
296private:
297 QPSLimiter d_qps;
d67c1cbe 298 std::string d_pool;
6bb38cd6
RG
299};
300
301class RCodeAction : public DNSAction
302{
303public:
f6007449 304 RCodeAction(uint8_t rcode) : d_rcode(rcode) {}
d67c1cbe 305 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
306 {
307 dq->dh->rcode = d_rcode;
308 dq->dh->qr = true; // for good measure
d545a872 309 setResponseHeadersFromConfig(*dq->dh, d_responseConfig);
6bb38cd6
RG
310 return Action::HeaderModify;
311 }
d67c1cbe 312 std::string toString() const override
6bb38cd6
RG
313 {
314 return "set rcode "+std::to_string(d_rcode);
315 }
316
d545a872 317 ResponseConfig d_responseConfig;
6bb38cd6 318private:
f6007449 319 uint8_t d_rcode;
6bb38cd6
RG
320};
321
a9613dfe
DA
322class ERCodeAction : public DNSAction
323{
324public:
325 ERCodeAction(uint8_t rcode) : d_rcode(rcode) {}
d67c1cbe 326 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
a9613dfe
DA
327 {
328 dq->dh->rcode = (d_rcode & 0xF);
329 dq->ednsRCode = ((d_rcode & 0xFFF0) >> 4);
330 dq->dh->qr = true; // for good measure
d545a872 331 setResponseHeadersFromConfig(*dq->dh, d_responseConfig);
a9613dfe
DA
332 return Action::HeaderModify;
333 }
d67c1cbe 334 std::string toString() const override
a9613dfe
DA
335 {
336 return "set ercode "+ERCode::to_s(d_rcode);
337 }
338
d545a872 339 ResponseConfig d_responseConfig;
a9613dfe
DA
340private:
341 uint8_t d_rcode;
342};
343
6bb38cd6
RG
344class TCAction : public DNSAction
345{
346public:
d67c1cbe 347 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
348 {
349 return Action::Truncate;
350 }
d67c1cbe 351 std::string toString() const override
6bb38cd6
RG
352 {
353 return "tc=1 answer";
354 }
355};
356
d67c1cbe 357DNSAction::Action LuaAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
ac2ccb4e
CH
358{
359 std::lock_guard<std::mutex> lock(g_luamutex);
360 try {
361 auto ret = d_func(dq);
c134cbaa 362 if (ruleresult) {
d67c1cbe 363 if (boost::optional<std::string> rule = std::get<1>(ret)) {
c134cbaa
CHB
364 *ruleresult = *rule;
365 }
366 else {
367 // default to empty string
368 ruleresult->clear();
369 }
370 }
ac2ccb4e
CH
371 return (Action)std::get<0>(ret);
372 } catch (std::exception &e) {
373 warnlog("LuaAction failed inside lua, returning ServFail: %s", e.what());
374 } catch (...) {
375 warnlog("LuaAction failed inside lua, returning ServFail: [unknown exception]");
376 }
377 return DNSAction::Action::ServFail;
378}
379
d67c1cbe 380DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, std::string* ruleresult) const
ac2ccb4e
CH
381{
382 std::lock_guard<std::mutex> lock(g_luamutex);
383 try {
384 auto ret = d_func(dr);
c134cbaa 385 if(ruleresult) {
d67c1cbe 386 if (boost::optional<std::string> rule = std::get<1>(ret)) {
c134cbaa
CHB
387 *ruleresult = *rule;
388 }
389 else {
390 // default to empty string
391 ruleresult->clear();
392 }
393 }
ac2ccb4e
CH
394 return (Action)std::get<0>(ret);
395 } catch (std::exception &e) {
396 warnlog("LuaResponseAction failed inside lua, returning ServFail: %s", e.what());
397 } catch (...) {
398 warnlog("LuaResponseAction failed inside lua, returning ServFail: [unknown exception]");
399 }
400 return DNSResponseAction::Action::ServFail;
401}
402
d67c1cbe 403DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
6bb38cd6
RG
404{
405 uint16_t qtype = dq->qtype;
406 // do we even have a response?
202c4ab9
RG
407 if (d_cname.empty() &&
408 d_rawResponse.empty() &&
409 d_types.count(qtype) == 0) {
6bb38cd6 410 return Action::None;
202c4ab9 411 }
6bb38cd6
RG
412
413 vector<ComboAddress> addrs;
202c4ab9
RG
414 unsigned int totrdatalen = 0;
415 uint16_t numberOfRecords = 0;
6bb38cd6
RG
416 if (!d_cname.empty()) {
417 qtype = QType::CNAME;
418 totrdatalen += d_cname.toDNSString().size();
202c4ab9
RG
419 numberOfRecords = 1;
420 } else if (!d_rawResponse.empty()) {
421 totrdatalen += d_rawResponse.size();
422 numberOfRecords = 1;
423 }
424 else {
6bb38cd6
RG
425 for(const auto& addr : d_addrs) {
426 if(qtype != QType::ANY && ((addr.sin4.sin_family == AF_INET && qtype != QType::A) ||
427 (addr.sin4.sin_family == AF_INET6 && qtype != QType::AAAA))) {
428 continue;
429 }
430 totrdatalen += addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
431 addrs.push_back(addr);
202c4ab9 432 ++numberOfRecords;
6bb38cd6
RG
433 }
434 }
435
436 if(addrs.size() > 1)
437 random_shuffle(addrs.begin(), addrs.end());
438
439 unsigned int consumed=0;
440 DNSName ignore((char*)dq->dh, dq->len, sizeof(dnsheader), false, 0, 0, &consumed);
441
202c4ab9 442 if (dq->size < (sizeof(dnsheader) + consumed + 4 + numberOfRecords*12 /* recordstart */ + totrdatalen)) {
6bb38cd6
RG
443 return Action::None;
444 }
445
e7c732b8
RG
446 bool dnssecOK = false;
447 bool hadEDNS = false;
448 if (g_addEDNSToSelfGeneratedResponses && queryHasEDNS(*dq)) {
449 hadEDNS = true;
450 dnssecOK = getEDNSZ(*dq) & EDNS_HEADER_FLAG_DO;
451 }
452
6bb38cd6
RG
453 dq->len = sizeof(dnsheader) + consumed + 4; // there goes your EDNS
454 char* dest = ((char*)dq->dh) + dq->len;
455
456 dq->dh->qr = true; // for good measure
d545a872 457 setResponseHeadersFromConfig(*dq->dh, d_responseConfig);
6bb38cd6
RG
458 dq->dh->ancount = 0;
459 dq->dh->arcount = 0; // for now, forget about your EDNS, we're marching over it
460
202c4ab9
RG
461 uint32_t ttl = htonl(d_responseConfig.ttl);
462 unsigned char recordstart[] = {0xc0, 0x0c, // compressed name
463 0, 0, // QTYPE
464 0, QClass::IN,
465 0, 0, 0, 0, // TTL
466 0, 0 }; // rdata length
467 static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
468 memcpy(&recordstart[6], &ttl, sizeof(ttl));
469
470 if (qtype == QType::CNAME) {
471 const std::string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
472 uint16_t rdataLen = htons(wireData.length());
473 qtype = htons(qtype);
474 memcpy(&recordstart[2], &qtype, sizeof(qtype));
475 memcpy(&recordstart[10], &rdataLen, sizeof(rdataLen));
6bb38cd6
RG
476
477 memcpy(dest, recordstart, sizeof(recordstart));
478 dest += sizeof(recordstart);
479 memcpy(dest, wireData.c_str(), wireData.length());
480 dq->len += wireData.length() + sizeof(recordstart);
481 dq->dh->ancount++;
482 }
202c4ab9
RG
483 else if (!d_rawResponse.empty()) {
484 uint16_t rdataLen = htons(d_rawResponse.size());
485 qtype = htons(qtype);
486 memcpy(&recordstart[2], &qtype, sizeof(qtype));
487 memcpy(&recordstart[10], &rdataLen, sizeof(rdataLen));
488
489 memcpy(dest, recordstart, sizeof(recordstart));
490 dest += sizeof(recordstart);
491 memcpy(dest, d_rawResponse.c_str(), d_rawResponse.size());
492 dq->len += d_rawResponse.size() + sizeof(recordstart);
493 dq->dh->ancount++;
494 }
6bb38cd6
RG
495 else {
496 for(const auto& addr : addrs) {
202c4ab9
RG
497 uint16_t rdataLen = htons(addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
498 qtype = htons(addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA);
499 memcpy(&recordstart[2], &qtype, sizeof(qtype));
500 memcpy(&recordstart[10], &rdataLen, sizeof(rdataLen));
6bb38cd6
RG
501
502 memcpy(dest, recordstart, sizeof(recordstart));
503 dest += sizeof(recordstart);
504
505 memcpy(dest,
506 addr.sin4.sin_family == AF_INET ? (void*)&addr.sin4.sin_addr.s_addr : (void*)&addr.sin6.sin6_addr.s6_addr,
202c4ab9
RG
507 addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
508 dest += (addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
509 dq->len += (addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr)) + sizeof(recordstart);
6bb38cd6
RG
510 dq->dh->ancount++;
511 }
512 }
513
514 dq->dh->ancount = htons(dq->dh->ancount);
515
8d72bcdd 516 if (hadEDNS) {
5e98ccfa 517 addEDNS(dq->dh, dq->len, dq->size, dnssecOK, g_PayloadSizeSelfGenAnswers, 0);
e7c732b8
RG
518 }
519
6bb38cd6
RG
520 return Action::HeaderModify;
521}
522
523class MacAddrAction : public DNSAction
524{
525public:
526 MacAddrAction(uint16_t code) : d_code(code)
527 {}
d67c1cbe 528 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
529 {
530 if(dq->dh->arcount)
531 return Action::None;
532
d67c1cbe 533 std::string mac = getMACAddress(*dq->remote);
6bb38cd6
RG
534 if(mac.empty())
535 return Action::None;
536
d67c1cbe 537 std::string optRData;
6bb38cd6
RG
538 generateEDNSOption(d_code, mac, optRData);
539
d67c1cbe 540 std::string res;
5e98ccfa 541 generateOptRR(optRData, res, g_EdnsUDPPayloadSize, 0, false);
6bb38cd6
RG
542
543 if ((dq->size - dq->len) < res.length())
544 return Action::None;
545
546 dq->dh->arcount = htons(1);
547 char* dest = ((char*)dq->dh) + dq->len;
548 memcpy(dest, res.c_str(), res.length());
549 dq->len += res.length();
550
551 return Action::None;
552 }
d67c1cbe 553 std::string toString() const override
6bb38cd6
RG
554 {
555 return "add EDNS MAC (code="+std::to_string(d_code)+")";
556 }
557private:
558 uint16_t d_code{3};
559};
560
561class NoRecurseAction : public DNSAction
562{
563public:
d67c1cbe 564 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
565 {
566 dq->dh->rd = false;
567 return Action::None;
568 }
d67c1cbe 569 std::string toString() const override
6bb38cd6
RG
570 {
571 return "set rd=0";
572 }
573};
574
575class LogAction : public DNSAction, public boost::noncopyable
576{
577public:
320b20cd 578 LogAction(): d_fp(nullptr, fclose)
6bb38cd6
RG
579 {
580 }
320b20cd
RG
581
582 LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_binary(binary), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp)
6bb38cd6
RG
583 {
584 if(str.empty())
585 return;
586 if(append)
320b20cd 587 d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(str.c_str(), "a+"), fclose);
6bb38cd6 588 else
320b20cd 589 d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(str.c_str(), "w"), fclose);
6bb38cd6 590 if(!d_fp)
a2a81d42 591 throw std::runtime_error("Unable to open file '"+str+"' for logging: "+stringerror());
6bb38cd6 592 if(!buffered)
320b20cd 593 setbuf(d_fp.get(), 0);
6bb38cd6 594 }
320b20cd 595
d67c1cbe 596 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6 597 {
320b20cd
RG
598 if (!d_fp) {
599 if (!d_verboseOnly || g_verbose) {
600 if (d_includeTimestamp) {
601 infolog("[%u.%u] Packet from %s for %s %s with id %d", static_cast<unsigned long long>(dq->queryTime->tv_sec), static_cast<unsigned long>(dq->queryTime->tv_nsec), dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id);
602 }
603 else {
604 infolog("Packet from %s for %s %s with id %d", dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id);
605 }
606 }
6bb38cd6
RG
607 }
608 else {
320b20cd 609 if (d_binary) {
d67c1cbe 610 std::string out = dq->qname->toDNSString();
320b20cd
RG
611 if (d_includeTimestamp) {
612 uint64_t tv_sec = static_cast<uint64_t>(dq->queryTime->tv_sec);
613 uint32_t tv_nsec = static_cast<uint32_t>(dq->queryTime->tv_nsec);
614 fwrite(&tv_sec, sizeof(tv_sec), 1, d_fp.get());
615 fwrite(&tv_nsec, sizeof(tv_nsec), 1, d_fp.get());
616 }
617 uint16_t id = dq->dh->id;
618 fwrite(&id, sizeof(id), 1, d_fp.get());
619 fwrite(out.c_str(), 1, out.size(), d_fp.get());
620 fwrite(&dq->qtype, sizeof(dq->qtype), 1, d_fp.get());
621 fwrite(&dq->remote->sin4.sin_family, sizeof(dq->remote->sin4.sin_family), 1, d_fp.get());
622 if (dq->remote->sin4.sin_family == AF_INET) {
623 fwrite(&dq->remote->sin4.sin_addr.s_addr, sizeof(dq->remote->sin4.sin_addr.s_addr), 1, d_fp.get());
624 }
625 else if (dq->remote->sin4.sin_family == AF_INET6) {
626 fwrite(&dq->remote->sin6.sin6_addr.s6_addr, sizeof(dq->remote->sin6.sin6_addr.s6_addr), 1, d_fp.get());
627 }
628 fwrite(&dq->remote->sin4.sin_port, sizeof(dq->remote->sin4.sin_port), 1, d_fp.get());
6bb38cd6
RG
629 }
630 else {
320b20cd
RG
631 if (d_includeTimestamp) {
632 fprintf(d_fp.get(), "[%llu.%lu] Packet from %s for %s %s with id %d\n", static_cast<unsigned long long>(dq->queryTime->tv_sec), static_cast<unsigned long>(dq->queryTime->tv_nsec), dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).getName().c_str(), dq->dh->id);
633 }
634 else {
635 fprintf(d_fp.get(), "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);
636 }
6bb38cd6
RG
637 }
638 }
639 return Action::None;
640 }
320b20cd 641
d67c1cbe 642 std::string toString() const override
6bb38cd6
RG
643 {
644 if (!d_fname.empty()) {
645 return "log to " + d_fname;
646 }
647 return "log";
648 }
649private:
d67c1cbe 650 std::string d_fname;
320b20cd 651 std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
6bb38cd6 652 bool d_binary{true};
320b20cd
RG
653 bool d_verboseOnly{true};
654 bool d_includeTimestamp{false};
6bb38cd6
RG
655};
656
b62c160b 657class LogResponseAction : public DNSResponseAction, public boost::noncopyable
658{
659public:
660 LogResponseAction(): d_fp(nullptr, fclose)
661 {
662 }
663
664 LogResponseAction(const std::string& str, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp)
665 {
666 if(str.empty())
667 return;
668 if(append)
669 d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(str.c_str(), "a+"), fclose);
670 else
671 d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(str.c_str(), "w"), fclose);
672 if(!d_fp)
673 throw std::runtime_error("Unable to open file '"+str+"' for logging: "+stringerror());
674 if(!buffered)
675 setbuf(d_fp.get(), 0);
676 }
677
678 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
679 {
680 if (!d_fp) {
681 if (!d_verboseOnly || g_verbose) {
682 if (d_includeTimestamp) {
683 infolog("[%u.%u] Answer to %s for %s %s (%s) with id %d", static_cast<unsigned long long>(dr->queryTime->tv_sec), static_cast<unsigned long>(dr->queryTime->tv_nsec), dr->remote->toStringWithPort(), dr->qname->toString(), QType(dr->qtype).getName(), RCode::to_s(dr->dh->rcode), dr->dh->id);
684 }
685 else {
686 infolog("Answer to %s for %s %s (%s) with id %d", dr->remote->toStringWithPort(), dr->qname->toString(), QType(dr->qtype).getName(), RCode::to_s(dr->dh->rcode), dr->dh->id);
687 }
688 }
689 }
690 else {
691 if (d_includeTimestamp) {
692 fprintf(d_fp.get(), "[%llu.%lu] Answer to %s for %s %s (%s) with id %d\n", static_cast<unsigned long long>(dr->queryTime->tv_sec), static_cast<unsigned long>(dr->queryTime->tv_nsec), dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).getName().c_str(), RCode::to_s(dr->dh->rcode).c_str(), dr->dh->id);
693 }
694 else {
695 fprintf(d_fp.get(), "Answer to %s for %s %s (%s) with id %d\n", dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).getName().c_str(), RCode::to_s(dr->dh->rcode).c_str(), dr->dh->id);
696 }
697 }
698 return Action::None;
699 }
700
701 std::string toString() const override
702 {
703 if (!d_fname.empty()) {
704 return "log to " + d_fname;
705 }
706 return "log";
707 }
708private:
709 std::string d_fname;
710 std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
711 bool d_verboseOnly{true};
712 bool d_includeTimestamp{false};
713};
714
6bb38cd6
RG
715
716class DisableValidationAction : public DNSAction
717{
718public:
d67c1cbe 719 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
720 {
721 dq->dh->cd = true;
722 return Action::None;
723 }
d67c1cbe 724 std::string toString() const override
6bb38cd6
RG
725 {
726 return "set cd=1";
727 }
728};
729
730class SkipCacheAction : public DNSAction
731{
732public:
d67c1cbe 733 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
734 {
735 dq->skipCache = true;
736 return Action::None;
737 }
d67c1cbe 738 std::string toString() const override
6bb38cd6
RG
739 {
740 return "skip cache";
741 }
742};
743
acb8f5d5
CH
744class TempFailureCacheTTLAction : public DNSAction
745{
746public:
747 TempFailureCacheTTLAction(uint32_t ttl) : d_ttl(ttl)
748 {}
d67c1cbe 749 TempFailureCacheTTLAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
acb8f5d5
CH
750 {
751 dq->tempFailureTTL = d_ttl;
752 return Action::None;
753 }
d67c1cbe 754 std::string toString() const override
acb8f5d5
CH
755 {
756 return "set tempfailure cache ttl to "+std::to_string(d_ttl);
757 }
758private:
759 uint32_t d_ttl;
760};
761
6bb38cd6
RG
762class ECSPrefixLengthAction : public DNSAction
763{
764public:
765 ECSPrefixLengthAction(uint16_t v4Length, uint16_t v6Length) : d_v4PrefixLength(v4Length), d_v6PrefixLength(v6Length)
766 {
767 }
d67c1cbe 768 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
769 {
770 dq->ecsPrefixLength = dq->remote->sin4.sin_family == AF_INET ? d_v4PrefixLength : d_v6PrefixLength;
771 return Action::None;
772 }
d67c1cbe 773 std::string toString() const override
6bb38cd6
RG
774 {
775 return "set ECS prefix length to " + std::to_string(d_v4PrefixLength) + "/" + std::to_string(d_v6PrefixLength);
776 }
777private:
778 uint16_t d_v4PrefixLength;
779 uint16_t d_v6PrefixLength;
780};
781
782class ECSOverrideAction : public DNSAction
783{
784public:
785 ECSOverrideAction(bool ecsOverride) : d_ecsOverride(ecsOverride)
786 {
787 }
d67c1cbe 788 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
789 {
790 dq->ecsOverride = d_ecsOverride;
791 return Action::None;
792 }
d67c1cbe 793 std::string toString() const override
6bb38cd6
RG
794 {
795 return "set ECS override to " + std::to_string(d_ecsOverride);
796 }
797private:
798 bool d_ecsOverride;
799};
800
801
802class DisableECSAction : public DNSAction
803{
804public:
d67c1cbe 805 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
806 {
807 dq->useECS = false;
808 return Action::None;
809 }
d67c1cbe 810 std::string toString() const override
6bb38cd6
RG
811 {
812 return "disable ECS";
813 }
814};
815
bd14f087
RG
816class SetECSAction : public DNSAction
817{
818public:
4bd24482 819 SetECSAction(const Netmask& v4): d_v4(v4), d_hasV6(false)
bd14f087
RG
820 {
821 }
822
4bd24482 823 SetECSAction(const Netmask& v4, const Netmask& v6): d_v4(v4), d_v6(v6), d_hasV6(true)
bd14f087
RG
824 {
825 }
826
d67c1cbe 827 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
bd14f087
RG
828 {
829 dq->ecsSet = true;
830
831 if (d_hasV6) {
832 dq->ecs = dq->remote->isIPv4() ? d_v4 : d_v6;
833 }
834 else {
835 dq->ecs = d_v4;
836 }
837
838 return Action::None;
839 }
840
d67c1cbe 841 std::string toString() const override
bd14f087 842 {
d67c1cbe 843 std::string result = "set ECS to " + d_v4.toString();
bd14f087
RG
844 if (d_hasV6) {
845 result += " / " + d_v6.toString();
846 }
847 return result;
848 }
849
850private:
851 Netmask d_v4;
852 Netmask d_v6;
853 bool d_hasV6;
854};
855
856
82a91ddf
CH
857class DnstapLogAction : public DNSAction, public boost::noncopyable
858{
859public:
dd1a3034 860 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)
82a91ddf
CH
861 {
862 }
d67c1cbe 863 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
82a91ddf
CH
864 {
865#ifdef HAVE_PROTOBUF
866 DnstapMessage message(d_identity, dq->remote, dq->local, dq->tcp, reinterpret_cast<const char*>(dq->dh), dq->len, dq->queryTime, nullptr);
867 {
868 if (d_alterFunc) {
869 std::lock_guard<std::mutex> lock(g_luamutex);
dd1a3034 870 (*d_alterFunc)(dq, &message);
82a91ddf
CH
871 }
872 }
873 std::string data;
874 message.serialize(data);
875 d_logger->queueData(data);
876#endif /* HAVE_PROTOBUF */
877 return Action::None;
878 }
d67c1cbe 879 std::string toString() const override
82a91ddf
CH
880 {
881 return "remote log as dnstap to " + (d_logger ? d_logger->toString() : "");
882 }
883private:
884 std::string d_identity;
885 std::shared_ptr<RemoteLoggerInterface> d_logger;
dd1a3034 886 boost::optional<std::function<void(DNSQuestion*, DnstapMessage*)> > d_alterFunc;
82a91ddf
CH
887};
888
6bb38cd6
RG
889class RemoteLogAction : public DNSAction, public boost::noncopyable
890{
891public:
dd1a3034 892 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)
6bb38cd6
RG
893 {
894 }
d67c1cbe 895 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
896 {
897#ifdef HAVE_PROTOBUF
898 if (!dq->uniqueId) {
d61aa945 899 dq->uniqueId = getUniqueID();
6bb38cd6
RG
900 }
901
902 DNSDistProtoBufMessage message(*dq);
312a09a6
RG
903 if (!d_serverID.empty()) {
904 message.setServerIdentity(d_serverID);
6bb38cd6 905 }
312a09a6 906
af7afecf
RG
907#if HAVE_LIBCRYPTO
908 if (!d_ipEncryptKey.empty())
909 {
910 message.setRequestor(encryptCA(*dq->remote, d_ipEncryptKey));
911 }
912#endif /* HAVE_LIBCRYPTO */
913
312a09a6
RG
914 if (d_alterFunc) {
915 std::lock_guard<std::mutex> lock(g_luamutex);
dd1a3034 916 (*d_alterFunc)(dq, &message);
312a09a6
RG
917 }
918
6bb38cd6
RG
919 std::string data;
920 message.serialize(data);
921 d_logger->queueData(data);
922#endif /* HAVE_PROTOBUF */
923 return Action::None;
924 }
d67c1cbe 925 std::string toString() const override
6bb38cd6
RG
926 {
927 return "remote log to " + (d_logger ? d_logger->toString() : "");
928 }
929private:
82a91ddf 930 std::shared_ptr<RemoteLoggerInterface> d_logger;
dd1a3034 931 boost::optional<std::function<void(DNSQuestion*, DNSDistProtoBufMessage*)> > d_alterFunc;
312a09a6 932 std::string d_serverID;
af7afecf 933 std::string d_ipEncryptKey;
6bb38cd6
RG
934};
935
936class SNMPTrapAction : public DNSAction
937{
938public:
939 SNMPTrapAction(const std::string& reason): d_reason(reason)
940 {
941 }
d67c1cbe 942 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6
RG
943 {
944 if (g_snmpAgent && g_snmpTrapsEnabled) {
945 g_snmpAgent->sendDNSTrap(*dq, d_reason);
946 }
947
948 return Action::None;
949 }
d67c1cbe 950 std::string toString() const override
6bb38cd6
RG
951 {
952 return "send SNMP trap";
953 }
954private:
955 std::string d_reason;
956};
957
958class TagAction : public DNSAction
959{
960public:
f3b1a1ef 961 TagAction(const std::string& tag, const std::string& value): d_tag(tag), d_value(value)
6bb38cd6
RG
962 {
963 }
d67c1cbe 964 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
6bb38cd6 965 {
15fac047 966 if (!dq->qTag) {
6bb38cd6
RG
967 dq->qTag = std::make_shared<QTag>();
968 }
969
15fac047 970 dq->qTag->insert({d_tag, d_value});
6bb38cd6
RG
971
972 return Action::None;
973 }
d67c1cbe 974 std::string toString() const override
6bb38cd6
RG
975 {
976 return "set tag '" + d_tag + "' to value '" + d_value + "'";
977 }
978private:
979 std::string d_tag;
980 std::string d_value;
981};
982
82a91ddf
CH
983class DnstapLogResponseAction : public DNSResponseAction, public boost::noncopyable
984{
985public:
dd1a3034 986 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)
82a91ddf
CH
987 {
988 }
d67c1cbe 989 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
82a91ddf
CH
990 {
991#ifdef HAVE_PROTOBUF
992 struct timespec now;
993 gettime(&now, true);
994 DnstapMessage message(d_identity, dr->remote, dr->local, dr->tcp, reinterpret_cast<const char*>(dr->dh), dr->len, dr->queryTime, &now);
995 {
996 if (d_alterFunc) {
997 std::lock_guard<std::mutex> lock(g_luamutex);
dd1a3034 998 (*d_alterFunc)(dr, &message);
82a91ddf
CH
999 }
1000 }
1001 std::string data;
1002 message.serialize(data);
1003 d_logger->queueData(data);
1004#endif /* HAVE_PROTOBUF */
1005 return Action::None;
1006 }
d67c1cbe 1007 std::string toString() const override
82a91ddf
CH
1008 {
1009 return "log response as dnstap to " + (d_logger ? d_logger->toString() : "");
1010 }
1011private:
1012 std::string d_identity;
1013 std::shared_ptr<RemoteLoggerInterface> d_logger;
dd1a3034 1014 boost::optional<std::function<void(DNSResponse*, DnstapMessage*)> > d_alterFunc;
82a91ddf
CH
1015};
1016
6bb38cd6
RG
1017class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
1018{
1019public:
dd1a3034 1020 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)
6bb38cd6
RG
1021 {
1022 }
d67c1cbe 1023 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
6bb38cd6
RG
1024 {
1025#ifdef HAVE_PROTOBUF
1026 if (!dr->uniqueId) {
d61aa945 1027 dr->uniqueId = getUniqueID();
6bb38cd6
RG
1028 }
1029
1030 DNSDistProtoBufMessage message(*dr, d_includeCNAME);
312a09a6
RG
1031 if (!d_serverID.empty()) {
1032 message.setServerIdentity(d_serverID);
1033 }
1034
af7afecf
RG
1035#if HAVE_LIBCRYPTO
1036 if (!d_ipEncryptKey.empty())
1037 {
1038 message.setRequestor(encryptCA(*dr->remote, d_ipEncryptKey));
1039 }
1040#endif /* HAVE_LIBCRYPTO */
1041
312a09a6
RG
1042 if (d_alterFunc) {
1043 std::lock_guard<std::mutex> lock(g_luamutex);
dd1a3034 1044 (*d_alterFunc)(dr, &message);
6bb38cd6 1045 }
312a09a6 1046
6bb38cd6
RG
1047 std::string data;
1048 message.serialize(data);
1049 d_logger->queueData(data);
1050#endif /* HAVE_PROTOBUF */
1051 return Action::None;
1052 }
d67c1cbe 1053 std::string toString() const override
6bb38cd6
RG
1054 {
1055 return "remote log response to " + (d_logger ? d_logger->toString() : "");
1056 }
1057private:
82a91ddf 1058 std::shared_ptr<RemoteLoggerInterface> d_logger;
dd1a3034 1059 boost::optional<std::function<void(DNSResponse*, DNSDistProtoBufMessage*)> > d_alterFunc;
312a09a6 1060 std::string d_serverID;
af7afecf 1061 std::string d_ipEncryptKey;
6bb38cd6
RG
1062 bool d_includeCNAME;
1063};
1064
1065class DropResponseAction : public DNSResponseAction
1066{
1067public:
d67c1cbe 1068 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
6bb38cd6
RG
1069 {
1070 return Action::Drop;
1071 }
d67c1cbe 1072 std::string toString() const override
6bb38cd6
RG
1073 {
1074 return "drop";
1075 }
1076};
1077
1078class AllowResponseAction : public DNSResponseAction
1079{
1080public:
d67c1cbe 1081 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
6bb38cd6
RG
1082 {
1083 return Action::Allow;
1084 }
d67c1cbe 1085 std::string toString() const override
6bb38cd6
RG
1086 {
1087 return "allow";
1088 }
1089};
1090
1091class DelayResponseAction : public DNSResponseAction
1092{
1093public:
1094 DelayResponseAction(int msec) : d_msec(msec)
1095 {}
d67c1cbe 1096 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
6bb38cd6
RG
1097 {
1098 *ruleresult=std::to_string(d_msec);
1099 return Action::Delay;
1100 }
d67c1cbe 1101 std::string toString() const override
6bb38cd6
RG
1102 {
1103 return "delay by "+std::to_string(d_msec)+ " msec";
1104 }
1105private:
1106 int d_msec;
1107};
1108
1109class SNMPTrapResponseAction : public DNSResponseAction
1110{
1111public:
1112 SNMPTrapResponseAction(const std::string& reason): d_reason(reason)
1113 {
1114 }
d67c1cbe 1115 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
6bb38cd6
RG
1116 {
1117 if (g_snmpAgent && g_snmpTrapsEnabled) {
1118 g_snmpAgent->sendDNSTrap(*dr, d_reason);
1119 }
1120
1121 return Action::None;
1122 }
d67c1cbe 1123 std::string toString() const override
6bb38cd6
RG
1124 {
1125 return "send SNMP trap";
1126 }
1127private:
1128 std::string d_reason;
1129};
1130
1131class TagResponseAction : public DNSResponseAction
1132{
1133public:
f3b1a1ef 1134 TagResponseAction(const std::string& tag, const std::string& value): d_tag(tag), d_value(value)
6bb38cd6
RG
1135 {
1136 }
d67c1cbe 1137 DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
6bb38cd6 1138 {
15fac047 1139 if (!dr->qTag) {
6bb38cd6
RG
1140 dr->qTag = std::make_shared<QTag>();
1141 }
1142
15fac047 1143 dr->qTag->insert({d_tag, d_value});
6bb38cd6
RG
1144
1145 return Action::None;
1146 }
d67c1cbe 1147 std::string toString() const override
6bb38cd6
RG
1148 {
1149 return "set tag '" + d_tag + "' to value '" + d_value + "'";
1150 }
1151private:
1152 std::string d_tag;
1153 std::string d_value;
1154};
1155
2a28db86
RG
1156class ContinueAction : public DNSAction
1157{
1158public:
1159 ContinueAction(std::shared_ptr<DNSAction>& action): d_action(action)
1160 {
1161 }
1162
1163 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
1164 {
1165 if (d_action) {
1166 /* call the action */
1167 auto action = (*d_action)(dq, ruleresult);
1168 bool drop = false;
1169 /* apply the changes if needed (pool selection, flags, etc */
1170 processRulesResult(action, *dq, *ruleresult, drop);
1171 }
1172
1173 /* but ignore the resulting action no matter what */
1174 return Action::None;
1175 }
1176
1177 std::string toString() const override
1178 {
1179 if (d_action) {
1180 return "continue after: " + (d_action ? d_action->toString() : "");
1181 }
1182 else {
1183 return "no op";
1184 }
1185 }
1186
1187private:
1188 std::shared_ptr<DNSAction> d_action;
1189};
1190
13c1fc12
RG
1191#ifdef HAVE_DNS_OVER_HTTPS
1192class HTTPStatusAction: public DNSAction
1193{
1194public:
9676d2a9 1195 HTTPStatusAction(int code, const std::string& body, const std::string& contentType): d_body(body), d_contentType(contentType), d_code(code)
13c1fc12
RG
1196 {
1197 }
1198
1199 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
1200 {
1201 if (!dq->du) {
1202 return Action::None;
1203 }
1204
9676d2a9 1205 dq->du->setHTTPResponse(d_code, d_body, d_contentType);
13c1fc12 1206 dq->dh->qr = true; // for good measure
d545a872 1207 setResponseHeadersFromConfig(*dq->dh, d_responseConfig);
13c1fc12
RG
1208 return Action::HeaderModify;
1209 }
1210
1211 std::string toString() const override
1212 {
1213 return "return an HTTP status of " + std::to_string(d_code);
1214 }
d545a872
RG
1215
1216 ResponseConfig d_responseConfig;
13c1fc12 1217private:
13c1fc12 1218 std::string d_body;
9676d2a9 1219 std::string d_contentType;
13c1fc12
RG
1220 int d_code;
1221};
1222#endif /* HAVE_DNS_OVER_HTTPS */
1223
f441962a
RG
1224class KeyValueStoreLookupAction : public DNSAction
1225{
1226public:
1227 KeyValueStoreLookupAction(std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag): d_kvs(kvs), d_key(lookupKey), d_tag(destinationTag)
1228 {
1229 }
1230
1231 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
1232 {
77eebb37 1233 std::vector<std::string> keys = d_key->getKeys(*dq);
90fe8ae6 1234 std::string result;
77eebb37
RG
1235 for (const auto& key : keys) {
1236 if (d_kvs->getValue(key, result) == true) {
1237 break;
1238 }
1239 }
f441962a
RG
1240
1241 if (!dq->qTag) {
1242 dq->qTag = std::make_shared<QTag>();
1243 }
1244
1245 dq->qTag->insert({d_tag, std::move(result)});
1246
1247 return Action::None;
1248 }
1249
1250 std::string toString() const override
1251 {
1252 return "lookup key-value store based on '" + d_key->toString() + "' and set the result in tag '" + d_tag + "'";
1253 }
1254
1255private:
1256 std::shared_ptr<KeyValueStore> d_kvs;
1257 std::shared_ptr<KeyValueLookupKey> d_key;
1258 std::string d_tag;
1259};
1260
af9f750c
RG
1261class SetNegativeAndSOAAction: public DNSAction
1262{
1263public:
1264 SetNegativeAndSOAAction(bool nxd, const DNSName& zone, uint32_t ttl, const DNSName& mname, const DNSName& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum): d_zone(zone), d_mname(mname), d_rname(rname), d_ttl(ttl), d_serial(serial), d_refresh(refresh), d_retry(retry), d_expire(expire), d_minimum(minimum), d_nxd(nxd)
1265 {
1266 }
1267
1268 DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
1269 {
1270 if (!setNegativeAndAdditionalSOA(*dq, d_nxd, d_zone, d_ttl, d_mname, d_rname, d_serial, d_refresh, d_retry, d_expire, d_minimum)) {
1271 return Action::None;
1272 }
1273
1274 return Action::Allow;
1275 }
1276
1277 std::string toString() const override
1278 {
1279 return std::string(d_nxd ? "NXD " : "NODATA") + " with SOA";
1280 }
1281
1282private:
1283 DNSName d_zone;
1284 DNSName d_mname;
1285 DNSName d_rname;
1286 uint32_t d_ttl;
1287 uint32_t d_serial;
1288 uint32_t d_refresh;
1289 uint32_t d_retry;
1290 uint32_t d_expire;
1291 uint32_t d_minimum;
1292 bool d_nxd;
1293};
1294
d18eab67 1295template<typename T, typename ActionT>
3ebf2998 1296static void addAction(GlobalStateHolder<vector<T> > *someRulActions, const luadnsrule_t& var, const std::shared_ptr<ActionT>& action, boost::optional<luaruleparams_t>& params) {
d18eab67
CH
1297 setLuaSideEffect();
1298
1299 boost::uuids::uuid uuid;
f8a222ac
RG
1300 uint64_t creationOrder;
1301 parseRuleParams(params, uuid, creationOrder);
d18eab67
CH
1302
1303 auto rule=makeRule(var);
bccee370
RG
1304 someRulActions->modify([&rule, &action, &uuid, creationOrder](vector<T>& rulactions){
1305 rulactions.push_back({std::move(rule), std::move(action), std::move(uuid), creationOrder});
d18eab67
CH
1306 });
1307}
1308
202c4ab9 1309typedef std::unordered_map<std::string, boost::variant<bool, uint32_t> > responseParams_t;
955b9377 1310
d545a872 1311static void parseResponseConfig(boost::optional<responseParams_t> vars, ResponseConfig& config)
955b9377
RG
1312{
1313 if (vars) {
202c4ab9
RG
1314 if (vars->count("ttl")) {
1315 config.ttl = boost::get<uint32_t>((*vars)["ttl"]);
1316 }
955b9377 1317 if (vars->count("aa")) {
d545a872 1318 config.setAA = boost::get<bool>((*vars)["aa"]);
955b9377
RG
1319 }
1320 if (vars->count("ad")) {
d545a872 1321 config.setAD = boost::get<bool>((*vars)["ad"]);
955b9377
RG
1322 }
1323 if (vars->count("ra")) {
d545a872 1324 config.setRA = boost::get<bool>((*vars)["ra"]);
955b9377
RG
1325 }
1326 }
1327}
1328
d545a872
RG
1329void setResponseHeadersFromConfig(dnsheader& dh, const ResponseConfig& config)
1330{
1331 if (config.setAA) {
1332 dh.aa = *config.setAA;
1333 }
1334 if (config.setAD) {
1335 dh.ad = *config.setAD;
1336 }
1337 else {
1338 dh.ad = false;
1339 }
1340 if (config.setRA) {
1341 dh.ra = *config.setRA;
1342 }
1343 else {
1344 dh.ra = dh.rd; // for good measure
1345 }
1346}
1347
6bb38cd6
RG
1348void setupLuaActions()
1349{
4d5959e6
RG
1350 g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action, boost::optional<luaruleparams_t> params) {
1351 boost::uuids::uuid uuid;
f8a222ac
RG
1352 uint64_t creationOrder;
1353 parseRuleParams(params, uuid, creationOrder);
4d5959e6 1354
6bb38cd6 1355 auto rule=makeRule(dnsrule);
bccee370 1356 DNSDistRuleAction ra({std::move(rule), action, uuid, creationOrder});
4d5959e6 1357 return std::make_shared<DNSDistRuleAction>(ra);
6bb38cd6
RG
1358 });
1359
4d5959e6 1360 g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
d18eab67 1361 if (era.type() != typeid(std::shared_ptr<DNSAction>)) {
6bb38cd6
RG
1362 throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
1363 }
1364
d18eab67 1365 addAction(&g_rulactions, var, boost::get<std::shared_ptr<DNSAction> >(era), params);
6bb38cd6
RG
1366 });
1367
4d5959e6 1368 g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
d18eab67 1369 if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
6bb38cd6
RG
1370 throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
1371 }
1372
d18eab67 1373 addAction(&g_resprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
6bb38cd6
RG
1374 });
1375
4d5959e6 1376 g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
d18eab67 1377 if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
4d5959e6
RG
1378 throw std::runtime_error("addCacheHitResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
1379 }
1380
d18eab67 1381 addAction(&g_cachehitresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
6bb38cd6
RG
1382 });
1383
2d4783a8
CH
1384 g_lua.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
1385 if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
1386 throw std::runtime_error("addSelfAnsweredResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
1387 }
1388
1389 addAction(&g_selfansweredresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
1390 });
1391
6bb38cd6
RG
1392 g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
1393 setLuaNoSideEffect();
1394 auto stats = ta.getStats();
1395 for(const auto& s : stats) {
1396 g_outputBuffer+=s.first+"\t";
1397 if((uint64_t)s.second == s.second)
1398 g_outputBuffer += std::to_string((uint64_t)s.second)+"\n";
1399 else
1400 g_outputBuffer += std::to_string(s.second)+"\n";
1401 }
1402 });
1403
1404 g_lua.writeFunction("getAction", [](unsigned int num) {
1405 setLuaNoSideEffect();
1406 boost::optional<std::shared_ptr<DNSAction>> ret;
1407 auto rulactions = g_rulactions.getCopy();
1408 if(num < rulactions.size())
4d5959e6 1409 ret=rulactions[num].d_action;
6bb38cd6
RG
1410 return ret;
1411 });
1412
1413 g_lua.registerFunction("getStats", &DNSAction::getStats);
1414
1415 g_lua.writeFunction("LuaAction", [](LuaAction::func_t func) {
1416 setLuaSideEffect();
1417 return std::shared_ptr<DNSAction>(new LuaAction(func));
1418 });
1419
1420 g_lua.writeFunction("NoRecurseAction", []() {
1421 return std::shared_ptr<DNSAction>(new NoRecurseAction);
1422 });
1423
1424 g_lua.writeFunction("MacAddrAction", [](int code) {
1425 return std::shared_ptr<DNSAction>(new MacAddrAction(code));
1426 });
1427
d67c1cbe 1428 g_lua.writeFunction("PoolAction", [](const std::string& a) {
6bb38cd6
RG
1429 return std::shared_ptr<DNSAction>(new PoolAction(a));
1430 });
1431
1432 g_lua.writeFunction("QPSAction", [](int limit) {
1433 return std::shared_ptr<DNSAction>(new QPSAction(limit));
1434 });
1435
d67c1cbe 1436 g_lua.writeFunction("QPSPoolAction", [](int limit, const std::string& a) {
6bb38cd6
RG
1437 return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
1438 });
1439
d545a872 1440 g_lua.writeFunction("SpoofAction", [](boost::variant<std::string,vector<pair<int, std::string>>> inp, boost::optional<std::string> b, boost::optional<responseParams_t> vars ) {
6bb38cd6 1441 vector<ComboAddress> addrs;
d67c1cbe 1442 if(auto s = boost::get<std::string>(&inp))
6bb38cd6
RG
1443 addrs.push_back(ComboAddress(*s));
1444 else {
d67c1cbe 1445 const auto& v = boost::get<vector<pair<int,std::string>>>(inp);
6bb38cd6
RG
1446 for(const auto& a: v)
1447 addrs.push_back(ComboAddress(a.second));
1448 }
955b9377 1449 if(b) {
6bb38cd6 1450 addrs.push_back(ComboAddress(*b));
955b9377
RG
1451 }
1452
1453 auto ret = std::shared_ptr<DNSAction>(new SpoofAction(addrs));
955b9377 1454 auto sa = std::dynamic_pointer_cast<SpoofAction>(ret);
d545a872 1455 parseResponseConfig(vars, sa->d_responseConfig);
955b9377 1456 return ret;
6bb38cd6
RG
1457 });
1458
d545a872 1459 g_lua.writeFunction("SpoofCNAMEAction", [](const std::string& a, boost::optional<responseParams_t> vars) {
202c4ab9 1460 auto ret = std::shared_ptr<DNSAction>(new SpoofAction(DNSName(a)));
955b9377 1461 auto sa = std::dynamic_pointer_cast<SpoofAction>(ret);
202c4ab9
RG
1462 parseResponseConfig(vars, sa->d_responseConfig);
1463 return ret;
1464 });
1465
1466 g_lua.writeFunction("SpoofRawAction", [](const std::string& raw, boost::optional<responseParams_t> vars) {
1467 auto ret = std::shared_ptr<DNSAction>(new SpoofAction(raw));
1468 auto sa = std::dynamic_pointer_cast<SpoofAction>(ret);
1469 parseResponseConfig(vars, sa->d_responseConfig);
955b9377 1470 return ret;
6bb38cd6
RG
1471 });
1472
1473 g_lua.writeFunction("DropAction", []() {
1474 return std::shared_ptr<DNSAction>(new DropAction);
1475 });
1476
1477 g_lua.writeFunction("AllowAction", []() {
1478 return std::shared_ptr<DNSAction>(new AllowAction);
1479 });
1480
bc084a31
RG
1481 g_lua.writeFunction("NoneAction", []() {
1482 return std::shared_ptr<DNSAction>(new NoneAction);
1483 });
1484
6bb38cd6
RG
1485 g_lua.writeFunction("DelayAction", [](int msec) {
1486 return std::shared_ptr<DNSAction>(new DelayAction(msec));
1487 });
1488
1489 g_lua.writeFunction("TCAction", []() {
1490 return std::shared_ptr<DNSAction>(new TCAction);
1491 });
1492
1493 g_lua.writeFunction("DisableValidationAction", []() {
1494 return std::shared_ptr<DNSAction>(new DisableValidationAction);
1495 });
1496
320b20cd
RG
1497 g_lua.writeFunction("LogAction", [](boost::optional<std::string> fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
1498 return std::shared_ptr<DNSAction>(new LogAction(fname ? *fname : "", binary ? *binary : true, append ? *append : false, buffered ? *buffered : false, verboseOnly ? *verboseOnly : true, includeTimestamp ? *includeTimestamp : false));
6bb38cd6
RG
1499 });
1500
b62c160b 1501 g_lua.writeFunction("LogResponseAction", [](boost::optional<std::string> fname, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
1502 return std::shared_ptr<DNSResponseAction>(new LogResponseAction(fname ? *fname : "", append ? *append : false, buffered ? *buffered : false, verboseOnly ? *verboseOnly : true, includeTimestamp ? *includeTimestamp : false));
1503 });
1504
d545a872
RG
1505 g_lua.writeFunction("RCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
1506 auto ret = std::shared_ptr<DNSAction>(new RCodeAction(rcode));
1507 auto rca = std::dynamic_pointer_cast<RCodeAction>(ret);
1508 parseResponseConfig(vars, rca->d_responseConfig);
1509 return ret;
6bb38cd6
RG
1510 });
1511
d545a872
RG
1512 g_lua.writeFunction("ERCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
1513 auto ret = std::shared_ptr<DNSAction>(new ERCodeAction(rcode));
1514 auto erca = std::dynamic_pointer_cast<ERCodeAction>(ret);
1515 parseResponseConfig(vars, erca->d_responseConfig);
1516 return ret;
a9613dfe
DA
1517 });
1518
6bb38cd6
RG
1519 g_lua.writeFunction("SkipCacheAction", []() {
1520 return std::shared_ptr<DNSAction>(new SkipCacheAction);
1521 });
1522
acb8f5d5
CH
1523 g_lua.writeFunction("TempFailureCacheTTLAction", [](int maxTTL) {
1524 return std::shared_ptr<DNSAction>(new TempFailureCacheTTLAction(maxTTL));
1525 });
1526
6bb38cd6
RG
1527 g_lua.writeFunction("DropResponseAction", []() {
1528 return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
1529 });
1530
1531 g_lua.writeFunction("AllowResponseAction", []() {
1532 return std::shared_ptr<DNSResponseAction>(new AllowResponseAction);
1533 });
1534
1535 g_lua.writeFunction("DelayResponseAction", [](int msec) {
1536 return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
1537 });
1538
1539 g_lua.writeFunction("LuaResponseAction", [](LuaResponseAction::func_t func) {
1540 setLuaSideEffect();
1541 return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
1542 });
1543
dd1a3034 1544 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) {
7f29529f
RG
1545 if (logger) {
1546 // avoids potentially-evaluated-expression warning with clang.
1547 RemoteLoggerInterface& rl = *logger.get();
1548 if (typeid(rl) != typeid(RemoteLogger)) {
1549 // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
1550 throw std::runtime_error(std::string("RemoteLogAction only takes RemoteLogger. For other types, please look at DnstapLogAction."));
1551 }
82a91ddf 1552 }
312a09a6
RG
1553
1554 std::string serverID;
af7afecf 1555 std::string ipEncryptKey;
312a09a6
RG
1556 if (vars) {
1557 if (vars->count("serverID")) {
1558 serverID = boost::get<std::string>((*vars)["serverID"]);
1559 }
af7afecf
RG
1560 if (vars->count("ipEncryptKey")) {
1561 ipEncryptKey = boost::get<std::string>((*vars)["ipEncryptKey"]);
1562 }
312a09a6
RG
1563 }
1564
6bb38cd6 1565#ifdef HAVE_PROTOBUF
af7afecf 1566 return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc, serverID, ipEncryptKey));
6bb38cd6
RG
1567#else
1568 throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
1569#endif
1570 });
1571
dd1a3034 1572 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) {
7f29529f
RG
1573 if (logger) {
1574 // avoids potentially-evaluated-expression warning with clang.
1575 RemoteLoggerInterface& rl = *logger.get();
1576 if (typeid(rl) != typeid(RemoteLogger)) {
1577 // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
1578 throw std::runtime_error("RemoteLogResponseAction only takes RemoteLogger. For other types, please look at DnstapLogResponseAction.");
1579 }
82a91ddf 1580 }
312a09a6
RG
1581
1582 std::string serverID;
af7afecf 1583 std::string ipEncryptKey;
312a09a6
RG
1584 if (vars) {
1585 if (vars->count("serverID")) {
1586 serverID = boost::get<std::string>((*vars)["serverID"]);
1587 }
af7afecf
RG
1588 if (vars->count("ipEncryptKey")) {
1589 ipEncryptKey = boost::get<std::string>((*vars)["ipEncryptKey"]);
1590 }
312a09a6
RG
1591 }
1592
6bb38cd6 1593#ifdef HAVE_PROTOBUF
af7afecf 1594 return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, serverID, ipEncryptKey, includeCNAME ? *includeCNAME : false));
6bb38cd6
RG
1595#else
1596 throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
1597#endif
1598 });
1599
dd1a3034 1600 g_lua.writeFunction("DnstapLogAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSQuestion*, DnstapMessage*)> > alterFunc) {
82a91ddf
CH
1601#ifdef HAVE_PROTOBUF
1602 return std::shared_ptr<DNSAction>(new DnstapLogAction(identity, logger, alterFunc));
1603#else
1604 throw std::runtime_error("Protobuf support is required to use DnstapLogAction");
1605#endif
1606 });
1607
dd1a3034 1608 g_lua.writeFunction("DnstapLogResponseAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSResponse*, DnstapMessage*)> > alterFunc) {
82a91ddf
CH
1609#ifdef HAVE_PROTOBUF
1610 return std::shared_ptr<DNSResponseAction>(new DnstapLogResponseAction(identity, logger, alterFunc));
1611#else
1612 throw std::runtime_error("Protobuf support is required to use DnstapLogResponseAction");
1613#endif
1614 });
1615
6bb38cd6
RG
1616 g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
1617 return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
1618 });
1619
1620 g_lua.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
1621 return std::shared_ptr<DNSAction>(new ECSPrefixLengthAction(v4PrefixLength, v6PrefixLength));
1622 });
1623
1624 g_lua.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
1625 return std::shared_ptr<DNSAction>(new ECSOverrideAction(ecsOverride));
1626 });
1627
1628 g_lua.writeFunction("DisableECSAction", []() {
1629 return std::shared_ptr<DNSAction>(new DisableECSAction());
1630 });
1631
bd14f087
RG
1632 g_lua.writeFunction("SetECSAction", [](const std::string v4, boost::optional<std::string> v6) {
1633 if (v6) {
1634 return std::shared_ptr<DNSAction>(new SetECSAction(Netmask(v4), Netmask(*v6)));
1635 }
1636 return std::shared_ptr<DNSAction>(new SetECSAction(Netmask(v4)));
1637 });
1638
6bb38cd6
RG
1639 g_lua.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
1640#ifdef HAVE_NET_SNMP
1641 return std::shared_ptr<DNSAction>(new SNMPTrapAction(reason ? *reason : ""));
1642#else
1643 throw std::runtime_error("NET SNMP support is required to use SNMPTrapAction()");
1644#endif /* HAVE_NET_SNMP */
1645 });
1646
1647 g_lua.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
1648#ifdef HAVE_NET_SNMP
1649 return std::shared_ptr<DNSResponseAction>(new SNMPTrapResponseAction(reason ? *reason : ""));
1650#else
1651 throw std::runtime_error("NET SNMP support is required to use SNMPTrapResponseAction()");
1652#endif /* HAVE_NET_SNMP */
1653 });
1654
1655 g_lua.writeFunction("TagAction", [](std::string tag, std::string value) {
1656 return std::shared_ptr<DNSAction>(new TagAction(tag, value));
1657 });
1658
1659 g_lua.writeFunction("TagResponseAction", [](std::string tag, std::string value) {
1660 return std::shared_ptr<DNSResponseAction>(new TagResponseAction(tag, value));
1661 });
2a28db86
RG
1662
1663 g_lua.writeFunction("ContinueAction", [](std::shared_ptr<DNSAction> action) {
1664 return std::shared_ptr<DNSAction>(new ContinueAction(action));
1665 });
13c1fc12
RG
1666
1667#ifdef HAVE_DNS_OVER_HTTPS
d545a872
RG
1668 g_lua.writeFunction("HTTPStatusAction", [](uint16_t status, std::string body, boost::optional<std::string> contentType, boost::optional<responseParams_t> vars) {
1669 auto ret = std::shared_ptr<DNSAction>(new HTTPStatusAction(status, body, contentType ? *contentType : ""));
1670 auto hsa = std::dynamic_pointer_cast<HTTPStatusAction>(ret);
1671 parseResponseConfig(vars, hsa->d_responseConfig);
1672 return ret;
13c1fc12
RG
1673 });
1674#endif /* HAVE_DNS_OVER_HTTPS */
f441962a
RG
1675
1676 g_lua.writeFunction("KeyValueStoreLookupAction", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag) {
1677 return std::shared_ptr<DNSAction>(new KeyValueStoreLookupAction(kvs, lookupKey, destinationTag));
1678 });
af9f750c
RG
1679
1680 g_lua.writeFunction("SetNegativeAndSOAAction", [](bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) {
1681 return std::shared_ptr<DNSAction>(new SetNegativeAndSOAAction(nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), serial, refresh, retry, expire, minimum));
1682 });
6bb38cd6 1683}