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