This repository has been archived by the owner on Apr 14, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
qpp-analyse.py
executable file
·97 lines (79 loc) · 2.89 KB
/
qpp-analyse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python
from __future__ import print_function
import sys
import csv
import json
import collections
import ipaddress
def split_value(row, name, map_cb=None):
value = row.get(name, "")
values = value.split(",") if value != "" else []
if map_cb is None:
return values
else:
return [map_cb(v) for v in values]
def decode_row(row):
if row["tcp.stream"] == "":
return
client = (row["ip.src"] or row["ipv6.src"], int(row["tcp.srcport"]))
server = (row["ip.dst"] or row["ipv6.dst"], int(row["tcp.dstport"]))
if client[1] == 53:
client, server = server, client
return {
"flow": int(row["tcp.stream"]),
"time": float(row["frame.time_relative"]),
"client": client,
"server": server,
"dns_id": split_value(row, "dns.id"),
"dns_query": split_value(row, "dns.flags.response", lambda flag: flag == "0"),
"dns_response": split_value(row, "dns.flags.response", lambda flag: flag == "1"),
"dns_qtype": split_value(row, "dns.qry.type", lambda type: int(type)),
}
def private_ip(ip):
return ipaddress.ip_address(unicode(ip)).is_private
packets = []
servers = set()
with open(sys.argv[1]) as cvsfile:
reader = csv.DictReader(cvsfile, delimiter="\t", strict=True)
for row in reader:
packet = decode_row(row)
if not packet:
continue
if private_ip(packet["client"][0]) or private_ip(packet["server"][0]):
continue
packets.append(packet)
servers.add(packet["server"][0])
# count flows
flows = {}
for packet in packets:
# skip server initiated connections
if packet["client"][0] in servers:
continue
flows.setdefault(packet["flow"], {"qpp": collections.Counter(), "packets": 0, "client": packet["client"], "qtypes": set()})
flow = flows[packet["flow"]]
flow["packets"] += 1
flow["qtypes"] |= set(packet["dns_qtype"])
qcount = len([q for q in packet["dns_query"] if q])
if qcount > 0:
flow["qpp"][qcount] += 1
# delete flows with unassigned types, AXFR/IXFR, or ANY
def standard_type(type):
return 1 <= type and type <= 258 and type not in [251, 252, 255]
flows = dict((k, v) for (k, v) in flows.items() if all(standard_type(t) for t in v["qtypes"]))
# histogram for each client
result = {}
for flow, data in flows.items():
total_queries = sum(k * v for k, v in data["qpp"].items())
if total_queries > 1:
r = result.setdefault(data["client"][0], dict(packets=0, qpp=collections.Counter()))
r["packets"] += data["packets"]
r["qpp"] += data["qpp"]
class Encoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return list(obj)
elif isinstance(obj, collections.Counter):
return dict(obj)
else:
return json.JSONEncoder.default(self, obj)
print(json.dumps(result, cls=Encoder))