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