]> git.ipfire.org Git - thirdparty/pdns.git/blob - contrib/ProtobufLogger.py
Merge pull request #7004 from rgacogne/rec-outgoing-protobuf-ecs-serverid
[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 if rr.HasField('class'):
115 rrclass = getattr(rr, 'class')
116 rrtype = rr.type
117 if (rrclass == 1 or rrclass == 255) and rr.HasField('rdata'):
118 if rrtype == 1:
119 rdatastr = socket.inet_ntop(socket.AF_INET, rr.rdata)
120 elif rrtype == 5:
121 rdatastr = rr.rdata
122 elif rrtype == 28:
123 rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
124
125 print("\t - %d, %d, %s, %d, %s" % (rrclass,
126 rrtype,
127 rr.name,
128 rr.ttl,
129 rdatastr))
130
131 def printSummary(self, msg, typestr):
132 datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime('%Y-%m-%d %H:%M:%S')
133 if msg.HasField('timeUsec'):
134 datestr = datestr + '.' + str(msg.timeUsec)
135 ipfromstr = 'N/A'
136 iptostr = 'N/A'
137 fromvalue = getattr(msg, 'from')
138 if msg.socketFamily == dnsmessage_pb2.PBDNSMessage.INET:
139 if msg.HasField('from'):
140 ipfromstr = socket.inet_ntop(socket.AF_INET, fromvalue)
141 if msg.HasField('to'):
142 iptostr = socket.inet_ntop(socket.AF_INET, msg.to)
143 else:
144 if msg.HasField('from'):
145 ipfromstr = socket.inet_ntop(socket.AF_INET6, fromvalue)
146 if msg.HasField('to'):
147 iptostr = socket.inet_ntop(socket.AF_INET6, msg.to)
148
149 if msg.socketProtocol == dnsmessage_pb2.PBDNSMessage.UDP:
150 protostr = 'UDP'
151 else:
152 protostr = 'TCP'
153
154 messageidstr = binascii.hexlify(bytearray(msg.messageId))
155
156 serveridstr = 'N/A'
157 if msg.HasField('serverIdentity'):
158 serveridstr = msg.serverIdentity
159
160 initialrequestidstr = ''
161 if msg.HasField('initialRequestId'):
162 initialrequestidstr = ', initial uuid: %s ' % (binascii.hexlify(bytearray(msg.initialRequestId)))
163
164 requestorstr = ''
165 requestor = self.getRequestorSubnet(msg)
166 if requestor:
167 requestorstr = ' (' + requestor + ')'
168
169 deviceId = binascii.hexlify(bytearray(msg.deviceId))
170 requestorId = msg.requestorId
171
172 print('[%s] %s of size %d: %s%s -> %s (%s), id: %d, uuid: %s%s '
173 'requestorid: %s deviceid: %s serverid: %s' % (datestr,
174 typestr,
175 msg.inBytes,
176 ipfromstr,
177 requestorstr,
178 iptostr,
179 protostr,
180 msg.id,
181 messageidstr,
182 initialrequestidstr,
183 requestorId,
184 deviceId,
185 serveridstr))
186
187 def getRequestorSubnet(self, msg):
188 requestorstr = None
189 if msg.HasField('originalRequestorSubnet'):
190 if len(msg.originalRequestorSubnet) == 4:
191 requestorstr = socket.inet_ntop(socket.AF_INET,
192 msg.originalRequestorSubnet)
193 elif len(msg.originalRequestorSubnet) == 16:
194 requestorstr = socket.inet_ntop(socket.AF_INET6,
195 msg.originalRequestorSubnet)
196 return requestorstr
197
198 class PDNSPBListener(object):
199
200 def __init__(self, addr, port):
201 res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
202 socket.SOCK_STREAM, 0,
203 socket.AI_PASSIVE)
204 if len(res) != 1:
205 print("Error parsing the supplied address")
206 sys.exit(1)
207 family, socktype, _, _, sockaddr = res[0]
208 self._sock = socket.socket(family, socktype)
209 self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
210 try:
211 self._sock.bind(sockaddr)
212 except socket.error as exp:
213 print("Error while binding: %s" % str(exp))
214 sys.exit(1)
215
216 self._sock.listen(100)
217
218 def run(self):
219 while True:
220 (conn, _) = self._sock.accept()
221
222 handler = PDNSPBConnHandler(conn)
223 thread = threading.Thread(name='Connection Handler',
224 target=PDNSPBConnHandler.run,
225 args=[handler])
226 thread.setDaemon(True)
227 thread.start()
228
229 self._sock.close()
230
231
232 if __name__ == "__main__":
233 if len(sys.argv) != 3:
234 sys.exit('Usage: %s <address> <port>' % (sys.argv[0]))
235
236 PDNSPBListener(sys.argv[1], sys.argv[2]).run()
237 sys.exit(0)