2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
23 #include "dnsdist-lua.hh"
24 #include "dnsdist-rules.hh"
26 std::shared_ptr
<DNSRule
> makeRule(const luadnsrule_t
& var
)
28 if (var
.type() == typeid(std::shared_ptr
<DNSRule
>))
29 return *boost::get
<std::shared_ptr
<DNSRule
>>(&var
);
33 auto add
=[&](string src
) {
35 nmg
.addMask(src
); // need to try mask first, all masks are domain names!
37 smn
.add(DNSName(src
));
41 if (var
.type() == typeid(string
))
42 add(*boost::get
<string
>(&var
));
44 else if (var
.type() == typeid(vector
<pair
<int, string
>>))
45 for(const auto& a
: *boost::get
<vector
<pair
<int, string
>>>(&var
))
48 else if (var
.type() == typeid(DNSName
))
49 smn
.add(*boost::get
<DNSName
>(&var
));
51 else if (var
.type() == typeid(vector
<pair
<int, DNSName
>>))
52 for(const auto& a
: *boost::get
<vector
<pair
<int, DNSName
>>>(&var
))
56 return std::make_shared
<SuffixMatchNodeRule
>(smn
);
58 return std::make_shared
<NetmaskGroupRule
>(nmg
, true);
61 static boost::uuids::uuid
makeRuleID(std::string
& id
)
67 return getUniqueID(id
);
70 void parseRuleParams(boost::optional
<luaruleparams_t
> params
, boost::uuids::uuid
& uuid
, uint64_t& creationOrder
)
72 static uint64_t s_creationOrder
= 0;
77 if (params
->count("uuid")) {
78 uuidStr
= boost::get
<std::string
>((*params
)["uuid"]);
82 uuid
= makeRuleID(uuidStr
);
83 creationOrder
= s_creationOrder
++;
86 typedef std::unordered_map
<std::string
, boost::variant
<bool, int, std::string
, std::vector
<std::pair
<int,int> > > > ruleparams_t
;
89 static void showRules(GlobalStateHolder
<vector
<T
> > *someRulActions
, boost::optional
<ruleparams_t
> vars
) {
92 bool showUUIDs
= false;
93 size_t truncateRuleWidth
= string::npos
;
96 if (vars
->count("showUUIDs")) {
97 showUUIDs
= boost::get
<bool>((*vars
)["showUUIDs"]);
99 if (vars
->count("truncateRuleWidth")) {
100 truncateRuleWidth
= boost::get
<int>((*vars
)["truncateRuleWidth"]);
104 auto rules
= someRulActions
->getLocal();
106 boost::format
fmt("%-3d %-38s %9d %9d %-56s %s\n");
107 g_outputBuffer
+= (fmt
% "#" % "UUID" % "Cr. Order" % "Matches" % "Rule" % "Action").str();
108 for(const auto& lim
: *rules
) {
109 string name
= lim
.d_rule
->toString().substr(0, truncateRuleWidth
);
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();
115 boost::format
fmt("%-3d %9d %-56s %s\n");
116 g_outputBuffer
+= (fmt
% "#" % "Matches" % "Rule" % "Action").str();
117 for(const auto& lim
: *rules
) {
118 string name
= lim
.d_rule
->toString().substr(0, truncateRuleWidth
);
119 g_outputBuffer
+= (fmt
% num
% lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
126 static void rmRule(GlobalStateHolder
<vector
<T
> > *someRulActions
, boost::variant
<unsigned int, std::string
> id
) {
128 auto rules
= someRulActions
->getCopy();
129 if (auto str
= boost::get
<std::string
>(&id
)) {
130 const auto uuid
= getUniqueID(*str
);
131 if (rules
.erase(std::remove_if(rules
.begin(),
133 [uuid
](const T
& a
) { return a
.d_id
== uuid
; }),
134 rules
.end()) == rules
.end()) {
135 g_outputBuffer
= "Error: no rule matched\n";
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";
144 rules
.erase(rules
.begin()+*pos
);
146 someRulActions
->setState(rules
);
150 static void topRule(GlobalStateHolder
<vector
<T
> > *someRulActions
) {
152 auto rules
= someRulActions
->getCopy();
155 auto subject
= *rules
.rbegin();
156 rules
.erase(std::prev(rules
.end()));
157 rules
.insert(rules
.begin(), subject
);
158 someRulActions
->setState(rules
);
162 static void mvRule(GlobalStateHolder
<vector
<T
> > *someRespRulActions
, unsigned int from
, unsigned int to
) {
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";
169 auto subject
= rules
[from
];
170 rules
.erase(rules
.begin()+from
);
171 if(to
> rules
.size())
172 rules
.push_back(subject
);
176 rules
.insert(rules
.begin()+to
, subject
);
178 someRespRulActions
->setState(rules
);
183 g_lua
.writeFunction("makeRule", makeRule
);
185 g_lua
.registerFunction
<string(std::shared_ptr
<DNSRule
>::*)()>("toString", [](const std::shared_ptr
<DNSRule
>& rule
) { return rule
->toString(); });
187 g_lua
.writeFunction("showResponseRules", [](boost::optional
<ruleparams_t
> vars
) {
188 showRules(&g_resprulactions
, vars
);
191 g_lua
.writeFunction("rmResponseRule", [](boost::variant
<unsigned int, std::string
> id
) {
192 rmRule(&g_resprulactions
, id
);
195 g_lua
.writeFunction("topResponseRule", []() {
196 topRule(&g_resprulactions
);
199 g_lua
.writeFunction("mvResponseRule", [](unsigned int from
, unsigned int to
) {
200 mvRule(&g_resprulactions
, from
, to
);
203 g_lua
.writeFunction("showCacheHitResponseRules", [](boost::optional
<ruleparams_t
> vars
) {
204 showRules(&g_cachehitresprulactions
, vars
);
207 g_lua
.writeFunction("rmCacheHitResponseRule", [](boost::variant
<unsigned int, std::string
> id
) {
208 rmRule(&g_cachehitresprulactions
, id
);
211 g_lua
.writeFunction("topCacheHitResponseRule", []() {
212 topRule(&g_cachehitresprulactions
);
215 g_lua
.writeFunction("mvCacheHitResponseRule", [](unsigned int from
, unsigned int to
) {
216 mvRule(&g_cachehitresprulactions
, from
, to
);
219 g_lua
.writeFunction("showSelfAnsweredResponseRules", [](boost::optional
<ruleparams_t
> vars
) {
220 showRules(&g_selfansweredresprulactions
, vars
);
223 g_lua
.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant
<unsigned int, std::string
> id
) {
224 rmRule(&g_selfansweredresprulactions
, id
);
227 g_lua
.writeFunction("topSelfAnsweredResponseRule", []() {
228 topRule(&g_selfansweredresprulactions
);
231 g_lua
.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from
, unsigned int to
) {
232 mvRule(&g_selfansweredresprulactions
, from
, to
);
235 g_lua
.writeFunction("rmRule", [](boost::variant
<unsigned int, std::string
> id
) {
236 rmRule(&g_rulactions
, id
);
239 g_lua
.writeFunction("topRule", []() {
240 topRule(&g_rulactions
);
243 g_lua
.writeFunction("mvRule", [](unsigned int from
, unsigned int to
) {
244 mvRule(&g_rulactions
, from
, to
);
247 g_lua
.writeFunction("clearRules", []() {
249 g_rulactions
.modify([](decltype(g_rulactions
)::value_type
& rulactions
) {
254 g_lua
.writeFunction("setRules", [](std::vector
<DNSDistRuleAction
>& newruleactions
) {
256 g_rulactions
.modify([newruleactions
](decltype(g_rulactions
)::value_type
& gruleactions
) {
257 gruleactions
.clear();
258 for (const auto& newruleaction
: newruleactions
) {
259 if (newruleaction
.d_action
) {
260 auto rule
=makeRule(newruleaction
.d_rule
);
261 gruleactions
.push_back({rule
, newruleaction
.d_action
, newruleaction
.d_id
});
267 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
) {
268 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)));
271 g_lua
.writeFunction("MaxQPSRule", [](unsigned int qps
, boost::optional
<int> burst
) {
273 return std::shared_ptr
<DNSRule
>(new MaxQPSRule(qps
));
275 return std::shared_ptr
<DNSRule
>(new MaxQPSRule(qps
, *burst
));
278 g_lua
.writeFunction("RegexRule", [](const std::string
& str
) {
279 return std::shared_ptr
<DNSRule
>(new RegexRule(str
));
283 g_lua
.writeFunction("RE2Rule", [](const std::string
& str
) {
284 return std::shared_ptr
<DNSRule
>(new RE2Rule(str
));
288 g_lua
.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode
& smn
, boost::optional
<bool> quiet
) {
289 return std::shared_ptr
<DNSRule
>(new SuffixMatchNodeRule(smn
, quiet
? *quiet
: false));
292 g_lua
.writeFunction("NetmaskGroupRule", [](const NetmaskGroup
& nmg
, boost::optional
<bool> src
) {
293 return std::shared_ptr
<DNSRule
>(new NetmaskGroupRule(nmg
, src
? *src
: true));
296 g_lua
.writeFunction("benchRule", [](std::shared_ptr
<DNSRule
> rule
, boost::optional
<int> times_
, boost::optional
<string
> suffix_
) {
297 setLuaNoSideEffect();
298 int times
= times_
.get_value_or(100000);
299 DNSName
suffix(suffix_
.get_value_or("powerdns.com"));
301 vector
<uint8_t> packet
;
304 uint16_t qtype
, qclass
;
308 for(int n
=0; n
< 1000; ++n
) {
310 i
.qname
=DNSName(std::to_string(random()));
312 i
.qtype
= random() % 0xff;
314 i
.rem
=ComboAddress("127.0.0.1");
315 i
.rem
.sin4
.sin_addr
.s_addr
= random();
316 DNSPacketWriter
pw(i
.packet
, i
.qname
, i
.qtype
);
321 ComboAddress
dummy("127.0.0.1");
324 for(int n
=0; n
< times
; ++n
) {
325 const item
& i
= items
[n
% items
.size()];
326 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
);
327 if(rule
->matches(&dq
))
330 double udiff
=sw
.udiff();
331 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();
335 g_lua
.writeFunction("AllRule", []() {
336 return std::shared_ptr
<DNSRule
>(new AllRule());
339 g_lua
.writeFunction("ProbaRule", [](double proba
) {
340 return std::shared_ptr
<DNSRule
>(new ProbaRule(proba
));
343 g_lua
.writeFunction("QNameRule", [](const std::string
& qname
) {
344 return std::shared_ptr
<DNSRule
>(new QNameRule(DNSName(qname
)));
347 g_lua
.writeFunction("QTypeRule", [](boost::variant
<int, std::string
> str
) {
349 if(auto dir
= boost::get
<int>(&str
)) {
353 string val
=boost::get
<string
>(str
);
354 qtype
= QType::chartocode(val
.c_str());
356 throw std::runtime_error("Unable to convert '"+val
+"' to a DNS type");
358 return std::shared_ptr
<DNSRule
>(new QTypeRule(qtype
));
361 g_lua
.writeFunction("QClassRule", [](int c
) {
362 return std::shared_ptr
<DNSRule
>(new QClassRule(c
));
365 g_lua
.writeFunction("OpcodeRule", [](uint8_t code
) {
366 return std::shared_ptr
<DNSRule
>(new OpcodeRule(code
));
369 g_lua
.writeFunction("AndRule", [](vector
<pair
<int, std::shared_ptr
<DNSRule
> > >a
) {
370 return std::shared_ptr
<DNSRule
>(new AndRule(a
));
373 g_lua
.writeFunction("OrRule", [](vector
<pair
<int, std::shared_ptr
<DNSRule
> > >a
) {
374 return std::shared_ptr
<DNSRule
>(new OrRule(a
));
377 g_lua
.writeFunction("DSTPortRule", [](uint16_t port
) {
378 return std::shared_ptr
<DNSRule
>(new DSTPortRule(port
));
381 g_lua
.writeFunction("TCPRule", [](bool tcp
) {
382 return std::shared_ptr
<DNSRule
>(new TCPRule(tcp
));
385 g_lua
.writeFunction("DNSSECRule", []() {
386 return std::shared_ptr
<DNSRule
>(new DNSSECRule());
389 g_lua
.writeFunction("NotRule", [](std::shared_ptr
<DNSRule
>rule
) {
390 return std::shared_ptr
<DNSRule
>(new NotRule(rule
));
393 g_lua
.writeFunction("RecordsCountRule", [](uint8_t section
, uint16_t minCount
, uint16_t maxCount
) {
394 return std::shared_ptr
<DNSRule
>(new RecordsCountRule(section
, minCount
, maxCount
));
397 g_lua
.writeFunction("RecordsTypeCountRule", [](uint8_t section
, uint16_t type
, uint16_t minCount
, uint16_t maxCount
) {
398 return std::shared_ptr
<DNSRule
>(new RecordsTypeCountRule(section
, type
, minCount
, maxCount
));
401 g_lua
.writeFunction("TrailingDataRule", []() {
402 return std::shared_ptr
<DNSRule
>(new TrailingDataRule());
405 g_lua
.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount
, unsigned int maxLabelsCount
) {
406 return std::shared_ptr
<DNSRule
>(new QNameLabelsCountRule(minLabelsCount
, maxLabelsCount
));
409 g_lua
.writeFunction("QNameWireLengthRule", [](size_t min
, size_t max
) {
410 return std::shared_ptr
<DNSRule
>(new QNameWireLengthRule(min
, max
));
413 g_lua
.writeFunction("RCodeRule", [](uint8_t rcode
) {
414 return std::shared_ptr
<DNSRule
>(new RCodeRule(rcode
));
417 g_lua
.writeFunction("ERCodeRule", [](uint8_t rcode
) {
418 return std::shared_ptr
<DNSRule
>(new ERCodeRule(rcode
));
421 g_lua
.writeFunction("EDNSVersionRule", [](uint8_t version
) {
422 return std::shared_ptr
<DNSRule
>(new EDNSVersionRule(version
));
425 g_lua
.writeFunction("EDNSOptionRule", [](uint16_t optcode
) {
426 return std::shared_ptr
<DNSRule
>(new EDNSOptionRule(optcode
));
429 g_lua
.writeFunction("showRules", [](boost::optional
<ruleparams_t
> vars
) {
430 showRules(&g_rulactions
, vars
);
433 g_lua
.writeFunction("RDRule", []() {
434 return std::shared_ptr
<DNSRule
>(new RDRule());
437 g_lua
.writeFunction("TagRule", [](std::string tag
, boost::optional
<std::string
> value
) {
438 return std::shared_ptr
<DNSRule
>(new TagRule(tag
, value
));
441 g_lua
.writeFunction("TimedIPSetRule", []() {
442 return std::shared_ptr
<TimedIPSetRule
>(new TimedIPSetRule());
445 g_lua
.writeFunction("PoolAvailableRule", [](std::string poolname
) {
446 return std::shared_ptr
<DNSRule
>(new PoolAvailableRule(poolname
));
449 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)()>("clear", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
453 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)()>("cleanup", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
457 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)(const ComboAddress
& ca
, int t
)>("add", [](std::shared_ptr
<TimedIPSetRule
> tisr
, const ComboAddress
& ca
, int t
) {
458 tisr
->add(ca
, time(0)+t
);
461 g_lua
.registerFunction
<std::shared_ptr
<DNSRule
>(std::shared_ptr
<TimedIPSetRule
>::*)()>("slice", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
462 return std::dynamic_pointer_cast
<DNSRule
>(tisr
);