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-ecs.hh"
24 #include "dnsdist-lua.hh"
26 #include "dnsparser.hh"
28 class MaxQPSIPRule
: public DNSRule
31 MaxQPSIPRule(unsigned int qps
, unsigned int burst
, unsigned int ipv4trunc
=32, unsigned int ipv6trunc
=64) :
32 d_qps(qps
), d_burst(burst
), d_ipv4trunc(ipv4trunc
), d_ipv6trunc(ipv6trunc
)
34 pthread_rwlock_init(&d_lock
, 0);
37 bool matches(const DNSQuestion
* dq
) const override
39 ComboAddress
zeroport(*dq
->remote
);
40 zeroport
.sin4
.sin_port
=0;
41 zeroport
.truncate(zeroport
.sin4
.sin_family
== AF_INET
? d_ipv4trunc
: d_ipv6trunc
);
44 const auto iter
= d_limits
.find(zeroport
);
45 if (iter
!= d_limits
.end()) {
46 return !iter
->second
.check();
51 auto iter
= d_limits
.find(zeroport
);
52 if(iter
== d_limits
.end()) {
53 iter
=d_limits
.insert({zeroport
,QPSLimiter(d_qps
, d_burst
)}).first
;
55 return !iter
->second
.check();
59 string
toString() const override
61 return "IP (/"+std::to_string(d_ipv4trunc
)+", /"+std::to_string(d_ipv6trunc
)+") match for QPS over " + std::to_string(d_qps
) + " burst "+ std::to_string(d_burst
);
66 mutable pthread_rwlock_t d_lock
;
67 mutable std::map
<ComboAddress
, QPSLimiter
> d_limits
;
68 unsigned int d_qps
, d_burst
, d_ipv4trunc
, d_ipv6trunc
;
72 class MaxQPSRule
: public DNSRule
75 MaxQPSRule(unsigned int qps
)
79 MaxQPSRule(unsigned int qps
, unsigned int burst
)
84 bool matches(const DNSQuestion
* qd
) const override
89 string
toString() const override
91 return "Max " + std::to_string(d_qps
.getRate()) + " qps";
96 mutable QPSLimiter d_qps
;
99 class NMGRule
: public DNSRule
102 NMGRule(const NetmaskGroup
& nmg
) : d_nmg(nmg
) {}
107 class NetmaskGroupRule
: public NMGRule
110 NetmaskGroupRule(const NetmaskGroup
& nmg
, bool src
) : NMGRule(nmg
)
114 bool matches(const DNSQuestion
* dq
) const override
117 return d_nmg
.match(*dq
->local
);
119 return d_nmg
.match(*dq
->remote
);
122 string
toString() const override
125 return "Dst: "+d_nmg
.toString();
127 return "Src: "+d_nmg
.toString();
133 class TimedIPSetRule
: public DNSRule
, boost::noncopyable
137 IPv6(const ComboAddress
& ca
)
139 static_assert(sizeof(*this)==16, "IPv6 struct has wrong size");
140 memcpy((char*)this, ca
.sin6
.sin6_addr
.s6_addr
, 16);
142 bool operator==(const IPv6
& rhs
) const
144 return a
==rhs
.a
&& b
==rhs
.b
;
152 pthread_rwlock_init(&d_lock4
, 0);
153 pthread_rwlock_init(&d_lock6
, 0);
155 bool matches(const DNSQuestion
* dq
) const override
157 if(dq
->remote
->sin4
.sin_family
== AF_INET
) {
158 ReadLock
rl(&d_lock4
);
159 auto fnd
= d_ip4s
.find(dq
->remote
->sin4
.sin_addr
.s_addr
);
160 if(fnd
== d_ip4s
.end()) {
163 return time(0) < fnd
->second
;
165 ReadLock
rl(&d_lock6
);
166 auto fnd
= d_ip6s
.find({*dq
->remote
});
167 if(fnd
== d_ip6s
.end()) {
170 return time(0) < fnd
->second
;
174 void add(const ComboAddress
& ca
, time_t ttd
)
176 // think twice before adding templates here
177 if(ca
.sin4
.sin_family
== AF_INET
) {
178 WriteLock
rl(&d_lock4
);
179 auto res
=d_ip4s
.insert({ca
.sin4
.sin_addr
.s_addr
, ttd
});
180 if(!res
.second
&& (time_t)res
.first
->second
< ttd
)
181 res
.first
->second
= (uint32_t)ttd
;
184 WriteLock
rl(&d_lock6
);
185 auto res
=d_ip6s
.insert({{ca
}, ttd
});
186 if(!res
.second
&& (time_t)res
.first
->second
< ttd
)
187 res
.first
->second
= (uint32_t)ttd
;
191 void remove(const ComboAddress
& ca
)
193 if(ca
.sin4
.sin_family
== AF_INET
) {
194 WriteLock
rl(&d_lock4
);
195 d_ip4s
.erase(ca
.sin4
.sin_addr
.s_addr
);
198 WriteLock
rl(&d_lock6
);
206 WriteLock
rl(&d_lock4
);
209 WriteLock
rl(&d_lock6
);
217 WriteLock
rl(&d_lock4
);
219 for(auto iter
= d_ip4s
.begin(); iter
!= d_ip4s
.end(); ) {
220 if(iter
->second
< now
)
221 iter
=d_ip4s
.erase(iter
);
229 WriteLock
rl(&d_lock6
);
231 for(auto iter
= d_ip6s
.begin(); iter
!= d_ip6s
.end(); ) {
232 if(iter
->second
< now
)
233 iter
=d_ip6s
.erase(iter
);
242 string
toString() const override
247 ReadLock
rl(&d_lock4
);
248 for(const auto& ip
: d_ip4s
)
253 ReadLock
rl(&d_lock6
);
254 for(const auto& ip
: d_ip6s
)
259 return "Src: "+std::to_string(count
)+" ips";
264 std::size_t operator()(const IPv6
& ip
) const
266 auto ah
=std::hash
<uint64_t>{}(ip
.a
);
267 auto bh
=std::hash
<uint64_t>{}(ip
.b
);
271 std::unordered_map
<IPv6
, time_t, IPv6Hash
> d_ip6s
;
272 std::unordered_map
<uint32_t, time_t> d_ip4s
;
273 mutable pthread_rwlock_t d_lock4
;
274 mutable pthread_rwlock_t d_lock6
;
278 class AllRule
: public DNSRule
282 bool matches(const DNSQuestion
* dq
) const override
287 string
toString() const override
295 class DNSSECRule
: public DNSRule
302 bool matches(const DNSQuestion
* dq
) const override
304 return dq
->dh
->cd
|| (getEDNSZ((const char*)dq
->dh
, dq
->len
) & EDNS_HEADER_FLAG_DO
); // turns out dig sets ad by default..
307 string
toString() const override
313 class AndRule
: public DNSRule
316 AndRule(const vector
<pair
<int, shared_ptr
<DNSRule
> > >& rules
)
318 for(const auto& r
: rules
)
319 d_rules
.push_back(r
.second
);
322 bool matches(const DNSQuestion
* dq
) const override
324 auto iter
= d_rules
.begin();
325 for(; iter
!= d_rules
.end(); ++iter
)
326 if(!(*iter
)->matches(dq
))
328 return iter
== d_rules
.end();
331 string
toString() const override
334 for(const auto& rule
: d_rules
) {
337 ret
+= "("+ rule
->toString()+")";
343 vector
<std::shared_ptr
<DNSRule
> > d_rules
;
348 class OrRule
: public DNSRule
351 OrRule(const vector
<pair
<int, shared_ptr
<DNSRule
> > >& rules
)
353 for(const auto& r
: rules
)
354 d_rules
.push_back(r
.second
);
357 bool matches(const DNSQuestion
* dq
) const override
359 auto iter
= d_rules
.begin();
360 for(; iter
!= d_rules
.end(); ++iter
)
361 if((*iter
)->matches(dq
))
366 string
toString() const override
369 for(const auto& rule
: d_rules
) {
372 ret
+= "("+ rule
->toString()+")";
378 vector
<std::shared_ptr
<DNSRule
> > d_rules
;
383 class RegexRule
: public DNSRule
386 RegexRule(const std::string
& regex
) : d_regex(regex
), d_visual(regex
)
390 bool matches(const DNSQuestion
* dq
) const override
392 return d_regex
.match(dq
->qname
->toStringNoDot());
395 string
toString() const override
397 return "Regex: "+d_visual
;
406 class RE2Rule
: public DNSRule
409 RE2Rule(const std::string
& re2
) : d_re2(re2
, RE2::Latin1
), d_visual(re2
)
413 bool matches(const DNSQuestion
* dq
) const override
415 return RE2::FullMatch(dq
->qname
->toStringNoDot(), d_re2
);
418 string
toString() const override
420 return "RE2 match: "+d_visual
;
429 class SuffixMatchNodeRule
: public DNSRule
432 SuffixMatchNodeRule(const SuffixMatchNode
& smn
, bool quiet
=false) : d_smn(smn
), d_quiet(quiet
)
435 bool matches(const DNSQuestion
* dq
) const override
437 return d_smn
.check(*dq
->qname
);
439 string
toString() const override
442 return "qname==in-set";
444 return "qname in "+d_smn
.toString();
447 SuffixMatchNode d_smn
;
451 class QNameRule
: public DNSRule
454 QNameRule(const DNSName
& qname
) : d_qname(qname
)
457 bool matches(const DNSQuestion
* dq
) const override
459 return d_qname
==*dq
->qname
;
461 string
toString() const override
463 return "qname=="+d_qname
.toString();
470 class QTypeRule
: public DNSRule
473 QTypeRule(uint16_t qtype
) : d_qtype(qtype
)
476 bool matches(const DNSQuestion
* dq
) const override
478 return d_qtype
== dq
->qtype
;
480 string
toString() const override
483 return "qtype=="+qt
.getName();
489 class QClassRule
: public DNSRule
492 QClassRule(uint16_t qclass
) : d_qclass(qclass
)
495 bool matches(const DNSQuestion
* dq
) const override
497 return d_qclass
== dq
->qclass
;
499 string
toString() const override
501 return "qclass=="+std::to_string(d_qclass
);
507 class OpcodeRule
: public DNSRule
510 OpcodeRule(uint8_t opcode
) : d_opcode(opcode
)
513 bool matches(const DNSQuestion
* dq
) const override
515 return d_opcode
== dq
->dh
->opcode
;
517 string
toString() const override
519 return "opcode=="+std::to_string(d_opcode
);
525 class TCPRule
: public DNSRule
528 TCPRule(bool tcp
): d_tcp(tcp
)
531 bool matches(const DNSQuestion
* dq
) const override
533 return dq
->tcp
== d_tcp
;
535 string
toString() const override
537 return (d_tcp
? "TCP" : "UDP");
544 class NotRule
: public DNSRule
547 NotRule(shared_ptr
<DNSRule
>& rule
): d_rule(rule
)
550 bool matches(const DNSQuestion
* dq
) const override
552 return !d_rule
->matches(dq
);
554 string
toString() const override
556 return "!("+ d_rule
->toString()+")";
559 shared_ptr
<DNSRule
> d_rule
;
562 class RecordsCountRule
: public DNSRule
565 RecordsCountRule(uint8_t section
, uint16_t minCount
, uint16_t maxCount
): d_minCount(minCount
), d_maxCount(maxCount
), d_section(section
)
568 bool matches(const DNSQuestion
* dq
) const override
573 count
= ntohs(dq
->dh
->qdcount
);
576 count
= ntohs(dq
->dh
->ancount
);
579 count
= ntohs(dq
->dh
->nscount
);
582 count
= ntohs(dq
->dh
->arcount
);
585 return count
>= d_minCount
&& count
<= d_maxCount
;
587 string
toString() const override
604 return std::to_string(d_minCount
) + " <= records in " + section
+ " <= "+ std::to_string(d_maxCount
);
612 class RecordsTypeCountRule
: public DNSRule
615 RecordsTypeCountRule(uint8_t section
, uint16_t type
, uint16_t minCount
, uint16_t maxCount
): d_type(type
), d_minCount(minCount
), d_maxCount(maxCount
), d_section(section
)
618 bool matches(const DNSQuestion
* dq
) const override
623 count
= ntohs(dq
->dh
->qdcount
);
626 count
= ntohs(dq
->dh
->ancount
);
629 count
= ntohs(dq
->dh
->nscount
);
632 count
= ntohs(dq
->dh
->arcount
);
635 if (count
< d_minCount
) {
638 count
= getRecordsOfTypeCount(reinterpret_cast<const char*>(dq
->dh
), dq
->len
, d_section
, d_type
);
639 return count
>= d_minCount
&& count
<= d_maxCount
;
641 string
toString() const override
658 return std::to_string(d_minCount
) + " <= " + QType(d_type
).getName() + " records in " + section
+ " <= "+ std::to_string(d_maxCount
);
667 class TrailingDataRule
: public DNSRule
673 bool matches(const DNSQuestion
* dq
) const override
675 uint16_t length
= getDNSPacketLength(reinterpret_cast<const char*>(dq
->dh
), dq
->len
);
676 return length
< dq
->len
;
678 string
toString() const override
680 return "trailing data";
684 class QNameLabelsCountRule
: public DNSRule
687 QNameLabelsCountRule(unsigned int minLabelsCount
, unsigned int maxLabelsCount
): d_min(minLabelsCount
), d_max(maxLabelsCount
)
690 bool matches(const DNSQuestion
* dq
) const override
692 unsigned int count
= dq
->qname
->countLabels();
693 return count
< d_min
|| count
> d_max
;
695 string
toString() const override
697 return "labels count < " + std::to_string(d_min
) + " || labels count > " + std::to_string(d_max
);
704 class QNameWireLengthRule
: public DNSRule
707 QNameWireLengthRule(size_t min
, size_t max
): d_min(min
), d_max(max
)
710 bool matches(const DNSQuestion
* dq
) const override
712 size_t const wirelength
= dq
->qname
->wirelength();
713 return wirelength
< d_min
|| wirelength
> d_max
;
715 string
toString() const override
717 return "wire length < " + std::to_string(d_min
) + " || wire length > " + std::to_string(d_max
);
724 class RCodeRule
: public DNSRule
727 RCodeRule(uint8_t rcode
) : d_rcode(rcode
)
730 bool matches(const DNSQuestion
* dq
) const override
732 return d_rcode
== dq
->dh
->rcode
;
734 string
toString() const override
736 return "rcode=="+RCode::to_s(d_rcode
);
742 class ERCodeRule
: public DNSRule
745 ERCodeRule(uint8_t rcode
) : d_rcode(rcode
& 0xF), d_extrcode(rcode
>> 4)
748 bool matches(const DNSQuestion
* dq
) const override
750 // avoid parsing EDNS OPT RR when not needed.
751 if (d_rcode
!= dq
->dh
->rcode
) {
755 char * optStart
= NULL
;
758 int res
= locateEDNSOptRR(const_cast<char*>(reinterpret_cast<const char*>(dq
->dh
)), dq
->len
, &optStart
, &optLen
, &last
);
761 return d_extrcode
== 0;
764 // root label (1), type (2), class (2), ttl (4) + rdlen (2)
769 if (*optStart
!= 0) {
770 // OPT RR Name != '.'
774 static_assert(sizeof(EDNS0Record
) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
775 // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
776 memcpy(&edns0
, optStart
+ 5, sizeof edns0
);
778 return d_extrcode
== edns0
.extRCode
;
780 string
toString() const override
782 return "ercode=="+ERCode::to_s(d_rcode
| (d_extrcode
<< 4));
785 uint8_t d_rcode
; // plain DNS Rcode
786 uint8_t d_extrcode
; // upper bits in EDNS0 record
789 class RDRule
: public DNSRule
795 bool matches(const DNSQuestion
* dq
) const override
797 return dq
->dh
->rd
== 1;
799 string
toString() const override
805 class ProbaRule
: public DNSRule
808 ProbaRule(double proba
) : d_proba(proba
)
811 bool matches(const DNSQuestion
* dq
) const override
815 double rnd
= 1.0*random() / RAND_MAX
;
816 return rnd
> (1.0 - d_proba
);
818 string
toString() const override
820 return "match with prob. " + (boost::format("%0.2f") % d_proba
).str();
826 class TagRule
: public DNSRule
829 TagRule(std::string tag
, boost::optional
<std::string
> value
) : d_value(value
), d_tag(tag
)
832 bool matches(const DNSQuestion
* dq
) const override
834 if (dq
->qTag
== nullptr) {
838 const auto got
= dq
->qTag
->tagData
.find(d_tag
);
839 if (got
== dq
->qTag
->tagData
.cend()) {
847 return got
->second
== *d_value
;
850 string
toString() const override
852 return "tag '" + d_tag
+ "' is set" + (d_value
? (" to '" + *d_value
+ "'") : "");
856 boost::optional
<std::string
> d_value
;
860 std::shared_ptr
<DNSRule
> makeRule(const luadnsrule_t
& var
)
862 if (var
.type() == typeid(std::shared_ptr
<DNSRule
>))
863 return *boost::get
<std::shared_ptr
<DNSRule
>>(&var
);
867 auto add
=[&](string src
) {
869 nmg
.addMask(src
); // need to try mask first, all masks are domain names!
871 smn
.add(DNSName(src
));
875 if (var
.type() == typeid(string
))
876 add(*boost::get
<string
>(&var
));
878 else if (var
.type() == typeid(vector
<pair
<int, string
>>))
879 for(const auto& a
: *boost::get
<vector
<pair
<int, string
>>>(&var
))
882 else if (var
.type() == typeid(DNSName
))
883 smn
.add(*boost::get
<DNSName
>(&var
));
885 else if (var
.type() == typeid(vector
<pair
<int, DNSName
>>))
886 for(const auto& a
: *boost::get
<vector
<pair
<int, DNSName
>>>(&var
))
890 return std::make_shared
<SuffixMatchNodeRule
>(smn
);
892 return std::make_shared
<NetmaskGroupRule
>(nmg
, true);
895 static boost::uuids::uuid
makeRuleID(std::string
& id
)
898 return t_uuidGenerator();
901 boost::uuids::string_generator gen
;
905 void parseRuleParams(boost::optional
<luaruleparams_t
> params
, boost::uuids::uuid
& uuid
)
910 if (params
->count("uuid")) {
911 uuidStr
= boost::get
<std::string
>((*params
)["uuid"]);
915 uuid
= makeRuleID(uuidStr
);
920 g_lua
.writeFunction("makeRule", makeRule
);
922 g_lua
.registerFunction
<string(std::shared_ptr
<DNSRule
>::*)()>("toString", [](const std::shared_ptr
<DNSRule
>& rule
) { return rule
->toString(); });
924 g_lua
.writeFunction("showResponseRules", [](boost::optional
<bool> showUUIDs
) {
925 setLuaNoSideEffect();
927 if (showUUIDs
.get_value_or(false)) {
928 boost::format
fmt("%-3d %-38s %9d %-50s %s\n");
929 g_outputBuffer
+= (fmt
% "#" % "UUID" % "Matches" % "Rule" % "Action").str();
930 for(const auto& lim
: g_resprulactions
.getCopy()) {
931 string name
= lim
.d_rule
->toString();
932 g_outputBuffer
+= (fmt
% num
% boost::uuids::to_string(lim
.d_id
) % lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
937 boost::format
fmt("%-3d %9d %-50s %s\n");
938 g_outputBuffer
+= (fmt
% "#" % "Matches" % "Rule" % "Action").str();
939 for(const auto& lim
: g_resprulactions
.getCopy()) {
940 string name
= lim
.d_rule
->toString();
941 g_outputBuffer
+= (fmt
% num
% lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
947 g_lua
.writeFunction("rmResponseRule", [](boost::variant
<unsigned int, std::string
> id
) {
949 auto rules
= g_resprulactions
.getCopy();
950 if (auto str
= boost::get
<std::string
>(&id
)) {
951 boost::uuids::string_generator gen
;
952 const auto uuid
= gen(*str
);
953 rules
.erase(std::remove_if(rules
.begin(),
955 [uuid
](const DNSDistResponseRuleAction
& a
) { return a
.d_id
== uuid
; }),
958 else if (auto pos
= boost::get
<unsigned int>(&id
)) {
959 if (*pos
>= rules
.size()) {
960 g_outputBuffer
= "Error: attempt to delete non-existing rule\n";
963 rules
.erase(rules
.begin()+*pos
);
965 g_resprulactions
.setState(rules
);
968 g_lua
.writeFunction("topResponseRule", []() {
970 auto rules
= g_resprulactions
.getCopy();
973 auto subject
= *rules
.rbegin();
974 rules
.erase(std::prev(rules
.end()));
975 rules
.insert(rules
.begin(), subject
);
976 g_resprulactions
.setState(rules
);
979 g_lua
.writeFunction("mvResponseRule", [](unsigned int from
, unsigned int to
) {
981 auto rules
= g_resprulactions
.getCopy();
982 if(from
>= rules
.size() || to
> rules
.size()) {
983 g_outputBuffer
= "Error: attempt to move rules from/to invalid index\n";
986 auto subject
= rules
[from
];
987 rules
.erase(rules
.begin()+from
);
988 if(to
== rules
.size())
989 rules
.push_back(subject
);
993 rules
.insert(rules
.begin()+to
, subject
);
995 g_resprulactions
.setState(rules
);
998 g_lua
.writeFunction("showCacheHitResponseRules", [](boost::optional
<bool> showUUIDs
) {
999 setLuaNoSideEffect();
1001 if (showUUIDs
.get_value_or(false)) {
1002 boost::format
fmt("%-3d %-38s %9d %-50s %s\n");
1003 g_outputBuffer
+= (fmt
% "#" % "UUID" % "Matches" % "Rule" % "Action").str();
1004 for(const auto& lim
: g_cachehitresprulactions
.getCopy()) {
1005 string name
= lim
.d_rule
->toString();
1006 g_outputBuffer
+= (fmt
% num
% boost::uuids::to_string(lim
.d_id
) % lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
1011 boost::format
fmt("%-3d %9d %-50s %s\n");
1012 g_outputBuffer
+= (fmt
% "#" % "Matches" % "Rule" % "Action").str();
1013 for(const auto& lim
: g_cachehitresprulactions
.getCopy()) {
1014 string name
= lim
.d_rule
->toString();
1015 g_outputBuffer
+= (fmt
% num
% lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
1021 g_lua
.writeFunction("rmCacheHitResponseRule", [](boost::variant
<unsigned int, std::string
> id
) {
1023 auto rules
= g_cachehitresprulactions
.getCopy();
1024 if (auto str
= boost::get
<std::string
>(&id
)) {
1025 boost::uuids::string_generator gen
;
1026 const auto uuid
= gen(*str
);
1027 rules
.erase(std::remove_if(rules
.begin(),
1029 [uuid
](const DNSDistResponseRuleAction
& a
) { return a
.d_id
== uuid
; }),
1032 else if (auto pos
= boost::get
<unsigned int>(&id
)) {
1033 if (*pos
>= rules
.size()) {
1034 g_outputBuffer
= "Error: attempt to delete non-existing rule\n";
1037 rules
.erase(rules
.begin()+*pos
);
1039 g_cachehitresprulactions
.setState(rules
);
1042 g_lua
.writeFunction("topCacheHitResponseRule", []() {
1044 auto rules
= g_cachehitresprulactions
.getCopy();
1047 auto subject
= *rules
.rbegin();
1048 rules
.erase(std::prev(rules
.end()));
1049 rules
.insert(rules
.begin(), subject
);
1050 g_cachehitresprulactions
.setState(rules
);
1053 g_lua
.writeFunction("mvCacheHitResponseRule", [](unsigned int from
, unsigned int to
) {
1055 auto rules
= g_cachehitresprulactions
.getCopy();
1056 if(from
>= rules
.size() || to
> rules
.size()) {
1057 g_outputBuffer
= "Error: attempt to move rules from/to invalid index\n";
1060 auto subject
= rules
[from
];
1061 rules
.erase(rules
.begin()+from
);
1062 if(to
== rules
.size())
1063 rules
.push_back(subject
);
1067 rules
.insert(rules
.begin()+to
, subject
);
1069 g_cachehitresprulactions
.setState(rules
);
1072 g_lua
.writeFunction("rmRule", [](boost::variant
<unsigned int, std::string
> id
) {
1074 auto rules
= g_rulactions
.getCopy();
1075 if (auto str
= boost::get
<std::string
>(&id
)) {
1076 boost::uuids::string_generator gen
;
1077 const auto uuid
= gen(*str
);
1078 rules
.erase(std::remove_if(rules
.begin(),
1080 [uuid
](const DNSDistRuleAction
& a
) { return a
.d_id
== uuid
; }),
1083 else if (auto pos
= boost::get
<unsigned int>(&id
)) {
1084 if (*pos
>= rules
.size()) {
1085 g_outputBuffer
= "Error: attempt to delete non-existing rule\n";
1088 rules
.erase(rules
.begin()+*pos
);
1090 g_rulactions
.setState(rules
);
1093 g_lua
.writeFunction("topRule", []() {
1095 auto rules
= g_rulactions
.getCopy();
1098 auto subject
= *rules
.rbegin();
1099 rules
.erase(std::prev(rules
.end()));
1100 rules
.insert(rules
.begin(), subject
);
1101 g_rulactions
.setState(rules
);
1104 g_lua
.writeFunction("mvRule", [](unsigned int from
, unsigned int to
) {
1106 auto rules
= g_rulactions
.getCopy();
1107 if(from
>= rules
.size() || to
> rules
.size()) {
1108 g_outputBuffer
= "Error: attempt to move rules from/to invalid index\n";
1112 auto subject
= rules
[from
];
1113 rules
.erase(rules
.begin()+from
);
1114 if(to
== rules
.size())
1115 rules
.push_back(subject
);
1119 rules
.insert(rules
.begin()+to
, subject
);
1121 g_rulactions
.setState(rules
);
1124 g_lua
.writeFunction("clearRules", []() {
1126 g_rulactions
.modify([](decltype(g_rulactions
)::value_type
& rulactions
) {
1131 g_lua
.writeFunction("setRules", [](std::vector
<DNSDistRuleAction
>& newruleactions
) {
1133 g_rulactions
.modify([newruleactions
](decltype(g_rulactions
)::value_type
& gruleactions
) {
1134 gruleactions
.clear();
1135 for (const auto& newruleaction
: newruleactions
) {
1136 if (newruleaction
.d_action
) {
1137 auto rule
=makeRule(newruleaction
.d_rule
);
1138 gruleactions
.push_back({rule
, newruleaction
.d_action
, newruleaction
.d_id
});
1144 g_lua
.writeFunction("MaxQPSIPRule", [](unsigned int qps
, boost::optional
<int> ipv4trunc
, boost::optional
<int> ipv6trunc
, boost::optional
<int> burst
) {
1145 return std::shared_ptr
<DNSRule
>(new MaxQPSIPRule(qps
, burst
.get_value_or(qps
), ipv4trunc
.get_value_or(32), ipv6trunc
.get_value_or(64)));
1148 g_lua
.writeFunction("MaxQPSRule", [](unsigned int qps
, boost::optional
<int> burst
) {
1150 return std::shared_ptr
<DNSRule
>(new MaxQPSRule(qps
));
1152 return std::shared_ptr
<DNSRule
>(new MaxQPSRule(qps
, *burst
));
1155 g_lua
.writeFunction("RegexRule", [](const std::string
& str
) {
1156 return std::shared_ptr
<DNSRule
>(new RegexRule(str
));
1160 g_lua
.writeFunction("RE2Rule", [](const std::string
& str
) {
1161 return std::shared_ptr
<DNSRule
>(new RE2Rule(str
));
1165 g_lua
.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode
& smn
, boost::optional
<bool> quiet
) {
1166 return std::shared_ptr
<DNSRule
>(new SuffixMatchNodeRule(smn
, quiet
? *quiet
: false));
1169 g_lua
.writeFunction("NetmaskGroupRule", [](const NetmaskGroup
& nmg
, boost::optional
<bool> src
) {
1170 return std::shared_ptr
<DNSRule
>(new NetmaskGroupRule(nmg
, src
? *src
: true));
1173 g_lua
.writeFunction("benchRule", [](std::shared_ptr
<DNSRule
> rule
, boost::optional
<int> times_
, boost::optional
<string
> suffix_
) {
1174 setLuaNoSideEffect();
1175 int times
= times_
.get_value_or(100000);
1176 DNSName
suffix(suffix_
.get_value_or("powerdns.com"));
1178 vector
<uint8_t> packet
;
1181 uint16_t qtype
, qclass
;
1184 items
.reserve(1000);
1185 for(int n
=0; n
< 1000; ++n
) {
1187 i
.qname
=DNSName(std::to_string(random()));
1189 i
.qtype
= random() % 0xff;
1191 i
.rem
=ComboAddress("127.0.0.1");
1192 i
.rem
.sin4
.sin_addr
.s_addr
= random();
1193 DNSPacketWriter
pw(i
.packet
, i
.qname
, i
.qtype
);
1198 ComboAddress
dummy("127.0.0.1");
1201 for(int n
=0; n
< times
; ++n
) {
1202 const item
& i
= items
[n
% items
.size()];
1203 DNSQuestion
dq(&i
.qname
, i
.qtype
, i
.qclass
, &i
.rem
, &i
.rem
, (struct dnsheader
*)&i
.packet
[0], i
.packet
.size(), i
.packet
.size(), false);
1204 if(rule
->matches(&dq
))
1207 double udiff
=dt
.udiff();
1208 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();
1212 g_lua
.writeFunction("AllRule", []() {
1213 return std::shared_ptr
<DNSRule
>(new AllRule());
1216 g_lua
.writeFunction("ProbaRule", [](double proba
) {
1217 return std::shared_ptr
<DNSRule
>(new ProbaRule(proba
));
1220 g_lua
.writeFunction("QNameRule", [](const std::string
& qname
) {
1221 return std::shared_ptr
<DNSRule
>(new QNameRule(DNSName(qname
)));
1224 g_lua
.writeFunction("QTypeRule", [](boost::variant
<int, std::string
> str
) {
1226 if(auto dir
= boost::get
<int>(&str
)) {
1230 string val
=boost::get
<string
>(str
);
1231 qtype
= QType::chartocode(val
.c_str());
1233 throw std::runtime_error("Unable to convert '"+val
+"' to a DNS type");
1235 return std::shared_ptr
<DNSRule
>(new QTypeRule(qtype
));
1238 g_lua
.writeFunction("QClassRule", [](int c
) {
1239 return std::shared_ptr
<DNSRule
>(new QClassRule(c
));
1242 g_lua
.writeFunction("OpcodeRule", [](uint8_t code
) {
1243 return std::shared_ptr
<DNSRule
>(new OpcodeRule(code
));
1246 g_lua
.writeFunction("AndRule", [](vector
<pair
<int, std::shared_ptr
<DNSRule
> > >a
) {
1247 return std::shared_ptr
<DNSRule
>(new AndRule(a
));
1250 g_lua
.writeFunction("OrRule", [](vector
<pair
<int, std::shared_ptr
<DNSRule
> > >a
) {
1251 return std::shared_ptr
<DNSRule
>(new OrRule(a
));
1254 g_lua
.writeFunction("TCPRule", [](bool tcp
) {
1255 return std::shared_ptr
<DNSRule
>(new TCPRule(tcp
));
1258 g_lua
.writeFunction("DNSSECRule", []() {
1259 return std::shared_ptr
<DNSRule
>(new DNSSECRule());
1262 g_lua
.writeFunction("NotRule", [](std::shared_ptr
<DNSRule
>rule
) {
1263 return std::shared_ptr
<DNSRule
>(new NotRule(rule
));
1266 g_lua
.writeFunction("RecordsCountRule", [](uint8_t section
, uint16_t minCount
, uint16_t maxCount
) {
1267 return std::shared_ptr
<DNSRule
>(new RecordsCountRule(section
, minCount
, maxCount
));
1270 g_lua
.writeFunction("RecordsTypeCountRule", [](uint8_t section
, uint16_t type
, uint16_t minCount
, uint16_t maxCount
) {
1271 return std::shared_ptr
<DNSRule
>(new RecordsTypeCountRule(section
, type
, minCount
, maxCount
));
1274 g_lua
.writeFunction("TrailingDataRule", []() {
1275 return std::shared_ptr
<DNSRule
>(new TrailingDataRule());
1278 g_lua
.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount
, unsigned int maxLabelsCount
) {
1279 return std::shared_ptr
<DNSRule
>(new QNameLabelsCountRule(minLabelsCount
, maxLabelsCount
));
1282 g_lua
.writeFunction("QNameWireLengthRule", [](size_t min
, size_t max
) {
1283 return std::shared_ptr
<DNSRule
>(new QNameWireLengthRule(min
, max
));
1286 g_lua
.writeFunction("RCodeRule", [](uint8_t rcode
) {
1287 return std::shared_ptr
<DNSRule
>(new RCodeRule(rcode
));
1290 g_lua
.writeFunction("ERCodeRule", [](uint8_t rcode
) {
1291 return std::shared_ptr
<DNSRule
>(new ERCodeRule(rcode
));
1294 g_lua
.writeFunction("showRules", [](boost::optional
<bool> showUUIDs
) {
1295 setLuaNoSideEffect();
1297 if (showUUIDs
.get_value_or(false)) {
1298 boost::format
fmt("%-3d %-38s %9d %-56s %s\n");
1299 g_outputBuffer
+= (fmt
% "#" % "UUID" % "Matches" % "Rule" % "Action").str();
1300 for(const auto& lim
: g_rulactions
.getCopy()) {
1301 string name
= lim
.d_rule
->toString();
1302 g_outputBuffer
+= (fmt
% num
% boost::uuids::to_string(lim
.d_id
) % lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
1307 boost::format
fmt("%-3d %9d %-50s %s\n");
1308 g_outputBuffer
+= (fmt
% "#" % "Matches" % "Rule" % "Action").str();
1309 for(const auto& lim
: g_rulactions
.getCopy()) {
1310 string name
= lim
.d_rule
->toString();
1311 g_outputBuffer
+= (fmt
% num
% lim
.d_rule
->d_matches
% name
% lim
.d_action
->toString()).str();
1317 g_lua
.writeFunction("RDRule", []() {
1318 return std::shared_ptr
<DNSRule
>(new RDRule());
1321 g_lua
.writeFunction("TagRule", [](std::string tag
, boost::optional
<std::string
> value
) {
1322 return std::shared_ptr
<DNSRule
>(new TagRule(tag
, value
));
1325 g_lua
.writeFunction("TimedIPSetRule", []() {
1326 return std::shared_ptr
<TimedIPSetRule
>(new TimedIPSetRule());
1329 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)()>("clear", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
1333 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)()>("cleanup", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
1337 g_lua
.registerFunction
<void(std::shared_ptr
<TimedIPSetRule
>::*)(const ComboAddress
& ca
, int t
)>("add", [](std::shared_ptr
<TimedIPSetRule
> tisr
, const ComboAddress
& ca
, int t
) {
1338 tisr
->add(ca
, time(0)+t
);
1341 g_lua
.registerFunction
<std::shared_ptr
<DNSRule
>(std::shared_ptr
<TimedIPSetRule
>::*)()>("slice", [](std::shared_ptr
<TimedIPSetRule
> tisr
) {
1342 return std::dynamic_pointer_cast
<DNSRule
>(tisr
);