]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add an optional Lua callback for altering a Protobuf message 4305/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 26 Aug 2016 07:53:24 +0000 (09:53 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 26 Aug 2016 07:53:24 +0000 (09:53 +0200)
For anonymization purposes, for example.

pdns/README-dnsdist.md
pdns/dnsdist-lua.cc
pdns/dnsdist-lua2.cc
pdns/dnsdistconf.lua
pdns/dnsrulactions.hh
pdns/iputils.hh
pdns/protobuf.cc
pdns/protobuf.hh

index 007d91b2fe325a36ea24e75e84854d37da48d55d..1fd11a794113e80f95136a327ef280a21ad91539 100644 (file)
@@ -1274,8 +1274,8 @@ instantiate a server with additional parameters
     * `QPSPoolAction(maxqps, poolname)`: set the packet into the specified pool only if it **does not** exceed the specified QPS limits, letting the subsequent rules apply otherwise
     * `QPSAction(rule, maxqps)`: drop these packets if the QPS limits are exceeded
     * `RCodeAction(rcode)`: reply immediatly by turning the query into a response with the specified rcode
-    * `RemoteLogAction(RemoteLogger)`: send the content of this query to a remote logger via Protocol Buffer
-    * `RemoteLogResponseAction(RemoteLogger)`: send the content of this response to a remote logger via Protocol Buffer
+    * `RemoteLogAction(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(RemoteLogger [,alterFunction])`: send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction`
     * `SkipCacheAction()`: don't lookup the cache for this query, don't store the answer
     * `SpoofAction(ip[, ip])` or `SpoofAction({ip, ip, ..}): forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in
     * `SpoofCNAMEAction(cname)`: forge a response with the specified CNAME value
@@ -1355,10 +1355,15 @@ instantiate a server with additional parameters
     * ComboAddress related:
         * `newCA(address)`: return a new ComboAddress
         * `getPort()`: return the port number
+        * `isIPv4()`: return true if the address is an IPv4, false otherwise
+        * `isIPv6()`: return true if the address is an IPv6, false otherwise
+        * `isMappedIPv4()`: return true if the address is an IPv4 mapped into an IPv6, false otherwise
+        * `mapToIPv4()`: convert an IPv4 address mapped in a v6 one into an IPv4
         * `tostring()`: return in human-friendly format
         * `toString()`: alias for `tostring()`
         * `tostringWithPort()`: return in human-friendly format, with port number
         * `toStringWithPort()`: alias for `tostringWithPort()`
+        * `truncate(bits)`: truncate the address to the specified number of bits
     * DNSName related:
         * `newDNSName(name)`: make a DNSName based on this .-terminated name
         * member `countLabels()`: return the number of labels
@@ -1422,6 +1427,18 @@ instantiate a server with additional parameters
     * member `getStats()`: print the block tables
     * member `unblock(ComboAddress)`: unblock this address
     * member `unblockQName(DNSName [, qtype=255])`: remove this qname from the block list
+ * DNSDistProtoBufMessage related:
+    * member `setBytes(bytes)`: set the size of the query
+    * member `setEDNSSubnet(Netmask)`: set the EDNS Subnet
+    * member `setQueryTime(sec, usec)`: in a response message, set the time at which the query has been received
+    * member `setQuestion(DNSName, qtype, qclass)`: set the question
+    * member `setRequestor(ComboAddress)`: set the requestor
+    * member `setRequestorFromString(string)`: set the requestor
+    * member `setResponder(ComboAddress)`: set the responder
+    * member `setResponderFromString(string)`: set the responder
+    * member `setResponseCode(rcode)`: set the response code
+    * member `setTime(sec, usec)`: set the time at which the query or response has been received
+    * member `toDebugString()`: return an string containing the content of the message
  * DynBPFFilter related:
     * function `newDynBPFFilter(BPFFilter)`: return a new DynBPFFilter object using this BPF Filter
     * member `block(ComboAddress[, seconds]): add this address to the underlying BPF Filter for `seconds` seconds (default to 10 seconds)
index 2b82fb97d33f9226cbe87eab4b23415640ffeb09..c2dbe8070dc85ce2c4621fa8f48c2eaecfe08321 100644 (file)
@@ -1096,6 +1096,12 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
   g_lua.registerFunction("toString", &ComboAddress::toString);
   g_lua.registerFunction("toStringWithPort", &ComboAddress::toStringWithPort);
   g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
+  g_lua.registerFunction("truncate", &ComboAddress::truncate);
+  g_lua.registerFunction("isIPv4", &ComboAddress::isIPv4);
+  g_lua.registerFunction("isIPv6", &ComboAddress::isIPv6);
+  g_lua.registerFunction("isMappedIPv4", &ComboAddress::isMappedIPv4);
+  g_lua.registerFunction("mapToIPv4", &ComboAddress::mapToIPv4);
+
   g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
   g_lua.registerFunction("countLabels", &DNSName::countLabels);
   g_lua.registerFunction("wirelength", &DNSName::wirelength);
index d30f6edb17a020894f5f7691a2276cc8f92ee2b6..1eb7272f9538fa7c0b696af2bbe47a754cf38e33 100644 (file)
@@ -163,7 +163,6 @@ void moreLua(bool client)
   typedef NetmaskTree<DynBlock> nmts_t;
   g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
 
-
   g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
   g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
                         {
@@ -679,20 +678,42 @@ void moreLua(bool client)
         return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
       });
 
-    g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger) {
+    g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
 #ifdef HAVE_PROTOBUF
-        return std::shared_ptr<DNSAction>(new RemoteLogAction(logger));
+        return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
 #else
         throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
 #endif
       });
-    g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger) {
+    g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc) {
 #ifdef HAVE_PROTOBUF
-        return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger));
+        return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc));
 #else
         throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
 #endif
       });
+
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const Netmask&)>("setEDNSSubnet", [](DNSDistProtoBufMessage& message, const Netmask& subnet) { message.setEDNSSubnet(subnet); });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const DNSName&, uint16_t, uint16_t)>("setQuestion", [](DNSDistProtoBufMessage& message, const DNSName& qname, uint16_t qtype, uint16_t qclass) { message.setQuestion(qname, qtype, qclass); });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(size_t)>("setBytes", [](DNSDistProtoBufMessage& message, size_t bytes) { message.setBytes(bytes); });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setTime(sec, usec); });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setQueryTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setQueryTime(sec, usec); });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(uint8_t)>("setResponseCode", [](DNSDistProtoBufMessage& message, uint8_t rcode) { message.setResponseCode(rcode); });
+    g_lua.registerFunction<std::string(DNSDistProtoBufMessage::*)()>("toDebugString", [](const DNSDistProtoBufMessage& message) { return message.toDebugString(); });
+
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setRequestor", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
+        message.setRequestor(addr);
+      });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setRequestorFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
+        message.setRequestor(str);
+      });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setResponder", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
+        message.setResponder(addr);
+      });
+    g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
+        message.setResponder(str);
+      });
+
     g_lua.writeFunction("newRemoteLogger", [client](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
         return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
       });
index 028f49cac8bc29040106f476d3cf44b7aa19d731..78ff583102553ba43b9a98fc2164383d59b49b4f 100644 (file)
@@ -222,3 +222,19 @@ end
     addLuaAction("luaspoof2.powerdns.com.", spoof2rule)
 
 --]]
+
+-- alter a protobuf response for anonymization purposes
+--[[
+function alterProtobuf(dq, protobuf)
+    requestor = newCA(dq.remoteaddr:toString())
+    if requestor:isIPv4() then
+        requestor:truncate(24)
+    else
+        requestor:truncate(56)
+    end
+    protobuf:setRequestor(requestor)
+end
+
+rl = newRemoteLogger("127.0.0.1:4242")
+addAction(AllRule(), RemoteLogAction(rl, alterProtobuf))
+--]]
index 95ab9eeec720481950c6de31a68159623552ada4..fb3a707fa2399e5faa062466219812cdc5b30556 100644 (file)
@@ -983,13 +983,19 @@ public:
 class RemoteLogAction : public DNSAction, public boost::noncopyable
 {
 public:
-  RemoteLogAction(std::shared_ptr<RemoteLogger> logger): d_logger(logger)
+  RemoteLogAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
   {
   }
   DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
     DNSDistProtoBufMessage message(DNSDistProtoBufMessage::Query, *dq);
+    {
+      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);
@@ -1002,18 +1008,25 @@ public:
   }
 private:
   std::shared_ptr<RemoteLogger> d_logger;
+  boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > d_alterFunc;
 };
 
 class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
 {
 public:
-  RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger): d_logger(logger)
+  RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
   {
   }
   DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
     DNSDistProtoBufMessage message(*dr);
+    {
+      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);
@@ -1026,6 +1039,7 @@ public:
   }
 private:
   std::shared_ptr<RemoteLogger> d_logger;
+  boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
 };
 
 class DropResponseAction : public DNSResponseAction
index 1cc3798ba5db851bac765a584b4020a3e8c4c98e..8a27f5fa5e2bd6de270c45b732462982beb80fe3 100644 (file)
@@ -218,6 +218,15 @@ union ComboAddress {
       sin4.sin_port=htons(port);
   }
 
+  bool isIPv6() const
+  {
+    return sin4.sin_family == AF_INET6;
+  }
+  bool isIPv4() const
+  {
+    return sin4.sin_family == AF_INET;
+  }
+
   bool isMappedIPv4()  const
   {
     if(sin4.sin_family!=AF_INET6)
index d04fa2540b1a702a88d2fbd577e822034080d540..5e7ec5607e36fd0edf119eb6a254547ae716d23b 100644 (file)
@@ -140,6 +140,44 @@ void DNSProtoBufMessage::addRRsFromPacket(const char* packet, const size_t len)
 #endif /* HAVE_PROTOBUF */
 }
 
+void DNSProtoBufMessage::setRequestor(const std::string& requestor)
+{
+#ifdef HAVE_PROTOBUF
+  d_message.set_from(requestor);
+#endif /* HAVE_PROTOBUF */
+}
+
+void DNSProtoBufMessage::setRequestor(const ComboAddress& requestor)
+{
+#ifdef HAVE_PROTOBUF
+  if (requestor.sin4.sin_family == AF_INET) {
+    d_message.set_from(&requestor.sin4.sin_addr.s_addr, sizeof(requestor.sin4.sin_addr.s_addr));
+  }
+  else if (requestor.sin4.sin_family == AF_INET6) {
+    d_message.set_from(&requestor.sin6.sin6_addr.s6_addr, sizeof(requestor.sin6.sin6_addr.s6_addr));
+  }
+#endif /* HAVE_PROTOBUF */
+}
+
+void DNSProtoBufMessage::setResponder(const std::string& responder)
+{
+#ifdef HAVE_PROTOBUF
+  d_message.set_from(responder);
+#endif /* HAVE_PROTOBUF */
+}
+
+void DNSProtoBufMessage::setResponder(const ComboAddress& responder)
+{
+#ifdef HAVE_PROTOBUF
+  if (responder.sin4.sin_family == AF_INET) {
+    d_message.set_from(&responder.sin4.sin_addr.s_addr, sizeof(responder.sin4.sin_addr.s_addr));
+  }
+  else if (responder.sin4.sin_family == AF_INET6) {
+    d_message.set_from(&responder.sin6.sin6_addr.s6_addr, sizeof(responder.sin6.sin6_addr.s6_addr));
+  }
+#endif /* HAVE_PROTOBUF */
+}
+
 void DNSProtoBufMessage::serialize(std::string& data) const
 {
 #ifdef HAVE_PROTOBUF
@@ -178,20 +216,10 @@ void DNSProtoBufMessage::update(const boost::uuids::uuid& uuid, const ComboAddre
   d_message.set_socketprotocol(isTCP ? PBDNSMessage_SocketProtocol_TCP : PBDNSMessage_SocketProtocol_UDP);
 
   if (responder) {
-    if (responder->sin4.sin_family == AF_INET) {
-      d_message.set_to(&responder->sin4.sin_addr.s_addr, sizeof(responder->sin4.sin_addr.s_addr));
-    }
-    else if (responder->sin4.sin_family == AF_INET6) {
-      d_message.set_to(&responder->sin6.sin6_addr.s6_addr, sizeof(responder->sin6.sin6_addr.s6_addr));
-    }
+    setResponder(*responder);
   }
   if (requestor) {
-    if (requestor->sin4.sin_family == AF_INET) {
-      d_message.set_from(&requestor->sin4.sin_addr.s_addr, sizeof(requestor->sin4.sin_addr.s_addr));
-    }
-    else if (requestor->sin4.sin_family == AF_INET6) {
-      d_message.set_from(&requestor->sin6.sin6_addr.s6_addr, sizeof(requestor->sin6.sin6_addr.s6_addr));
-    }
+    setRequestor(*requestor);
   }
 }
 
index c1cd49e4dbe8940a369fa2dc22266e9500e0ed75..9170795e6a042664a6f632274d43f0f0cc26b975 100644 (file)
@@ -41,6 +41,10 @@ public:
   void setResponseCode(uint8_t rcode);
   void addRRsFromPacket(const char* packet, const size_t len);
   void serialize(std::string& data) const;
+  void setRequestor(const std::string& requestor);
+  void setRequestor(const ComboAddress& requestor);
+  void setResponder(const std::string& responder);
+  void setResponder(const ComboAddress& responder);
   std::string toDebugString() const;
 
 #ifdef HAVE_PROTOBUF