]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add support for exporting a server ID in protobuf 7015/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 2 Oct 2018 14:41:59 +0000 (16:41 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 3 Oct 2018 08:02:49 +0000 (10:02 +0200)
pdns/dnsdist-console.cc
pdns/dnsdist-lua-actions.cc
pdns/dnsdist-lua-bindings.cc
pdns/dnsdistdist/docs/reference/protobuf.rst
pdns/dnsdistdist/docs/rules-actions.rst
regression-tests.dnsdist/test_Protobuf.py

index 4eda5b0553a3159c9083ba2d94f5281f6896b891..54aa42fe2344787ddd39ac736c10fef791c6a38e 100644 (file)
@@ -397,8 +397,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "RCodeRule", true, "rcode", "matches responses with the specified rcode" },
   { "RegexRule", true, "regex", "matches the query name against the supplied regex" },
   { "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" },
+  { "RemoteLogAction", true, "RemoteLogger [, alterFunction [, serverID]]", "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. `serverID` is the server identifier." },
+  { "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME [, serverID]]]", "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. `serverID` is the server identifier." },
   { "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" },
index 377add214c5aaee840c3380bf7853427baf500e8..b482164c637a28ae44ced45305101a8516e0b997 100644 (file)
@@ -747,7 +747,7 @@ private:
 class RemoteLogAction : public DNSAction, public boost::noncopyable
 {
 public:
-  RemoteLogAction(std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
+  RemoteLogAction(std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc, const std::string& serverID): d_logger(logger), d_alterFunc(alterFunc), d_serverID(serverID)
   {
   }
   DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
@@ -758,12 +758,15 @@ public:
     }
 
     DNSDistProtoBufMessage message(*dq);
-    {
-      if (d_alterFunc) {
-        std::lock_guard<std::mutex> lock(g_luamutex);
-        (*d_alterFunc)(*dq, &message);
-      }
+    if (!d_serverID.empty()) {
+      message.setServerIdentity(d_serverID);
     }
+
+    if (d_alterFunc) {
+      std::lock_guard<std::mutex> lock(g_luamutex);
+      (*d_alterFunc)(*dq, &message);
+    }
+
     std::string data;
     message.serialize(data);
     d_logger->queueData(data);
@@ -777,6 +780,7 @@ public:
 private:
   std::shared_ptr<RemoteLoggerInterface> d_logger;
   boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > d_alterFunc;
+  std::string d_serverID;
 };
 
 class SNMPTrapAction : public DNSAction
@@ -863,7 +867,7 @@ private:
 class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
 {
 public:
-  RemoteLogResponseAction(std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
+  RemoteLogResponseAction(std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, const std::string& serverID, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_serverID(serverID), d_includeCNAME(includeCNAME)
   {
   }
   DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
@@ -874,12 +878,15 @@ public:
     }
 
     DNSDistProtoBufMessage message(*dr, d_includeCNAME);
-    {
-      if (d_alterFunc) {
-        std::lock_guard<std::mutex> lock(g_luamutex);
-        (*d_alterFunc)(*dr, &message);
-      }
+    if (!d_serverID.empty()) {
+      message.setServerIdentity(d_serverID);
+    }
+
+    if (d_alterFunc) {
+      std::lock_guard<std::mutex> lock(g_luamutex);
+      (*d_alterFunc)(*dr, &message);
     }
+
     std::string data;
     message.serialize(data);
     d_logger->queueData(data);
@@ -893,6 +900,7 @@ public:
 private:
   std::shared_ptr<RemoteLoggerInterface> d_logger;
   boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
+  std::string d_serverID;
   bool d_includeCNAME;
 };
 
@@ -1174,29 +1182,45 @@ void setupLuaActions()
       return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
     });
 
-  g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
+  g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<std::unordered_map<std::string, std::string>> vars) {
       // avoids potentially-evaluated-expression warning with clang.
       RemoteLoggerInterface& rl = *logger.get();
       if (typeid(rl) != typeid(RemoteLogger)) {
         // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
         throw std::runtime_error(std::string("RemoteLogAction only takes RemoteLogger. For other types, please look at DnstapLogAction."));
       }
+
+      std::string serverID;
+      if (vars) {
+        if (vars->count("serverID")) {
+          serverID = boost::get<std::string>((*vars)["serverID"]);
+        }
+      }
+
 #ifdef HAVE_PROTOBUF
-      return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
+      return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc, serverID));
 #else
       throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
 #endif
     });
 
-  g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
+  g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME, boost::optional<std::unordered_map<std::string, std::string>> vars) {
       // avoids potentially-evaluated-expression warning with clang.
       RemoteLoggerInterface& rl = *logger.get();
       if (typeid(rl) != typeid(RemoteLogger)) {
         // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong.
         throw std::runtime_error("RemoteLogResponseAction only takes RemoteLogger. For other types, please look at DnstapLogResponseAction.");
       }
+
+      std::string serverID;
+      if (vars) {
+        if (vars->count("serverID")) {
+          serverID = boost::get<std::string>((*vars)["serverID"]);
+        }
+      }
+
 #ifdef HAVE_PROTOBUF
-      return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
+      return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, serverID, includeCNAME ? *includeCNAME : false));
 #else
       throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
 #endif
index 97c3a0ef43331318bb7d4deb2cd493d4c342d907..d5cdaa0ec683d2c9114bbdaa0224f26011fa1ad7 100644 (file)
@@ -309,6 +309,9 @@ void setupLuaBindings(bool client)
   g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
       message.setResponder(str);
     });
