From: Remi Gacogne Date: Thu, 26 May 2016 13:58:18 +0000 (+0200) Subject: Add ProtobufLogger.py, a script to display protobuf messages X-Git-Tag: rec-4.0.0-rc1~24^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=78a12e0a7d2210d9e503de640970ea2d29ffbcdd;p=pdns Add ProtobufLogger.py, a script to display protobuf messages Split from #3869. --- diff --git a/contrib/ProtobufLogger.py b/contrib/ProtobufLogger.py new file mode 100644 index 000000000..b146e837a --- /dev/null +++ b/contrib/ProtobufLogger.py @@ -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
' % (sys.argv[0])) + + PDNSPBListener(sys.argv[1], sys.argv[2]).run() + sys.exit(0)