]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add an option to export CNAME records over protobuf
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 15 Dec 2016 09:11:38 +0000 (10:11 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 13 Jan 2017 13:32:28 +0000 (14:32 +0100)
pdns/README-dnsdist.md
pdns/dnsdist-console.cc
pdns/dnsdist-lua2.cc
pdns/dnsdist-protobuf.cc
pdns/dnsdist-protobuf.hh
pdns/dnspcap2protobuf.cc
pdns/dnsrulactions.hh
pdns/protobuf.cc
pdns/protobuf.hh
regression-tests.dnsdist/test_Protobuf.py

index c5ce872ef6819805074fcc0b45b0e6588dc0a0ec..0c656aa3f4e5adbff8cd9d6353c6272b5d6918e0 100644 (file)
@@ -1367,7 +1367,7 @@ instantiate a server with additional parameters
     * `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 [, 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`
+    * `RemoteLogResponseAction(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
     * `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
index 0f233bf181b7fe5c0d2da13d48e435ff358bd6ac..b0f6eee76a1419ae44ecdbe83379b0c212ccfb15 100644 (file)
@@ -336,8 +336,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "printDNSCryptProviderFingerprint", true, "\"/path/to/providerPublic.key\"", "display the fingerprint of the provided resolver public key" },
   { "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", "send the content of this query to a remote logger via Protocol Buffer" },
-  { "RemoteLogResponseAction", true, "RemoteLogger", "send the content of this response to a remote logger via Protocol Buffer" },
+  { "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" },
   { "rmResponseRule", true, "n", "remove response rule n" },
   { "rmRule", true, "n", "remove rule n" },
   { "rmServer", true, "n", "remove server with index n" },
index 2a2e5bb125fcf52b882da23e0427ef119dffd5e8..97d0f09727aa7eff45b7d92c00a043be4a43609b 100644 (file)
@@ -723,9 +723,9 @@ void moreLua(bool client)
         throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
 #endif
       });
-    g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc) {
+    g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
 #ifdef HAVE_PROTOBUF
-        return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc));
+        return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
 #else
         throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
 #endif
index 1d2a1e37d22033a0c43dfb825214fedb2249d238..78724dda9598a11970158b90479118fea38ab009 100644 (file)
 #ifdef HAVE_PROTOBUF
 #include "dnsmessage.pb.h"
 
-DNSDistProtoBufMessage::DNSDistProtoBufMessage(DNSProtoBufMessageType type, const DNSQuestion& dq): DNSProtoBufMessage(type, dq.uniqueId, dq.remote, dq.local, *dq.qname, dq.qtype, dq.qclass, dq.dh->id, dq.tcp, dq.len)
+DNSDistProtoBufMessage::DNSDistProtoBufMessage(const DNSQuestion& dq): DNSProtoBufMessage(Query, dq.uniqueId, dq.remote, dq.local, *dq.qname, dq.qtype, dq.qclass, dq.dh->id, dq.tcp, dq.len)
 {
-  if (type == Response) {
-    setResponseCode(dq.dh->rcode);
-    addRRsFromPacket((const char*) dq.dh, dq.len);
-  }
 };
 
-DNSDistProtoBufMessage::DNSDistProtoBufMessage(const DNSResponse& dr): DNSProtoBufMessage(Response, dr.uniqueId, dr.remote, dr.local, *dr.qname, dr.qtype, dr.qclass, dr.dh->id, dr.tcp, dr.len)
+DNSDistProtoBufMessage::DNSDistProtoBufMessage(const DNSResponse& dr, bool includeCNAME): DNSProtoBufMessage(Response, dr.uniqueId, dr.remote, dr.local, *dr.qname, dr.qtype, dr.qclass, dr.dh->id, dr.tcp, dr.len)
 {
   setQueryTime(dr.queryTime->tv_sec, dr.queryTime->tv_nsec / 1000);
   setResponseCode(dr.dh->rcode);
-  addRRsFromPacket((const char*) dr.dh, dr.len);
+  addRRsFromPacket((const char*) dr.dh, dr.len, includeCNAME);
 };
 
 #endif /* HAVE_PROTOBUF */
index 7a16eec17f3cab23da20725d7984eec9db461a85..670ea796db68c0f72e5bd736b1bb3ee26d25e6f9 100644 (file)
@@ -26,6 +26,6 @@
 class DNSDistProtoBufMessage: public DNSProtoBufMessage
 {
 public:
-  DNSDistProtoBufMessage(DNSProtoBufMessageType type, const DNSQuestion& dq);
-  DNSDistProtoBufMessage(const DNSResponse& dr);
+  DNSDistProtoBufMessage(const DNSQuestion& dq);
+  DNSDistProtoBufMessage(const DNSResponse& dr, bool includeCNAME);
 };