+  g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setServerIdentity", [](DNSDistProtoBufMessage& message, const std::string& str) {
+      message.setServerIdentity(str);
+    });
 
   g_lua.registerFunction<std::string(DnstapMessage::*)()>("toDebugString", [](const DnstapMessage& message) { return message.toDebugString(); });
   g_lua.registerFunction<void(DnstapMessage::*)(const std::string&)>("setExtra", [](DnstapMessage& message, const std::string& str) {
index 8f3ee6c00fea991be8b83092a402a50341fb40d2..1cb71a7d667724e170845a591e5c723b670d2eff 100644 (file)
@@ -80,7 +80,7 @@ Protobuf Logging Reference
 
     :param ComboAddress address: The address to set to
 
-  .. method:: DNSDistProtoBufMessage:setResponderFromString(string)
+  .. method:: DNSDistProtoBufMessage:setResponderFromString(address)
 
     Set the responder's address.
 
@@ -92,6 +92,14 @@ Protobuf Logging Reference
 
     :param int rcode: The response code of the answer
 
+  .. method:: DNSDistProtoBufMessage:setServerIdentity(id)
+
+    .. versionadded:: 1.3.3
+
+    Set the server identify field.
+
+    :param string id: The server ID
+
   .. method:: DNSDistProtoBufMessage:setTag(value)
 
     .. versionadded:: 1.2.0
index 55709c9a860215e4a29f390a636d3588c496e5e2..801879fed15394893705accb4905e572b825d50d 100644 (file)
@@ -921,15 +921,26 @@ The following actions exist.
 
   :param int rcode: The RCODE to respond with.
 
-.. function:: RemoteLogAction(remoteLogger[, alterFunction])
+.. function:: RemoteLogAction(remoteLogger[, alterFunction [, options]])
+
+  .. versionchanged:: 1.3.0
+    ``options`` optional parameter added.
 
   Send the content of this query to a remote logger via Protocol Buffer.
   ``alterFunction`` is a callback, receiving a :class:`DNSQuestion` and a :class:`DNSDistProtoBufMessage`, that can be used to modify the Protocol Buffer content, for example for anonymization purposes
 
   :param string remoteLogger: The :func:`remoteLogger <newRemoteLogger>` object to write to
   :param string alterFunction: Name of a function to modify the contents of the logs before sending
+  :param table options: A table with key: value pairs.
+
+  Options:
+
+  * ``serverID=""``: str - Set the Server Identity field.
 
-.. function:: RemoteLogResponseAction(remoteLogger[, alterFunction[, includeCNAME]])
+.. function:: RemoteLogResponseAction(remoteLogger[, alterFunction[, includeCNAME [, options]]])
+
+  .. versionchanged:: 1.3.0
+    ``options`` optional parameter added.
 
   Send the content of this response to a remote logger via Protocol Buffer.
   ``alterFunction`` is the same callback that receiving a :class:`DNSQuestion` and a :class:`DNSDistProtoBufMessage`, that can be used to modify the Protocol Buffer content, for example for anonymization purposes
@@ -939,6 +950,11 @@ The following actions exist.
   :param string remoteLogger: The :func:`remoteLogger <newRemoteLogger>` object to write to
   :param string alterFunction: Name of a function to modify the contents of the logs before sending
   :param bool includeCNAME: Whether or not to parse and export CNAMEs. Default false
+  :param table options: A table with key: value pairs.
+
+  Options:
+
+  * ``serverID=""``: str - Set the Server Identity field.
 
 .. function:: SetECSAction(v4 [, v6])
 
index e24fac79460057bfc45b63f507b3c6e6ddda2dd2..cdee353ee10d7c110326936c6b01e32558012d7f 100644 (file)
@@ -13,8 +13,9 @@ import dnsmessage_pb2
 class TestProtobuf(DNSDistTest):
     _protobufServerPort = 4242
     _protobufQueue = Queue()
+    _protobufServerID = 'dnsdist-server-1'
     _protobufCounter = 0
-    _config_params = ['_testServerPort', '_protobufServerPort']
+    _config_params = ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
     _config_template = """
     luasmn = newSuffixMatchNode()
     luasmn:add(newDNSName('lua.protobuf.tests.powerdns.com.'))
@@ -116,9 +117,9 @@ class TestProtobuf(DNSDistTest):
 
     addAction(AllRule(), LuaAction(alterLuaFirst))                                                     -- Add tags to DNSQuery first
 
-    addAction(AllRule(), RemoteLogAction(rl, alterProtobufQuery))                              -- Send protobuf message before lookup
+    addAction(AllRule(), RemoteLogAction(rl, alterProtobufQuery, {serverID='%s'}))                             -- Send protobuf message before lookup
 
-    addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobufResponse, true))     -- Send protobuf message after lookup
+    addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobufResponse, true, {serverID='%s'}))    -- Send protobuf message after lookup
 
     """
 
@@ -186,6 +187,9 @@ class TestProtobuf(DNSDistTest):
         self.assertTrue(msg.HasField('id'))
         self.assertEquals(msg.id, query.id)
         self.assertTrue(msg.HasField('inBytes'))
+        self.assertTrue(msg.HasField('serverIdentity'))
+        self.assertEquals(msg.serverIdentity, self._protobufServerID)
+
         if normalQueryResponse:
           # compare inBytes with length of query/response
           self.assertEquals(msg.inBytes, len(query.to_wire()))