]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add UUIDs to rules
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 28 Nov 2017 16:16:19 +0000 (17:16 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 12 Jan 2018 11:40:54 +0000 (12:40 +0100)
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.

12 files changed:
pdns/dnsdist-console.cc
pdns/dnsdist-lua-actions.cc
pdns/dnsdist-lua-rules.cc
pdns/dnsdist-lua.cc
pdns/dnsdist-lua.hh
pdns/dnsdist-web.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistdist/configure.ac
pdns/dnsdistdist/docs/guides/webserver.rst
pdns/dnsdistdist/docs/rules-actions.rst
regression-tests.dnsdist/test_API.py

index 78d0ac4ec1648b1c0ef260b0d12ad17569475625..bb765f57962538a2b5de8100ce0ce2b2d81afdec 100644 (file)
@@ -284,14 +284,14 @@ void doConsole()
 const std::vector<ConsoleKeyword> 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<ConsoleKeyword> 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<ConsoleKeyword> 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<ConsoleKeyword> 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" },
index 05aa6ee70c987a5d6a914b3341b17d2f956fc58b..ed1cbea7e72e35ebf3cb9b921fb4a63e234718d8 100644 (file)
@@ -810,62 +810,83 @@ private:
 
 void setupLuaActions()
 {
-  g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action) {
+  g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action, boost::optional<luaruleparams_t> params) {
+      boost::uuids::uuid uuid;
+      parseRuleParams(params, uuid);
+
       auto rule=makeRule(dnsrule);
-      return std::make_shared<std::pair< luadnsrule_t, std::shared_ptr<DNSAction> > >(rule, action);
+      DNSDistRuleAction ra({rule, action, uuid});
+      return std::make_shared<DNSDistRuleAction>(ra);
     });
 
-  g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
+  g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
       if (era.type() == typeid(std::shared_ptr<DNSResponseAction>)) {
         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<std::shared_ptr<DNSAction>>(&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<LuaAction>(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<LuaAction>(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<LuaResponseAction>(func)});
+          rulactions.push_back({
+                rule,
+                std::make_shared<LuaResponseAction>(func),
+                t_uuidGenerator() });
         });
     });
 