index 5b7ead080e6a495e063b8bb38766a41fc1b661f1..441927c32ab49a9e4ed762b48511b6469e3f09dc 100644 (file)
@@ -134,7 +134,7 @@ try {
         }
 
         try {
-          message.addRRsFromPacket((const char*) dh, pr.d_len);
+          message.addRRsFromPacket((const char*) dh, pr.d_len, true);
         }
         catch(std::exception& e)
         {
index 0244cd6ea97773bcdc6315c5d5734bf9b6087fa0..bb515231637a7c99f563fc71bb2d9d791a965564 100644 (file)
@@ -1064,7 +1064,7 @@ public:
   DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
-    DNSDistProtoBufMessage message(DNSDistProtoBufMessage::Query, *dq);
+    DNSDistProtoBufMessage message(*dq);
     {
       if (d_alterFunc) {
         std::lock_guard<std::mutex> lock(g_luamutex);
@@ -1089,13 +1089,13 @@ private:
 class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
 {
 public:
-  RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
+  RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
   {
   }
   DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
-    DNSDistProtoBufMessage message(*dr);
+    DNSDistProtoBufMessage message(*dr, d_includeCNAME);
     {
       if (d_alterFunc) {
         std::lock_guard<std::mutex> lock(g_luamutex);
@@ -1115,6 +1115,7 @@ public:
 private:
   std::shared_ptr<RemoteLogger> d_logger;
   boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
+  bool d_includeCNAME;
 };
 
 class DropResponseAction : public DNSResponseAction
index 2dfd930100b5c05cd0bc40c4fe28e43d85c687dd..3d4e359f32eebe49cd17edc0039ff11ac598fc76 100644 (file)
@@ -96,7 +96,7 @@ void DNSProtoBufMessage::setEDNSSubnet(const Netmask& subnet, uint8_t mask)
 #endif /* HAVE_PROTOBUF */
 }
 
-void DNSProtoBufMessage::addRRsFromPacket(const char* packet, const size_t len)
+void DNSProtoBufMessage::addRRsFromPacket(const char* packet, const size_t len, bool includeCNAME)
 {
 #ifdef HAVE_PROTOBUF
   if (len < sizeof(struct dnsheader))
@@ -147,9 +147,9 @@ void DNSProtoBufMessage::addRRsFromPacket(const char* packet, const size_t len)
     rrname = pr.getName();
     pr.getDnsrecordheader(ah);
 
-    pr.xfrBlob(blob);
-
     if (ah.d_type == QType::A || ah.d_type == QType::AAAA) {
+      pr.xfrBlob(blob);
+
       PBDNSMessage_DNSResponse_DNSRR* rr = response->add_rrs();
       if (rr) {
         rr->set_name(rrname.toString());
@@ -158,6 +158,20 @@ void DNSProtoBufMessage::addRRsFromPacket(const char* packet, const size_t len)
         rr->set_ttl(ah.d_ttl);
         rr->set_rdata(blob.c_str(), blob.length());
       }
+    } else if (ah.d_type == QType::CNAME && includeCNAME) {
+      PBDNSMessage_DNSResponse_DNSRR* rr = response->add_rrs();
+      if (rr) {
+        rr->set_name(rrname.toString());
+        rr->set_type(ah.d_type);
+        rr->set_class_(ah.d_class);
+        rr->set_ttl(ah.d_ttl);
+        DNSName target;
+        pr.xfrName(target, true);
+        rr->set_rdata(target.toString());
+      }
+    }
+    else {
+      pr.xfrBlob(blob);
     }
   }
 #endif /* HAVE_PROTOBUF */
index b9aeaf5c9b3ea5137d8ac8127d6bb62881174a5b..9834a99ced877e26b7882c6aeab027b35c28832b 100644 (file)
@@ -62,7 +62,7 @@ public:
   void setTime(time_t sec, uint32_t usec);
   void setQueryTime(time_t sec, uint32_t usec);
   void setResponseCode(uint8_t rcode);
-  void addRRsFromPacket(const char* packet, const size_t len);
+  void addRRsFromPacket(const char* packet, const size_t len, bool includeCNAME=false);
   void serialize(std::string& data) const;
   void setRequestor(const std::string& requestor);
   void setRequestor(const ComboAddress& requestor);
index d4f017dc6bbbe97347ff2a0d81dec30087bccaf6..6bbd4cfb41976ccab12b86e046319f7aca87905a 100644 (file)
@@ -35,7 +35,7 @@ class TestProtobuf(DNSDistTest):
     newServer{address="127.0.0.1:%s", useClientSubnet=true}
     rl = newRemoteLogger('127.0.0.1:%s')
     addAction(AllRule(), RemoteLogAction(rl, alterProtobuf))
-    addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobuf))
+    addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobuf, true))
     """
 
     @classmethod
@@ -145,9 +145,16 @@ class TestProtobuf(DNSDistTest):
         Protobuf: Send data to a protobuf server
         """
         name = 'query.protobuf.tests.powerdns.com.'
+        target = 'target.protobuf.tests.powerdns.com.'
         query = dns.message.make_query(name, 'A', 'IN')
         response = dns.message.make_response(query)
         rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.CNAME,
+                                    target)
+        response.answer.append(rrset)
+        rrset = dns.rrset.from_text(target,
                                     3600,
                                     dns.rdataclass.IN,
                                     dns.rdatatype.A,
@@ -171,10 +178,13 @@ class TestProtobuf(DNSDistTest):
         # check the protobuf message corresponding to the UDP response
         msg = self.getFirstProtobufMessage()
         self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response)
-        self.assertEquals(len(msg.response.rrs), 1)
-        for rr in msg.response.rrs:
-            self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 3600)
-            self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+        self.assertEquals(len(msg.response.rrs), 2)
+        rr = msg.response.rrs[0]
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
+        self.assertEquals(rr.rdata, target)
+        rr = msg.response.rrs[1]
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
 
         (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
         self.assertTrue(receivedQuery)
@@ -193,10 +203,13 @@ class TestProtobuf(DNSDistTest):
         # check the protobuf message corresponding to the TCP response
         msg = self.getFirstProtobufMessage()
         self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response)
-        self.assertEquals(len(msg.response.rrs), 1)
-        for rr in msg.response.rrs:
-            self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 3600)
-            self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
+        self.assertEquals(len(msg.response.rrs), 2)
+        rr = msg.response.rrs[0]
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600)
+        self.assertEquals(rr.rdata, target)
+        rr = msg.response.rrs[1]
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1')
 
     def testLuaProtobuf(self):
         """