]> git.ipfire.org Git - thirdparty/pdns.git/blob - contrib/ProtobufLogger.py
Protobuf refactoring
[thirdparty/pdns.git] / contrib / ProtobufLogger.py
1 #!/usr/bin/env python2
2
3 import binascii
4 import datetime
5 import socket
6 import struct
7 import sys
8 import threading
9
10 # run: protoc -I=../pdns/ --python_out=. ../pdns/dnsmessage.proto
11 # to generate dnsmessage_pb2
12 import dnsmessage_pb2
13
14 class PDNSPBConnHandler(object):
15
16 def __init__(self, conn):
17 self._conn = conn
18
19 def run(self):
20 while True:
21 data = self._conn.recv(2)
22 if not data:
23 break
24 (datalen,) = struct.unpack("!H", data)
25 data = self._conn.recv(datalen)
26
27 msg = dnsmessage_pb2.PBDNSMessage()
28 msg.ParseFromString(data)
29 if msg.type == dnsmessage_pb2.PBDNSMessage.DNSQueryType:
30 self.printQueryMessage(msg)
31 elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSResponseType:
32 self.printResponseMessage(msg)
33 # PR #3869
34 # elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType:
35 # self.printOutgoingQueryMessage(msg)
36 # elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType:
37 # self.printIncomingResponseMessage(msg)
38 else:
39 print('Discarding unsupported message type %d' % (msg.type))
40
41 self._conn.close()
42
43 def printQueryMessage(self, message):
44 self.printSummary(message, 'Query')
45 self.printQuery(message)
46
47 def printOutgoingQueryMessage(self, message):
48 self.printSummary(message, 'Query (O)')
49 self.printQuery(message)
50
51 def printResponseMessage(self, message):
52 self.printSummary(message, 'Response')
53 self.printQuery(message)
54 self.printResponse(message)
55
56 def printIncomingResponseMessage(self, message):
57 self.printSummary(message, 'Response (I)')
58 self.printQuery(message)
59 self.printResponse(message)
60
61 def printQuery(self, message):
62 if message.HasField('question'):
63 qclass = 1
64 if message.question.HasField('qClass'):
65 qclass = message.question.qClass
66 print("- Question: %d, %d, %s" % (qclass,
67 message.question.qType,
68 message.question.qName))
69
70 def printResponse(self, message):
71 if message.HasField('response'):
72 response = message.response
73 policystr = ''
74 if response.HasField('appliedPolicy') and response.appliedPolicy:
75 policystr = ', Applied policy: ' + response.appliedPolicy
76
77 tagsstr = ''
78 if response.tags:
79 tagsstr = ', Tags: ' + ','.join(response.tags)
80
81 rrscount = len(response.rrs)
82
83 print("- Response Code: %d, RRs: %d%s%s" % (response.rcode,
84 rrscount,
85 policystr,
86 tagsstr))
87
88 for rr in response.rrs:
89 rrclass = 1
90 rdatastr = ''
91 if rr.HasField('class'):
92 rrclass = getattr(rr, 'class')
93 rrtype = rr.type
94 if (rrclass == 1 or rrclass == 255) and rr.HasField('rdata'):
95 if rrtype == 1:
96 rdatastr = socket.inet_ntop(socket.AF_INET, rr.rdata)
97 elif rrtype == 5:
98 rdatastr = rr.rdata
99 elif rrtype == 28:
100 rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
101
102 print("\t - %d, %d, %s, %d, %s" % (rrclass,
103 rrtype,
104 rr.name,
105 rr.ttl,
106 rdatastr))
107
108 def printSummary(self, msg, typestr):
109 datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime('%Y-%m-%d %H:%M:%S')
110 if msg.HasField('timeUsec'):
111 datestr = datestr + '.' + str(msg.timeUsec)
112 ipfromstr = 'N/A'
113 iptostr = 'N/A'
114 fromvalue = getattr(msg, 'from')
115 if msg.socketFamily == dnsmessage_pb2.PBDNSMessage.INET:
116 if msg.HasField('from'):
117 ipfromstr = socket.inet_ntop(socket.AF_INET, fromvalue)
118 if msg.HasField('to'):
119 iptostr = socket.inet_ntop(socket.AF_INET, msg.to)
120 else:
121 if msg.HasField('from'):
122 ipfromstr = socket.inet_ntop(socket.AF_INET6, fromvalue)
123 if msg.HasField('to'):
124 iptostr = socket.inet_ntop(socket.AF_INET6, msg.to)
125
126 if msg.socketProtocol == dnsmessage_pb2.PBDNSMessage.UDP:
127 protostr = 'UDP'
128 else:
129 protostr = 'TCP'
130
131 messageidstr = binascii.hexlify(bytearray(msg.messageId))
132 initialrequestidstr = ''
133 # PR #3869
134 # if msg.HasField('initialRequestId'):
135 # initialrequestidstr = ', initial uuid: ' + binascii.hexlify(bytearray(msg.initialRequestId))
136 requestorstr = ''
137 requestor = self.getRequestorSubnet(msg)
138 if requestor:
139 requestorstr = ' (' + requestor + ')'
140
141 print('[%s] %s of size %d: %s%s -> %s (%s), id: %d, uuid: %s%s' % (datestr,
142 typestr,
143 msg.inBytes,
144 ipfromstr,
145 requestorstr,
146 iptostr,
147 protostr,
148 msg.id,
149 messageidstr,
150 initialrequestidstr))
151
152 def getRequestorSubnet(self, msg):
153 requestorstr = None
154 if msg.HasField('originalRequestorSubnet'):
155 if len(msg.originalRequestorSubnet) == 4:
156 requestorstr = socket.inet_ntop(socket.AF_INET,
157 msg.originalRequestorSubnet)
158 elif len(msg.originalRequestorSubnet) == 16:
159 requestorstr = socket.inet_ntop(socket.AF_INET6,
160 msg.originalRequestorSubnet)
161 return requestorstr
162
163 class PDNSPBListener(object):
164
165 def __init__(self, addr, port):
166 res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
167 socket.SOCK_STREAM, 0,
168 socket.AI_PASSIVE)
169 if len(res) != 1:
170 print("Error parsing the supplied address")
171 sys.exit(1)
172 family, socktype, _, _, sockaddr = res[0]
173 self._sock = socket.socket(family, socktype)
174 self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
175 try:
176 self._sock.bind(sockaddr)
177 except socket.error as exp:
178 print("Error while binding: %s" % str(exp))
179 sys.exit(1)
180
181 self._sock.listen(100)
182
183 def run(self):
184 while True:
185 (conn, _) = self._sock.accept()
186
187 handler = PDNSPBConnHandler(conn)
188 thread = threading.Thread(name='Connection Handler',
189 target=PDNSPBConnHandler.run,
190 args=[handler])
191 thread.setDaemon(True)
192 thread.start()
193
194 self._sock.close()
195
196
197 if __name__ == "__main__":
198 if len(sys.argv) != 3:
199 sys.exit('Usage: %s <address> <port>' % (sys.argv[0]))
200
201 PDNSPBListener(sys.argv[1], sys.argv[2]).run()
202 sys.exit(0)