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(std::move(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(std::move(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(std::move(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", [](const std::vector
<std::pair
<int, std::shared_ptr
<DNSDistRuleAction
>>>& newruleactions
) {
256 g_rulactions
.modify([newruleactions
](decltype(g_rulactions
)::value_type
& gruleactions
) {
257 gruleactions
.clear();
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({std::move(rule
), newruleaction
->d_action
, newruleaction
->d_id
, newruleaction
->d_creationOrder
});
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)));
272 g_lua
.writeFunction("MaxQPSRule", [](unsigned int qps
, boost::optional
<int> burst
) {
274 return std::shared_ptr
<DNSRule
>(new MaxQPSRule(qps
));
276 return std::shared_ptr
<DNSRule
>(new MaxQPSRule(qps
, *burst
));
279 g_lua
.writeFunction("RegexRule", [](const std::string
& str
) {
280 return std::shared_ptr
<DNSRule
>(new RegexRule(str
));
283 #ifdef HAVE_DNS_OVER_HTTPS
284 g_lua
.writeFunction("HTTPHeaderRule", [](const std::string
& header
, const std::string
& regex
) {
285 return std::shared_ptr
<DNSRule
>(new HTTPHeaderRule(header
, regex
));
287 g_lua
.writeFunction("HTTPPathRule", [](const std::string
& path
) {
288 return std::shared_ptr
<DNSRule
>(new HTTPPathRule(path
));
290 g_lua
.writeFunction("HTTPPathRegexRule", [](const std::string
& regex
) {
291 return std::shared_ptr
<DNSRule
>(new HTTPPathRegexRule(regex
));
296 g_lua
.writeFunction("RE2Rule", [](const std::string
& str
) {
297 return std::shared_ptr
<DNSRule
>(new RE2Rule(str
));
301 g_lua
.writeFunction("SNIRule", [](const std::string
& name
) {
302 return std::shared_ptr
<DNSRule
>(new SNIRule(name
));
305 g_lua
.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode
& smn
, boost::optional
<bool> quiet
) {
306 return std::shared_ptr
<DNSRule
>(new SuffixMatchNodeRule(smn
, quiet
? *quiet
: false));
309 g_lua
.writeFunction("NetmaskGroupRule", [](const NetmaskGroup
& nmg
, boost::optional
<bool> src
, boost::optional
<bool> quiet
) {
310 return std::shared_ptr
<DNSRule
>(new NetmaskGroupRule(nmg
, src
? *src
: true, quiet
? *quiet
: false));
313 g_lua
.writeFunction("benchRule", [](std::shared_ptr
<DNSRule
> rule
, boost::optional
<int> times_
, boost::optional
<string
> suffix_
) {
314 setLuaNoSideEffect();
315 int times
= times_
.get_value_or(100000);
316 DNSName
suffix(suffix_
.get_value_or("powerdns.com"));
318 vector
<uint8_t> packet
;
321 uint16_t qtype
, qclass
;
325 for(int n
=0; n
< 1000; ++n
) {
327 i
.qname
=DNSName(std::to_string(random()));
329 i
.qtype
= random() % 0xff;
331 i
.rem
=ComboAddress("127.0.0.1");
332 i
.rem
.sin4
.sin_addr
.s_addr
= random();
333 DNSPacketWriter
pw(i
.packet
, i
.qname
, i
.qtype
);
338 ComboAddress
dummy("127.0.0.1");
341 for(int n
=0; n
< times
; ++n
) {
342 const item
& i
= items
[n
% items
.size()];
343 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
);
344 if(rule
->matches(&dq
))
347 double udiff
=sw
.udiff();
348 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();
352 g_lua
.writeFunction("AllRule", []() {
353 return std::shared_ptr
<DNSRule
>(new AllRule());
356 g_lua
.writeFunction("ProbaRule", [](double proba
) {
357 return std::shared_ptr
<DNSRule
>(new ProbaRule(proba
));
360 g_lua
.writeFunction("QNameRule", [](const std::string
& qname
) {
361 return std::shared_ptr
<DNSRule
>(new QNameRule(DNSName(qname
)));
364 g_lua
.writeFunction("QTypeRule", [](boost::variant
<int, std::string
> str
) {
366 if(auto dir
= boost::get
<int>(&str
)) {
370 string val
=boost::get
<string
>(str
);
371 qtype
= QType::chartocode(val
.c_str());
373 throw std::runtime_error("Unable to convert '"+val
+"' to a DNS type");
375 return std::shared_ptr
<DNSRule
>(new QTypeRule(qtype
));
378 g_lua
.writeFunction("QClassRule", [](int c
) {
379 return std::shared_ptr
<DNSRule
>(new QClassRule(c
));
382 g_lua
.writeFunction("OpcodeRule", [](uint8_t code
) {
383 return std::shared_ptr
<DNSRule
>(new OpcodeRule(code
));
386 g_lua
.writeFunction("AndRule", [](vector
<pair
<int, std::shared_ptr
<DNSRule
> > >a
) {
387 return std::shared_ptr
<DNSRule
>(new AndRule(a
));
390 g_lua
.writeFunction("OrRule", [](vector
<pair
<int, std::shared_ptr
<DNSRule
> > >a
) {
391 return std::shared_ptr
<DNSRule
>(new OrRule(a
));
394 g_lua
.writeFunction("DSTPortRule", [](uint16_t port
) {
395 return std::shared_ptr
<DNSRule
>(new DSTPortRule(port
));
398 g_lua
.writeFunction("TCPRule", [](bool tcp
) {
399 return std::shared_ptr
<DNSRule
>(new TCPRule(tcp
));
402 g_lua
.writeFunction("DNSSECRule", []() {
403 return std::shared_ptr
<DNSRule
>(new DNSSECRule());
406 g_lua
.writeFunction("NotRule", [](std::shared_ptr
<DNSRule
>rule
) {
407 return std::shared_ptr
<DNSRule
>(new NotRule(rule
));
410 g_lua
.writeFunction("RecordsCountRule", [](uint8_t section
, uint16_t minCount
, uint16_t maxCount
) {
411 return std::shared_ptr
<DNSRule
>(new RecordsCountRule(section
, minCount
, maxCount
));
414 g_lua
.writeFunction("RecordsTypeCountRule", [](uint8_t section
, uint16_t type
, uint16_t minCount
, uint16_t maxCount
) {
415 return std::shared_ptr
<DNSRule
>(new RecordsTypeCountRule(section
, type
, minCount
, maxCount
));
418 g_lua
.writeFunction("TrailingDataRule", []() {
419 return std::shared_ptr
<DNSRule
>(new TrailingDataRule());
422 g_lua
.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount
, unsigned int maxLabelsCount
) {
423 return std::shared_ptr
<DNSRule
>(new QNameLabelsCountRule(minLabelsCount
, maxLabelsCount
));
426 g_lua
.writeFunction("QNameWireLengthRule", [](size_t min
, size_t max
) {
427 return std::shared_ptr
<DNSRule
>(new QNameWireLengthRule(min
, max
));
430 g_lua
.writeFunction("RCodeRule", [](uint8_t rcode
) {
431 return std::shared_ptr
<DNSRule
>(new RCodeRule(rcode
));
434 g_lua
.writeFunction("ERCodeRule", [](uint8_t rcode
) {
435 return std::shared_ptr
<DNSRule
>(new ERCodeRule(rcode
));
438 g_lua
.writeFunction("EDNSVersionRule", [](uint8_t version
) {
439 return std::shared_ptr
<DNSRule
>(new EDNSVersionRule(version
));
442 g_lua
.writeFunction("EDNSOptionRule", [](uint16_t optcode
) {
443 return std::shared_ptr
<DNSRule
>(new EDNSOptionRule(optcode
));
446 g_lua
.writeFunction("showRules", [](boost::optional
<ruleparams_t
> vars
) {
447 showRules(&g_rulactions
, vars
);
450 g_lua
.writeFunction("RDRule", []() {
451 return std::shared_ptr
<DNSRule
>(new RDRule());
454 g_lua
.writeFunction("TagRule", [](std::string tag
, boost::optional
<std::string
> value
) {
455 return std::shared_ptr
<DNSRule
>(new TagRule(tag
, value
));
458 g_lua
.writeFunction("TimedIPSetRule", []() {
459 return std::shared_ptr
<TimedIPSetRule
>(new TimedIPSetRule());
462 g_lua
.writeFunction("PoolAvailableRule", [](std::string poolname
) {
463 return std::shared_ptr
<DNSRule
>(new PoolAvailableRule(poolname
));
466 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)()>("clear", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
470 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)()>("cleanup", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
474 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)(const ComboAddress
& ca
, int t
)>("add", [](std::shared_ptr
<TimedIPSetRule
> tisr
, const ComboAddress
& ca
, int t
) {
475 tisr
->add(ca
, time(0)+t
);
478 g_lua
.registerFunction
<std::shared_ptr
<DNSRule
>(std::shared_ptr
<TimedIPSetRule
>::*)()>("slice", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
479 return std::dynamic_pointer_cast
<DNSRule
>(tisr
);
482 g_lua
.writeFunction("QNameSetRule", [](const DNSNameSet
& names
) {
483 return std::shared_ptr
<DNSRule
>(new QNameSetRule(names
));
486 g_lua
.writeFunction("KeyValueStoreLookupRule", [](std::shared_ptr
<KeyValueStore
>& kvs
, std::shared_ptr
<KeyValueLookupKey
>& lookupKey
) {
487 return std::shared_ptr
<DNSRule
>(new KeyValueStoreLookupRule(kvs
, lookupKey
));