]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua-rules.cc
b2b-migrate did not open a transaction, breaking it for lmdb
[thirdparty/pdns.git] / pdns / dnsdist-lua-rules.cc
CommitLineData
6bb38cd6
RG
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22#include "dnsdist.hh"
23#include "dnsdist-lua.hh"
05f4003d 24#include "dnsdist-rules.hh"
6bb38cd6 25
6bb38cd6
RG
26std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
27{
28 if (var.type() == typeid(std::shared_ptr<DNSRule>))
29 return *boost::get<std::shared_ptr<DNSRule>>(&var);
30
31 SuffixMatchNode smn;
32 NetmaskGroup nmg;
33 auto add=[&](string src) {
34 try {
35 nmg.addMask(src); // need to try mask first, all masks are domain names!
36 } catch(...) {
37 smn.add(DNSName(src));
38 }
39 };
40
41 if (var.type() == typeid(string))
42 add(*boost::get<string>(&var));
43
44 else if (var.type() == typeid(vector<pair<int, string>>))
45 for(const auto& a : *boost::get<vector<pair<int, string>>>(&var))
46 add(a.second);
47
48 else if (var.type() == typeid(DNSName))
49 smn.add(*boost::get<DNSName>(&var));
50
51 else if (var.type() == typeid(vector<pair<int, DNSName>>))
52 for(const auto& a : *boost::get<vector<pair<int, DNSName>>>(&var))
53 smn.add(a.second);
54
55 if(nmg.empty())
56 return std::make_shared<SuffixMatchNodeRule>(smn);
57 else
58 return std::make_shared<NetmaskGroupRule>(nmg, true);
59}
60
6a510eff 61static boost::uuids::uuid makeRuleID(std::string& id)
4d5959e6
RG
62{
63 if (id.empty()) {
d61aa945 64 return getUniqueID();
4d5959e6
RG
65 }
66
d61aa945 67 return getUniqueID(id);
4d5959e6
RG
68}
69
f8a222ac 70void parseRuleParams(boost::optional<luaruleparams_t> params, boost::uuids::uuid& uuid, uint64_t& creationOrder)
4d5959e6 71{
f8a222ac
RG
72 static uint64_t s_creationOrder = 0;
73
4d5959e6
RG
74 string uuidStr;
75
76 if (params) {
77 if (params->count("uuid")) {
78 uuidStr = boost::get<std::string>((*params)["uuid"]);
79 }
80 }
81
6a510eff 82 uuid = makeRuleID(uuidStr);
f8a222ac 83 creationOrder = s_creationOrder++;
4d5959e6
RG
84}
85
15bb664c 86typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> > > > ruleparams_t;
3a5a3376 87
d18eab67 88template<typename T>
15bb664c 89static void showRules(GlobalStateHolder<vector<T> > *someRulActions, boost::optional<ruleparams_t> vars) {
d18eab67
CH
90 setLuaNoSideEffect();
91 int num=0;
3a5a3376
CHB
92 bool showUUIDs = false;
93 size_t truncateRuleWidth = string::npos;
94
95 if (vars) {
96 if (vars->count("showUUIDs")) {
97 showUUIDs = boost::get<bool>((*vars)["showUUIDs"]);
98 }
99 if (vars->count("truncateRuleWidth")) {
100 truncateRuleWidth = boost::get<int>((*vars)["truncateRuleWidth"]);
101 }
102 }
103
104 auto rules = someRulActions->getLocal();
105 if (showUUIDs) {
f8a222ac
RG
106 boost::format fmt("%-3d %-38s %9d %9d %-56s %s\n");
107 g_outputBuffer += (fmt % "#" % "UUID" % "Cr. Order" % "Matches" % "Rule" % "Action").str();
3a5a3376
CHB
108 for(const auto& lim : *rules) {
109 string name = lim.d_rule->toString().substr(0, truncateRuleWidth);
f8a222ac 110 g_outputBuffer += (fmt % num % boost::uuids::to_string(lim.d_id) % lim.d_creationOrder % lim.d_rule->d_matches % name % lim.d_action->toString()).str();
d18eab67
CH
111 ++num;
112 }
113 }
114 else {
115 boost::format fmt("%-3d %9d %-56s %s\n");
116 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
3a5a3376
CHB
117 for(const auto& lim : *rules) {
118 string name = lim.d_rule->toString().substr(0, truncateRuleWidth);
d18eab67
CH
119 g_outputBuffer += (fmt % num % lim.d_rule->d_matches % name % lim.d_action->toString()).str();
120 ++num;
121 }
122 }
123}
124
125template<typename T>
126static void rmRule(GlobalStateHolder<vector<T> > *someRulActions, boost::variant<unsigned int, std::string> id) {
127 setLuaSideEffect();
128 auto rules = someRulActions->getCopy();
129 if (auto str = boost::get<std::string>(&id)) {
d61aa945 130 const auto uuid = getUniqueID(*str);
d18eab67
CH
131 if (rules.erase(std::remove_if(rules.begin(),
132 rules.end(),
133 [uuid](const T& a) { return a.d_id == uuid; }),
134 rules.end()) == rules.end()) {
135 g_outputBuffer = "Error: no rule matched\n";
136 return;
137 }
138 }
139 else if (auto pos = boost::get<unsigned int>(&id)) {
140 if (*pos >= rules.size()) {
141 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
142 return;
143 }
144 rules.erase(rules.begin()+*pos);
145 }
146 someRulActions->setState(rules);
147}
148
149template<typename T>
150static void topRule(GlobalStateHolder<vector<T> > *someRulActions) {
151 setLuaSideEffect();
152 auto rules = someRulActions->getCopy();
153 if(rules.empty())
154 return;
155 auto subject = *rules.rbegin();
156 rules.erase(std::prev(rules.end()));
157 rules.insert(rules.begin(), subject);
158 someRulActions->setState(rules);
159}
160
161template<typename T>
162static void mvRule(GlobalStateHolder<vector<T> > *someRespRulActions, unsigned int from, unsigned int to) {
163 setLuaSideEffect();
164 auto rules = someRespRulActions->getCopy();
165 if(from >= rules.size() || to > rules.size()) {
166 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
167 return;
168 }
169 auto subject = rules[from];
170 rules.erase(rules.begin()+from);
ca5eb63f 171 if(to > rules.size())
d18eab67
CH
172 rules.push_back(subject);
173 else {
174 if(from < to)
175 --to;
176 rules.insert(rules.begin()+to, subject);
177 }
178 someRespRulActions->setState(rules);
179}
180
6bb38cd6
RG
181void setupLuaRules()
182{
183 g_lua.writeFunction("makeRule", makeRule);
184
185 g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
186
15bb664c 187 g_lua.writeFunction("showResponseRules", [](boost::optional<ruleparams_t> vars) {
3a5a3376 188 showRules(&g_resprulactions, vars);
6bb38cd6
RG
189 });
190
7762339e 191 g_lua.writeFunction("rmResponseRule", [](boost::variant<unsigned int, std::string> id) {
d18eab67 192 rmRule(&g_resprulactions, id);
6bb38cd6
RG
193 });
194
195 g_lua.writeFunction("topResponseRule", []() {
d18eab67 196 topRule(&g_resprulactions);
6bb38cd6
RG
197 });
198
199 g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
d18eab67 200 mvRule(&g_resprulactions, from, to);
6bb38cd6
RG
201 });
202
15bb664c 203 g_lua.writeFunction("showCacheHitResponseRules", [](boost::optional<ruleparams_t> vars) {
3a5a3376 204 showRules(&g_cachehitresprulactions, vars);
6bb38cd6
RG
205 });
206
7762339e 207 g_lua.writeFunction("rmCacheHitResponseRule", [](boost::variant<unsigned int, std::string> id) {
d18eab67 208 rmRule(&g_cachehitresprulactions, id);
6bb38cd6
RG
209 });
210
211 g_lua.writeFunction("topCacheHitResponseRule", []() {
d18eab67 212 topRule(&g_cachehitresprulactions);
6bb38cd6
RG
213 });
214
215 g_lua.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
d18eab67 216 mvRule(&g_cachehitresprulactions, from, to);
6bb38cd6
RG
217 });
218
15bb664c 219 g_lua.writeFunction("showSelfAnsweredResponseRules", [](boost::optional<ruleparams_t> vars) {
3a5a3376 220 showRules(&g_selfansweredresprulactions, vars);
2d4783a8
CH
221 });
222
223 g_lua.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant<unsigned int, std::string> id) {
224 rmRule(&g_selfansweredresprulactions, id);
225 });
226
227 g_lua.writeFunction("topSelfAnsweredResponseRule", []() {
228 topRule(&g_selfansweredresprulactions);
229 });
230
231 g_lua.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from, unsigned int to) {
232 mvRule(&g_selfansweredresprulactions, from, to);
233 });
234
7762339e 235 g_lua.writeFunction("rmRule", [](boost::variant<unsigned int, std::string> id) {
d18eab67 236 rmRule(&g_rulactions, id);
6bb38cd6
RG
237 });
238
239 g_lua.writeFunction("topRule", []() {
d18eab67 240 topRule(&g_rulactions);
6bb38cd6
RG
241 });
242
243 g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
d18eab67 244 mvRule(&g_rulactions, from, to);
6bb38cd6
RG
245 });
246
247 g_lua.writeFunction("clearRules", []() {
248 setLuaSideEffect();
249 g_rulactions.modify([](decltype(g_rulactions)::value_type& rulactions) {
250 rulactions.clear();
251 });
252 });
253
a8f9f9c8 254 g_lua.writeFunction("setRules", [](const std::vector<std::pair<int, std::shared_ptr<DNSDistRuleAction>>>& newruleactions) {
6bb38cd6
RG
255 setLuaSideEffect();
256 g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) {
257 gruleactions.clear();
a8f9f9c8
RG
258 for (const auto& pair : newruleactions) {
259 const auto& newruleaction = pair.second;
260 if (newruleaction->d_action) {
261 auto rule=makeRule(newruleaction->d_rule);
262 gruleactions.push_back({rule, newruleaction->d_action, newruleaction->d_id});
6bb38cd6
RG
263 }
264 }
265 });
266 });
267
05f4003d
RG
268 g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc, boost::optional<int> burst, boost::optional<unsigned int> expiration, boost::optional<unsigned int> cleanupDelay, boost::optional<unsigned int> scanFraction) {
269 return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, burst.get_value_or(qps), ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64), expiration.get_value_or(300), cleanupDelay.get_value_or(60), scanFraction.get_value_or(10)));
6bb38cd6
RG
270 });
271
272 g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
273 if(!burst)
274 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
275 else
276 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
277 });
278
279 g_lua.writeFunction("RegexRule", [](const std::string& str) {
280 return std::shared_ptr<DNSRule>(new RegexRule(str));
281 });
282
283#ifdef HAVE_RE2
284 g_lua.writeFunction("RE2Rule", [](const std::string& str) {
285 return std::shared_ptr<DNSRule>(new RE2Rule(str));
286 });
287#endif
288
289 g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
290 return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn, quiet ? *quiet : false));
291 });
292
293 g_lua.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional<bool> src) {
294 return std::shared_ptr<DNSRule>(new NetmaskGroupRule(nmg, src ? *src : true));
295 });
296
297 g_lua.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
298 setLuaNoSideEffect();
299 int times = times_.get_value_or(100000);
300 DNSName suffix(suffix_.get_value_or("powerdns.com"));
301 struct item {
302 vector<uint8_t> packet;
303 ComboAddress rem;
304 DNSName qname;
305 uint16_t qtype, qclass;
306 };
307 vector<item> items;
308 items.reserve(1000);
309 for(int n=0; n < 1000; ++n) {
310 struct item i;
311 i.qname=DNSName(std::to_string(random()));
312 i.qname += suffix;
313 i.qtype = random() % 0xff;
314 i.qclass = 1;
315 i.rem=ComboAddress("127.0.0.1");
316 i.rem.sin4.sin_addr.s_addr = random();
317 DNSPacketWriter pw(i.packet, i.qname, i.qtype);
318 items.push_back(i);
319 }
320
321 int matches=0;
322 ComboAddress dummy("127.0.0.1");
5ffb2f83
CH
323 StopWatch sw;
324 sw.start();
6bb38cd6
RG
325 for(int n=0; n < times; ++n) {
326 const item& i = items[n % items.size()];
e7c732b8 327 DNSQuestion dq(&i.qname, i.qtype, i.qclass, 0, &i.rem, &i.rem, (struct dnsheader*)&i.packet[0], i.packet.size(), i.packet.size(), false, &sw.d_start);
6bb38cd6
RG
328 if(rule->matches(&dq))
329 matches++;
330 }
5ffb2f83 331 double udiff=sw.udiff();
6bb38cd6
RG
332 g_outputBuffer=(boost::format("Had %d matches out of %d, %.1f qps, in %.1f usec\n") % matches % times % (1000000*(1.0*times/udiff)) % udiff).str();
333
334 });
335
336 g_lua.writeFunction("AllRule", []() {
337 return std::shared_ptr<DNSRule>(new AllRule());
338 });
339
340 g_lua.writeFunction("ProbaRule", [](double proba) {
341 return std::shared_ptr<DNSRule>(new ProbaRule(proba));
342 });
343
344 g_lua.writeFunction("QNameRule", [](const std::string& qname) {
345 return std::shared_ptr<DNSRule>(new QNameRule(DNSName(qname)));
346 });
347
348 g_lua.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
349 uint16_t qtype;
350 if(auto dir = boost::get<int>(&str)) {
351 qtype = *dir;
352 }
353 else {
354 string val=boost::get<string>(str);
355 qtype = QType::chartocode(val.c_str());
356 if(!qtype)
357 throw std::runtime_error("Unable to convert '"+val+"' to a DNS type");
358 }
359 return std::shared_ptr<DNSRule>(new QTypeRule(qtype));
360 });
361
362 g_lua.writeFunction("QClassRule", [](int c) {
363 return std::shared_ptr<DNSRule>(new QClassRule(c));
364 });
365
366 g_lua.writeFunction("OpcodeRule", [](uint8_t code) {
367 return std::shared_ptr<DNSRule>(new OpcodeRule(code));
368 });
369
370 g_lua.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
371 return std::shared_ptr<DNSRule>(new AndRule(a));
372 });
373
374 g_lua.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
375 return std::shared_ptr<DNSRule>(new OrRule(a));
376 });
377
dfa098fb 378 g_lua.writeFunction("DSTPortRule", [](uint16_t port) {
379 return std::shared_ptr<DNSRule>(new DSTPortRule(port));
380 });
381
6bb38cd6
RG
382 g_lua.writeFunction("TCPRule", [](bool tcp) {
383 return std::shared_ptr<DNSRule>(new TCPRule(tcp));
384 });
385
386 g_lua.writeFunction("DNSSECRule", []() {
387 return std::shared_ptr<DNSRule>(new DNSSECRule());
388 });
389
390 g_lua.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
391 return std::shared_ptr<DNSRule>(new NotRule(rule));
392 });
393
394 g_lua.writeFunction("RecordsCountRule", [](uint8_t section, uint16_t minCount, uint16_t maxCount) {
395 return std::shared_ptr<DNSRule>(new RecordsCountRule(section, minCount, maxCount));
396 });
397
398 g_lua.writeFunction("RecordsTypeCountRule", [](uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) {
399 return std::shared_ptr<DNSRule>(new RecordsTypeCountRule(section, type, minCount, maxCount));
400 });
401
402 g_lua.writeFunction("TrailingDataRule", []() {
403 return std::shared_ptr<DNSRule>(new TrailingDataRule());
404 });
405
406 g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
407 return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
408 });
409
410 g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
411 return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
412 });
413
f6007449 414 g_lua.writeFunction("RCodeRule", [](uint8_t rcode) {
6bb38cd6
RG
415 return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
416 });
417
f6007449 418 g_lua.writeFunction("ERCodeRule", [](uint8_t rcode) {
d83feb68
CH
419 return std::shared_ptr<DNSRule>(new ERCodeRule(rcode));
420 });
421
a9613dfe
DA
422 g_lua.writeFunction("EDNSVersionRule", [](uint8_t version) {
423 return std::shared_ptr<DNSRule>(new EDNSVersionRule(version));
424 });
425
4bfdbd34
PD
426 g_lua.writeFunction("EDNSOptionRule", [](uint16_t optcode) {
427 return std::shared_ptr<DNSRule>(new EDNSOptionRule(optcode));
428 });
429
15bb664c 430 g_lua.writeFunction("showRules", [](boost::optional<ruleparams_t> vars) {
3a5a3376 431 showRules(&g_rulactions, vars);
6bb38cd6
RG
432 });
433
434 g_lua.writeFunction("RDRule", []() {
435 return std::shared_ptr<DNSRule>(new RDRule());
436 });
437
438 g_lua.writeFunction("TagRule", [](std::string tag, boost::optional<std::string> value) {
439 return std::shared_ptr<DNSRule>(new TagRule(tag, value));
440 });
441
442 g_lua.writeFunction("TimedIPSetRule", []() {
443 return std::shared_ptr<TimedIPSetRule>(new TimedIPSetRule());
444 });
445
83145026 446 g_lua.writeFunction("PoolAvailableRule", [](std::string poolname) {
510c45d0 447 return std::shared_ptr<DNSRule>(new PoolAvailableRule(poolname));
83145026
RG
448 });
449
6bb38cd6
RG
450 g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("clear", [](std::shared_ptr<TimedIPSetRule> tisr) {
451 tisr->clear();
452 });
453
454 g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("cleanup", [](std::shared_ptr<TimedIPSetRule> tisr) {
455 tisr->cleanup();
456 });
457
458 g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr<TimedIPSetRule> tisr, const ComboAddress& ca, int t) {
459 tisr->add(ca, time(0)+t);
460 });
461
462 g_lua.registerFunction<std::shared_ptr<DNSRule>(std::shared_ptr<TimedIPSetRule>::*)()>("slice", [](std::shared_ptr<TimedIPSetRule> tisr) {
463 return std::dynamic_pointer_cast<DNSRule>(tisr);
464 });
9f618bcc
AD
465
466 g_lua.writeFunction("QNameSetRule", [](const DNSNameSet& names) {
467 return std::shared_ptr<DNSRule>(new QNameSetRule(names));
468 });
6bb38cd6 469}