* `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
{ "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" },
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
#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 */
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);
};
}
try {
- message.addRRsFromPacket((const char*) dh, pr.d_len);
+ message.addRRsFromPacket((const char*) dh, pr.d_len, true);
}
catch(std::exception& e)
{
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);
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);
private:
std::shared_ptr<RemoteLogger> d_logger;
boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
+ bool d_includeCNAME;
};
class DropResponseAction : public DNSResponseAction
#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))
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());
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 */
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);
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
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,
# 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)
# 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):
"""