Write our NSIS installer to the build directory.
[metze/wireshark/wip.git] / doc / extcap_example.py
1 #!/usr/bin/env python
2
3 # Copyright 2014 Roland Knall <rknall [AT] gmail.com>
4 #
5 # Wireshark - Network traffic analyzer
6 # By Gerald Combs <gerald@wireshark.org>
7 # Copyright 1998 Gerald Combs
8 #
9 # SPDX-License-Identifier: GPL-2.0-or-later
10 #
11
12 """
13 This is a generic example, which produces pcap packages every n seconds, and
14 is configurable via extcap options.
15
16 @note
17 {
18 To use this script on Windows, please generate an extcap_example.bat inside
19 the extcap folder, with the following content:
20
21 -------
22 @echo off
23 <Path to python interpreter> <Path to script file> %*
24 -------
25
26 Windows is not able to execute Python scripts directly, which also goes for all
27 other script-based formates beside VBScript
28 }
29
30 """
31
32 from __future__ import print_function
33 import os
34 import sys
35 import signal
36 import re
37 import argparse
38 import time
39 import struct
40 import binascii
41 from threading import Thread
42
43 ERROR_USAGE          = 0
44 ERROR_ARG            = 1
45 ERROR_INTERFACE      = 2
46 ERROR_FIFO           = 3
47 ERROR_DELAY          = 4
48
49 CTRL_CMD_INITIALIZED = 0
50 CTRL_CMD_SET         = 1
51 CTRL_CMD_ADD         = 2
52 CTRL_CMD_REMOVE      = 3
53 CTRL_CMD_ENABLE      = 4
54 CTRL_CMD_DISABLE     = 5
55 CTRL_CMD_STATUSBAR   = 6
56 CTRL_CMD_INFORMATION = 7
57 CTRL_CMD_WARNING     = 8
58 CTRL_CMD_ERROR       = 9
59
60 CTRL_ARG_MESSAGE     = 0
61 CTRL_ARG_DELAY       = 1
62 CTRL_ARG_VERIFY      = 2
63 CTRL_ARG_BUTTON      = 3
64 CTRL_ARG_HELP        = 4
65 CTRL_ARG_RESTORE     = 5
66 CTRL_ARG_LOGGER      = 6
67 CTRL_ARG_NONE        = 255
68
69 initialized = False
70 message = ''
71 delay = 0.0
72 verify = False
73 button = False
74 button_disabled = False
75
76 """
77 This code has been taken from http://stackoverflow.com/questions/5943249/python-argparse-and-controlling-overriding-the-exit-status-code - originally developed by Rob Cowie http://stackoverflow.com/users/46690/rob-cowie
78 """
79 class ArgumentParser(argparse.ArgumentParser):
80         def _get_action_from_name(self, name):
81                 """Given a name, get the Action instance registered with this parser.
82                 If only it were made available in the ArgumentError object. It is
83                 passed as it's first arg...
84                 """
85                 container = self._actions
86                 if name is None:
87                         return None
88                 for action in container:
89                         if '/'.join(action.option_strings) == name:
90                                 return action
91                         elif action.metavar == name:
92                                 return action
93                         elif action.dest == name:
94                                 return action
95
96         def error(self, message):
97                 exc = sys.exc_info()[1]
98                 if exc:
99                         exc.argument = self._get_action_from_name(exc.argument_name)
100                         raise exc
101                 super(ArgumentParser, self).error(message)
102
103 #### EXTCAP FUNCTIONALITY
104
105 """@brief Extcap configuration
106 This method prints the extcap configuration, which will be picked up by the
107 interface in Wireshark to present a interface specific configuration for
108 this extcap plugin
109 """
110 def extcap_config(interface):
111         args = []
112         values = []
113
114         args.append ( (0, '--delay', 'Time delay', 'Time delay between packages', 'integer', '{range=1,15}{default=5}') )
115         args.append ( (1, '--message', 'Message', 'Package message content', 'string', '{required=true}{placeholder=Please enter a message here ...}') )
116         args.append ( (2, '--verify', 'Verify', 'Verify package content', 'boolflag', '{default=yes}') )
117         args.append ( (3, '--remote', 'Remote Channel', 'Remote Channel Selector', 'selector', ''))
118         args.append ( (4, '--fake_ip', 'Fake IP Address', 'Use this ip address as sender', 'string', '{save=false}{validation=\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b}'))
119         args.append ( (5, '--ltest', 'Long Test', 'Long Test Value', 'long', '{default=123123123123123123}'))
120         args.append ( (6, '--d1test', 'Double 1 Test', 'Long Test Value', 'double', '{default=123.456}'))
121         args.append ( (7, '--d2test', 'Double 2 Test', 'Long Test Value', 'double', '{default= 123,456}'))
122         args.append ( (8, '--password', 'Password', 'Package message password', 'password', '') )
123         args.append ( (9, '--ts', 'Start Time', 'Capture start time', 'timestamp', '') )
124         args.append ( (10, '--logfile', 'Log File Test', 'The Log File Test', 'fileselect', '') )
125         args.append ( (11, '--radio', 'Radio Test', 'Radio Test Value', 'radio', '') )
126         args.append ( (12, '--multi', 'MultiCheck Test', 'MultiCheck Test Value', 'multicheck', '') )
127
128         values.append ( (3, "if1", "Remote1", "true" ) )
129         values.append ( (3, "if2", "Remote2", "false" ) )
130
131         values.append ( (11, "r1", "Radio1", "false" ) )
132         values.append ( (11, "r2", "Radio2", "true" ) )
133
134         values.append ( (12, "m1", "MultiCheck1", "false" ) )
135         values.append ( (12, "m2", "MultiCheck2", "false" ) )
136
137         for arg in args:
138                 print ("arg {number=%d}{call=%s}{display=%s}{tooltip=%s}{type=%s}%s" % arg)
139
140         for value in values:
141                 print ("value {arg=%d}{value=%s}{display=%s}{default=%s}" % value)
142
143
144 def extcap_interfaces():
145         print ("extcap {version=1.0}{help=http://www.wireshark.org}{display=Example extcap interface}")
146         print ("interface {value=example1}{display=Example interface 1 for extcap}")
147         print ("interface {value=example2}{display=Example interface 2 for extcap}")
148         print ("control {number=%d}{type=string}{display=Message}{tooltip=Package message content. Must start with a capital letter.}{placeholder=Enter package message content here ...}{validation=^[A-Z]+}" % CTRL_ARG_MESSAGE)
149         print ("control {number=%d}{type=selector}{display=Time delay}{tooltip=Time delay between packages}" % CTRL_ARG_DELAY)
150         print ("control {number=%d}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}" % CTRL_ARG_VERIFY)
151         print ("control {number=%d}{type=button}{display=Turn on}{tooltip=Turn on or off}" % CTRL_ARG_BUTTON)
152         print ("control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Show help}" % CTRL_ARG_HELP)
153         print ("control {number=%d}{type=button}{role=restore}{display=Restore}{tooltip=Restore default values}" % CTRL_ARG_RESTORE)
154         print ("control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}" % CTRL_ARG_LOGGER)
155         print ("value {control=%d}{value=1}{display=1}" % CTRL_ARG_DELAY)
156         print ("value {control=%d}{value=2}{display=2}" % CTRL_ARG_DELAY)
157         print ("value {control=%d}{value=3}{display=3}" % CTRL_ARG_DELAY)
158         print ("value {control=%d}{value=4}{display=4}" % CTRL_ARG_DELAY)
159         print ("value {control=%d}{value=5}{display=5}{default=true}" % CTRL_ARG_DELAY)
160         print ("value {control=%d}{value=60}{display=60}" % CTRL_ARG_DELAY)
161
162
163 def extcap_dlts(interface):
164         if ( interface == '1' ):
165                 print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
166         elif ( interface == '2' ):
167                 print ("dlt {number=148}{name=USER1}{display=Demo Implementation for Extcap}")
168
169 def validate_capture_filter(capture_filter):
170         if capture_filter != "filter" and capture_filter != "valid":
171                 print("Illegal capture filter")
172
173 """
174
175 ### FAKE DATA GENERATOR
176
177 Extcap capture routine
178  This routine simulates a capture by any kind of user defined device. The parameters
179  are user specified and must be handled by the extcap.
180
181  The data captured inside this routine is fake, so change this routine to present
182  your own input data, or call your own capture program via Popen for example. See
183
184  for more details.
185
186 """
187 def unsigned(n):
188         return int(n) & 0xFFFFFFFF
189
190 def pcap_fake_header():
191
192         header = bytearray()
193         header += struct.pack('<L', int ('a1b2c3d4', 16 ))
194         header += struct.pack('<H', unsigned(2) ) # Pcap Major Version
195         header += struct.pack('<H', unsigned(4) ) # Pcap Minor Version
196         header += struct.pack('<I', int(0)) # Timezone
197         header += struct.pack('<I', int(0)) # Accurancy of timestamps
198         header += struct.pack('<L', int ('0000ffff', 16 )) # Max Length of capture frame
199         header += struct.pack('<L', unsigned(1)) # Ethernet
200         return header
201
202 # Calculates and returns the IP checksum based on the given IP Header
203 def ip_checksum(iph):
204         #split into bytes
205         words = splitN(''.join(iph.split()),4)
206         csum = 0;
207         for word in words:
208                 csum += int(word, base=16)
209         csum += (csum >> 16)
210         csum = csum & 0xFFFF ^ 0xFFFF
211         return csum
212
213 def pcap_fake_package ( message, fake_ip ):
214
215         pcap = bytearray()
216         #length = 14 bytes [ eth ] + 20 bytes [ ip ] + messagelength
217
218         caplength = len(message) + 14 + 20
219         timestamp = int(time.time())
220
221         pcap += struct.pack('<L', unsigned(timestamp ) ) # timestamp seconds
222         pcap += struct.pack('<L', 0x00  ) # timestamp nanoseconds
223         pcap += struct.pack('<L', unsigned(caplength ) ) # length captured
224         pcap += struct.pack('<L', unsigned(caplength ) ) # length in frame
225
226 # ETH
227         pcap += struct.pack('h', 0 ) # source mac
228         pcap += struct.pack('h', 0 ) # source mac
229         pcap += struct.pack('h', 0 ) # source mac
230         pcap += struct.pack('h', 0 ) # dest mac
231         pcap += struct.pack('h', 0 ) # dest mac
232         pcap += struct.pack('h', 0 ) # dest mac
233         pcap += struct.pack('<h', unsigned(8 )) # protocol (ip)
234
235 # IP
236         pcap += struct.pack('b', int ( '45', 16 )) # IP version
237         pcap += struct.pack('b', int ( '0', 16 )) #
238         pcap += struct.pack('>H', unsigned(len(message)+20) ) # length of data + payload
239         pcap += struct.pack('<H', int ( '0', 16 )) # Identification
240         pcap += struct.pack('b', int ( '40', 16 )) # Don't fragment
241         pcap += struct.pack('b', int ( '0', 16 )) # Fragment Offset
242         pcap += struct.pack('b', int ( '40', 16 ))
243         pcap += struct.pack('B', 0xFE ) # Protocol (2 = unspecified)
244         pcap += struct.pack('<H', int ( '0000', 16 )) # Checksum
245
246         parts = fake_ip.split('.')
247         ipadr = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
248         pcap += struct.pack('>L', ipadr ) # Source IP
249         pcap += struct.pack('>L', int ( '7F000001', 16 )) # Dest IP
250
251         pcap += message
252         return pcap
253
254 def control_read(fn):
255         try:
256                 header = fn.read(6)
257                 sp, _, length, arg, typ = struct.unpack('>sBHBB', header)
258                 if length > 2:
259                         payload = fn.read(length - 2)
260                 else:
261                         payload = ''
262                 return arg, typ, payload
263         except:
264                 return None, None, None
265
266 def control_read_thread(control_in, fn_out):
267         global initialized, message, delay, verify, button, button_disabled
268         with open(control_in, 'rb', 0 ) as fn:
269                 arg = 0
270                 while arg != None:
271                         arg, typ, payload = control_read(fn)
272                         log = ''
273                         if typ == CTRL_CMD_INITIALIZED:
274                                 initialized = True
275                         elif arg == CTRL_ARG_MESSAGE:
276                                 message = payload
277                                 log = "Message = " + payload
278                         elif arg == CTRL_ARG_DELAY:
279                                 delay = float(payload)
280                                 log = "Time delay = " + payload
281                         elif arg == CTRL_ARG_VERIFY:
282                                 # Only read this after initialized
283                                 if initialized:
284                                         verify = (payload[0] != '\0')
285                                         log = "Verify = " + str(verify)
286                                         control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_STATUSBAR, "Verify changed")
287                         elif arg == CTRL_ARG_BUTTON:
288                                 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_DISABLE, "")
289                                 button_disabled = True
290                                 if button == True:
291                                         control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn on")
292                                         button = False
293                                         log = "Button turned off"
294                                 else:
295                                         control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn off")
296                                         button = True
297                                         log = "Button turned on"
298
299                         if len(log) > 0:
300                                 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log + "\n")
301
302 def control_write(fn, arg, typ, payload):
303         packet = bytearray()
304         packet += struct.pack('>sBHBB', 'T', 0, len(payload) + 2, arg, typ)
305         packet += payload
306         fn.write(packet)
307
308 def control_write_defaults(fn_out):
309         global initialized, message, delay, verify
310
311         while not initialized:
312                 time.sleep(.1)  # Wait for initial control values
313
314         # Write startup configuration to Toolbar controls
315         control_write(fn_out, CTRL_ARG_MESSAGE, CTRL_CMD_SET, message)
316         control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(int(delay)))
317         control_write(fn_out, CTRL_ARG_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
318
319         for i in range(1,16):
320                 item = bytearray()
321                 item += str(i) + struct.pack('B', 0) + str(i) + " sec"
322                 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_ADD, item)
323
324         control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_REMOVE, str(60))
325
326 def extcap_capture(interface, fifo, control_in, control_out, in_delay, in_verify, in_message, remote, fake_ip):
327         global message, delay, verify, button_disabled
328         delay = in_delay if in_delay != 0 else 5
329         message = in_message
330         verify = in_verify
331         counter = 1
332         fn_out = None
333
334         with open(fifo, 'wb', 0 ) as fh:
335                 fh.write (pcap_fake_header())
336
337                 if control_out != None:
338                         fn_out = open(control_out, 'wb', 0)
339                         control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_SET, "Log started at " + time.strftime("%c") + "\n")
340
341                 if control_in != None:
342                         # Start reading thread
343                         thread = Thread(target = control_read_thread, args = (control_in, fn_out))
344                         thread.start()
345
346                 if fn_out != None:
347                         control_write_defaults(fn_out)
348
349                 while True:
350                         if fn_out != None:
351                                 log = "Received packet #" + str(counter) + "\n"
352                                 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log)
353                                 counter = counter + 1
354
355                                 if button_disabled == True:
356                                         control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_ENABLE, "")
357                                         control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_INFORMATION, "Turn action finished.")
358                                         button_disabled = False
359
360                         out = ("%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify )).encode("utf8")
361                         fh.write (pcap_fake_package(out, fake_ip))
362                         time.sleep(delay)
363
364         thread.join()
365         if fn_out != None:
366                 fn_out.close()
367
368 def extcap_close_fifo(fifo):
369         # This is apparently needed to workaround an issue on Windows/macOS
370         # where the message cannot be read. (really?)
371         fh = open(fifo, 'wb', 0 )
372         fh.close()
373
374 ####
375
376 def usage():
377         print ( "Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --extcap-capture-filter | --fifo>" % sys.argv[0] )
378
379 if __name__ == '__main__':
380         interface = ""
381
382         # Capture options
383         delay = 0
384         message = ""
385         fake_ip = ""
386         ts = 0
387
388         parser = ArgumentParser(
389                 prog="Extcap Example",
390                 description="Extcap example program for python"
391                 )
392
393         # Extcap Arguments
394         parser.add_argument("--capture", help="Start the capture routine", action="store_true" )
395         parser.add_argument("--extcap-interfaces", help="Provide a list of interfaces to capture from", action="store_true")
396         parser.add_argument("--extcap-interface", help="Provide the interface to capture from")
397         parser.add_argument("--extcap-dlts", help="Provide a list of dlts for the given interface", action="store_true")
398         parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
399         parser.add_argument("--extcap-capture-filter", help="Used together with capture to provide a capture filter")
400         parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to")
401         parser.add_argument("--extcap-control-in", help="Used to get control messages from toolbar")
402         parser.add_argument("--extcap-control-out", help="Used to send control messages to toolbar")
403
404         # Interface Arguments
405         parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" )
406         parser.add_argument("--delay", help="Demonstrates an integer variable", type=int, default=0, choices=[0, 1, 2, 3, 4, 5, 6] )
407         parser.add_argument("--remote", help="Demonstrates a selector choice", default="if1", choices=["if1", "if2"] )
408         parser.add_argument("--message", help="Demonstrates string variable", nargs='?', default="" )
409         parser.add_argument("--fake_ip", help="Add a fake sender IP adress", nargs='?', default="127.0.0.1" )
410         parser.add_argument("--ts", help="Capture start time", action="store_true" )
411
412         try:
413                 args, unknown = parser.parse_known_args()
414         except argparse.ArgumentError as exc:
415                 print( "%s: %s" % ( exc.argument.dest, exc.message ), file=sys.stderr)
416                 fifo_found = 0
417                 fifo = ""
418                 for arg in sys.argv:
419                         if (arg == "--fifo" or arg == "--extcap-fifo") :
420                                 fifo_found = 1
421                         elif ( fifo_found == 1 ):
422                                 fifo = arg
423                                 break
424                 extcap_close_fifo(fifo)
425                 sys.exit(ERROR_ARG)
426
427         if ( len(sys.argv) <= 1 ):
428                 parser.exit("No arguments given!")
429
430         if ( args.extcap_interfaces == False and args.extcap_interface == None ):
431                 parser.exit("An interface must be provided or the selection must be displayed")
432         if ( args.extcap_capture_filter and not args.capture ):
433                 validate_capture_filter(args.extcap_capture_filter)
434                 sys.exit(0)
435
436         if ( args.extcap_interfaces == True or args.extcap_interface == None ):
437                 extcap_interfaces()
438                 sys.exit(0)
439
440         if ( len(unknown) > 1 ):
441                 print("Extcap Example %d unknown arguments given" % len(unknown) )
442
443         m = re.match ( 'example(\d+)', args.extcap_interface )
444         if not m:
445                 sys.exit(ERROR_INTERFACE)
446         interface = m.group(1)
447
448         message = args.message
449         if ( args.message == None or len(args.message) == 0 ):
450                 message = "Extcap Test"
451
452         fake_ip = args.fake_ip
453         if ( args.fake_ip == None or len(args.fake_ip) < 7 or len(args.fake_ip.split('.')) != 4 ):
454                 fake_ip = "127.0.0.1"
455
456         ts = args.ts
457
458         if args.extcap_config:
459                 extcap_config(interface)
460         elif args.extcap_dlts:
461                 extcap_dlts(interface)
462         elif args.capture:
463                 if args.fifo is None:
464                         sys.exit(ERROR_FIFO)
465                 # The following code demonstrates error management with extcap
466                 if args.delay > 5:
467                         print("Value for delay [%d] too high" % args.delay, file=sys.stderr)
468                         extcap_close_fifo(args.fifo)
469                         sys.exit(ERROR_DELAY)
470
471                 try:
472                         extcap_capture(interface, args.fifo, args.extcap_control_in, args.extcap_control_out, args.delay, args.verify, message, args.remote, fake_ip)
473                 except KeyboardInterrupt:
474                         pass
475         else:
476                 usage()
477                 sys.exit(ERROR_USAGE)