nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 #!/usr/bin/env python
2 #
3 # Tool to index protocols that appears in the given capture files
4 #
5 # The script list_protos_in_cap.sh does the same thing.
6 #
7 # Copyright 2009, Kovarththanan Rajaratnam <kovarththanan.rajaratnam@gmail.com>
8 #
9 # Wireshark - Network traffic analyzer
10 # By Gerald Combs <gerald@wireshark.org>
11 # Copyright 1998 Gerald Combs
12 #
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; either version 2
16 # of the License, or (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #
27  
28 from optparse import OptionParser
29 import multiprocessing
30 import sys
31 import os
32 import subprocess
33 import re
34 import pickle
35 import tempfile
36 import filecmp
37 import random
38  
39 def extract_protos_from_file_proces(tshark, file):
40 try:
41 cmd = [tshark, "-Tfields", "-e", "frame.protocols", "-r", file]
42 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
43 (stdout, stderr) = p.communicate()
44 if sys.version_info[0] >= 3:
45 stdout = stdout.decode('utf-8')
46 if p.returncode != 0:
47 return (file, {})
48  
49 proto_hash = {}
50 for line in stdout.splitlines():
51 if not re.match(r'^[\w:-]+$', line):
52 continue
53  
54 for proto in line.split(':'):
55 proto_hash[proto] = 1 + proto_hash.setdefault(proto, 0)
56  
57 return (file, proto_hash)
58 except KeyboardInterrupt:
59 return None
60  
61 def extract_protos_from_file(tshark, num_procs, max_files, cap_files, cap_hash, index_file_name):
62 pool = multiprocessing.Pool(num_procs)
63 results = [pool.apply_async(extract_protos_from_file_proces, [tshark, file]) for file in cap_files]
64 try:
65 for (cur_item_idx,result_async) in enumerate(results):
66 file_result = result_async.get()
67 action = "SKIPPED" if file_result[1] is {} else "PROCESSED"
68 print("%s [%u/%u] %s %u bytes" % (action, cur_item_idx+1, max_files, file_result[0], os.path.getsize(file_result[0])))
69 cap_hash.update(dict([file_result]))
70 except KeyboardInterrupt:
71 print("%s was interrupted by user" % (sys.argv[0]))
72 pool.terminate()
73 exit(1)
74  
75 index_file = open(index_file_name, "wb")
76 pickle.dump(cap_hash, index_file)
77 index_file.close()
78 exit(0)
79  
80 def dissect_file_process(tshark, tmpdir, file):
81 try:
82 (handle_o, tmpfile_o) = tempfile.mkstemp(suffix='_stdout', dir=tmpdir)
83 (handle_e, tmpfile_e) = tempfile.mkstemp(suffix='_stderr', dir=tmpdir)
84 cmd = [tshark, "-nxVr", file]
85 p = subprocess.Popen(cmd, stdout=handle_o, stderr=handle_e)
86 (stdout, stderr) = p.communicate()
87 if p.returncode == 0:
88 return (file, True, tmpfile_o, tmpfile_e)
89 else:
90 return (file, False, tmpfile_o, tmpfile_e)
91  
92 except KeyboardInterrupt:
93 return False
94  
95 finally:
96 os.close(handle_o)
97 os.close(handle_e)
98  
99 def dissect_files(tshark, tmpdir, num_procs, max_files, cap_files):
100 pool = multiprocessing.Pool(num_procs)
101 results = [pool.apply_async(dissect_file_process, [tshark, tmpdir, file]) for file in cap_files]
102 try:
103 for (cur_item_idx,result_async) in enumerate(results):
104 file_result = result_async.get()
105 action = "FAILED" if file_result[1] is False else "PASSED"
106 print("%s [%u/%u] %s %u bytes" % (action, cur_item_idx+1, max_files, file_result[0], os.path.getsize(file_result[0])))
107 except KeyboardInterrupt:
108 print("%s was interrupted by user" % (sys.argv[0]))
109 pool.terminate()
110 exit(1)
111  
112 def compare_files(tshark_bin, tmpdir, tshark_cmp, num_procs, max_files, cap_files):
113 pool = multiprocessing.Pool(num_procs)
114 results_bin = [pool.apply_async(dissect_file_process, [tshark_bin, tmpdir, file]) for file in cap_files]
115 results_cmp = [pool.apply_async(dissect_file_process, [tshark_cmp, tmpdir, file]) for file in cap_files]
116 try:
117 for (cur_item_idx,(result_async_bin, result_async_cmp)) in enumerate(zip(results_bin, results_cmp)):
118 file_result_bin = result_async_bin.get()
119 file_result_cmp = result_async_cmp.get()
120 if file_result_cmp[1] is False or file_result_bin[1] is False:
121 action = "FAILED (exitcode)"
122 if not filecmp.cmp(file_result_bin[2], file_result_cmp[2]):
123 action = "FAILED (stdout)"
124 if not filecmp.cmp(file_result_bin[3], file_result_cmp[3]):
125 action = "FAILED (stderr)"
126 else:
127 action = "PASSED"
128 os.remove(file_result_bin[2])
129 os.remove(file_result_cmp[2])
130 os.remove(file_result_bin[3])
131 os.remove(file_result_cmp[3])
132  
133 print("%s [%u/%u] %s %u bytes" % (action, cur_item_idx+1, max_files, file_result_bin[0], os.path.getsize(file_result_bin[0])))
134 print("%s [%u/%u] %s %u bytes" % (action, cur_item_idx+1, max_files, file_result_cmp[0], os.path.getsize(file_result_cmp[0])))
135 except KeyboardInterrupt:
136 print("%s was interrupted by user" % (sys.argv[0]))
137 pool.terminate()
138 exit(1)
139  
140 def list_all_proto(cap_hash):
141 proto_hash = {}
142 for files_hash in cap_hash.values():
143 for proto,count in files_hash.items():
144 proto_hash[proto] = count + proto_hash.setdefault(proto, 0)
145  
146 return proto_hash
147  
148 def list_all_files(cap_hash):
149 files = list(cap_hash.keys())
150 files.sort()
151  
152 return files
153  
154 def list_all_proto_files(cap_hash, proto_comma_delit):
155 protos = [ x.strip() for x in proto_comma_delit.split(',') ]
156 files = []
157 for (file, files_hash) in cap_hash.items():
158 for proto in files_hash.keys():
159 if proto in protos:
160 files.append(file)
161 break
162  
163 return files
164  
165 def index_file_action(options):
166 return options.list_all_proto or \
167 options.list_all_files or \
168 options.list_all_proto_files or \
169 options.dissect_files
170  
171 def find_capture_files(paths, cap_hash):
172 cap_files = []
173 for path in paths:
174 if os.path.isdir(path):
175 path = os.path.normpath(path)
176 for root, dirs, files in os.walk(path):
177 cap_files += [os.path.join(root, name) for name in files if os.path.join(root, name) not in cap_hash]
178 elif path not in cap_hash:
179 cap_files.append(path)
180 return cap_files
181  
182 def find_tshark_executable(bin_dir):
183 for file in ["tshark.exe", "tshark"]:
184 tshark = os.path.join(bin_dir, file)
185 if os.access(tshark, os.X_OK):
186 return tshark
187  
188 return None
189  
190 def main():
191 parser = OptionParser(usage="usage: %prog [options] index_file [file_1|dir_1 [.. file_n|dir_n]]")
192 parser.add_option("-d", "--dissect-files", dest="dissect_files", default=False, action="store_true",
193 help="Dissect all matching files")
194 parser.add_option("-m", "--max-files", dest="max_files", default=sys.maxsize, type="int",
195 help="Max number of files to process")
196 parser.add_option("-b", "--binary-dir", dest="bin_dir", default=os.getcwd(),
197 help="Directory containing tshark executable")
198 parser.add_option("-c", "--compare-dir", dest="compare_dir", default=None,
199 help="Directory containing tshark executable which is used for comparison")
200 parser.add_option("-j", dest="num_procs", default=multiprocessing.cpu_count(), type=int,
201 help="Max number of processes to spawn")
202 parser.add_option("-r", "--randomize", default=False, action="store_true",
203 help="Randomize the file list order")
204 parser.add_option("", "--list-all-proto", dest="list_all_proto", default=False, action="store_true",
205 help="List all protocols in index file")
206 parser.add_option("", "--list-all-files", dest="list_all_files", default=False, action="store_true",
207 help="List all files in index file")
208 parser.add_option("", "--list-all-proto-files", dest="list_all_proto_files", default=False,
209 metavar="PROTO_1[, .. PROTO_N]",
210 help="List all files in index file containing the given protocol")
211  
212 (options, args) = parser.parse_args()
213  
214 if len(args) == 0:
215 parser.error("index_file is a required argument")
216  
217 if len(args) == 1 and not index_file_action(options):
218 parser.error("one capture file/directory must be specified")
219  
220 if options.dissect_files and not options.list_all_files and not options.list_all_proto_files:
221 parser.error("--list-all-files or --list-all-proto-files must be specified")
222  
223 if options.dissect_files and not options.compare_dir is None:
224 parser.error("--dissect-files and --compare-dir cannot be specified at the same time")
225  
226 index_file_name = args.pop(0)
227 paths = args
228 cap_hash = {}
229 try:
230 index_file = open(index_file_name, "rb")
231 print("index file: %s [OPENED]" % index_file.name)
232 cap_hash = pickle.load(index_file)
233 index_file.close()
234 print("%d files" % len(cap_hash))
235 except IOError:
236 print("index file: %s [NEW]" % index_file_name)
237  
238 if options.list_all_proto:
239 print(list_all_proto(cap_hash))
240 exit(0)
241  
242 indexed_files = []
243 if options.list_all_files:
244 indexed_files = list_all_files(cap_hash)
245 print(indexed_files)
246  
247 if options.list_all_proto_files:
248 indexed_files = list_all_proto_files(cap_hash, options.list_all_proto_files)
249 print(indexed_files)
250  
251 tshark_bin = find_tshark_executable(options.bin_dir)
252 if not tshark_bin is None:
253 print("tshark: %s [FOUND]" % tshark_bin)
254 else:
255 print("tshark: %s [MISSING]" % tshark_bin)
256 exit(1)
257  
258 if not options.compare_dir is None:
259 tshark_cmp = find_tshark_executable(options.compare_dir)
260 if not tshark_cmp is None:
261 print("tshark: %s [FOUND]" % tshark_cmp)
262 else:
263 print("tshark: %s [MISSING]" % tshark_cmp)
264 exit(1)
265  
266 if options.dissect_files or options.compare_dir:
267 cap_files = indexed_files
268 elif options.list_all_proto_files or options.list_all_files:
269 exit(0)
270 else:
271 cap_files = find_capture_files(paths, cap_hash)
272  
273 if options.randomize:
274 random.shuffle(cap_files)
275 else:
276 cap_files.sort()
277  
278 options.max_files = min(options.max_files, len(cap_files))
279 print("%u total files, %u working files" % (len(cap_files), options.max_files))
280 cap_files = cap_files[:options.max_files]
281 if options.compare_dir or options.dissect_files:
282 tmpdir = tempfile.mkdtemp()
283 print("Temporary working dir: %s" % tmpdir)
284 try:
285 if options.compare_dir:
286 compare_files(tshark_bin, tmpdir, tshark_cmp, options.num_procs, options.max_files, cap_files)
287 elif options.dissect_files:
288 dissect_files(tshark_bin, tmpdir, options.num_procs, options.max_files, cap_files)
289 else:
290 extract_protos_from_file(tshark_bin, options.num_procs, options.max_files, cap_files, cap_hash, index_file_name)
291 finally:
292 # Dissection may result in a non-empty directory.
293 if options.compare_dir:
294 os.rmdir(tmpdir)
295 if __name__ == "__main__":
296 main()