]> granicus.if.org Git - pdns/commitdiff
rec: Log outgoing queries / incoming responses via protobuf
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 4 Nov 2016 16:28:22 +0000 (17:28 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 13 Jan 2017 13:43:50 +0000 (14:43 +0100)
(cherry picked from commit 4898a34807043c2af442ef983f9ef45e0b473651)

20 files changed:
contrib/ProtobufLogger.py
docs/markdown/recursor/settings.md
pdns/dnsdist-protobuf.cc
pdns/dnsmessage.proto
pdns/dnspcap2protobuf.cc
pdns/lwres.cc
pdns/lwres.hh
pdns/pdns_recursor.cc
pdns/protobuf.cc
pdns/protobuf.hh
pdns/rec-lua-conf.cc
pdns/rec-lua-conf.hh
pdns/recursordist/Makefile.am
pdns/recursordist/resolve-context.hh [new symlink]
pdns/resolve-context.hh [new file with mode: 0644]
pdns/secpoll-recursor.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/validate-recursor.cc
pdns/validate-recursor.hh

index 0983e1d437ac40318ef1c6e41cb4b31747921316..5075e43f7d9f53adc3386359a8cc3185cc86bcd6 100644 (file)
@@ -30,11 +30,10 @@ class PDNSPBConnHandler(object):
                 self.printQueryMessage(msg)
             elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSResponseType:
                 self.printResponseMessage(msg)
-            # PR #3869
-            # elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType:
-            #     self.printOutgoingQueryMessage(msg)
-            # elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType:
-            #     self.printIncomingResponseMessage(msg)
+            elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType:
+                self.printOutgoingQueryMessage(msg)
+            elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType:
+                self.printIncomingResponseMessage(msg)
             else:
                 print('Discarding unsupported message type %d' % (msg.type))
 
@@ -137,9 +136,9 @@ class PDNSPBConnHandler(object):
 
         messageidstr = binascii.hexlify(bytearray(msg.messageId))
         initialrequestidstr = ''
-        # PR #3869
-        # if msg.HasField('initialRequestId'):
-        #    initialrequestidstr = ', initial uuid: ' + binascii.hexlify(bytearray(msg.initialRequestId))
+        if msg.HasField('initialRequestId'):
+            initialrequestidstr = ', initial uuid: ' + binascii.hexlify(bytearray(msg.initialRequestId))
+
         requestorstr = ''
         requestor = self.getRequestorSubnet(msg)
         if requestor:
index 9e67c4efa4994057926e9bc75181b40cb7def033..65064058d91b36d1eba56b6e2def396dc29b427d 100644 (file)
@@ -544,6 +544,22 @@ The optional parameters are:
 * asyncConnect = if set to false (default) the first connection to the server during startup will block up to `timeout` seconds,
 otherwise the connection is done in a separate thread.
 
+While `protobufServer()` only exports the queries sent to the recursor from clients, with the corresponding responses,
+`outgoingProtobufServer()` can be used to export outgoing queries sent by the recursor to authoritative servers,
+along with the corresponding responses.
+
+```
+outgoingProtobufServer("192.0.2.1:4242" [[[[, timeout], maxQueuedEntries], reconnectWaitTime], asynConnect])
+```
+
+The optional parameters for `outgoingProtobufServer()` are:
+
+* timeout = time in seconds to wait when sending a message, default to 2
+* maxQueuedEntries = how many entries will be kept in memory if the server becomes unreachable, default to 100
+* reconnectWaitTime = how long to wait, in seconds, between two reconnection attempts, default to 1
+* asyncConnect = if set to false (default) the first connection to the server during startup will block up to `timeout` seconds,
+otherwise the connection is done in a separate thread.
+
 The protocol buffers message types can be found in the [`dnsmessage.proto`](https://github.com/PowerDNS/pdns/blob/master/pdns/dnsmessage.proto) file.
 
 ## `lua-dns-script`
index a399a38c79a79ae61dcb848cea6eaedbb8e0c3a6..1d2a1e37d22033a0c43dfb825214fedb2249d238 100644 (file)
@@ -22,9 +22,7 @@
 #include "config.h"
 
 #include "dnsdist.hh"
-#include "gettime.hh"
 
-#include "dnsparser.hh"
 #include "dnsdist-protobuf.hh"
 
 #ifdef HAVE_PROTOBUF
 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)
 {
   if (type == Response) {
-    PBDNSMessage_DNSResponse* response = d_message.mutable_response();
-    if (response) {
-      response->set_rcode(dq.dh->rcode);
-    }
+    setResponseCode(dq.dh->rcode);
     addRRsFromPacket((const char*) dq.dh, dq.len);
   }
 };
index e5acc5bc5d5a8b13563cb81872f2731c996aab64..d64de370c8270b4a3ca23a0ce7351f26d7d7ddd1 100644 (file)
@@ -25,6 +25,8 @@ message PBDNSMessage {
   enum Type {
     DNSQueryType = 1;
     DNSResponseType = 2;
+    DNSOutgoingQueryType = 3;
+    DNSIncomingResponseType = 4;
   }
   enum SocketFamily {
     INET = 1;                                   // IPv4 (RFC 791)
@@ -72,4 +74,5 @@ message PBDNSMessage {
   optional DNSResponse response = 13;
   optional bytes originalRequestorSubnet = 14;  // EDNS Client Subnet value
   optional string requestorId = 15;             // Username of the requestor
+  optional bytes initialRequestId = 16;         // UUID of the incoming query that initiated this outgoing query or incoming response
 }
index e36ae8698d55fb3033d313dfb0c7274758d79ed7..5b7ead080e6a495e063b8bb38766a41fc1b661f1 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "iputils.hh"
 #include "misc.hh"
+#include "protobuf.hh"
 #include "dns.hh"
 #include "dnspcap.hh"
 #include "dnsparser.hh"
index 931da94ab85139a2f21324031988e7cc946ae482..f3c1f23fcd11cfbfcb7935a9e2916db29ab72bde 100644 (file)
 #include "validate-recursor.hh"
 #include "ednssubnet.hh"
 
+#ifdef HAVE_PROTOBUF
+
+static void logOutgoingQuery(std::shared_ptr<RemoteLogger> outgoingLogger, boost::optional<const boost::uuids::uuid&> initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& ip, const DNSName& domain, int type, uint16_t qid, bool doTCP, size_t bytes)
+{
+  if(!outgoingLogger)
+    return;
+
+  RecProtoBufMessage message(DNSProtoBufMessage::OutgoingQuery, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
+  if (initialRequestId) {
+    message.setInitialRequestID(*initialRequestId);
+  }
+
+//  cerr <<message.toDebugString()<<endl;
+  std::string str;
+  message.serialize(str);
+  outgoingLogger->queueData(str);
+}
+
+static void logIncomingResponse(std::shared_ptr<RemoteLogger> outgoingLogger, boost::optional<const boost::uuids::uuid&> initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& ip, const DNSName& domain, int type, uint16_t qid, bool doTCP, size_t bytes, int rcode, const std::vector<DNSRecord>& records, const struct timeval& queryTime)
+{
+  if(!outgoingLogger)
+    return;
+
+  RecProtoBufMessage message(DNSProtoBufMessage::IncomingResponse, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
+  if (initialRequestId) {
+    message.setInitialRequestID(*initialRequestId);
+  }
+  message.setQueryTime(queryTime.tv_sec, queryTime.tv_usec);
+  message.setResponseCode(rcode);
+  message.addRRs(records);
+
+//  cerr <<message.toDebugString()<<endl;
+  std::string str;
+  message.serialize(str);
+  outgoingLogger->queueData(str);
+}
+#endif /* HAVE_PROTOBUF */
+
 //! returns -2 for OS limits error, -1 for permanent error that has to do with remote **transport**, 0 for timeout, 1 for success
 /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
     Never throws! 
  */
-int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult *lwr)
+int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult *lwr)
 {
   size_t len;
   size_t bufsize=g_outgoingEDNSBufsize;
   scoped_array<unsigned char> buf(new unsigned char[bufsize]);
   vector<uint8_t> vpacket;
   //  string mapped0x20=dns0x20(domain);
+  uint16_t qid = dns_random(0xffff);
   DNSPacketWriter pw(vpacket, domain, type);
 
   pw.getHeader()->rd=sendRDQuery;
-  pw.getHeader()->id=dns_random(0xffff);
+  pw.getHeader()->id=qid;
   /* RFC 6840 section 5.9:
    *  This document further specifies that validating resolvers SHOULD set
    *  the CD bit on every upstream query.  This is regardless of whether
@@ -98,20 +137,31 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
   DTime dt;
   dt.set();
   *now=dt.getTimeval();
+
+#ifdef HAVE_PROTOBUF
+  boost::uuids::uuid uuid;
+  const struct timeval queryTime = *now;
+
+  if (outgoingLogger) {
+    uuid = (*t_uuidGenerator)();
+    logOutgoingQuery(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size());
+  }
+#endif
+
   errno=0;
   if(!doTCP) {
     int queryfd;
     if(ip.sin4.sin_family==AF_INET6)
       g_stats.ipv6queries++;
 
-    if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, pw.getHeader()->id,
+    if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid,
                     domain, type, &queryfd)) < 0) {
       return ret; // passes back the -2 EMFILE
     }
   
     // sleep until we see an answer to this, interface to mtasker
     
-    ret=arecvfrom(reinterpret_cast<char *>(buf.get()), bufsize-1,0, ip, &len, pw.getHeader()->id, 
+    ret=arecvfrom(reinterpret_cast<char *>(buf.get()), bufsize-1,0, ip, &len, qid,
                   domain, type, queryfd, now);
   }
   else {
@@ -178,6 +228,11 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
     lwr->d_rcode=mdp.d_header.rcode;
     
     if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
+#ifdef HAVE_PROTOBUF
+      if(outgoingLogger) {
+        logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
+      }
+#endif
       return 1; // this is "success", the error is set in lwr->d_rcode
     }
 
@@ -210,13 +265,23 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
       }
     }
         
+#ifdef HAVE_PROTOBUF
+    if(outgoingLogger) {
+      logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
+    }
+#endif
     return 1;
   }
   catch(std::exception &mde) {
     if(::arg().mustDo("log-common-errors"))
       L<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
     lwr->d_rcode = RCode::FormErr;
-    g_stats.serverParseError++; 
+    g_stats.serverParseError++;
+#ifdef HAVE_PROTOBUF
+    if(outgoingLogger) {
+      logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
+    }
+#endif
     return 1; // success - oddly enough
   }
   catch(...) {
index 0e0fe22635e3dfec0f5a4bdd445b3743dfaf3f43..1762ed98a9bfa30e754f554dc9f05d53e4611785 100644 (file)
@@ -40,6 +40,8 @@
 #include "pdnsexception.hh"
 #include "dns.hh"
 #include "namespaces.hh"
+#include "remote_logger.hh"
+#include "resolve-context.hh"
 
 int asendto(const char *data, size_t len, int flags, const ComboAddress& ip, uint16_t id,
             const DNSName& domain, uint16_t qtype,  int* fd);
@@ -65,5 +67,5 @@ public:
   bool d_haveEDNS{false};
 };
 
-int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res);
+int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res);
 #endif // PDNS_LWRES_HH
index 0443ff9b75739608bcf14c06a59fcb701ee5a3b5..b24ac28d2f9bfc41065adee0ddf6a72f1f60e030 100644 (file)
@@ -735,6 +735,9 @@ void startDoResolve(void *p)
       // Ignore the client-set CD flag
       pw.getHeader()->cd=0;
     }
+#ifdef HAVE_PROTOBUF
+    sr.d_initialRequestId = dc->d_uuid;
+#endif
 
     bool tracedQuery=false; // we could consider letting Lua know about this too
     bool variableAnswer = false;
@@ -983,7 +986,11 @@ void startDoResolve(void *p)
             L<<Logger::Warning<<"Starting validation of answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<endl;
           }
           
-          auto state=validateRecords(ret);
+          ResolveContext ctx;
+#ifdef HAVE_PROTOBUF
+          ctx.d_initialRequestId = dc->d_uuid;
+#endif
+          auto state=validateRecords(ctx, ret);
           if(state == Secure) {
             if(sr.doLog()) {
               L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates correctly"<<endl;
@@ -1353,9 +1360,11 @@ void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
         }
       }
 #ifdef HAVE_PROTOBUF
-      if(luaconfsLocal->protobufServer) {
+      if(luaconfsLocal->protobufServer || luaconfsLocal->outgoingProtobufServer) {
         dc->d_uuid = (*t_uuidGenerator)();
+      }
 
+      if(luaconfsLocal->protobufServer) {
         try {
           const struct dnsheader* dh = (const struct dnsheader*) conn->data;
           dc->d_ednssubnet = ednssubnet;
@@ -1455,7 +1464,9 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
   boost::uuids::uuid uniqueId;
   auto luaconfsLocal = g_luaconfs.getLocal();
   if (luaconfsLocal->protobufServer) {
+    uniqueId = (*t_uuidGenerator)();
     needECS = true;
+  } else if (luaconfsLocal->outgoingProtobufServer) {
     uniqueId = (*t_uuidGenerator)();
   }
 #endif
@@ -1576,7 +1587,7 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
   dc->d_tcp=false;
   dc->d_policyTags = policyTags;
 #ifdef HAVE_PROTOBUF
-  if (luaconfsLocal->protobufServer) {
+  if (luaconfsLocal->protobufServer || luaconfsLocal->outgoingProtobufServer) {
     dc->d_uuid = uniqueId;
   }
   dc->d_ednssubnet = ednssubnet;
@@ -3140,7 +3151,8 @@ int getRootNS(void) {
   try {
     res=sr.beginResolve(DNSName("."), QType(QType::NS), 1, ret);
     if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
-      auto state = validateRecords(ret);
+      ResolveContext ctx;
+      auto state = validateRecords(ctx, ret);
       if (state == Bogus)
         throw PDNSException("Got Bogus validation result for .|NS");
     }
index e78e22af0e3d10d5ae812d3b2b130d4a439719b7..2dfd930100b5c05cd0bc40c4fe28e43d85c687dd 100644 (file)
@@ -1,15 +1,37 @@
 
+#include "gettime.hh"
+#include "dnsparser.hh"
 #include "protobuf.hh"
 #include "dnsparser.hh"
 #include "gettime.hh"
 
-DNSProtoBufMessage::DNSProtoBufMessage(DNSProtoBufMessageType type)
+void DNSProtoBufMessage::setType(DNSProtoBufMessageType type)
 {
 #ifdef HAVE_PROTOBUF
-  d_message.set_type(type == DNSProtoBufMessage::DNSProtoBufMessageType::Query ? PBDNSMessage_Type_DNSQueryType : PBDNSMessage_Type_DNSResponseType);
+  switch(type) {
+  case DNSProtoBufMessage::DNSProtoBufMessageType::Query:
+    d_message.set_type(PBDNSMessage_Type_DNSQueryType);
+    break;
+  case DNSProtoBufMessage::DNSProtoBufMessageType::Response:
+    d_message.set_type(PBDNSMessage_Type_DNSResponseType);
+    break;
+  case DNSProtoBufMessage::DNSProtoBufMessageType::OutgoingQuery:
+    d_message.set_type(PBDNSMessage_Type_DNSOutgoingQueryType);
+    break;
+  case DNSProtoBufMessage::DNSProtoBufMessageType::IncomingResponse:
+    d_message.set_type(PBDNSMessage_Type_DNSIncomingResponseType);
+    break;
+  default:
+    throw std::runtime_error("Unsupported protobuf type: "+std::to_string(type));
+  }
 #endif /* HAVE_PROTOBUF */
 }
 
+DNSProtoBufMessage::DNSProtoBufMessage(DNSProtoBufMessageType type)
+{
+  setType(type);
+}
+
 void DNSProtoBufMessage::setQuestion(const DNSName& qname, uint16_t qtype, uint16_t qclass)
 {
 #ifdef HAVE_PROTOBUF
@@ -204,6 +226,13 @@ void DNSProtoBufMessage::setUUID(const boost::uuids::uuid& uuid)
   std::copy(uuid.begin(), uuid.end(), messageId->begin());
 }
 
+void DNSProtoBufMessage::setInitialRequestID(const boost::uuids::uuid& uuid)
+{
+  std::string* messageId = d_message.mutable_initialrequestid();
+  messageId->resize(uuid.size());
+  std::copy(uuid.begin(), uuid.end(), messageId->begin());
+}
+
 void DNSProtoBufMessage::update(const boost::uuids::uuid& uuid, const ComboAddress* requestor, const ComboAddress* responder, bool isTCP, uint16_t id)
 {
   struct timespec ts;
@@ -213,7 +242,13 @@ void DNSProtoBufMessage::update(const boost::uuids::uuid& uuid, const ComboAddre
   setUUID(uuid);
   d_message.set_id(ntohs(id));
 
-  d_message.set_socketfamily((requestor && requestor->sin4.sin_family == AF_INET) ? PBDNSMessage_SocketFamily_INET : PBDNSMessage_SocketFamily_INET6);
+  if (requestor) {
+    d_message.set_socketfamily(requestor->sin4.sin_family == AF_INET ? PBDNSMessage_SocketFamily_INET : PBDNSMessage_SocketFamily_INET6);
+  }
+  else if (responder) {
+    d_message.set_socketfamily(responder->sin4.sin_family == AF_INET ? PBDNSMessage_SocketFamily_INET : PBDNSMessage_SocketFamily_INET6);
+  }
+
   d_message.set_socketprotocol(isTCP ? PBDNSMessage_SocketProtocol_TCP : PBDNSMessage_SocketProtocol_UDP);
 
   if (responder) {
@@ -225,11 +260,11 @@ void DNSProtoBufMessage::update(const boost::uuids::uuid& uuid, const ComboAddre
 }
 
 
-DNSProtoBufMessage::DNSProtoBufMessage(DNSProtoBufMessageType type, const boost::uuids::uuid& uuid, const ComboAddress* requestor, const ComboAddress* to, const DNSName& domain, int qtype, uint16_t qclass, uint16_t qid, bool isTCP, size_t bytes)
+DNSProtoBufMessage::DNSProtoBufMessage(DNSProtoBufMessageType type, const boost::uuids::uuid& uuid, const ComboAddress* requestor, const ComboAddress* responder, const DNSName& domain, int qtype, uint16_t qclass, uint16_t qid, bool isTCP, size_t bytes)
 {
-  update(uuid, requestor, to, isTCP, qid);
+  update(uuid, requestor, responder, isTCP, qid);
 
-  d_message.set_type(type == DNSProtoBufMessage::DNSProtoBufMessageType::Query ? PBDNSMessage_Type_DNSQueryType : PBDNSMessage_Type_DNSResponseType);
+  setType(type);
 
   setBytes(bytes);
   setQuestion(domain, qtype, qclass);
index 4b972a5f75b343323c7ed2748d8dbd83b49dc89d..b9aeaf5c9b3ea5137d8ac8127d6bb62881174a5b 100644 (file)
@@ -40,7 +40,9 @@ class DNSProtoBufMessage
 public:
   enum DNSProtoBufMessageType {
     Query,
-    Response
+    Response,
+    OutgoingQuery,
+    IncomingResponse
   };
 
   DNSProtoBufMessage()
@@ -53,6 +55,7 @@ public:
   {
   }
 
+  void setType(DNSProtoBufMessage::DNSProtoBufMessageType type);
   void setQuestion(const DNSName& qname, uint16_t qtype, uint16_t qclass);
   void setEDNSSubnet(const Netmask& subnet, uint8_t mask=128);
   void setBytes(size_t bytes);
@@ -71,6 +74,7 @@ public:
   DNSProtoBufMessage(DNSProtoBufMessage::DNSProtoBufMessageType type, const boost::uuids::uuid& uuid, const ComboAddress* requestor, const ComboAddress* responder, const DNSName& domain, int qtype, uint16_t qclass, uint16_t qid, bool isTCP, size_t bytes);
   void update(const boost::uuids::uuid& uuid, const ComboAddress* requestor, const ComboAddress* responder, bool isTCP, uint16_t id);
   void setUUID(const boost::uuids::uuid& uuid);
+  void setInitialRequestID(const boost::uuids::uuid& uuid);
 
 protected:
   PBDNSMessage d_message;
index 6d8189c1526cc8b89278d1ee39b412cfbea46723..1b91ad85a8388fd4ca763a1d57b074ff6e0342e2 100644 (file)
@@ -287,6 +287,24 @@ void loadRecursorLuaConfig(const std::string& fname, bool checkOnly)
         theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
       }
     });
+
+  Lua.writeFunction("outgoingProtobufServer", [&lci](const string& server_, const boost::optional<uint16_t> timeout, const boost::optional<uint64_t> maxQueuedEntries, const boost::optional<uint8_t> reconnectWaitTime, boost::optional<bool> asyncConnect) {
+      try {
+       ComboAddress server(server_);
+        if (!lci.outgoingProtobufServer) {
+          lci.outgoingProtobufServer = std::make_shared<RemoteLogger>(server, timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1, asyncConnect ? *asyncConnect : false);
+        }
+        else {
+          theL()<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.protobufServer->toString()<<endl;
+        }
+      }
+      catch(std::exception& e) {
+       theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
+      }
+      catch(PDNSException& e) {
+        theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
+      }
+    });
 #endif
 
   try {
index 07c3d7577274c17c8043ad36234d6e1fbc6bc3c0..2b07a7352d03753c8b1c72842d483b7a7043d10f 100644 (file)
@@ -35,6 +35,7 @@ public:
   map<DNSName,dsmap_t> dsAnchors;
   map<DNSName,std::string> negAnchors;
   std::shared_ptr<RemoteLogger> protobufServer{nullptr};
+  std::shared_ptr<RemoteLogger> outgoingProtobufServer{nullptr};
   uint8_t protobufMaskV4{32};
   uint8_t protobufMaskV6{128};
 };
index 0ee02e5790b76b8eb629bc76f1683afc102f4b1d..3b8b402d27ebc19b641408f74ea6a731df4cba1d 100644 (file)
@@ -116,6 +116,7 @@ pdns_recursor_SOURCES = \
        reczones.cc \
        remote_logger.cc remote_logger.hh \
        resolver.hh resolver.cc \
+       resolve-context.hh \
        responsestats.hh responsestats.cc \
        root-addresses.hh \
        root-dnssec.hh \
diff --git a/pdns/recursordist/resolve-context.hh b/pdns/recursordist/resolve-context.hh
new file mode 120000 (symlink)
index 0000000..ad6002f
--- /dev/null
@@ -0,0 +1 @@
+../resolve-context.hh
\ No newline at end of file
diff --git a/pdns/resolve-context.hh b/pdns/resolve-context.hh
new file mode 100644 (file)
index 0000000..c22abcd
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "config.h"
+
+#ifdef HAVE_PROTOBUF
+#include <boost/uuid/uuid.hpp>
+#endif
+
+struct ResolveContext {
+  ResolveContext()
+  {
+  }
+  ResolveContext(const ResolveContext& ctx)
+  {
+#ifdef HAVE_PROTOBUF
+    this->d_initialRequestId = ctx.d_initialRequestId;
+#endif
+  }
+#ifdef HAVE_PROTOBUF
+  boost::optional<const boost::uuids::uuid&> d_initialRequestId;
+#endif
+};
index 4266445644033f7553c481f3dd90c5d48c65bbf3..c7fa5b817942a40f0fc495a1b8a4fbbfc59dd33a 100644 (file)
@@ -42,8 +42,10 @@ void doSecPoll(time_t* last_secpoll)
   DNSName query(qstring);
   int res=sr.beginResolve(query, QType(QType::TXT), 1, ret);
 
-  if (g_dnssecmode != DNSSECMode::Off && res)
-    state = validateRecords(ret);
+  if (g_dnssecmode != DNSSECMode::Off && res) {
+    ResolveContext ctx;
+    state = validateRecords(ctx, ret);
+  }
 
   if(state == Bogus) {
     L<<Logger::Error<<"Could not retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
index f121cfac2d88ac7e470860469f1f740d007b28ac..f90c2b2344d558005c919a7c04bb86739275620b 100644 (file)
@@ -357,6 +357,11 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
   SyncRes::EDNSStatus::EDNSMode& mode=ednsstatus->mode;
   SyncRes::EDNSStatus::EDNSMode oldmode = mode;
   int EDNSLevel=0;
+  auto luaconfsLocal = g_luaconfs.getLocal();
+  ResolveContext ctx;
+#ifdef HAVE_PROTOBUF
+  ctx.d_initialRequestId = d_initialRequestId;
+#endif
 
   int ret;
   for(int tries = 0; tries < 3; ++tries) {
@@ -369,7 +374,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
     else if(ednsMANDATORY || mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode==EDNSStatus::EDNSIGNORANT)
       EDNSLevel = 1;
     
-    ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, res);
+    ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, luaconfsLocal->outgoingProtobufServer, res);
     if(ret < 0) {
       return ret; // transport error, nothing to learn here
     }
index a1151b116334e3fa5ae715d91d92a865ea307c04..698c73b8efe046b0ffea644456eee3e8ac0aa30a 100644 (file)
@@ -35,6 +35,7 @@
 #include <utility>
 #include "misc.hh"
 #include "lwres.hh"
+#include <boost/optional.hpp>
 #include <boost/circular_buffer.hpp>
 #include <boost/utility.hpp>
 #include "sstuff.hh"
 
 #include "filterpo.hh"
 
+#include "config.h"
+#ifdef HAVE_PROTOBUF
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_generators.hpp>
+#endif
+
 void primeHints(void);
 int getRootNS(void);
 class RecursorLua4;
@@ -366,6 +373,9 @@ public:
   static unsigned int s_maxdepth;
   std::unordered_map<std::string,bool> d_discardedPolicies;
   DNSFilterEngine::Policy d_appliedPolicy;
+#ifdef HAVE_PROTOBUF
+  boost::optional<const boost::uuids::uuid&> d_initialRequestId;
+#endif
   unsigned int d_outqueries;
   unsigned int d_tcpoutqueries;
   unsigned int d_throttledqueries;
@@ -730,4 +740,8 @@ void  parseEDNSSubnetWhitelist(const std::string& wlist);
 
 extern __thread struct timeval g_now;
 
+#ifdef HAVE_PROTOBUF
+extern __thread boost::uuids::random_generator* t_uuidGenerator;
+#endif
+
 #endif
index 12a9984d6d38691a279d862d94e1460125d248cc..799ed053615509b7742d652638d05a8e25c0d697 100644 (file)
@@ -11,11 +11,18 @@ bool g_dnssecLogBogus;
 class SRRecordOracle : public DNSRecordOracle
 {
 public:
+  SRRecordOracle(const ResolveContext& ctx): d_ctx(ctx)
+  {
+  }
   vector<DNSRecord> get(const DNSName& qname, uint16_t qtype) override
   {
     struct timeval tv;
     gettimeofday(&tv, 0);
     SyncRes sr(tv);
+    sr.setId(MT->getTid());
+#ifdef HAVE_PROTOBUF
+    sr.d_initialRequestId = d_ctx.d_initialRequestId;
+#endif
 
     vector<DNSRecord> ret;
     sr.d_doDNSSEC=true;
@@ -25,6 +32,7 @@ public:
     d_queries += sr.d_outqueries;
     return ret;
   }
+  const ResolveContext& d_ctx;
   int d_queries{0};
 };
 
@@ -53,7 +61,7 @@ inline void processNewState(vState& currentState, const vState& newState, bool&
     hadNTA = true;
 }
 
-vState validateRecords(const vector<DNSRecord>& recs)
+vState validateRecords(const ResolveContext& ctx, const vector<DNSRecord>& recs)
 {
   if(recs.empty())
     return Insecure; // can't secure nothing 
@@ -71,7 +79,7 @@ vState validateRecords(const vector<DNSRecord>& recs)
   set<DNSKEYRecordContent> keys;
   cspmap_t validrrsets;
 
-  SRRecordOracle sro;
+  SRRecordOracle sro(ctx);
 
   vState state=Insecure;
   bool hadNTA = false;
index 5604ac53386a950c84ff2fb462013b47b5fb616e..63b891d0e3ced0961e5c911a96cb18247cb0a4da 100644 (file)
@@ -23,8 +23,9 @@
 #include "dnsparser.hh"
 #include "namespaces.hh"
 #include "validate.hh"
+#include "resolve-context.hh"
 
-vState validateRecords(const vector<DNSRecord>& recs);
+vState validateRecords(const ResolveContext& ctx, const vector<DNSRecord>& recs);
 
 /* Off: 3.x behaviour, we do no DNSSEC, no EDNS
    ProcessNoValidate: we gather DNSSEC records on all queries, but we will never validate