From: Remi Gacogne Date: Tue, 28 Nov 2017 16:16:19 +0000 (+0100) Subject: dnsdist: Add UUIDs to rules X-Git-Tag: dnsdist-1.3.0~144^2~6 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4d5959e62e069ca3baf7d52e645211e179de14d1;p=pdns dnsdist: Add UUIDs to rules Having UUID assigned to rules makes it possible to track a given rule, as opposed to the existing rule numbers that changed everytime a rule was deleted or moved around. A rule now keeps the same UUID for the lifetime of the dnsdist process and can even keep this UUID persistent across restart if the UUID is provided when the rule is added: ``` addAction(AllRule(), AllowAction(), {uuid="123e4567-e89b-12d3-a456-426655440000"}) ``` This is especially useful if the rules are managed via a central controller using the console, or to display metrics retrieved via the API. --- diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 78d0ac4ec..bb765f579 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -284,14 +284,14 @@ void doConsole() const std::vector g_consoleKeywords{ /* keyword, function, parameters, description */ { "addACL", true, "netmask", "add to the ACL set who can use this server" }, - { "addAction", true, "DNS rule, DNS action", "add a rule" }, + { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "add a rule" }, { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" }, { "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" }, { "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" }, { "addLuaAction", true, "x, func", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dq`, which returns an action to be taken on this packet. Good for rare packets but where you want to do a lot of processing" }, { "addLuaResponseAction", true, "x, func", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dr`, which returns an action to be taken on this response packet. Good for rare packets but where you want to do a lot of processing" }, - { "addCacheHitResponseAction", true, "DNS rule, DNS response action", "add a cache hit response rule" }, - { "addResponseAction", true, "DNS rule, DNS response action", "add a response rule" }, + { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" }, + { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" }, { "addTLSLocal", true, "addr, certFile, keyFile[,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate and key. The last parameter is a table" }, { "AllowAction", true, "", "let these packets go through" }, { "AllowResponseAction", true, "", "let these packets go through" }, @@ -342,7 +342,7 @@ const std::vector g_consoleKeywords{ { "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true]", "return a new Packet Cache" }, { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" }, { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" }, - { "newRuleAction", true, "DNS rule, DNS action", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" }, + { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" }, { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\"}", "instantiate a server" }, { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" }, { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" }, @@ -353,9 +353,9 @@ const std::vector g_consoleKeywords{ { "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" }, { "RemoteLogAction", true, "RemoteLogger [, alterFunction]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes" }, { "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records" }, - { "rmCacheHitResponseRule", true, "n", "remove cache hit response rule n" }, - { "rmResponseRule", true, "n", "remove response rule n" }, - { "rmRule", true, "n", "remove rule n" }, + { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, + { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, + { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, { "rmServer", true, "n", "remove server with index n" }, { "roundrobin", false, "", "Simple round robin over available servers" }, { "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" }, @@ -401,14 +401,14 @@ const std::vector g_consoleKeywords{ { "show", true, "string", "outputs `string`" }, { "showACL", true, "", "show our ACL set" }, { "showBinds", true, "", "show listening addresses (frontends)" }, - { "showCacheHitResponseRules", true, "", "show all defined cache hit response rules" }, + { "showCacheHitResponseRules", true, "[showUUIDs]", "show all defined cache hit response rules, optionally with their UUIDs" }, { "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" }, { "showDynBlocks", true, "", "show dynamic blocks in force" }, { "showPools", true, "", "show the available pools" }, { "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" }, { "showResponseLatency", true, "", "show a plot of the response time latency distribution" }, - { "showResponseRules", true, "", "show all defined response rules" }, - { "showRules", true, "", "show all defined rules" }, + { "showResponseRules", true, "[showUUIDs]", "show all defined response rules, optionally with their UUIDs" }, + { "showRules", true, "[showUUIDs]", "show all defined rules, optionally with their UUIDs" }, { "showServerPolicy", true, "", "show name of currently operational server selection policy" }, { "showServers", true, "", "output all servers" }, { "showTCPStats", true, "", "show some statistics regarding TCP" }, diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index 05aa6ee70..ed1cbea7e 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -810,62 +810,83 @@ private: void setupLuaActions() { - g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr action) { + g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr action, boost::optional params) { + boost::uuids::uuid uuid; + parseRuleParams(params, uuid); + auto rule=makeRule(dnsrule); - return std::make_shared > >(rule, action); + DNSDistRuleAction ra({rule, action, uuid}); + return std::make_shared(ra); }); - g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant, std::shared_ptr > era) { + g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant, std::shared_ptr > era, boost::optional params) { if (era.type() == typeid(std::shared_ptr)) { throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?"); } + boost::uuids::uuid uuid; + parseRuleParams(params, uuid); + auto ea = *boost::get>(&era); setLuaSideEffect(); auto rule=makeRule(var); - g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){ - rulactions.push_back({rule, ea}); + g_rulactions.modify([rule, ea, uuid](decltype(g_rulactions)::value_type& rulactions){ + rulactions.push_back({rule, ea, uuid}); }); }); - g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func) - { - setLuaSideEffect(); - auto rule=makeRule(var); - g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){ - rulactions.push_back({rule, - std::make_shared(func)}); - }); - }); + g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func) { + setLuaSideEffect(); + auto rule=makeRule(var); + g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){ + rulactions.push_back({ + rule, + std::make_shared(func), + t_uuidGenerator() }); + }); + }); g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func) { setLuaSideEffect(); auto rule=makeRule(var); g_resprulactions.modify([rule,func](decltype(g_resprulactions)::value_type& rulactions){ - rulactions.push_back({rule, - std::make_shared(func)}); + rulactions.push_back({ + rule, + std::make_shared(func), + t_uuidGenerator() }); }); }); - g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant, std::shared_ptr > era) { + g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant, std::shared_ptr > era, boost::optional params) { if (era.type() == typeid(std::shared_ptr)) { throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?"); } auto ea = *boost::get>(&era); + boost::uuids::uuid uuid; + parseRuleParams(params, uuid); setLuaSideEffect(); auto rule=makeRule(var); - g_resprulactions.modify([rule, ea](decltype(g_resprulactions)::value_type& rulactions){ - rulactions.push_back({rule, ea}); + g_resprulactions.modify([rule, ea, uuid](decltype(g_resprulactions)::value_type& rulactions){ + rulactions.push_back({rule, ea, uuid}); }); }); - g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, std::shared_ptr ea) { + g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant, std::shared_ptr> era, boost::optional params) { + if (era.type() == typeid(std::shared_ptr)) { + throw std::runtime_error("addCacheHitResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?"); + } + setLuaSideEffect(); auto rule=makeRule(var); - g_cachehitresprulactions.modify([rule, ea](decltype(g_cachehitresprulactions)::value_type& rulactions){ - rulactions.push_back({rule, ea}); + + boost::uuids::uuid uuid; + parseRuleParams(params, uuid); + + auto ea = *boost::get>(&era); + g_cachehitresprulactions.modify([rule, ea, uuid](decltype(g_cachehitresprulactions)::value_type& rulactions){ + rulactions.push_back({rule, ea, uuid}); }); }); @@ -886,7 +907,7 @@ void setupLuaActions() boost::optional> ret; auto rulactions = g_rulactions.getCopy(); if(num < rulactions.size()) - ret=rulactions[num].second; + ret=rulactions[num].d_action; return ret; }); diff --git a/pdns/dnsdist-lua-rules.cc b/pdns/dnsdist-lua-rules.cc index edfc9f24b..2be77aa6a 100644 --- a/pdns/dnsdist-lua-rules.cc +++ b/pdns/dnsdist-lua-rules.cc @@ -892,32 +892,76 @@ std::shared_ptr makeRule(const luadnsrule_t& var) return std::make_shared(nmg, true); } +static boost::uuids::uuid getRuleID(std::string& id) +{ + if (id.empty()) { + return t_uuidGenerator(); + } + + boost::uuids::string_generator gen; + return gen(id); +} + +void parseRuleParams(boost::optional params, boost::uuids::uuid& uuid) +{ + string uuidStr; + + if (params) { + if (params->count("uuid")) { + uuidStr = boost::get((*params)["uuid"]); + } + } + + uuid = getRuleID(uuidStr); +} + void setupLuaRules() { g_lua.writeFunction("makeRule", makeRule); g_lua.registerFunction::*)()>("toString", [](const std::shared_ptr& rule) { return rule->toString(); }); - g_lua.writeFunction("showResponseRules", []() { + g_lua.writeFunction("showResponseRules", [](boost::optional showUUIDs) { setLuaNoSideEffect(); - boost::format fmt("%-3d %9d %-50s %s\n"); - g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); int num=0; - for(const auto& lim : g_resprulactions.getCopy()) { - string name = lim.first->toString(); - g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str(); - ++num; + if (showUUIDs.get_value_or(false)) { + boost::format fmt("%-3d %-38s %9d %-50s %s\n"); + g_outputBuffer += (fmt % "#" % "UUID" % "Matches" % "Rule" % "Action").str(); + for(const auto& lim : g_resprulactions.getCopy()) { + string name = lim.d_rule->toString(); + g_outputBuffer += (fmt % num % boost::uuids::to_string(lim.d_id) % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + ++num; + } + } + else { + boost::format fmt("%-3d %9d %-50s %s\n"); + g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); + for(const auto& lim : g_resprulactions.getCopy()) { + string name = lim.d_rule->toString(); + g_outputBuffer += (fmt % num % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + ++num; + } } }); - g_lua.writeFunction("rmResponseRule", [](unsigned int num) { + g_lua.writeFunction("rmResponseRule", [](boost::variant num) { setLuaSideEffect(); auto rules = g_resprulactions.getCopy(); - if(num >= rules.size()) { - g_outputBuffer = "Error: attempt to delete non-existing rule\n"; - return; + if (auto str = boost::get(&num)) { + boost::uuids::string_generator gen; + const auto uuid = gen(*str); + rules.erase(std::remove_if(rules.begin(), + rules.end(), + [uuid](const DNSDistResponseRuleAction& a) { return a.d_id == uuid; }), + rules.end()); + } + else if (auto pos = boost::get(&num)) { + if (*pos >= rules.size()) { + g_outputBuffer = "Error: attempt to delete non-existing rule\n"; + return; + } + rules.erase(rules.begin()+*pos); } - rules.erase(rules.begin()+num); g_resprulactions.setState(rules); }); @@ -951,26 +995,47 @@ void setupLuaRules() g_resprulactions.setState(rules); }); - g_lua.writeFunction("showCacheHitResponseRules", []() { + g_lua.writeFunction("showCacheHitResponseRules", [](boost::optional showUUIDs) { setLuaNoSideEffect(); - boost::format fmt("%-3d %9d %-50s %s\n"); - g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); int num=0; - for(const auto& lim : g_cachehitresprulactions.getCopy()) { - string name = lim.first->toString(); - g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str(); - ++num; + if (showUUIDs.get_value_or(false)) { + boost::format fmt("%-3d %-38s %9d %-50s %s\n"); + g_outputBuffer += (fmt % "#" % "UUID" % "Matches" % "Rule" % "Action").str(); + for(const auto& lim : g_cachehitresprulactions.getCopy()) { + string name = lim.d_rule->toString(); + g_outputBuffer += (fmt % num % boost::uuids::to_string(lim.d_id) % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + ++num; + } + } + else { + boost::format fmt("%-3d %9d %-50s %s\n"); + g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); + for(const auto& lim : g_cachehitresprulactions.getCopy()) { + string name = lim.d_rule->toString(); + g_outputBuffer += (fmt % num % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + ++num; + } } }); - g_lua.writeFunction("rmCacheHitResponseRule", [](unsigned int num) { + g_lua.writeFunction("rmCacheHitResponseRule", [](boost::variant num) { setLuaSideEffect(); auto rules = g_cachehitresprulactions.getCopy(); - if(num >= rules.size()) { - g_outputBuffer = "Error: attempt to delete non-existing rule\n"; - return; + if (auto str = boost::get(&num)) { + boost::uuids::string_generator gen; + const auto uuid = gen(*str); + rules.erase(std::remove_if(rules.begin(), + rules.end(), + [uuid](const DNSDistResponseRuleAction& a) { return a.d_id == uuid; }), + rules.end()); + } + else if (auto pos = boost::get(&num)) { + if (*pos >= rules.size()) { + g_outputBuffer = "Error: attempt to delete non-existing rule\n"; + return; + } + rules.erase(rules.begin()+*pos); } - rules.erase(rules.begin()+num); g_cachehitresprulactions.setState(rules); }); @@ -1004,14 +1069,24 @@ void setupLuaRules() g_cachehitresprulactions.setState(rules); }); - g_lua.writeFunction("rmRule", [](unsigned int num) { + g_lua.writeFunction("rmRule", [](boost::variant num) { setLuaSideEffect(); auto rules = g_rulactions.getCopy(); - if(num >= rules.size()) { - g_outputBuffer = "Error: attempt to delete non-existing rule\n"; - return; + if (auto str = boost::get(&num)) { + boost::uuids::string_generator gen; + const auto uuid = gen(*str); + rules.erase(std::remove_if(rules.begin(), + rules.end(), + [uuid](const DNSDistRuleAction& a) { return a.d_id == uuid; }), + rules.end()); + } + else if (auto pos = boost::get(&num)) { + if (*pos >= rules.size()) { + g_outputBuffer = "Error: attempt to delete non-existing rule\n"; + return; + } + rules.erase(rules.begin()+*pos); } - rules.erase(rules.begin()+num); g_rulactions.setState(rules); }); @@ -1053,14 +1128,14 @@ void setupLuaRules() }); }); - g_lua.writeFunction("setRules", [](std::vector< std::pair > > > > newruleactions) { + g_lua.writeFunction("setRules", [](std::vector& newruleactions) { setLuaSideEffect(); g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) { gruleactions.clear(); for (const auto& newruleaction : newruleactions) { - if (newruleaction.second) { - auto rule=makeRule(newruleaction.second->first); - gruleactions.push_back({rule, newruleaction.second->second}); + if (newruleaction.d_action) { + auto rule=makeRule(newruleaction.d_rule); + gruleactions.push_back({rule, newruleaction.d_action, newruleaction.d_id}); } } }); @@ -1216,15 +1291,26 @@ void setupLuaRules() return std::shared_ptr(new ERCodeRule(rcode)); }); - g_lua.writeFunction("showRules", []() { - setLuaNoSideEffect(); - boost::format fmt("%-3d %9d %-50s %s\n"); - g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); - int num=0; - for(const auto& lim : g_rulactions.getCopy()) { - string name = lim.first->toString(); - g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str(); - ++num; + g_lua.writeFunction("showRules", [](boost::optional showUUIDs) { + setLuaNoSideEffect(); + int num=0; + if (showUUIDs.get_value_or(false)) { + boost::format fmt("%-3d %-38s %9d %-56s %s\n"); + g_outputBuffer += (fmt % "#" % "UUID" % "Matches" % "Rule" % "Action").str(); + for(const auto& lim : g_rulactions.getCopy()) { + string name = lim.d_rule->toString(); + g_outputBuffer += (fmt % num % boost::uuids::to_string(lim.d_id) % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + ++num; + } + } + else { + boost::format fmt("%-3d %9d %-50s %s\n"); + g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); + for(const auto& lim : g_rulactions.getCopy()) { + string name = lim.d_rule->toString(); + g_outputBuffer += (fmt % num % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + ++num; + } } }); diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index ce8efadda..03e988f5c 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -918,6 +918,7 @@ void setupLuaConfig(bool client) #else g_outputBuffer="Error: DNSCrypt support is not enabled.\n"; #endif + }); g_lua.writeFunction("showDNSCryptBinds", []() { diff --git a/pdns/dnsdist-lua.hh b/pdns/dnsdist-lua.hh index 32eabb3d3..ee5a9ba6c 100644 --- a/pdns/dnsdist-lua.hh +++ b/pdns/dnsdist-lua.hh @@ -99,6 +99,8 @@ private: typedef boost::variant>, std::shared_ptr, DNSName, vector > > luadnsrule_t; std::shared_ptr makeRule(const luadnsrule_t& var); +typedef std::unordered_map > luaruleparams_t; +void parseRuleParams(boost::optional params, boost::uuids::uuid& uuid); typedef NetmaskTree nmts_t; diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index 3d5186156..75b8f3190 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -448,10 +448,11 @@ static void connectionThread(int sock, ComboAddress remote, string password, str for(const auto& a : localRules) { Json::object rule{ {"id", num++}, - {"matches", (double)a.first->d_matches}, - {"rule", a.first->toString()}, - {"action", a.second->toString()}, - {"action-stats", a.second->getStats()} + {"uuid", boost::uuids::to_string(a.d_id)}, + {"matches", (double)a.d_rule->d_matches}, + {"rule", a.d_rule->toString()}, + {"action", a.d_action->toString()}, + {"action-stats", a.d_action->getStats()} }; rules.push_back(rule); } @@ -462,9 +463,10 @@ static void connectionThread(int sock, ComboAddress remote, string password, str for(const auto& a : localResponseRules) { Json::object rule{ {"id", num++}, - {"matches", (double)a.first->d_matches}, - {"rule", a.first->toString()}, - {"action", a.second->toString()}, + {"uuid", boost::uuids::to_string(a.d_id)}, + {"matches", (double)a.d_rule->d_matches}, + {"rule", a.d_rule->toString()}, + {"action", a.d_action->toString()}, }; responseRules.push_back(rule); } @@ -475,9 +477,10 @@ static void connectionThread(int sock, ComboAddress remote, string password, str for(const auto& a : localCacheHitResponseRules) { Json::object rule{ {"id", num++}, - {"matches", (double)a.first->d_matches}, - {"rule", a.first->toString()}, - {"action", a.second->toString()}, + {"uuid", boost::uuids::to_string(a.d_id)}, + {"matches", (double)a.d_rule->d_matches}, + {"rule", a.d_rule->toString()}, + {"action", a.d_action->toString()}, }; cacheHitResponseRules.push_back(rule); } diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index afd0e9d7a..a48a78692 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -54,9 +54,7 @@ #include #endif -#ifdef HAVE_PROTOBUF thread_local boost::uuids::random_generator t_uuidGenerator; -#endif /* Known sins: @@ -134,9 +132,10 @@ DNSDistSNMPAgent* g_snmpAgent{nullptr}; If all downstreams are over QPS, we pick the fastest server */ -GlobalStateHolder, std::shared_ptr > > > g_rulactions; -GlobalStateHolder, std::shared_ptr > > > g_resprulactions; -GlobalStateHolder, std::shared_ptr > > > g_cachehitresprulactions; +GlobalStateHolder > g_rulactions; +GlobalStateHolder > g_resprulactions; +GlobalStateHolder > g_cachehitresprulactions; + Rings g_rings; QueryCount g_qcount; @@ -981,9 +980,9 @@ bool processQuery(LocalHolders& holders, DNSQuestion& dq, string& poolname, int* DNSAction::Action action=DNSAction::Action::None; string ruleresult; for(const auto& lr : *holders.rulactions) { - if(lr.first->matches(&dq)) { - lr.first->d_matches++; - action=(*lr.second)(&dq, &ruleresult); + if(lr.d_rule->matches(&dq)) { + lr.d_rule->d_matches++; + action=(*lr.d_action)(&dq, &ruleresult); switch(action) { case DNSAction::Action::Allow: @@ -1034,14 +1033,14 @@ bool processQuery(LocalHolders& holders, DNSQuestion& dq, string& poolname, int* return true; } -bool processResponse(LocalStateHolder, std::shared_ptr > > >& localRespRulactions, DNSResponse& dr, int* delayMsec) +bool processResponse(LocalStateHolder >& localRespRulactions, DNSResponse& dr, int* delayMsec) { DNSResponseAction::Action action=DNSResponseAction::Action::None; std::string ruleresult; for(const auto& lr : *localRespRulactions) { - if(lr.first->matches(&dr)) { - lr.first->d_matches++; - action=(*lr.second)(&dr, &ruleresult); + if(lr.d_rule->matches(&dr)) { + lr.d_rule->d_matches++; + action=(*lr.d_action)(&dr, &ruleresult); switch(action) { case DNSResponseAction::Action::Allow: return true; diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index a993fd7ce..aff57f207 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -42,10 +42,9 @@ #include #include "tcpiohandler.hh" -#ifdef HAVE_PROTOBUF #include #include -#endif +#include void* carbonDumpThread(); uint64_t uptimeOfProcess(const std::string& str); @@ -123,9 +122,7 @@ private: static constexpr char const *strSep = "\t"; }; -#ifdef HAVE_PROTOBUF extern thread_local boost::uuids::random_generator t_uuidGenerator; -#endif struct DNSQuestion { @@ -728,6 +725,20 @@ enum ednsHeaderFlags { EDNS_HEADER_FLAG_DO = 32768 }; +struct DNSDistRuleAction +{ + std::shared_ptr d_rule; + std::shared_ptr d_action; + boost::uuids::uuid d_id; +}; + +struct DNSDistResponseRuleAction +{ + std::shared_ptr d_rule; + std::shared_ptr d_action; + boost::uuids::uuid d_id; +}; + extern GlobalStateHolder> g_dynblockSMT; extern DNSAction::Action g_dynBlockAction; @@ -735,9 +746,9 @@ extern GlobalStateHolder > g_carbon; extern GlobalStateHolder g_policy; extern GlobalStateHolder g_dstates; extern GlobalStateHolder g_pools; -extern GlobalStateHolder, std::shared_ptr > > > g_rulactions; -extern GlobalStateHolder, std::shared_ptr > > > g_resprulactions; -extern GlobalStateHolder, std::shared_ptr > > > g_cachehitresprulactions; +extern GlobalStateHolder > g_rulactions; +extern GlobalStateHolder > g_resprulactions; +extern GlobalStateHolder > g_cachehitresprulactions; extern GlobalStateHolder g_ACL; extern ComboAddress g_serverControl; // not changed during runtime @@ -802,8 +813,8 @@ struct LocalHolders LocalStateHolder acl; LocalStateHolder policy; - LocalStateHolder, std::shared_ptr > > > rulactions; - LocalStateHolder, std::shared_ptr > > > cacheHitRespRulactions; + LocalStateHolder > rulactions; + LocalStateHolder > cacheHitRespRulactions; LocalStateHolder servers; LocalStateHolder > dynNMGBlock; LocalStateHolder > dynSMTBlock; @@ -844,7 +855,7 @@ void resetLuaSideEffect(); // reset to indeterminate state bool responseContentMatches(const char* response, const uint16_t responseLen, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& remote); bool processQuery(LocalHolders& holders, DNSQuestion& dq, string& poolname, int* delayMsec, const struct timespec& now); -bool processResponse(LocalStateHolder, std::shared_ptr > > >& localRespRulactions, DNSResponse& dr, int* delayMsec); +bool processResponse(LocalStateHolder >& localRespRulactions, DNSResponse& dr, int* delayMsec); bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, std::vector& rewrittenResponse, uint16_t addRoom); void restoreFlags(struct dnsheader* dh, uint16_t origFlags); bool checkQueryHeaders(const struct dnsheader* dh); diff --git a/pdns/dnsdistdist/configure.ac b/pdns/dnsdistdist/configure.ac index 4dcd09e56..5354fea36 100644 --- a/pdns/dnsdistdist/configure.ac +++ b/pdns/dnsdistdist/configure.ac @@ -22,15 +22,10 @@ PDNS_CHECK_OS PDNS_CHECK_NETWORK_LIBS PDNS_CHECK_PTHREAD_NP -boost_required_version=1.35 PDNS_WITH_PROTOBUF -AS_IF([test "x$PROTOBUF_LIBS" != "x" -a x"$PROTOC" != "x"], - # The protobuf code needs boost::uuid, which is available from 1.42 onward - [AC_MSG_WARN([Bumping minimal Boost requirement to 1.42. To keep the requirement at 1.35, disable protobuf support]) - boost_required_version=1.42] -) +boost_required_version=1.42 BOOST_REQUIRE([$boost_required_version]) PDNS_ENABLE_UNIT_TESTS diff --git a/pdns/dnsdistdist/docs/guides/webserver.rst b/pdns/dnsdistdist/docs/guides/webserver.rst index 6f3767a37..ff0eda289 100644 --- a/pdns/dnsdistdist/docs/guides/webserver.rst +++ b/pdns/dnsdistdist/docs/guides/webserver.rst @@ -187,9 +187,10 @@ JSON Objects :property string action: The action taken when the rule matches (e.g. "to pool abuse") :property dict action-stats: A list of statistics whose content varies depending on the kind of rule - :property integer id: The identifier (or order) of this rule + :property integer id: The order of this rule :property integer matches: How many times this rule was hit :property string rule: The matchers for the packet (e.g. "qname==bad-domain1.example., bad-domain2.example.") + :property string uuid: The UUID of this rule .. json:object:: ResponseRule diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 566d9de30..da0c31de2 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -233,12 +233,20 @@ Active Rules can be shown with :func:`showRules` and removed with :func:`rmRule` For Rules related to the incoming query: -.. function:: addAction(DNSrule, action) +.. function:: addAction(DNSrule, action [, options]) + + .. versionchanged:: 1.3.0 + Added the optional parameter ``options``. Add a Rule and Action to the existing rules. :param DNSrule rule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule` :param action: The action to take + :param table options: A table with key: value pairs with options. + + Options: + + * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. .. function:: clearRules() @@ -258,12 +266,20 @@ For Rules related to the incoming query: :param int from: Rule number to move :param int to: Location to more the Rule to -.. function:: newRuleAction(rule, action) +.. function:: newRuleAction(rule, action[, options]) + + .. versionchanged:: 1.3.0 + Added the optional parameter ``options``. Return a pair of DNS Rule and DNS Action, to be used with :func:`setRules`. :param Rule rule: A `Rule <#traffic-matching>`_ :param Action action: The `Action <#actions>`_ to apply to the matched traffic + :param table options: A table with key: value pairs with options. + + Options: + + * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. .. function:: setRules(rules) @@ -271,9 +287,11 @@ For Rules related to the incoming query: :param [RuleAction] rules: A list of RuleActions -.. function:: showRules() +.. function:: showRules([showUUIDs]) + + Show all defined rules for queries, optionally displaying their UUIDs. - Show all defined rules for queries. + :param bool showUUIDs: Whether to display the UUIDs, defaults to false .. function:: topRule() @@ -281,18 +299,29 @@ For Rules related to the incoming query: .. function:: rmRule(n) - Remove rule ``n``. + .. versionchanged:: 1.3.0 + ``id`` can now be an UUID. - :param int n: Rule number to remove + Remove rule ``id``. + + :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise For Rules related to responses: -.. function:: addResponseAction(DNSRule, action) +.. function:: addResponseAction(DNSRule, action [, options]) + + .. versionchanged:: 1.3.0 + Added the optional parameter ``options``. Add a Rule and Action for responses to the existing rules. :param DNSRule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule` :param action: The action to take + :param table options: A table with key: value pairs with options. + + Options: + + * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. .. function:: mvResponseRule(from, to) @@ -304,13 +333,18 @@ For Rules related to responses: .. function:: rmResponseRule(n) - Remove response rule ``n``. + .. versionchanged:: 1.3.0 + ``id`` can now be an UUID. - :param int n: Rule number to remove + Remove response rule ``id``. + + :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise + +.. function:: showResponseRules([showUUIDs]) -.. function:: showResponseRules() + Show all defined response rules, optionally displaying their UUIDs. - Show all defined response rules. + :param bool showUUIDs: Whether to display the UUIDs, defaults to false .. function:: topResponseRule() @@ -318,14 +352,22 @@ For Rules related to responses: Functions for manipulation Cache Hit Rules: -.. function:: addCacheHitAction(DNSRule, action) +.. function:: addCacheHitResponseAction(DNSRule, action) .. versionadded:: 1.2.0 - Add a Rule and Action for Cache Hits to the existing rules. + .. versionchanged:: 1.3.0 + Added the optional parameter ``options``. + + Add a Rule and ResponseAction for Cache Hits to the existing rules. :param DNSRule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule` :param action: The action to take + :param table options: A table with key: value pairs with options. + + Options: + + * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. .. function:: mvCacheHitResponseRule(from, to) @@ -337,19 +379,24 @@ Functions for manipulation Cache Hit Rules: :param int from: Rule number to move :param int to: Location to more the Rule to -.. function:: rmCacheHitResponseRule(n) +.. function:: rmCacheHitResponseRule(id) .. versionadded:: 1.2.0 - Remove cache hit response rule ``n``. + .. versionchanged:: 1.3.0 + ``id`` can now be an UUID. + + :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise :param int n: Rule number to remove -.. function:: showCacheHitResponseRules() +.. function:: showCacheHitResponseRules([showUUIDs]) .. versionadded:: 1.2.0 - Show all defined cache hit response rules. + Show all defined cache hit response rules, optionally displaying their UUIDs. + + :param bool showUUIDs: Whether to display the UUIDs, defaults to false .. function:: topCacheHitResponseRule() diff --git a/regression-tests.dnsdist/test_API.py b/regression-tests.dnsdist/test_API.py index c1d43b076..3d1e802b3 100644 --- a/regression-tests.dnsdist/test_API.py +++ b/regression-tests.dnsdist/test_API.py @@ -82,13 +82,13 @@ class TestAPIBasics(DNSDistTest): self.assertIn(key, content) for rule in content['rules']: - for key in ['id', 'matches', 'rule', 'action']: + for key in ['id', 'matches', 'rule', 'action', 'uuid']: self.assertIn(key, rule) for key in ['id', 'matches']: self.assertTrue(rule[key] >= 0) for rule in content['response-rules']: - for key in ['id', 'matches', 'rule', 'action']: + for key in ['id', 'matches', 'rule', 'action', 'uuid']: self.assertIn(key, rule) for key in ['id', 'matches']: self.assertTrue(rule[key] >= 0)