-  g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
+  g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
       if (era.type() == typeid(std::shared_ptr<DNSAction>)) {
         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<std::shared_ptr<DNSResponseAction>>(&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<DNSResponseAction> ea) {
+  g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+      if (era.type() == typeid(std::shared_ptr<DNSAction>)) {
+        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<std::shared_ptr<DNSResponseAction>>(&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<std::shared_ptr<DNSAction>> ret;
       auto rulactions = g_rulactions.getCopy();
       if(num < rulactions.size())
-        ret=rulactions[num].second;
+        ret=rulactions[num].d_action;
       return ret;
     });
 
index edfc9f24bd7bd01cf578251e14db745095c8ef9a..2be77aa6ab006f76560214902262e36e97ece191 100644 (file)
@@ -892,32 +892,76 @@ std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
     return std::make_shared<NetmaskGroupRule>(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<luaruleparams_t> params, boost::uuids::uuid& uuid)
+{
+  string uuidStr;
+
+  if (params) {
+    if (params->count("uuid")) {
+      uuidStr = boost::get<std::string>((*params)["uuid"]);
+    }
+  }
+
+  uuid = getRuleID(uuidStr);
+}
+
 void setupLuaRules()
 {
   g_lua.writeFunction("makeRule", makeRule);
 
   g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
 
-  g_lua.writeFunction("showResponseRules", []() {
+  g_lua.writeFunction("showResponseRules", [](boost::optional<bool> 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<unsigned int, std::string> 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<std::string>(&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<unsigned int>(&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<bool> 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<unsigned int, std::string> 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<std::string>(&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<unsigned int>(&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<unsigned int, std::string> 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<std::string>(&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<unsigned int>(&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<int, std::shared_ptr<std::pair<luadnsrule_t, std::shared_ptr<DNSAction> > > > > newruleactions) {
+  g_lua.writeFunction("setRules", [](std::vector<DNSDistRuleAction>& 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<DNSRule>(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<bool> 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;
+        }
       }
     });
 
index ce8efadda6c83d46f5ec3a4dcbdb2279a97fa276..03e988f5cdca272bad35c8413da64156fe992556 100644 (file)
@@ -918,6 +918,7 @@ void setupLuaConfig(bool client)
 #else
       g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
 #endif
+
     });
 
   g_lua.writeFunction("showDNSCryptBinds", []() {
index 32eabb3d33a6bae5fccf7e0efb95dc24962962e0..ee5a9ba6c3932243a5be1474753728c58d61a0f2 100644 (file)
@@ -99,6 +99,8 @@ private:
 
 typedef boost::variant<string, vector<pair<int, string>>, std::shared_ptr<DNSRule>, DNSName, vector<pair<int, DNSName> > > luadnsrule_t;
 std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var);
+typedef std::unordered_map<std::string, boost::variant<std::string> > luaruleparams_t;
+void parseRuleParams(boost::optional<luaruleparams_t> params, boost::uuids::uuid& uuid);
 
 typedef NetmaskTree<DynBlock> nmts_t;
 
index 3d5186156062284917e56b997c9b7f9dddcb8209..75b8f3190e9660baf3a8e0c32f480339db84b5c2 100644 (file)
@@ -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);
       }
index afd0e9d7a7e2b5d4183772287ed3ea85586e96e3..a48a786920faefd3e0f7c9717edd3bf9bdc37667 100644 (file)
@@ -54,9 +54,7 @@
 #include <systemd/sd-daemon.h>
 #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<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > g_rulactions;
-GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_resprulactions;
-GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_cachehitresprulactions;
+GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > 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<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > >& localRespRulactions, DNSResponse& dr, int* delayMsec)
+bool processResponse(LocalStateHolder<vector<DNSDistResponseRuleAction> >& 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;
index a993fd7ce5bc59f1933edfd0e1b3c2241cb770db..aff57f207486e37e7bf6d0ca83633102970ecdb6 100644 (file)
 #include <unordered_map>
 #include "tcpiohandler.hh"
 
-#ifdef HAVE_PROTOBUF
 #include <boost/uuid/uuid.hpp>
 #include <boost/uuid/uuid_generators.hpp>
-#endif
+#include <boost/uuid/uuid_io.hpp>
 
 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<DNSRule> d_rule;
+  std::shared_ptr<DNSAction> d_action;
+  boost::uuids::uuid d_id;
+};
+
+struct DNSDistResponseRuleAction
+{
+  std::shared_ptr<DNSRule> d_rule;
+  std::shared_ptr<DNSResponseAction> d_action;
+  boost::uuids::uuid d_id;
+};
+
 extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
 extern DNSAction::Action g_dynBlockAction;
 
@@ -735,9 +746,9 @@ extern GlobalStateHolder<vector<CarbonConfig> > g_carbon;
 extern GlobalStateHolder<ServerPolicy> g_policy;
 extern GlobalStateHolder<servers_t> g_dstates;
 extern GlobalStateHolder<pools_t> g_pools;
-extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > g_rulactions;
-extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_resprulactions;
-extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_cachehitresprulactions;
+extern GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions;
 extern GlobalStateHolder<NetmaskGroup> g_ACL;
 
 extern ComboAddress g_serverControl; // not changed during runtime
@@ -802,8 +813,8 @@ struct LocalHolders
 
   LocalStateHolder<NetmaskGroup> acl;
   LocalStateHolder<ServerPolicy> policy;
-  LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > rulactions;
-  LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > cacheHitRespRulactions;
+  LocalStateHolder<vector<DNSDistRuleAction> > rulactions;
+  LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRulactions;
   LocalStateHolder<servers_t> servers;
   LocalStateHolder<NetmaskTree<DynBlock> > dynNMGBlock;
   LocalStateHolder<SuffixMatchTree<DynBlock> > 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<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > >& localRespRulactions, DNSResponse& dr, int* delayMsec);
+bool processResponse(LocalStateHolder<vector<DNSDistResponseRuleAction> >& 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<uint8_t>& rewrittenResponse, uint16_t addRoom);
 void restoreFlags(struct dnsheader* dh, uint16_t origFlags);
 bool checkQueryHeaders(const struct dnsheader* dh);
index 4dcd09e56de84ee345ba44378a0e67cb79f44e35..5354fea36dc46281d92b874721d174e1a96fcc6e 100644 (file)
@@ -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
index 6f3767a37e88071ddb7601d312839eca8217d589..ff0eda289332087cdc40c5d3b60e55650e4e1ace 100644 (file)
@@ -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
 
index 566d9de301c04ed356439b277042937c9dd2d574..da0c31de24a03361e7a66569cee736be5f6c49cd 100644 (file)
@@ -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()
 
index c1d43b07695cd2cccaa6172724756470661ff92d..3d1e802b3e26fd4468a87f03709983b6cfa6f824 100644 (file)
@@ -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)