]> git.ipfire.org Git - thirdparty/pdns.git/blob - contrib/ProtobufLogger.py
b2b-migrate did not open a transaction, breaking it for lmdb
[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 elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType:
34 self.printOutgoingQueryMessage(msg)
35 elif msg.type == dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType:
36 self.printIncomingResponseMessage(msg)
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
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
84 def printResponse(self, message):
85 if message.HasField('response'):
86 response = message.response
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
94 policystr = ''
95 if response.HasField('appliedPolicy') and response.appliedPolicy:
96 policystr = ', Applied policy: ' + response.appliedPolicy
97 if response.HasField('appliedPolicyType'):
98 policystr = policystr + ' (' + self.getAppliedPolicyTypeAsString(response.appliedPolicyType) + ')'
99
100 tagsstr = ''
101 if response.tags:
102 tagsstr = ', Tags: ' + ','.join(response.tags)
103
104 rrscount = len(response.rrs)
105
106 print("- Response Code: %d, RRs: %d%s%s" % (response.rcode,
107 rrscount,
108 policystr,
109 tagsstr))
110
111 for rr in response.rrs:
112 rrclass = 1
113 rdatastr = ''
114 rrudr = 0
115 if rr.HasField('class'):
116 rrclass = getattr(rr, 'class')
117 rrtype = rr.type
118 if rr.HasField('udr'):
119 rrudr = rr.udr
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)
123 elif rrtype == 5:
124 rdatastr = rr.rdata
125 elif rrtype == 28:
126 rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
127
128 print("\t - %d, %d, %s, %d, %s, %d" % (rrclass,
129 rrtype,
130 rr.name,
131 rr.ttl,
132 rdatastr,
133 rrudr))
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))
159
160 serveridstr = 'N/A'
161 if msg.HasField('serverIdentity'):
162 serveridstr = msg.serverIdentity
163
164 initialrequestidstr = ''
165 if msg.HasField('initialRequestId'):
166 initialrequestidstr = ', initial uuid: %s ' % (binascii.hexlify(bytearray(msg.initialRequestId)))
167
168 requestorstr = ''
169 requestor = self.getRequestorSubnet(msg)
170 if requestor:
171 requestorstr = ' (' + requestor + ')'
172
173 deviceId = binascii.hexlify(bytearray(msg.deviceId))
174 requestorId = msg.requestorId
175 nod = 0
176 if (msg.HasField('newlyObservedDomain')):
177 nod = msg.newlyObservedDomain
178
179 print('[%s] %s of size %d: %s%s -> %s (%s), id: %d, uuid: %s%s '
180 'requestorid: %s deviceid: %s serverid: %s nod: %d' % (datestr,
181 typestr,
182 msg.inBytes,
183 ipfromstr,
184 requestorstr,
185 iptostr,
186 protostr,
187 msg.id,
188 messageidstr,
189 initialrequestidstr,
190 requestorId,
191 deviceId,
192 serveridstr,
193 nod))
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
206 class 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
240 if __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)