From: Remi Gacogne Date: Thu, 16 Nov 2023 12:05:31 +0000 (+0100) Subject: dnsdist: Improve `NetmaskGroupRule`/`SuffixMatchNodeRule`, deprecate `makeRule` X-Git-Tag: dnsdist-1.9.0-alpha4~26^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4299a70d3eb5984f39a1f03d6a335491f4fa5ed9;p=thirdparty%2Fpdns.git dnsdist: Improve `NetmaskGroupRule`/`SuffixMatchNodeRule`, deprecate `makeRule` - `NetmaskGroupRule` now accepts a string or list of strings, instead of requiring the use of `newNMG` - `SuffixMatchNodeRule` also accepts a string or list of strings, instead of requiring the use of `newSuffixMatchNode` - Passing a string or list of strings to `addAction` is now deprecated - `makeRule` is now deprecated --- diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index d0714c08dc..dabe0b8dac 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -2305,7 +2305,7 @@ static void addAction(GlobalStateHolder > *someRuleActions, const luad parseRuleParams(params, uuid, name, creationOrder); checkAllParametersConsumed("addAction", params); - auto rule = makeRule(var); + auto rule = makeRule(var, "addAction"); someRuleActions->modify([&rule, &action, &uuid, creationOrder, &name](vector& ruleactions){ ruleactions.push_back({std::move(rule), std::move(action), std::move(name), std::move(uuid), creationOrder}); }); @@ -2350,7 +2350,7 @@ void setupLuaActions(LuaContext& luaCtx) parseRuleParams(params, uuid, name, creationOrder); checkAllParametersConsumed("newRuleAction", params); - auto rule = makeRule(dnsrule); + auto rule = makeRule(dnsrule, "newRuleAction"); DNSDistRuleAction ra({std::move(rule), std::move(action), std::move(name), uuid, creationOrder}); return std::make_shared(ra); }); diff --git a/pdns/dnsdist-lua-rules.cc b/pdns/dnsdist-lua-rules.cc index 37f04218cc..f3d6dd623c 100644 --- a/pdns/dnsdist-lua-rules.cc +++ b/pdns/dnsdist-lua-rules.cc @@ -24,39 +24,49 @@ #include "dnsdist-rules.hh" #include "dns_random.hh" -std::shared_ptr makeRule(const luadnsrule_t& var) +std::shared_ptr makeRule(const luadnsrule_t& var, const std::string& calledFrom) { - if (var.type() == typeid(std::shared_ptr)) + if (var.type() == typeid(std::shared_ptr)) { return *boost::get>(&var); + } + bool suffixSeen = false; SuffixMatchNode smn; NetmaskGroup nmg; - auto add=[&](string src) { + auto add = [&nmg, &smn, &suffixSeen](string src) { try { nmg.addMask(src); // need to try mask first, all masks are domain names! - } catch(...) { + } catch (...) { + suffixSeen = true; smn.add(DNSName(src)); } }; - if (var.type() == typeid(string)) + if (var.type() == typeid(string)) { add(*boost::get(&var)); - - else if (var.type() == typeid(LuaArray)) - for(const auto& a : *boost::get>(&var)) - add(a.second); - - else if (var.type() == typeid(DNSName)) + } + else if (var.type() == typeid(LuaArray)) { + for (const auto& str : *boost::get>(&var)) { + add(str.second); + } + } + else if (var.type() == typeid(DNSName)) { smn.add(*boost::get(&var)); + } + else if (var.type() == typeid(LuaArray)) { + smn = SuffixMatchNode(); + for (const auto& name : *boost::get>(&var)) { + smn.add(name.second); + } + } - else if (var.type() == typeid(LuaArray)) - for(const auto& a : *boost::get>(&var)) - smn.add(a.second); - - if(nmg.empty()) + if (nmg.empty()) { return std::make_shared(smn); - else - return std::make_shared(nmg, true); + } + if (suffixSeen) { + warnlog("At least one parameter to %s has been parsed as a domain name amongst network masks, and will be ignored!", calledFrom); + } + return std::make_shared(nmg, true); } static boost::uuids::uuid makeRuleID(std::string& id) @@ -275,7 +285,9 @@ static boost::optional getRuleFromSelector(const std::vector& rules, const void setupLuaRules(LuaContext& luaCtx) { - luaCtx.writeFunction("makeRule", makeRule); + luaCtx.writeFunction("makeRule", [](const luadnsrule_t& var) -> std::shared_ptr { + return makeRule(var, "makeRule"); + }); luaCtx.registerFunction::*)()const>("toString", [](const std::shared_ptr& rule) { return rule->toString(); }); @@ -379,7 +391,7 @@ void setupLuaRules(LuaContext& luaCtx) for (const auto& pair : newruleactions) { const auto& newruleaction = pair.second; if (newruleaction->d_action) { - auto rule = makeRule(newruleaction->d_rule); + auto rule = newruleaction->d_rule; gruleactions.push_back({std::move(rule), newruleaction->d_action, newruleaction->d_name, newruleaction->d_id, newruleaction->d_creationOrder}); } } @@ -508,13 +520,43 @@ void setupLuaRules(LuaContext& luaCtx) return std::shared_ptr(new SNIRule(name)); }); - luaCtx.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional quiet) { + luaCtx.writeFunction("SuffixMatchNodeRule", [](const boost::variant> names, boost::optional quiet) { + if (names.type() == typeid(string)) { + SuffixMatchNode smn; + smn.add(DNSName(*boost::get(&names))); return std::shared_ptr(new SuffixMatchNodeRule(smn, quiet ? *quiet : false)); - }); + } - luaCtx.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional src, boost::optional quiet) { + if (names.type() == typeid(LuaArray)) { + SuffixMatchNode smn; + for (const auto& str : *boost::get>(&names)) { + smn.add(DNSName(str.second)); + } + return std::shared_ptr(new SuffixMatchNodeRule(smn, quiet ? *quiet : false)); + } + + const auto& smn = *boost::get(&names); + return std::shared_ptr(new SuffixMatchNodeRule(smn, quiet ? *quiet : false)); + }); + + luaCtx.writeFunction("NetmaskGroupRule", [](const boost::variant> netmasks, boost::optional src, boost::optional quiet) { + if (netmasks.type() == typeid(string)) { + NetmaskGroup nmg; + nmg.addMask(*boost::get(&netmasks)); return std::shared_ptr(new NetmaskGroupRule(nmg, src ? *src : true, quiet ? *quiet : false)); - }); + } + + if (netmasks.type() == typeid(LuaArray)) { + NetmaskGroup nmg; + for (const auto& str : *boost::get>(&netmasks)) { + nmg.addMask(str.second); + } + return std::shared_ptr(new NetmaskGroupRule(nmg, src ? *src : true, quiet ? *quiet : false)); + } + + const auto& nmg = *boost::get(&netmasks); + return std::shared_ptr(new NetmaskGroupRule(nmg, src ? *src : true, quiet ? *quiet : false)); + }); luaCtx.writeFunction("benchRule", [](std::shared_ptr rule, boost::optional times_, boost::optional suffix_) { setLuaNoSideEffect(); diff --git a/pdns/dnsdist-lua.hh b/pdns/dnsdist-lua.hh index 88439d91cf..7dfe0b30b9 100644 --- a/pdns/dnsdist-lua.hh +++ b/pdns/dnsdist-lua.hh @@ -156,11 +156,12 @@ template using LuaArray = std::vector>; template using LuaAssociativeTable = std::unordered_map; template using LuaTypeOrArrayOf = boost::variant>; -using luadnsrule_t = boost::variant, std::shared_ptr, DNSName, LuaArray>; using luaruleparams_t = LuaAssociativeTable; using nmts_t = NetmaskTree; -std::shared_ptr makeRule(const luadnsrule_t& var); +using luadnsrule_t = boost::variant, std::shared_ptr, DNSName, LuaArray>; +std::shared_ptr makeRule(const luadnsrule_t& var, const std::string& calledFrom); + void parseRuleParams(boost::optional& params, boost::uuids::uuid& uuid, std::string& name, uint64_t& creationOrder); void checkParameterBound(const std::string& parameter, uint64_t value, size_t max = std::numeric_limits::max()); diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 74a5b0c21c..22e337814c 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -74,7 +74,6 @@ The regex is applied case insensitively. Alternatively, if compiled in, :func:`RE2Rule` provides similar functionality, but against libre2. Note that to check if a name is in a list of domains, :func:`SuffixMatchNodeRule` is preferred over complex regular expressions or multiple instances of :func:`RegexRule`. -The :func:`makeRule` convenience function can be used to create a :func:`SuffixMatchNodeRule`. Rule Generators --------------- @@ -154,9 +153,13 @@ For Rules related to the incoming query: .. versionchanged:: 1.6.0 Added ``name`` to the ``options``. + .. versionchanged:: 1.9.0 + Passing a string or list of strings instead of a :class:`DNSRule` is deprecated, use :func:`NetmaskGroupRule` or :func:`SuffixMatchNodeRule` instead + Add a Rule and Action to the existing rules. + If a string (or list of) is passed as the first parameter instead of a :class:`DNSRule`, it behaves as if the string or list of strings was passed to :func:`NetmaskGroupRule` or :func:`SuffixMatchNodeRule`. - :param DNSrule rule: A DNSRule, e.g. an :func:`AllRule` or a compounded bunch of rules using e.g. :func:`AndRule` + :param DNSrule rule: A :class:`DNSRule`, e.g. an :func:`AllRule`, a compounded bunch of rules using e.g. :func:`AndRule`, or a string (or list of) (deprecated since 1.9.0) :param action: The action to take :param table options: A table with key: value pairs with options. @@ -687,12 +690,16 @@ These ``DNSRule``\ s be one of the following items: .. versionchanged:: 1.4.0 ``quiet`` parameter added - Matches traffic from/to the network range specified in ``nmg``. + .. versionchanged:: 1.9.0 + The ``nmg`` parameter now accepts a string or a list of strings in addition to a class:`NetmaskGroup` object. + + Matches traffic from/to the network range specified in the ``nmg``, which can be a string, a list of strings, + or a :class:`NetmaskGroup` object created via :func:`newNMG`. Set the ``src`` parameter to false to match ``nmg`` against destination address instead of source address. This can be used to differentiate between clients - :param NetMaskGroup nmg: The NetMaskGroup to match on + :param NetmaskGroup nmg: The netmasks to match, can be a string, a list of strings or a :class:`NetmaskGroup` object. :param bool src: Whether to match source or destination address of the packet. Defaults to true (matches source) :param bool quiet: Do not display the list of matched netmasks in Rules. Default is false. @@ -838,12 +845,16 @@ These ``DNSRule``\ s be one of the following items: .. function:: SuffixMatchNodeRule(smn[, quiet]) + .. versionchanged:: 1.9.0 + The ``smn`` parameter now accepts a string or a list of strings in addition to a class:`SuffixMatchNode` object. + Matches based on a group of domain suffixes for rapid testing of membership. + The first parameter, ``smn``, can be a string, list of strings or a class:`SuffixMatchNode` object created with :func:`newSuffixMatchNode`. Pass true as second parameter to prevent listing of all domains matched. To match domain names exactly, see :func:`QNameSetRule`. - :param SuffixMatchNode smn: The SuffixMatchNode to match on + :param SuffixMatchNode smn: A string, list of strings, or a :class:`SuffixMatchNode` to match on :param bool quiet: Do not display the list of matched domains in Rules. Default is false. .. function:: TagRule(name [, value]) @@ -917,10 +928,20 @@ Convenience Functions .. function:: makeRule(rule) - Make a :func:`NetmaskGroupRule` or a :func:`SuffixMatchNodeRule`, depending on it is called. + .. versionchanged:: 1.9.0 + This function is deprecated, please use :func:`NetmaskGroupRule` or :func:`SuffixMatchNodeRule` instead + + Make a :func:`NetmaskGroupRule` or a :func:`SuffixMatchNodeRule`, depending on how it is called. + The `rule` parameter can be a string, or a list of strings, that should contain either: + + * netmasks: in which case it will behave as :func:`NetmaskGroupRule`, or + * domain names: in which case it will behave as :func:`SuffixMatchNodeRule` + + Mixing both netmasks and domain names is not supported, and will result in domain names being ignored! + ``makeRule("0.0.0.0/0")`` will for example match all IPv4 traffic, ``makeRule({"be","nl","lu"})`` will match all Benelux DNS traffic. - :param string rule: A string to convert to a rule. + :param string rule: A string, or list of strings, to convert to a rule. Actions