]> git.ipfire.org Git - thirdparty/pdns.git/blob - contrib/ProtobufLogger.py
Merge pull request #4126 from rgacogne/auth-carbon-freebsd
[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
74 if response.HasField('queryTimeSec'):
75 datestr = datetime.datetime.fromtimestamp(response.queryTimeSec).strftime('%Y-%m-%d %H:%M:%S')
76 if response.HasField('queryTimeUsec'):
77 datestr = datestr + '.' + str(response.queryTimeUsec)
78 print("- Query time: %s" % (datestr))
79
80 policystr = ''
81 if response.HasField('appliedPolicy') and response.appliedPolicy:
82 policystr = ', Applied policy: ' + response.appliedPolicy
83
84 tagsstr = ''
85 if response.tags:
86 tagsstr = ', Tags: ' + ','.join(response.tags)
87
88 rrscount = len(response.rrs)
89
90 print("- Response Code: %d, RRs: %d%s%s" % (response.rcode,
91 rrscount,
92 policystr,
93 tagsstr))
94
95 for rr in response.rrs:
96 rrclass = 1
97 rdatastr = ''
98 if rr.HasField('class'):
99 rrclass = getattr(rr, 'class')
100 rrtype = rr.type
101 if (rrclass == 1 or rrclass == 255) and rr.HasField('rdata'):
102 if rrtype == 1:
103 rdatastr = socket.inet_ntop(socket.AF_INET, rr.rdata)
104 elif rrtype == 5:
105 rdatastr = rr.rdata
106 elif rrtype == 28:
107 rdatastr = socket.inet_ntop(socket.AF_INET6, rr.rdata)
108
109 print("\t - %d, %d, %s, %d, %s" % (rrclass,
110 rrtype,
111 rr.name,
112 rr.ttl,
113 rdatastr))
114
115 def printSummary(self, msg, typestr):
116 datestr = datetime.datetime.fromtimestamp(msg.timeSec).strftime('%Y-%m-%d %H:%M:%S')
117 if msg.HasField('timeUsec'):
118 datestr = datestr + '.' + str(msg.timeUsec)
119 ipfromstr = 'N/A'
120 iptostr = 'N/A'
121 fromvalue = getattr(msg, 'from')
122 if msg.socketFamily == dnsmessage_pb2.PBDNSMessage.INET:
123 if msg.HasField('from'):
124 ipfromstr = socket.inet_ntop(socket.AF_INET, fromvalue)
125 if msg.HasField('to'):
126 iptostr = socket.inet_ntop(socket.AF_INET, msg.to)
127 else:
128 if msg.HasField('from'):
129 ipfromstr = socket.inet_ntop(socket.AF_INET6, fromvalue)
130 if msg.HasField('to'):
131 iptostr = socket.inet_ntop(socket.AF_INET6, msg.to)
132
133 if msg.socketProtocol == dnsmessage_pb2.PBDNSMessage.UDP:
134 protostr = 'UDP'
135 else:
136 protostr = 'TCP'
137
138 messageidstr = binascii.hexlify(bytearray(msg.messageId))
139 initialrequestidstr = ''
140 # PR #3869
141 # if msg.HasField('initialRequestId'):
142 # initialrequestidstr = ', initial uuid: ' + binascii.hexlify(bytearray(msg.initialRequestId))
143 requestorstr = ''
144 requestor = self.getRequestorSubnet(msg)
145 if requestor:
146 requestorstr = ' (' + requestor + ')'
147
148 print('[%s] %s of size %d: %s%s -> %s (%s), id: %d, uuid: %s%s' % (datestr,
149 typestr,
150 msg.inBytes,
151 ipfromstr,
152 requestorstr,
153 iptostr,
154 protostr,
155 msg.id,
156 messageidstr,
157 initialrequestidstr))
158
159 def getRequestorSubnet(self, msg):
160 requestorstr = None
161 if msg.HasField('originalRequestorSubnet'):
162 if len(msg.originalRequestorSubnet) == 4:
163 requestorstr = socket.inet_ntop(socket.AF_INET,
164 msg.originalRequestorSubnet)
165 elif len(msg.originalRequestorSubnet) == 16:
166 requestorstr = socket.inet_ntop(socket.AF_INET6,
167 msg.originalRequestorSubnet)
168 return requestorstr
169
170 class PDNSPBListener(object):
171
172 def __init__(self, addr, port):
173 res = socket.getaddrinfo(addr, port, socket.AF_UNSPEC,
174 socket.SOCK_STREAM, 0,
175 socket.AI_PASSIVE)
176 if len(res) != 1:
177 print("Error parsing the supplied address")
178 sys.exit(1)
179 family, socktype, _, _, sockaddr = res[0]
180 self._sock = socket.socket(family, socktype)
181 self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
182 try:
183 self._sock.bind(sockaddr)
184 except socket.error as exp:
185 print("Error while binding: %s" % str(exp))
186 sys.exit(1)
187
188 self._sock.listen(100)
189
190 def run(self):
191 while True:
192 (conn, _) = self._sock.accept()
193
194 handler = PDNSPBConnHandler(conn)
195 thread = threading.Thread(name='Connection Handler',
196 target=PDNSPBConnHandler.run,
197 args=[handler])
198 thread.setDaemon(True)
199 thread.start()
200
201 self._sock.close()
202
203
204 if __name__ == "__main__":
205 if len(sys.argv) != 3:
206 sys.exit('Usage: %s <address> <port>' % (sys.argv[0]))
207
208 PDNSPBListener(sys.argv[1], sys.argv[2]).run()
209 sys.exit(0)