1 | #!/usr/bin/env python
|
---|
2 | #
|
---|
3 | # Reading NS1 files - http://www.stumbler.net/ns1files.html
|
---|
4 | #
|
---|
5 | # Rick van der Zwet <info@rickvanderzwet.nl>
|
---|
6 | #
|
---|
7 | import datetime
|
---|
8 | from struct import unpack
|
---|
9 |
|
---|
10 | from collections import defaultdict
|
---|
11 | import logging
|
---|
12 |
|
---|
13 | logger = logging.getLogger(__name__)
|
---|
14 |
|
---|
15 | def parse_ns1(fh):
|
---|
16 | def get_int32(size=1):
|
---|
17 | v = unpack('<' + 'i'*size,fh.read(4*size))
|
---|
18 | return v[0] if size == 1 else v
|
---|
19 |
|
---|
20 | def get_uint32(size=1):
|
---|
21 | v = unpack('<' + 'I'*size,fh.read(4*size))
|
---|
22 | return v[0] if size == 1 else v
|
---|
23 |
|
---|
24 | def get_uint64(size=1):
|
---|
25 | v = unpack('<' + 'Q'*size,fh.read(8*size))
|
---|
26 | return v[0] if size == 1 else v
|
---|
27 |
|
---|
28 | def get_uint8(size=1):
|
---|
29 | v = unpack('<' + 'B'*size,fh.read(1*size))
|
---|
30 | return v[0] if size == 1 else v
|
---|
31 |
|
---|
32 | def get_filetime():
|
---|
33 | _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0)
|
---|
34 | ns = unpack('<Q',fh.read(8))[0] * 10
|
---|
35 | sec = ns / 10**8
|
---|
36 | d = datetime.timedelta(seconds=sec)
|
---|
37 | return _FILETIME_null_date + d
|
---|
38 |
|
---|
39 | def get_char(size):
|
---|
40 | return fh.read(size)
|
---|
41 |
|
---|
42 | def get_double(size=1):
|
---|
43 | v = unpack('<' + 'd'*size,fh.read(8*size))
|
---|
44 | return v[0] if size == 1 else v
|
---|
45 |
|
---|
46 | def get_mac():
|
---|
47 | return ':'.join(["%02X" % x for x in unpack('BBBBBB',fh.read(6))])
|
---|
48 |
|
---|
49 | data = {}
|
---|
50 |
|
---|
51 | data["dwSignature"] = get_char(4)
|
---|
52 | data["dwFileVerunpack"] = get_uint32()
|
---|
53 | ApCount = get_uint32()
|
---|
54 | data["ApCount"] = ApCount
|
---|
55 |
|
---|
56 | data["aps"] = []
|
---|
57 | for a in range(0,ApCount):
|
---|
58 | ap = {}
|
---|
59 | SSIDLength = get_uint8()
|
---|
60 | ap["SSIDLength"] = SSIDLength
|
---|
61 | ap["SSID"] = get_char(SSIDLength)
|
---|
62 | ap["BSSID"] = get_mac()
|
---|
63 | ap["MaxSignal"] = get_int32()
|
---|
64 | ap["MinNoise"] = get_int32()
|
---|
65 | ap["MaxSNR"] = get_int32()
|
---|
66 | ap["Flags"] = get_uint32()
|
---|
67 | ap["BeaconInterval"] = get_uint32()
|
---|
68 | ap["FirstSeen"] = get_filetime()
|
---|
69 | ap["LastSeen"] = get_filetime()
|
---|
70 | ap["BestLat"] = get_double()
|
---|
71 | ap["BestLong"] = get_double()
|
---|
72 | DataCount = get_uint32()
|
---|
73 | ap["DataCount"] = DataCount
|
---|
74 | ap["measurements"] = []
|
---|
75 | for c in range(0,DataCount):
|
---|
76 | ms = {}
|
---|
77 | ms["Time"] = get_filetime()
|
---|
78 | ms["Signal"] = get_int32()
|
---|
79 | ms["Noice"] = get_int32()
|
---|
80 | LocationSource = get_int32()
|
---|
81 | ms["LocationSource"] = LocationSource
|
---|
82 | if LocationSource == 1:
|
---|
83 | ms["Latitude"] = get_double()
|
---|
84 | ms["Longitude"] = get_double()
|
---|
85 | ms["Altitude"] = get_double()
|
---|
86 | ms["NumStats"] = get_uint32()
|
---|
87 | ms["Speed"] = get_double()
|
---|
88 | ms["Track"] = get_double()
|
---|
89 | ms["MagVariation"] = get_double()
|
---|
90 | ms["Hdop"] = get_double()
|
---|
91 | ap["measurements"].append(ms)
|
---|
92 | NameLength = get_uint8()
|
---|
93 | ap["NameLength"] = NameLength
|
---|
94 | ap["Name"] = get_char(NameLength)
|
---|
95 | ap["Channels"] = get_uint64()
|
---|
96 | ap["LastChannel"] = get_uint32()
|
---|
97 | ap["IPAddress"] = get_uint32()
|
---|
98 | ap["MinSignal"] = get_int32()
|
---|
99 | ap["MaxSignal"] = get_int32()
|
---|
100 | ap["DataRate"] = get_uint32()
|
---|
101 | ap["IPSubnet"] = get_uint32()
|
---|
102 | ap["IPMask"] = get_uint32()
|
---|
103 | ap["ApFlags"] = get_uint32()
|
---|
104 | IELength = get_uint32()
|
---|
105 | ap["IELength"] = IELength
|
---|
106 | ap["InformationElements"] = get_uint8(IELength)
|
---|
107 | data["aps"].append(ap)
|
---|
108 | return data
|
---|
109 |
|
---|
110 | def process_ns1(fh, counters):
|
---|
111 | data = parse_ns1(fh)
|
---|
112 |
|
---|
113 |
|
---|
114 | # Temponary holders
|
---|
115 | meting_pool = defaultdict(list)
|
---|
116 | ap_pool = {}
|
---|
117 |
|
---|
118 | for ap in data['aps']:
|
---|
119 | # XXX: How is encryption coded?
|
---|
120 | encryption = False
|
---|
121 | ap_pool[ap['BSSID']]= (ap['SSID'], encryption)
|
---|
122 | for point in ap["measurements"]:
|
---|
123 | counters['meting_total'] += 1
|
---|
124 | if point['LocationSource'] == 0:
|
---|
125 | logger.debug("No GPS Coordinates found for BSSID %s @ %s",
|
---|
126 | ap['BSSID'], point['Time'])
|
---|
127 | counters['meting_ignored'] += 1
|
---|
128 | continue
|
---|
129 | # We store all values found, avg or max will be done later on
|
---|
130 | key = (ap['BSSID'], point["Latitude"], point["Longitude"])
|
---|
131 |
|
---|
132 | # Known measurement error
|
---|
133 | if point['Signal'] == -32767: continue
|
---|
134 |
|
---|
135 | # XXX: Signal need properly be a relation of signal_dbm and noice_dbm
|
---|
136 | signaal= 100 + point['Signal']
|
---|
137 | if signaal > 100 or signaal < 0:
|
---|
138 | logger.warning("Signal %s is not valid entry for BSSID %s @ %s",
|
---|
139 | point['Signal'], ap['BSSID'], point['Time'])
|
---|
140 | continue
|
---|
141 | meting_pool[key].append(signaal)
|
---|
142 |
|
---|
143 | return (counters, ap_pool, None, meting_pool)
|
---|
144 | if __name__ == '__main__':
|
---|
145 | import sys
|
---|
146 | import pprint
|
---|
147 | pp = pprint.PrettyPrinter(indent=2)
|
---|
148 | pp.pprint(parse_ns1(open(sys.argv[1],'r')))
|
---|