]>
Commit | Line | Data |
---|---|---|
78a12e0a RG |
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 | ||
33b55aa4 PD |
10 | # run: protoc -I=../pdns/ --python_out=. ../pdns/dnsmessage.proto |
11 | # to generate dnsmessage_pb2 | |
78a12e0a RG |
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) | |
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 | ||
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) |