From 165c90308671990ac8e8675be4e25e0ae1fdb7e5 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 15 Dec 2016 10:11:38 +0100 Subject: [PATCH] dnsdist: Add an option to export CNAME records over protobuf --- pdns/README-dnsdist.md | 2 +- pdns/dnsdist-console.cc | 4 +-- pdns/dnsdist-lua2.cc | 4 +-- pdns/dnsdist-protobuf.cc | 10 +++----- pdns/dnsdist-protobuf.hh | 4 +-- pdns/dnspcap2protobuf.cc | 2 +- pdns/dnsrulactions.hh | 7 ++--- pdns/protobuf.cc | 20 ++++++++++++--- pdns/protobuf.hh | 2 +- regression-tests.dnsdist/test_Protobuf.py | 31 ++++++++++++++++------- 10 files changed, 55 insertions(+), 31 deletions(-) diff --git a/pdns/README-dnsdist.md b/pdns/README-dnsdist.md index c5ce872ef..0c656aa3f 100644 --- a/pdns/README-dnsdist.md +++ b/pdns/README-dnsdist.md @@ -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 diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 0f233bf18..b0f6eee76 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -336,8 +336,8 @@ const std::vector 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" }, diff --git a/pdns/dnsdist-lua2.cc b/pdns/dnsdist-lua2.cc index 2a2e5bb12..97d0f0972 100644 --- a/pdns/dnsdist-lua2.cc +++ b/pdns/dnsdist-lua2.cc @@ -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 logger, boost::optional > alterFunc) { + g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr logger, boost::optional > alterFunc, boost::optional includeCNAME) { #ifdef HAVE_PROTOBUF - return std::shared_ptr(new RemoteLogResponseAction(logger, alterFunc)); + return std::shared_ptr(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false)); #else throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction"); #endif diff --git a/pdns/dnsdist-protobuf.cc b/pdns/dnsdist-protobuf.cc index 1d2a1e37d..78724dda9 100644 --- a/pdns/dnsdist-protobuf.cc +++ b/pdns/dnsdist-protobuf.cc @@ -28,19 +28,15 @@ #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 */ diff --git a/pdns/dnsdist-protobuf.hh b/pdns/dnsdist-protobuf.hh index 7a16eec17..670ea796d 100644 --- a/pdns/dnsdist-protobuf.hh +++ b/pdns/dnsdist-protobuf.hh @@ -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); }; diff --git a/pdns/dnspcap2protobuf.cc b/pdns/dnspcap2protobuf.cc index 5b7ead080..441927c32 100644 --- a/pdns/dnspcap2protobuf.cc +++ b/pdns/dnspcap2protobuf.cc @@ -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) { diff --git a/pdns/dnsrulactions.hh b/pdns/dnsrulactions.hh index 0244cd6ea..bb5152316 100644 --- a/pdns/dnsrulactions.hh +++ b/pdns/dnsrulactions.hh @@ -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 lock(g_luamutex); @@ -1089,13 +1089,13 @@ private: class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable { public: - RemoteLogResponseAction(std::shared_ptr logger, boost::optional > alterFunc): d_logger(logger), d_alterFunc(alterFunc) + RemoteLogResponseAction(std::shared_ptr logger, boost::optional > 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 lock(g_luamutex); @@ -1115,6 +1115,7 @@ public: private: std::shared_ptr d_logger; boost::optional > d_alterFunc; + bool d_includeCNAME; }; class DropResponseAction : public DNSResponseAction diff --git a/pdns/protobuf.cc b/pdns/protobuf.cc index 2dfd93010..3d4e359f3 100644 --- a/pdns/protobuf.cc +++ b/pdns/protobuf.cc @@ -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 */ diff --git a/pdns/protobuf.hh b/pdns/protobuf.hh index b9aeaf5c9..9834a99ce 100644 --- a/pdns/protobuf.hh +++ b/pdns/protobuf.hh @@ -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); diff --git a/regression-tests.dnsdist/test_Protobuf.py b/regression-tests.dnsdist/test_Protobuf.py index d4f017dc6..6bbd4cfb4 100644 --- a/regression-tests.dnsdist/test_Protobuf.py +++ b/regression-tests.dnsdist/test_Protobuf.py @@ -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): """ -- 2.40.0