]> git.ipfire.org Git - thirdparty/pdns.git/blame - contrib/ProtobufLogger.py
Merge pull request #7899 from Habbie/circleci-mssql-version
[thirdparty/pdns.git] / contrib / ProtobufLogger.py
CommitLineData
78a12e0a
RG
1#!/usr/bin/env python2
2
3import binascii
4import datetime
5import socket
6import struct
7import sys
8import threading
9
33b55aa4
PD
10# run: protoc -I=../pdns/ --python_out=. ../pdns/dnsmessage.proto
11# to generate dnsmessage_pb2
78a12e0a
RG
12import dnsmessage_pb2
13
14class 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)
4898a348
RG
33 elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType:
34 self.printOutgoingQueryMessage(msg)
35 elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType:
36 self.printIncomingResponseMessage(msg)
78a12e0a
RG
37 else:
38 print('Discarding unsupported message type %d' % (msg.type))
39
40 self._conn.close()
41
42 def printQueryMessage(self, message):
43 self.printSummary(message, 'Query')
44 self.printQuery(message)
45
46 def printOutgoingQueryMessage(self, message):
47 self.printSummary(message, 'Query (O)')
48 self.printQuery(message)
49
50 def printResponseMessage(self, message):
51 self.printSummary(message, 'Response')
52 self.printQuery(message)
53 self.printResponse(message)
54
55 def printIncomingResponseMessage(self, message):
56 self.printSummary(message, 'Response (I)')
57 self.printQuery(message)
58 self.printResponse(message)
59
60 def printQuery(self, message):
61 if message.HasField('question'):
62 qclass = 1
63 if message.question.HasField('qClass'):
64 qclass = message.question.qClass
65 print("- Question: %d, %d, %s" % (qclass,
66 message.question.qType,
67 message.question.qName))
68
f3da83fe
RG
69 @staticmethod
70 def getAppliedPolicyTypeAsString(polType):
71 if polType == dnsmessage_pb2.PBDNSMessage.UNKNOWN:
72 return 'Unknown'
73 elif polType == dnsmessage_pb2.PBDNSMessage.QNAME:
74 return 'QName'
75 elif polType == dnsmessage_pb2.PBDNSMessage.CLIENTIP:
76 return 'Client IP'
77 elif polType == dnsmessage_pb2.PBDNSMessage.RESPONSEIP:
78 return 'Response IP'
79 elif polType == dnsmessage_pb2.PBDNSMessage.NSDNAME:
80 return 'NS DName'
81 elif polType == dnsmessage_pb2.PBDNSMessage.NSIP:
82 return 'NS IP'
83
78a12e0a
RG
84 def printResponse(self, message):
85 if message.HasField('response'):
86 response = message.response
58307a85
RG
87
88 if response.HasField('queryTimeSec'):
89 datestr = datetime.datetime.fromtimestamp(response.queryTimeSec).strftime('%Y-%m-%d %H:%M:%S')
90 if response.HasField('queryTimeUsec'):
91 datestr = datestr + '.' + str(response.queryTimeUsec)
92 print("- Query time: %s" % (datestr))
93
78a12e0a
RG
94 policystr = ''
95 if response.HasField('appliedPolicy') and response.appliedPolicy:
96 policystr = ', Applied policy: ' + response.appliedPolicy
f3da83fe
RG
97 if response.HasField('appliedPolicyType'):
98 policystr = policystr + ' (' + self.getAppliedPolicyTypeAsString(response.appliedPolicyType) + ')'
78a12e0a 99
2a239b78
PD
100 tagsstr = ''
101 if response.tags:
102 tagsstr = ', Tags: ' + ','.join(response.tags)
103
78a12e0a
RG
104 rrscount = len(response.rrs)
105
2a239b78 106 print("- Response Code: %d, RRs: %d%s%s" % (response.rcode,
78a12e0a 107 rrscount,
2a239b78
PD
108 policystr,
109 tagsstr))
78a12e0a
RG
110
111 for rr in response.rrs:
112 rrclass = 1
113 rdatastr = ''
bf81317e 114 rrudr = 0
78a12e0a
RG
115 if rr.HasField('class'):
116 rrclass = getattr(rr, 'class')
117 rrtype = rr.type
bf81317e
NC
118 if rr.HasField('udr'):
119 rrudr = rr.udr
78a12e0a
RG
120 if (rrclass == 1 or rrclass == 255) and rr.HasField('rdata'):
121 if rrtype == 1:
122 rdatastr = socket.inet_ntop(socket.AF_INET, rr.rdata)
d9d3f9c1
RG
123 elif rrtype == 5:
124 rdatastr = rr.rdata
78a12e0a
RG
125 elif rrtype == 28:
126 rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
127
ee1db2a0 128 print("\t - %d, %d, %s, %d, %s, %d" % (rrclass,
78a12e0a
RG
129 rrtype,
130 rr.name,
131 rr.ttl,
ee1db2a0
NC
132 rdatastr,
133 rrudr))
78a12e0a
RG
134
135 def printSummary(self, msg, typestr):
136 datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime('%Y-%m-%d %H:%M:%S')
137 if msg.HasField('timeUsec'):
138 datestr = datestr + '.' + str(msg.timeUsec)
139 ipfromstr = 'N/A'
140 iptostr = 'N/A'
141 fromvalue = getattr(msg, 'from')
142 if msg.socketFamily == dnsmessage_pb2.PBDNSMessage.INET:
143 if msg.HasField('from'):
144 ipfromstr = socket.inet_ntop(socket.AF_INET, fromvalue)
145 if msg.HasField('to'):
146 iptostr = socket.inet_ntop(socket.AF_INET, msg.to)
147 else:
148 if msg.HasField('from'):
149 ipfromstr = socket.inet_ntop(socket.AF_INET6, fromvalue)
150 if msg.HasField('to'):
151 iptostr = socket.inet_ntop(socket.AF_INET6, msg.to)
152
153 if msg.socketProtocol == dnsmessage_pb2.PBDNSMessage.UDP:
154 protostr = 'UDP'
155 else:
156 protostr = 'TCP'
157
158 messageidstr = binascii.hexlify(bytearray(msg.messageId))
c5ffc56c
RG
159
160 serveridstr = 'N/A'
161 if msg.HasField('serverIdentity'):
162 serveridstr = msg.serverIdentity
163
78a12e0a 164 initialrequestidstr = ''
4898a348 165 if msg.HasField('initialRequestId'):
c5ffc56c 166 initialrequestidstr = ', initial uuid: %s ' % (binascii.hexlify(bytearray(msg.initialRequestId)))
4898a348 167
78a12e0a
RG
168 requestorstr = ''
169 requestor = self.getRequestorSubnet(msg)
170 if requestor:
171 requestorstr = ' (' + requestor + ')'
172
1127b082
NC
173 deviceId = binascii.hexlify(bytearray(msg.deviceId))
174 requestorId = msg.requestorId
bf81317e
NC
175 nod = 0
176 if (msg.HasField('newlyObservedDomain')):
177 nod = msg.newlyObservedDomain
1127b082
NC
178
179 print('[%s] %s of size %d: %s%s -> %s (%s), id: %d, uuid: %s%s '
ee1db2a0 180 'requestorid: %s deviceid: %s serverid: %s nod: %d' % (datestr,
1127b082
NC
181 typestr,
182 msg.inBytes,
183 ipfromstr,
184 requestorstr,
185 iptostr,
186 protostr,
187 msg.id,
188 messageidstr,
eb220244
NC
189 initialrequestidstr,
190 requestorId,
c5ffc56c 191 deviceId,
ee1db2a0
NC
192 serveridstr,
193 nod))
78a12e0a
RG
194
195 def getRequestorSubnet(self, msg):
196 requestorstr = None
197 if msg.HasField('originalRequestorSubnet'):
198 if len(msg.originalRequestorSubnet) == 4:
199 requestorstr = socket.inet_ntop(socket.AF_INET,
200 msg.originalRequestorSubnet)
201 elif len(msg.originalRequestorSubnet) == 16:
202 requestorstr = socket.inet_ntop(socket.AF_INET6,
203 msg.originalRequestorSubnet)
204 return requestorstr
205
206class PDNSPBListener(object):
207
208 def __init__(self, addr, port):
209 res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
210 socket.SOCK_STREAM, 0,
211 socket.AI_PASSIVE)
212 if len(res) != 1:
213 print("Error parsing the supplied address")
214 sys.exit(1)
215 family, socktype, _, _, sockaddr = res[0]
216 self._sock = socket.socket(family, socktype)
217 self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
218 try:
219 self._sock.bind(sockaddr)
220 except socket.error as exp:
221 print("Error while binding: %s" % str(exp))
222 sys.exit(1)
223
224 self._sock.listen(100)
225
226 def run(self):
227 while True:
228 (conn, _) = self._sock.accept()
229
230 handler = PDNSPBConnHandler(conn)
231 thread = threading.Thread(name='Connection Handler',
232 target=PDNSPBConnHandler.run,
233 args=[handler])
234 thread.setDaemon(True)
235 thread.start()
236
237 self._sock.close()
238
239
240if __name__ == "__main__":
241 if len(sys.argv) != 3:
242 sys.exit('Usage: %s <address> <port>' % (sys.argv[0]))
243
244 PDNSPBListener(sys.argv[1], sys.argv[2]).run()
245 sys.exit(0)