From 312a09a6272d7862c9440c2a5b54680665cc5a99 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 2 Oct 2018 16:41:59 +0200 Subject: [PATCH] dnsdist: Add support for exporting a server ID in protobuf --- pdns/dnsdist-console.cc | 4 +- pdns/dnsdist-lua-actions.cc | 56 ++++++++++++++------ pdns/dnsdist-lua-bindings.cc | 3 ++ pdns/dnsdistdist/docs/reference/protobuf.rst | 10 +++- pdns/dnsdistdist/docs/rules-actions.rst | 20 ++++++- regression-tests.dnsdist/test_Protobuf.py | 10 ++-- 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 4eda5b0553..54aa42fe23 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -397,8 +397,8 @@ const std::vector 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" }, diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index 377add214c..b482164c63 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -747,7 +747,7 @@ private: class RemoteLogAction : public DNSAction, public boost::noncopyable { public: - RemoteLogAction(std::shared_ptr& logger, boost::optional > alterFunc): d_logger(logger), d_alterFunc(alterFunc) + RemoteLogAction(std::shared_ptr& logger, boost::optional > 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 lock(g_luamutex); - (*d_alterFunc)(*dq, &message); - } + if (!d_serverID.empty()) { + message.setServerIdentity(d_serverID); } + + if (d_alterFunc) { + std::lock_guard 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 d_logger; boost::optional > 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& logger, boost::optional > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME) + RemoteLogResponseAction(std::shared_ptr& logger, boost::optional > 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 lock(g_luamutex); - (*d_alterFunc)(*dr, &message); - } + if (!d_serverID.empty()) { + message.setServerIdentity(d_serverID); + } + + if (d_alterFunc) { + std::lock_guard 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 d_logger; boost::optional > d_alterFunc; + std::string d_serverID; bool d_includeCNAME; }; @@ -1174,29 +1182,45 @@ void setupLuaActions() return std::shared_ptr(new LuaResponseAction(func)); }); - g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr logger, boost::optional > alterFunc) { + g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr logger, boost::optional > alterFunc, boost::optional> 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((*vars)["serverID"]); + } + } + #ifdef HAVE_PROTOBUF - return std::shared_ptr(new RemoteLogAction(logger, alterFunc)); + return std::shared_ptr(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 logger, boost::optional > alterFunc, boost::optional includeCNAME) { + g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr logger, boost::optional > alterFunc, boost::optional includeCNAME, boost::optional> 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((*vars)["serverID"]); + } + } + #ifdef HAVE_PROTOBUF - return std::shared_ptr(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false)); + return std::shared_ptr(new RemoteLogResponseAction(logger, alterFunc, serverID, includeCNAME ? *includeCNAME : false)); #else throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction"); #endif diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 97c3a0ef43..d5cdaa0ec6 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -309,6 +309,9 @@ void setupLuaBindings(bool client) g_lua.registerFunction("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str) { message.setResponder(str); }); + g_lua.registerFunction("setServerIdentity", [](DNSDistProtoBufMessage& message, const std::string& str) { + message.setServerIdentity(str); + }); g_lua.registerFunction("toDebugString", [](const DnstapMessage& message) { return message.toDebugString(); }); g_lua.registerFunction("setExtra", [](DnstapMessage& message, const std::string& str) { diff --git a/pdns/dnsdistdist/docs/reference/protobuf.rst b/pdns/dnsdistdist/docs/reference/protobuf.rst index 8f3ee6c00f..1cb71a7d66 100644 --- a/pdns/dnsdistdist/docs/reference/protobuf.rst +++ b/pdns/dnsdistdist/docs/reference/protobuf.rst @@ -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 diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 55709c9a86..801879fed1 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -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 ` 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 ` 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]) diff --git a/regression-tests.dnsdist/test_Protobuf.py b/regression-tests.dnsdist/test_Protobuf.py index e24fac7946..cdee353ee1 100644 --- a/regression-tests.dnsdist/test_Protobuf.py +++ b/regression-tests.dnsdist/test_Protobuf.py @@ -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())) -- 2.47.2