]> granicus.if.org Git - pdns/commitdiff
Add ProtobufLogger.py, a script to display protobuf messages
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 26 May 2016 13:58:18 +0000 (15:58 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 26 May 2016 13:58:18 +0000 (15:58 +0200)
Split from #3869.

contrib/ProtobufLogger.py [new file with mode: 0644]

diff --git a/contrib/ProtobufLogger.py b/contrib/ProtobufLogger.py
new file mode 100644 (file)
index 0000000..b146e83
--- /dev/null
@@ -0,0 +1,196 @@
+#!/usr/bin/env python2
+
+import binascii
+import datetime
+import socket
+import struct
+import sys
+import threading
+
+# you need to get the dnsmessage.proto file from the PDNS
+# repository, then run:
+# protoc -I=. --python_out=. dnsmessage.proto
+import dnsmessage_pb2
+
+class PDNSPBConnHandler(object):
+
+    def __init__(self, conn):
+        self._conn = conn
+
+    def run(self):
+        while True:
+            data = self._conn.recv(2)
+            if not data:
+                break
+            (datalen,) = struct.unpack("!H", data)
+            data = self._conn.recv(datalen)
+
+            msg = dnsmessage_pb2.PBDNSMessage()
+            msg.ParseFromString(data)
+            if msg.type == dnsmessage_pb2.PBDNSMessage.DNSQueryType:
+                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)
+            else:
+                print('Discarding unsupported message type %d' % (msg.type))
+
+        self._conn.close()
+
+    def printQueryMessage(self, message):
+        self.printSummary(message, 'Query')
+        self.printQuery(message)
+
+    def printOutgoingQueryMessage(self, message):
+        self.printSummary(message, 'Query (O)')
+        self.printQuery(message)
+
+    def printResponseMessage(self, message):
+        self.printSummary(message, 'Response')
+        self.printQuery(message)
+        self.printResponse(message)
+
+    def printIncomingResponseMessage(self, message):
+        self.printSummary(message, 'Response (I)')
+        self.printQuery(message)
+        self.printResponse(message)
+
+    def printQuery(self, message):
+        if message.HasField('question'):
+            qclass = 1
+            if message.question.HasField('qClass'):
+                qclass = message.question.qClass
+            print("- Question: %d, %d, %s" % (qclass,
+                                              message.question.qType,
+                                              message.question.qName))
+
+    def printResponse(self, message):
+        if message.HasField('response'):
+            response = message.response
+            policystr = ''
+            if response.HasField('appliedPolicy') and response.appliedPolicy:
+                policystr = ', Applied policy: ' + response.appliedPolicy
+
+            rrscount = len(response.rrs)
+
+            print("- Response Code: %d, RRs: %d%s" % (response.rcode,
+                                                      rrscount,
+                                                      policystr))
+
+            for rr in response.rrs:
+                rrclass = 1
+                rdatastr = ''
+                if rr.HasField('class'):
+                    rrclass = getattr(rr, 'class')
+                rrtype = rr.type
+                if (rrclass == 1 or rrclass == 255) and rr.HasField('rdata'):
+                    if rrtype == 1:
+                        rdatastr = socket.inet_ntop(socket.AF_INET, rr.rdata)
+                    elif rrtype == 28:
+                        rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
+
+                print("\t - %d, %d, %s, %d, %s" % (rrclass,
+                                                   rrtype,
+                                                   rr.name,
+                                                   rr.ttl,
+                                                   rdatastr))
+
+    def printSummary(self, msg, typestr):
+        datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime('%Y-%m-%d %H:%M:%S')
+        if msg.HasField('timeUsec'):
+            datestr = datestr + '.' + str(msg.timeUsec)
+        ipfromstr = 'N/A'
+        iptostr = 'N/A'
+        fromvalue = getattr(msg, 'from')
+        if msg.socketFamily == dnsmessage_pb2.PBDNSMessage.INET:
+            if msg.HasField('from'):
+                ipfromstr = socket.inet_ntop(socket.AF_INET, fromvalue)
+            if msg.HasField('to'):
+                iptostr = socket.inet_ntop(socket.AF_INET, msg.to)
+        else:
+            if msg.HasField('from'):
+                ipfromstr = socket.inet_ntop(socket.AF_INET6, fromvalue)
+            if msg.HasField('to'):
+                iptostr = socket.inet_ntop(socket.AF_INET6, msg.to)
+
+        if msg.socketProtocol == dnsmessage_pb2.PBDNSMessage.UDP:
+            protostr = 'UDP'
+        else:
+            protostr = 'TCP'
+
+        messageidstr = binascii.hexlify(bytearray(msg.messageId))
+        initialrequestidstr = ''
+        # PR #3869
+        # if msg.HasField('initialRequestId'):
+        #    initialrequestidstr = ', initial uuid: ' + binascii.hexlify(bytearray(msg.initialRequestId))
+        requestorstr = ''
+        requestor = self.getRequestorSubnet(msg)
+        if requestor:
+            requestorstr = ' (' + requestor + ')'
+
+        print('[%s] %s of size %d: %s%s -> %s (%s), id: %d, uuid: %s%s' % (datestr,
+                                                                           typestr,
+                                                                           msg.inBytes,
+                                                                           ipfromstr,
+                                                                           requestorstr,
+                                                                           iptostr,
+                                                                           protostr,
+                                                                           msg.id,
+                                                                           messageidstr,
+                                                                           initialrequestidstr))
+
+    def getRequestorSubnet(self, msg):
+        requestorstr = None
+        if msg.HasField('originalRequestorSubnet'):
+            if len(msg.originalRequestorSubnet) == 4:
+                requestorstr = socket.inet_ntop(socket.AF_INET,
+                                                msg.originalRequestorSubnet)
+            elif len(msg.originalRequestorSubnet) == 16:
+                requestorstr = socket.inet_ntop(socket.AF_INET6,
+                                                msg.originalRequestorSubnet)
+        return requestorstr
+
+class PDNSPBListener(object):
+
+    def __init__(self, addr, port):
+        res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
+                                 socket.SOCK_STREAM, 0,
+                                 socket.AI_PASSIVE)
+        if len(res) != 1:
+            print("Error parsing the supplied address")
+            sys.exit(1)
+        family, socktype, _, _, sockaddr = res[0]
+        self._sock = socket.socket(family, socktype)
+        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+        try:
+            self._sock.bind(sockaddr)
+        except socket.error as exp:
+            print("Error while binding: %s" % str(exp))
+            sys.exit(1)
+
+        self._sock.listen(100)
+
+    def run(self):
+        while True:
+            (conn, _) = self._sock.accept()
+
+            handler = PDNSPBConnHandler(conn)
+            thread = threading.Thread(name='Connection Handler',
+                                      target=PDNSPBConnHandler.run,
+                                      args=[handler])
+            thread.setDaemon(True)
+            thread.start()
+
+        self._sock.close()
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 3:
+        sys.exit('Usage: %s <address> <port>' % (sys.argv[0]))
+
+    PDNSPBListener(sys.argv[1], sys.argv[2]).run()
+    sys.exit(0)