1#!/usr/bin/perl
2#
3# Chaosreader can trace TCP/UDP/... sessions and fetch application data
4#  from tcpdump or snoop logs. This is like an "any-snarf" program, it will
5#  fetch telnet sessions, FTP files, HTTP transfers (HTML, GIF, JPEG, ...),
6#  SMTP emails, etc ... from the captured data inside the network traffic
7#  logs. It creates a html index file that links to all the session details,
8#  including realtime replay programs for telnet, rlogin or IRC sessions;
9#  and reports such as image reports and HTTP GET/POST content reports.
10#  It also creates replay programs for telnet sessions, so that you can
11#  play them back in realtime (or even different speeds).
12#
13# Chaosreader can also run in standalone mode - where it invokes tcpdump or
14#  snoop (if they are available) to create the log files and then processes
15#  them.
16#
17#
18# 29-May-2004, ver 0.94  (check for new versions, http://www.brendangregg.com)
19#			 (or run a web search for "chaosreader")
20#
21#
22# QUICK USAGE:
23#	tcpdump -s9000 -w out1; chaosreader out1; netscape index.html
24#  or,
25#	snoop -o out1; chaosreader out1; netscape index.html
26#  or,
27#	ethereal (save as "out1"); chaosreader out1; netscape index.html
28#  or,
29#	chaosreader -s 5; netscape index.html
30#
31#
32# USAGE: chaosreader [-aehikqrvxAHIRTUXY] [-D dir]
33#                    [-b port[,...]] [-B port[,...]]
34#                    [-j IPaddr[,...]] [-J IPaddr[,...]]
35#                    [-l port[,...]] [-L port[,...]] [-m bytes[k]]
36#                    [-M bytes[k]] [-o "time"|"size"|"type"|"ip"]
37#                    [-p port[,...]] [-P port[,...]]
38#                    infile [infile2 ...]
39#
40#        chaosreader -s [mins] | -S [mins[,count]]
41# 	           [-z] [-f 'filter']
42#
43#    chaosreader           # Create application session files, indexes
44#
45#    -a, --application     # Create application session files (default)
46#    -e, --everything      # Create HTML 2-way & hex files for everything
47#    -h                    # Print a brief help
48#    --help                # Print verbose help (this) and version
49#    --help2               # Print massive help
50#    -i, --info            # Create info file
51#    -q, --quiet           # Quiet, no output to screen
52#    -r, --raw             # Create raw files
53#    -v, --verbose         # Verbose - Create ALL files .. (except -e)
54#    -x, --index           # Create index files (default)
55#    -A, --noapplication   # Exclude application session files
56#    -H, --hex             # Include hex dumps (slow)
57#    -I, --noinfo          # Exclude info files
58#    -R, --noraw           # Exclude raw files
59#    -T, --notcp           # Exclude TCP traffic
60#    -U, --noudp           # Exclude UDP traffic
61#    -Y, --noicmp          # Exclude ICMP traffic
62#    -X, --noindex         # Exclude index files
63#    -k, --keydata         # Create extra files for keystroke analysis
64#    -D dir    --dir dir        # Output all files to this directory
65#    -b 25,79  --playtcp 25,79  # replay these TCP ports as well (playback)
66#    -B 36,42  --playudp 36,42  # replay these UDP ports as well (playback)
67#    -l 7,79   --htmltcp 7,79   # Create HTML for these TCP ports as well
68#    -L 7,123  --htmludp 7,123  # Create HTML for these UDP ports as well
69#    -m 1k     --min 1k         # Min size of connection to save ("k" for Kb)
70#    -M 1024k  --max 1k         # Max size of connection to save ("k" for Kb)
71#    -o size   --sort size      # sort Order: time/size/type/ip (Default time)
72#    -p 21,23  --port 21,23     # Only examine these ports (TCP & UDP)
73#    -P 80,81  --noport 80,81   # Exclude these ports (TCP & UDP)
74#    -s 5      --runonce 5      # Standalone. Run tcpdump/snoop for 5 mins.
75#    -S 5,10   --runmany 5,10   # Standalone, many. 10 samples of 5 mins each.
76#    -S 5      --runmany 5      # Standalone, endless. 5 min samples forever.
77#    -z        --runredo        # Standalone, redo. Rereads last run's logs.
78#    -j 10.1.2.1  --ipaddr 10.1.2.1    # Only examine these IPs
79#    -J 10.1.2.1  --noipaddr 10.1.2.1  # Exclude these IPs
80#    -f 'port 7'  --filter 'port 7'    # With standalone, use this dump filter.
81#
82# eg1,
83#      tcpdump -s9000 -w output1        # create tcpdump capture file
84#      chaosreader output1              # extract recognised sessions, or,
85#      chaosreader -ve output1          # gimme everything, or,
86#      chaosreader -p 20,21,23 output1  # only ftp and telnet...
87# eg2,
88#      snoop -o output1                 # create snoop capture file instead
89#      chaosreader output1              # extract recognised sessions...
90# eg3,
91#      chaosreader -S 2,5      # Standalone, sniff network 5 times for 2 mins
92#                              # each. View index.html for progress (or .text)
93#
94# Output Files: Many will be created, run this in a clean directory.
95#  Short example,
96#   index.html			Html index (full details)
97#   index.text			Text index
98#   index.file			File index for standalone redo mode
99#   image.html			HTML report of images
100#   getpost.html		HTML report of HTTP GET/POST requests
101#   session_0001.info		Info file describing TCP session #1
102#   session_0001.telnet.html	HTML coloured 2-way capture (time sorted)
103#   session_0001.telnet.raw	Raw data 2-way capture (time sorted)
104#   session_0001.telnet.raw1	Raw 1-way capture (assembeled) server->client
105#   session_0001.telnet.raw2	Raw 1-way capture (assembeled) client->server
106#   session_0002.web.html	HTML coloured 2-way
107#   session_0002.part_01.html	HTTP portion of the above, a HTML file
108#   session_0003.web.html	HTML coloured 2-way
109#   session_0003.part_01.jpeg	HTTP portion of the above, a JPEG file
110#   session_0004.web.html	HTML coloured 2-way
111#   session_0004.part_01.gif	HTTP portion of the above, a GIF file
112#   session_0005.part_01.ftp-data.gz	An FTP transfer, a gz file.
113#   ...
114#  The convention is,
115#   session_*		TCP Sessions
116#   stream_*		UDP Streams
117#   icmp_*		ICMP packets
118#   index.html		HTML Index
119#   index.text		Text Index
120#   index.file		File Index for standalone redo mode only
121#   image.html		HTML report of images
122#   getpost.html	HTML report of HTTP GET/POST requests
123#   *.info		Info file describing the Session/Stream
124#   *.raw		Raw data 2-way capture (time sorted)
125#   *.raw1 		Raw 1-way capture (assembeled) server->client
126#   *.raw2		Raw 1-way capture (assembeled) client->server
127#   *.replay		Session replay program (perl)
128#   *.partial.*		Partial capture (tcpdump/snoop were aware of drops)
129#   *.hex.html		2-way Hex dump, rendered in coloured HTML
130#   *.hex.text		2-way Hex dump in plain text
131#   *.X11.replay	X11 replay script (talks X11)
132#   *.textX11.replay	X11 communicated text replay script (text only)
133#   *.textX11.html	2-way text report, rendered in red/blue HTML
134#   *.keydata		Keystroke delay data file. Used for SSH analysis.
135#
136# Modes:
137#  * Normal - eg "chaosreader infile", this is where a tcpdump/snoop file
138#    was created previously and chaosreader reads and processes it.
139#  * Standalone, once - eg "chaosreader -s 10", this is where chaosreader
140#    runs tcpdump/snoop and generates the log file, in this case for 10 i
141#    minutes, and then processes the result. Some OS's may not have
142#    tcpdump or snoop available so this will not work (instead you may be
143#    able to get Ethereal, run it, save to a file, then use normal mode).
144#    There is a master index.html and the report index.html in a sub dir,
145#    which is of the format out_YYYYMMDD-hhmm, eg "out_20031003-2221".
146#  * Standalone, many - eg "chaosreader -S 5,12", this is where chaosreader
147#    runs tcpdump/snoop and generates many log files, in this case it
148#    samples 12 times for 5 minutes each. While this is running, the master
149#    index.html can be viewed to watch progress, which links to minor
150#    index.html reports in each sub directory.
151#  * Standalone, redo - eg "chaosreader -ve -z", (the -z), this is where
152#    a standalone capture was previously performed - and now you would like
153#    to reprocess the logs - perhaps with different options (in this case,
154#    "-ve"). It reads index.file to determine which capture logs to read.
155#  * Standalone, endless - eg "chaosreader -S 5", like standalone many -
156#    but runs forever (if you ever had the need?). Watch your disk space!
157#
158# Note: this is a work in progress, some of the code is a little unpolished.
159#
160# Advice:
161#  * Run chaosreader in an empty directory.
162#  * Create small packet dumps. Chaosreader uses around 5x the dump size
163#    in memory. A 100Mb file could need 500Mb of RAM to process.
164#  * Your tcpdump may allow "-s0" (entire packet) instead of "-s9000".
165#  * Beware of using too much disk space, especially standalone mode.
166#  * If you capture too many small connections giving a huge index.html,
167#    try using the -m option to ignore small connections. eg "-m 1k".
168#  * snoop logs may actually work better. Snoop logs are based on RFC1761,
169#    however there are many varients of tcpdump/libpcap and this program
170#    cannot read them all. If you have Ethereal you can create snoop logs
171#    during the "save as" option. On Solaris use "snoop -o logfile".
172#  * tcpdump logs may not be portable between OSs that use different sized
173#    timestamps or endian.
174#  * Logs are best created in a memory filesystem for speed, usually /tmp.
175#  * For X11 or VNC playbacks, first practise by replaying a recent captured
176#    session of your own. The biggest problem is colour depth, your screen
177#    must match the capture. For X11 check authentication (xhost +), for
178#    VNC check the viewers options (-8bit, "Hextile", ...)
179#  * SSH analysis can be performed with the "sshkeydata" program as
180#    demonstrated on http://www.brendangregg.com/sshanalysis.html .
181#    chaosreader provides the input files (*.keydata) that sshkeydata
182#    analyses.
183#
184# Bugs: The following assumptions may cause problems (check for new vers);
185#  * A lower port number = the service type. Eg with ports 31247 and 23,
186#    the actual type of session is telnet (23). This may not work for
187#    some things (eg, VNC).
188#  * Time based order is more important for 2-way sessions (eg telnet),
189#    SEQ order is more import for 1-way transfers (eg ftp-data).
190#  * One particular TCP session isn't active for long enough that the SEQ
191#    number loops (or even wraps).
192#
193# WARNING: Please don't use this software for anything illegal. That definition
194#  differs for every country, please check the law first.
195#  This is a great network troubleshooting and development tool, not a
196#  "cracking" or "hacking" tool - a misidentification that could render owning
197#  this software illegal in some countries.
198#
199# SEE ALSO: ethereal (GUI packet viewer), dsniff (sniffing toolkit)
200#
201# COPYRIGHT: Copyright (c) 2003, 2004 Brendan Gregg.
202#
203#  This program is free software; you can redistribute it and/or
204#  modify it under the terms of the GNU General Public License
205#  as published by the Free Software Foundation; either version 2
206#  of the License, or (at your option) any later version.
207#
208#  This program is distributed in the hope that it will be useful,
209#  but WITHOUT ANY WARRANTY; without even the implied warranty of
210#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
211#  GNU General Public License for more details.
212#
213#  You should have received a copy of the GNU General Public License
214#  along with this program; if not, write to the Free Software Foundation,
215#  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
216#
217#  (http://www.gnu.org/copyleft/gpl.html)
218#
219# Author: Brendan Gregg  [Sydney, Australia]
220#
221# Todo:
222#  * Rework code to improve structure.
223#  * Add more application protocol filters. ARP, RARP.
224#  * Ensure current application filters are robust (more testing).
225#  * Process captured filenames from FTP, HTTP and NFS transfers.
226#  * Add more file types (magic numbers/frequency analysis).
227#  * Process more IPv6 extension headers, ICMP types.
228#
229# 28-Sep-2003	Brendan Gregg	Began writing this.
230# 08-Oct-2003	   "      "	Released version 0.7 beta
231# 09-Oct-2003	   "      "  	Added telnet replays
232# 12-Oct-2003	   "      "  	Added IRC ports and replays
233# 19-Oct-2003	   "      "  	Made code more robust on different OSs
234# 01-Nov-2003	   "	  "	Code cleanup, complex data types, IPv6, ICMP
235# 03-Nov-2003	   "	  "	Added Standalone mode, standalone redo, ...
236# 05-Nov-2003	   "	  "	Added Image indexes, GETPOST indexes
237# 15-Nov-2003	   "	  "	Added HTTP proxy style log, hex dumps
238# 27-Jan-2004	   "	  "	Released experimental X11 & VNC processing
239# 30-Mar-2004	   "	  "	802.11b, sorts, less RAM used, tun packets.
240# 01-May-2004	   "	  "	CLI enhanced, faster, SSH analysis.
241
242
243use Getopt::Long;
244use Benchmark;
245
246
247#####################
248# --- Variables ---
249#
250
251#
252#  Some defaults
253#
254$PERL = "/usr/bin/perl";		# perl path for replay scripts
255$integerSize = length(pack('I',0));	# can make a difference for tcpdumps
256$the_date = scalar localtime();		# this is printed in the reports
257$WRAP = 108;				# wordwrap chars
258$BM = 0;				# benchmark counter
259$| = 1;					# flush output
260
261#
262#  The following is needed for old perl5 multiline matching. New perl5 uses
263#  a "/s" on the RE (which is used in this program as well).
264#
265$* = 1;					# old perl5
266
267#
268#  These ports have been selected to be saved as coloured 2-way HTML files
269#
270@Save_As_HTML_TCP_Ports = (21,23,25,79,80,109,110,119,143,513,514,1080,
271 3128,4110,5000,5555,6660,6665,6666,6667,6668,7000,8000,8080,9000);
272@Save_As_HTML_UDP_Ports = (53);
273
274#
275#  These ports have been selected to be saved as realtime playback scripts
276#  (telnet, login, and numerous IRC ports)
277#
278@Save_As_TCP_Playback_Ports = (23,513,4110,5000,5555,6660,6666,6667,
279 6668,7000,8000,9000);
280@Save_As_UDP_Playback_Ports = (7);
281
282#
283#  These are the X11 ports to save as X11 playback scripts
284#
285@Save_As_X11_Playback_Ports = (6000,6001,6002,6003,6004,6005,6006,6007);
286
287#
288#  These X11 ports will have the text saved as coloured 2-way HTML files
289#
290@Save_As_HTML_X11_Ports = (6000,6001,6002,6003,6004,6005,6006,6007);
291
292#
293#  These are the VNC ports to save as VNC playback scripts
294#
295@Save_As_VNC_Playback_Ports = (5900,5901,5902,5903,5904,5905,5906,5907);
296
297#
298# --- Arguments ---
299#
300&Process_Command_Line_Arguments();
301
302### Record program start
303$Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
304$Bench{$BM}{text} = "Program Start";
305
306#
307#  Load some lookup tables for number -> name translations.
308#
309&Load_Etc_Services();
310&Set_IP_Protocols();
311&Set_ICMP_Types();
312&Set_Result_Names();
313&Set_X11_Codes();
314&Set_X11_KeyCodes();
315&Set_VNC_Codes();
316
317
318###########################
319# --- MODE 1 - Normal --- #
320###########################
321
322#
323#  Process log files,
324#
325if ($Arg{normal}) {
326	#
327	#  Initial values
328	#
329	$frame = 0; $number = 0;
330	%IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = ();
331
332	### Print version
333	&Print_Welcome();
334
335	######################################
336	# --- INPUT - Read Packet Log(s) ---
337	#
338
339	foreach $filename (@{$Arg{infiles}}) {
340		#
341		#  Check input file type and Open
342		#
343		&Open_Input_File($filename);
344
345		#
346		#  Read though the entire input file, saving all packet
347		#  data in memory (mainly %TCP and %UDP).
348		#
349		&Read_Input_File();
350	}
351
352
353	#############################################
354	# --- OUTPUT - Process TCP/UDP Sessions ---
355	#
356
357	### cd to output
358	&Chdir($Arg{output_dir});
359	&Print_Header2();
360
361	### Determine Session and Stream time order
362	%Index = (); %Image = (); %GETPOST = ();
363	&Sort_Index();
364
365	#
366	#  Process %TCP and create session* output files, write %Index
367	#
368	&Process_TCP_Sessions();
369
370	#
371	#  Process %UDP and create session* output files, write %Index
372	#
373	&Process_UDP_Streams();
374
375	#
376	# Process %ICMP
377	#
378	&Process_ICMP();
379
380	#
381	#  Create Index Files from %Index
382	#
383	&Create_Index_Files();
384	&Create_Log_Files();
385
386	###############
387	# --- END ---
388	#
389	&Print_Footer1();
390}
391
392
393###############################
394# --- MODE 2 - Standalone --- #
395###############################
396
397elsif ($Arg{standalone}) {
398
399   ############################################################
400   # --- STANDALONE - Create Packet Logs and Process them ---
401   #
402
403   $limit = $Arg{count};
404   $filenum = 0;
405
406   ### Check for the sniffer command
407   &Check_Command();
408
409   ### cd to output
410   &Chdir($Arg{output_dir});
411
412   ### Print welcome
413   &Print_Welcome();
414
415   #
416   #  MAIN LOOP
417   #
418   while ($limit != 0) {
419	#
420	#  Create a meaningful directory and filename
421	#
422	@Times = localtime();
423	$dirname = sprintf("out_%d%02d%02d-%02d%02d",($Times[5]+1900),
424	 $Times[4],$Times[3],$Times[2],$Times[1]);
425	$filename = "$dirname.log";
426
427	#
428	#  Initial values
429	#
430	$frame = 0; $number = 0;
431	%IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = ();
432
433	#
434	#  Record details in a Master Index
435	#
436	$Master[$filenum]{starttime} = scalar localtime();
437	$Master[$filenum]{duration} = - time();			# will +end time
438	$Master[$filenum]{dir} = $dirname;
439	$Master[$filenum]{file} = $filename;
440
441	#
442	#  Create and cd to output dir
443	#
444	mkdir ("$dirname",0755) || die "ERROR01: Couldn't mkdir (perms?): $!\n";
445	chdir "$dirname" || die "ERROR02: Couldn't cd $dirname: $!\n";
446
447	print "\nCreating log: $dirname/$filename\n" unless $Arg{quiet};
448
449	#
450	#  fork, so that one process can exec tcpdump/snoop while the other
451	#  sleeps and then kills it.
452	#
453	$pid = fork();
454	die "ERROR03: Can't fork (resources?): $!\n" if (! defined $pid);
455
456	if ($pid == 0) {
457		###############################
458		# --- CREATE - Packet Log ---
459		#
460
461		print "Running: $command $filename $Arg{filter}\n"
462		 unless $Arg{quiet};
463		### exec, so $pid points to sniffer
464		exec("$command $filename $Arg{filter}") &&
465			die "ERROR04: couldn't run $command file: $!\n";
466	} else {
467		### Wait for logfile to be populated
468		sleep($Arg{mins} * 60);
469
470		### Kill child (TERM, INT)
471		kill 15, $pid;
472		kill 2, $pid;
473	}
474	exit if $pid == 0;	# check for impossibility
475
476
477	### Record end time, duration, size
478	$Master[$filenum]{endtime} = scalar localtime();
479	$Master[$filenum]{duration} += time();
480	# finish writing the log before reading it's size
481	system("sync") if (($^O eq "linux") || ($^O eq "solaris"));
482	$Master[$filenum]{size} = -s "$filename";
483
484	print "\nProcessing:   $dirname/$filename\n" unless $Arg{quiet};
485	$bak = $Arg{quiet}; $Arg{quiet} = 1;
486
487	###############################
488	# --- INPUT - Process Log ---
489	#
490	&Open_Input_File($filename);
491
492	### Populate memory (%TCP, %UDP, ...).
493	&Read_Input_File();
494
495	#############################################
496	# --- OUTPUT - Process TCP/UDP Sessions ---
497	#
498
499	### Determine Session and Stream time order
500	%Index = (); %Image = (); %GETPOST = ();
501	&Sort_Index();
502
503	### Process %TCP, %UDP, ..., create output fies, write %Index
504	&Process_TCP_Sessions();
505	&Process_UDP_Streams();
506	&Process_ICMP();
507
508	### Create Index Files from %Index
509	&Create_Index_Files();
510	&Create_Log_Files();
511
512
513	chdir ".." || die "ERROR05: Couldn't cd ..: $!\n";
514
515	$Arg{quiet} = $bak;
516
517	### Create Master Index from @Master
518	&Create_Index_Master();
519
520	$limit--;
521	$filenum++;
522   }
523
524}
525
526
527##########################
528# --- MODE 3 - Redo  --- #
529##########################
530
531elsif ($Arg{redo}) {
532
533   #############################################################
534   # --- STANDALONE REDO - Redo last run from sniffer logs ---
535   #
536
537   $filenum = 0;
538
539   ### Read index.file for logs to process
540   &Load_Index_File();
541
542   ### Print welcome
543   &Print_Welcome();
544
545   #
546   #  MAIN LOOP
547   #
548   for ($index=0; $index <= $#Master; $index++) {
549
550	### Get previous run values
551	$dirname = $Master[$index]{dir};
552	$filename = $Master[$index]{file};
553
554	### Initial values
555	$frame = 0; $number = 0;
556	%IP = (); %TCP = (); %UDP = (); %ICMP = (); %Count = (); %Hex = ();
557
558	### Create and cd to output dir
559	chdir "$dirname" || die "ERROR06: Couldn't cd $dirname: $!\n";
560
561	print "Processing:   $dirname/$filename\n" unless $Arg{quiet};
562	$bak = $Arg{quiet}; $Arg{quiet} = 1;
563
564	###############################
565	# --- INPUT - Process Log ---
566	#
567	&Open_Input_File($filename);
568
569	### Populate memory (%TCP, %UDP, ...).
570	&Read_Input_File();
571
572	#############################################
573	# --- OUTPUT - Process TCP/UDP Sessions ---
574	#
575
576	### Determine Session and Stream time order
577	%Index = (); %Image = (); %GETPOST = ();
578	&Sort_Index();
579
580	### Process %TCP, %UDP, ..., create output fies, write %Index
581	&Process_TCP_Sessions();
582	&Process_UDP_Streams();
583	&Process_ICMP();
584
585	### Create Index Files from %Index
586	&Create_Index_Files();
587	&Create_Log_Files();
588
589	chdir ".." || die "ERROR07: Couldn't cd ..: $!\n";
590	$Arg{quiet} = $bak;
591
592	$limit--;
593	$filenum++;
594   }
595   ### Create Master Index from @Master
596   &Create_Index_Master();
597}
598
599
600#
601#  BENCHMARK REPORT
602#
603if ($Arg{bench}) {
604	$Bench{++$BM}{mark} = new Benchmark;
605	$Bench{$BM}{text} = "Program End";
606
607	print "\nBenchmarks,\n\n";
608	for ($bm=1; $bm <= $BM; $bm++) {
609		$bdiff = timediff($Bench{$bm}{mark},$Bench{1}{mark});
610		printf(" %-32s %s\n",$Bench{$bm}{text},timestr($bdiff));
611	}
612}
613
614
615#####################
616# --- SUBROUTINES ---
617
618# (Most of these subroutines are used as shortcuts to code, not traditional
619#  scoped subroutines as with other languages)
620
621
622
623# Open_Input_File - open the packet log specified. This checks the header
624#	of the file to determine whether it is a tcpdump/libpcap or snoop
625#	log (including several styles of tcpdump/libpcap).
626#
627sub Open_Input_File {
628
629	my $infile = shift;
630	my ($length,$size);
631
632	$Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
633	$Bench{$BM}{text} = "Open Input File";
634
635	print "Opening, $infile\n\n" unless $Arg{quiet};
636
637	#
638	#  Open packet log
639	#
640	open(INFILE,$infile) || die "Can't open $infile: $!\n";
641	binmode(INFILE);	# for backward OSs
642
643	#
644	#  Fetch header
645	#
646	$length = read(INFILE,$header,8);
647	die "ERROR08: Can't read from $infile\n" if $length < 8;
648
649	### Print status
650	print "Reading file contents,\n" unless $Arg{quiet};
651	$SIZE = -s $infile;
652
653	#
654	#  Try to determine if this is a tcpdump or a snoop file
655	#
656	($ident) = unpack('a8',$header);
657
658	if ($ident =~ /^snoop/) {
659
660		$TYPE = "snoop";
661		$length = read(INFILE,$header,8);
662		($version,$type) = unpack('NN',$header);
663
664	} elsif ($ident =~ /^\241\262\303\324|^\324\303\262\241/ ||
665		 $ident =~ /^\241\262\315\064|^\064\315\262\241/) {
666
667		$TYPE = "tcpdump";
668		$ident = unpack('a4',$header);  # try again
669		# standard/modified defines style, 1/2 defines endian
670		if ($ident =~ /^\241\262\303\324/) { $STYLE = "standard1"; }
671		if ($ident =~ /^\324\303\262\241/) { $STYLE = "standard2"; }
672		if ($ident =~ /^\241\262\315\064/) { $STYLE = "modified1"; }
673		if ($ident =~ /^\064\315\262\241/) { $STYLE = "modified2"; }
674		if ($STYLE =~ /1$/) {
675			# reread in big-endian
676			($ident,$major,$minor) = unpack('a4nn',$header);
677		} else {
678			# reread in little-endian
679			($ident,$major,$minor) = unpack('a4vv',$header);
680		}
681
682		#
683		#  Check tcpdump header carefully to ensure this is ver 2.4.
684		#
685		if ($major != 2 && $minor != 4) {
686			#
687			#  Die if this is an unknown version. (there could
688			#  be new vers of tcpdump/libpcap in the future).
689			#
690			print STDERR "ERROR09: Wrong tcpdump version ";
691			print STDERR "($version.$type).\n(expected 2.4).\n";
692			exit 1;
693	}
694		#
695		#  Nudge the filehandle past the rest of the header...
696		#
697		$length = read(INFILE,$header_rest,16);
698
699	} else {
700		#
701		#  Die - unknown file format
702		#
703		print STDERR "ERROR10: Input dosen't look like a tcpdump or ";
704		print STDERR "snoop output file.\n\tIf it is tcpdump, it ";
705		print STDERR "may be a wrong or new version.\n";
706		exit 1;
707	}
708
709	### Record the filename into the global %Arg
710	$Arg{infile} = $infile;
711}
712
713
714
715# Read_Input_File - this subroutine loops through the records in the packet
716#  log, storing all the TCP and UDP data into %TCP and %UDP. (see the end
717#  of the program for the structure of these data types). %Count is also
718#  populated with various frequency counts.
719#
720sub Read_Input_File {
721   my ($trailers,$pppoe_verNtype,$pppoe_code,$pppoe_id,$pppoe_length,
722    $ppp_protocol,$wless_fc,$wless_version,$wless_type,$wless_duration,
723    $wless_subtype,$wless_from,$wless_to,$wless_flag,$wless_WEP,
724    $wless_bss,$wless_src,$wless_dest,$wless_cksum,$llc_head,$llc_control,
725    $llc_org,$llc_type,$wless_OK,$bytes,$counter,$packets);
726
727   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
728   $Bench{$BM}{text} = "Read Input File - start";
729
730   local $packet = 0;			# counter
731   if ($TYPE eq "snoop") {
732   	$bytes = 16;
733   } else {
734	$bytes = 24;
735   }
736
737   #
738   # --- Pass #1, Store IP data in memory (%IP) --
739   #
740   while (1) {
741	#
742	# --- Read Record from Log ---
743	#
744	if ($TYPE eq "snoop") {
745		&Read_Snoop_Record();		# will "last" on error
746		$packet_data = $snoop_data;
747		$packet_time = $snoop_seconds;
748		$packet_timefull = $snoop_seconds + $snoop_msecs/1000000;
749		$record_size = $snoop_length_rec;
750	} else {
751		&Read_Tcpdump_Record();		# will "last" on error
752		$packet_data = $tcpdump_data;
753		$packet_time = $tcpdump_seconds;
754		$packet_timefull = $tcpdump_seconds + $tcpdump_msecs/1000000;
755		$record_size = $tcpdump_length + ($integerSize * 2 + 8);
756	}
757
758	### Print status summary
759	unless ($Arg{quiet}) {
760		$bytes += $record_size;
761		if (($packet % 16) == 0) {
762			printf("%s %2.0f%% (%d/%d)","\b"x24,
763			 (100*$bytes/$SIZE),$bytes,$SIZE);
764		}
765	}
766
767	#
768	# --- Parse TCP/IP layers (a little ;) ---
769	#
770
771	#-------------------------------------------------------------------
772	#
773	#  Wireless, 802.11b
774	#
775
776	$decoded = 0;		# this flag is true if wireless was found
777
778	# unpack a little first, (efficiency)
779	($wless_fc) = unpack('H4',$packet_data);
780
781	# this matches on possible send or receive wireless traffic, however
782	# this could also be the start of an 802.3 frame - making this part
783	# of a MAC address. (The IEEE list on OUIs had these as unassigned).
784	if ($wless_fc =~ /^080[1256]/) {
785		# now dig deeper,
786		# (this is one form of 802.11 - the form we are interested
787		# in, however note that there is a lot more to 802.11).
788		($wless_fc,$wless_duration,$wless_bss,$wless_src,
789		 $wless_dest,$wless_cksum,$llc_head,$llc_control,$llc_org,
790		 $llc_type,$ether_data)
791		 = unpack('nnH12H12H12na2CH6H4a*',$packet_data);
792
793		$wless_to = $wless_fc & 1;
794
795		# Check this is IP and encapsulated Ethernet,
796		if (($llc_type eq "0800") && ($llc_org eq "000000")) {
797
798		   ### Populate ether variables for use later on
799		   $ether_type = $llc_type;
800		   if ($wless_to) {
801			$ether_dest = $wless_dest;
802			$ether_src = $wless_src;
803		   } else {
804			$ether_dest = $wless_src;
805			$ether_src = $wless_dest;
806		   }
807
808		   $decoded = 1;	# remember we did this
809		}
810		# (else try redecoding this using 802.3)
811	}
812
813	#-------------------------------------------------------------------
814	#
815	#  Tun device
816	#
817
818	# unpack a little first, (efficiency)
819	($tun_id) = unpack('H8',$packet_data);
820
821	# this checks if the frame looks like a tun device frame
822	if ($tun_id eq "02000000") {
823		# now dig deeper,
824		($tun_id,$ether_data) = unpack('a4a*',$packet_data);
825		$ether_src = "0";
826		$ether_dest = "0";
827		$ether_type = "0800";
828
829		$decoded = 1;		# remember we did this
830	}
831
832	#-------------------------------------------------------------------
833	#
834	#  Ethernet, 802.3
835	#
836
837	### Unpack ether data
838	($ether_dest,$ether_src,$ether_type,$ether_data) =
839	 unpack('H12H12H4a*',$packet_data) unless $decoded;
840
841	### Count ether types seen
842	$Count{EtherType}{$ether_type}++;
843	$CountMaster{EtherType}{$ether_type}++;
844
845	#
846	#  Process extended Ethernet types (wireless, PPPoE)
847	#
848
849	### PPPoE
850	if ($ether_type eq "8864") {
851		($pppoe_verNtype,$pppoe_code,$pppoe_id,$pppoe_length,
852		 $ppp_protocol,$ether_data) = unpack("CCnnna*",$ether_data);
853
854		### Skip anything but data (we just want data - code 0)
855		next if $pppoe_code != 0;
856
857		# (May like to add code here later to process $ppp_protocol,
858		# eg, to process LCP).
859	}
860
861	elsif (($ether_type ne "0800") && ($ether_type ne "86dd")) {
862		next;
863	}
864
865	#-------------------------------------------------------------------
866	#
867	#  IP
868	#
869
870	### Check for IP ver
871	($ip_verNihl,$ip_rest) = unpack('Ca*',$ether_data);
872	$ip_ver = $ip_verNihl & 240;
873	$ip_ver = $ip_ver >> 4;
874
875	if ($ip_ver == 4) {
876
877		#-----------------------------------------------------------
878		#
879		#  IPv4
880		#
881
882		### Unpack IP data
883		($ip_verNihl,$ip_tos,$ip_length,$ip_ident,$ip_flagNfrag,
884		 $ip_ttl,$ip_protocol,$ip_checksum,@ip_src[0..3],
885		 @ip_dest[0..3],$ip_data) = unpack('CCnnnCCa2CCCCCCCCa*',
886		 $ether_data);
887
888		### Get frag and flag data
889		$ip_frag = $ip_flagNfrag & 8191;
890		$ip_flag = $ip_flagNfrag & 57344;
891		$ip_flag = $ip_flag >> 13;
892		$ip_MF = $ip_flag & 1;
893
894		### Strip off IP options if present
895		$ip_ihl = $ip_verNihl & 15;
896		$ip_ihl = $ip_ihl << 2;
897		$ip_options_num = $ip_ihl - 20;
898		if ($ip_options_num > 0) {
899			($ip_options,$ip_data) =
900			 unpack("a${ip_options_num}a*",$ip_data);
901		}
902
903		### Strip off Ethernet trailers
904		$ip_dlength = $ip_length - $ip_options_num - 20;
905		($ip_data,$trailers) = unpack("a${ip_dlength}a*",$ip_data);
906
907		### Build text strings of IP addresses
908		$ip_src = sprintf("%u.%u.%u.%u",@ip_src);
909		$ip_dest = sprintf("%u.%u.%u.%u",@ip_dest);
910
911	} elsif ($ip_ver == 6) {
912
913		#-----------------------------------------------------------
914		#
915		#  IPv6
916		#
917		($ip_verNihl,$ip_flow,$ip_length,$ip_next,$ip_hop,
918		 @ip_src[0..15],@ip_dest[0..15],$ip_data) =
919		 unpack('Ca3nCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCa*',
920		 $ether_data);
921		$ip_protocol = $ip_next;
922
923		### Build text strings of IP addresses
924		$ip_src = sprintf("%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x",
925		 @ip_src);
926		$ip_dest = sprintf("%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x",
927		 @ip_dest);
928
929		### Compress IPv6 text Address
930		$ip_src =~ s/:00:/:0:/g;
931		$ip_src =~ s/:00:/:0:/g;
932		$ip_dest =~ s/:00:/:0:/g;
933		$ip_dest =~ s/:00:/:0:/g;
934		$ip_src =~ s/(:0)+/::/;
935		$ip_dest =~ s/(:0)+/::/;
936
937
938		#
939		#  Check for IPv6 Fragmentation (embedded)
940		#
941		if ($ip_protocol == 44) {
942			($ip_next,$ip_reserved,$ip_fragNmf,$ip_ident,$ip_data)
943			 = unpack('CCnNa*',$ip_data);
944			 $ip_protocol = $ip_next;
945			$ip_MF = $ip_fragNmf & 1;
946			$ip_frag = $ip_fragNmf >> 3;
947		} else {
948			$ip_MF = 0;
949			$ip_ident = 0;
950			$ip_frag = 0;
951		}
952
953	} else {
954		### Not IPv4 or IPv6 - could be LCP (skip for now)
955		next;
956	}
957
958	### Count IP Protocols seen
959	$Count{IPprotocol}{$ip_protocol}++;
960	$CountMaster{IPprotocol}{$ip_protocol}++;
961
962	### Count IP Addresses seen
963	$Count{IP}{$ip_src}++;
964	$CountMaster{IP}{$ip_src}++;
965
966	### Generate unique IP id (not just the ident)
967	$ip_id = &Generate_IP_ID($ip_src,$ip_dest,$ip_ident);
968
969	#
970	#  Store IP data in %IP so we can do frag reassembly next
971	#
972	if (! defined $IP{id}{$ip_id}{StartTime}) {
973		$IP{time}{$packet_timefull}{ver} = $ip_ver;
974		$IP{time}{$packet_timefull}{src} = $ip_src;
975		$IP{time}{$packet_timefull}{dest} = $ip_dest;
976		$IP{time}{$packet_timefull}{protocol} = $ip_protocol;
977		$IP{time}{$packet_timefull}{frag}{$ip_frag} = $ip_data;
978		if ($snoop_drops || $tcpdump_drops) {
979			$IP{time}{$packet_timefull}{drops} = 1;
980		}
981		#
982		#  If there are more fragments, remember this starttime
983		#
984		unless (($ip_MF == 0) && ($ip_frag == 0)) {
985			$IP{id}{$ip_id}{StartTime} = $packet_timefull;
986		}
987		if (($ip_MF == 1) || ($ip_frag > 0)) {
988			$IP{time}{$packet_timefull}{fragged} = 1;
989		}
990	} else {
991		$start_time = $IP{id}{$ip_id}{StartTime};
992		$IP{time}{$start_time}{frag}{$ip_frag} = $ip_data;
993		if ($snoop_drops || $tcpdump_drops) {
994			$IP{time}{$packet_timefull}{drops} = 1;
995		}
996		if ($ip_MF == 0) {
997			#
998			#  Comlpete this IP packet. This assumes that the
999			#  last frag arrives last.
1000			#
1001			undef $IP{ident}{StartTime}{$ip_id};
1002		}
1003	}
1004	$packet++;
1005   }
1006
1007   close INFILE;
1008
1009   ### Print status summary
1010   unless ($Arg{quiet}) {
1011	printf("%s %2.0f%% (%d/%d)","\b"x24,
1012	 100,$bytes,$SIZE);
1013	print "\nReassembling packets,\n";
1014   }
1015
1016
1017
1018   ###################################################################
1019   #  --- Pass #2, Reassemble IP data in %IP; create %TCP and %UDP ---
1020   #
1021
1022   &Print_Header1() if $Arg{debug};
1023   $packets = $packet;
1024   $packet = 0;
1025   @Times = sort { $a <=> $b } ( keys(%{$IP{time}}) );
1026   foreach $time (@Times) {
1027
1028	### Print status summary
1029	unless ($Arg{quiet}) {
1030		if (($packet % 16) == 0) {
1031			printf("%s %2.0f%% (%d/%d)","\b"x32,
1032			 (100*$packet/$packets),$packet,$packets);
1033		}
1034	}
1035
1036	#
1037	#  Get IP data from %IP
1038	#
1039	$ip_ver = $IP{time}{$time}{ver};
1040	$ip_src = $IP{time}{$time}{src};
1041	$ip_dest = $IP{time}{$time}{dest};
1042	$ip_protocol = $IP{time}{$time}{protocol};
1043	$drops = $IP{time}{$time}{drops};
1044	undef $ip_data;
1045
1046	#
1047	#  Reassemble IP frags
1048	#
1049	if (defined $IP{time}{$time}{fragged}) {
1050		@IP_Frags = sort {$a <=> $b} (keys(%{$IP{time}{$time}{frag}}));
1051
1052		### If never recieved the start of the packet, skip
1053		if ($IP_Frags[0] != 0) { next; }
1054
1055		foreach $ip_frag (@IP_Frags) {
1056			$ip_data .= $IP{time}{$time}{frag}{$ip_frag};
1057		}
1058	} else {
1059		$ip_data = $IP{time}{$time}{frag}{0};
1060	}
1061	$length = length($ip_data);
1062
1063	#
1064	# --- UDP ---
1065	#
1066	if ($ip_protocol == 17 && $Arg{output_UDP}) {
1067		&Process_UDP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops);
1068	}
1069
1070	#
1071	# --- TCP ---
1072	#
1073	if ($ip_protocol == 6 && $Arg{output_TCP}) {
1074		&Process_TCP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops);
1075	}
1076
1077	#
1078	# --- ICMP ---
1079	#
1080	if ($ip_protocol == 1 && $Arg{output_ICMP}) {
1081		&Process_ICMP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops,
1082		 "ICMP");
1083	}
1084
1085	#
1086	# --- ICMPv6 ---
1087	#
1088	if ($ip_protocol == 58 && $Arg{output_ICMP}) {
1089		&Process_ICMP_Packet($ip_data,$ip_src,$ip_dest,$time,$drops,
1090		 "ICMPv6");
1091	}
1092
1093	#
1094	#  Skip packet if it isn't TCP (protocol = 6). (Will add routines for
1095	#  ICMP, ARP, RARP later on)...
1096	#
1097
1098	$packet++;
1099
1100	### Memory Cleanup
1101	delete $IP{time}{$time};
1102
1103   }
1104
1105   ### Memory Cleanup
1106   undef %IP;
1107
1108   ### Print status summary
1109   unless ($Arg{quiet}) {
1110	printf("%s %2.0f%% (%d/%d)\n","\b"x24,
1111	 100,$packet,$packets);
1112   }
1113
1114   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
1115   $Bench{$BM}{text} = "Read Input File - end";
1116}
1117
1118
1119
1120# Process_TCP_Packet - process a TCP packet and store it in memory. It takes
1121#	the raw ip data and populates the data structure %TCP. (and %Count).
1122#
1123sub Process_TCP_Packet {
1124
1125	my $ip_data = shift;
1126	my $ip_src = shift;
1127	my $ip_dest = shift;
1128	my $time = shift;
1129	my $drops = shift;
1130	my $copy;
1131
1132	#-------------------------------------------------------------------
1133	#
1134	#  TCP
1135	#
1136
1137	### Unpack TCP data
1138	($tcp_src_port,$tcp_dest_port,$tcp_seq,$tcp_ack,$tcp_offset,$tcp_flags,
1139	 $tcp_header_rest,$tcp_data) = unpack('nnNNCCa6a*',$ip_data);
1140
1141	### Strip off TCP options, if present
1142	$tcp_offset = $tcp_offset >> 4;		# chuck out reserved bits
1143	$tcp_offset = $tcp_offset << 2;		# now times by 4
1144	$tcp_options_num = $tcp_offset - 20;
1145	if ($tcp_options_num > 0) {
1146		($tcp_options,$tcp_data) =
1147		 unpack("a${tcp_options_num}a*",$tcp_data);
1148	}
1149
1150	### Fetch length and FIN,RST flags
1151	$tcp_length_data = length($tcp_data);
1152	$tcp_fin = $tcp_flags & 1;
1153	$tcp_syn = $tcp_flags & 2;
1154	$tcp_rst = $tcp_flags & 4;
1155	$tcp_ack = $tcp_flags & 16;
1156
1157	$copy = $tcp_data;
1158
1159	#
1160	#  Generate $session_id as a unique id for this stream
1161	#  (this is built from host:port,host:port - sorting on port).
1162	#
1163	($session_id,$from_server) = &Generate_SessionID($ip_src,$tcp_src_port,
1164	 $ip_dest,$tcp_dest_port,"TCP");
1165
1166	### Record direction if single SYN was seen
1167	if ($tcp_syn && ! $tcp_ack) {
1168		$TCP{id}{$session_id}{source} = $ip_src;
1169		# better repeat this,
1170		($session_id,$from_server) = &Generate_SessionID($ip_src,
1171		 $tcp_src_port,$ip_dest,$tcp_dest_port,"TCP");
1172	}
1173
1174	### Count TCP Ports seen
1175	if ($from_server) {
1176		$Count{TCPport}{$tcp_src_port}++;
1177		$CountMaster{TCPport}{$tcp_src_port}++;
1178	} else {
1179		$Count{TCPport}{$tcp_dest_port}++;
1180		$CountMaster{TCPport}{$tcp_dest_port}++;
1181	}
1182
1183	#
1184	#  Flag this session as a Partial if either tcpdump or snoop
1185	#  confesses to dropping packets.
1186	#
1187	$TCP{id}{$session_id}{Partial}++ if $drops;
1188
1189	### Store size
1190	$TCP{id}{$session_id}{size} += length($tcp_data);
1191
1192	### Store the packet timestamp for the first seen packet
1193	if (! defined $TCP{id}{$session_id}{StartTime}) {
1194		$TCP{id}{$session_id}{StartTime} = $time;
1195
1196		### Store other info once
1197		if ($from_server) {
1198			$TCP{id}{$session_id}{src} = $ip_dest;
1199			$TCP{id}{$session_id}{dest} = $ip_src;
1200			$TCP{id}{$session_id}{src_port} = $tcp_dest_port;
1201			$TCP{id}{$session_id}{dest_port} = $tcp_src_port;
1202		} else {
1203			$TCP{id}{$session_id}{src} = $ip_src;
1204			$TCP{id}{$session_id}{dest} = $ip_dest;
1205			$TCP{id}{$session_id}{src_port} = $tcp_src_port;
1206			$TCP{id}{$session_id}{dest_port} = $tcp_dest_port;
1207		}
1208	}
1209
1210	### Store the packet timestamp in case this is the last packet
1211	$TCP{id}{$session_id}{EndTime} = $time;
1212
1213	### Print status line
1214	printf "%6s  %-45s  %s\n",$packet,$session_id,$length
1215	 if $Arg{debug};
1216
1217
1218	#
1219	# --- Store Session Data in Memory ---
1220	#
1221	# Since TCP is usually the bulk of the data, we minimise
1222	# the number of copies of data in memory. UDP and ICMP
1223	# are handled differently.
1224
1225	if ($from_server) {
1226		#
1227		#  Populate %TCP{id}{}{time} with raw traffic by time.
1228		#  This is the master structure to store the data.
1229		#
1230		$TCP{id}{$session_id}{time}{$time}{data} .= $tcp_data;
1231		$TCP{id}{$session_id}{time}{$time}{dir} .= "A";
1232
1233		#
1234		#
1235		#  Populate %TCP{id}{}{Aseq} with server to client
1236		#  1-way raw traffic, with the TCP sequence number as
1237		#  the key (for future reassembly).
1238		#
1239		#  This is a pointer to the time structure above,
1240		#  to save on memory used (originally stored a
1241		#  duplicate copy of the data).
1242		#
1243		if ((! defined $TCP{id}{$session_id}{Aseq}{$tcp_seq}) ||
1244		 (length(${$TCP{id}{$session_id}{Aseq}{$tcp_seq}}) <
1245		 length($tcp_data))) {
1246			$TCP{id}{$session_id}{Aseq}{$tcp_seq} =
1247			 \$TCP{id}{$session_id}{time}{$time}{data};
1248		}
1249
1250		#
1251		#  Populate %Hex{TCP}{} with coloured HTML 2-way
1252		#  traffic, if needed.
1253		#
1254		if ($Arg{output_hex}) {
1255			&Process_Hex("TCP",$session_id,$tcp_data,"blue");
1256		}
1257
1258	   } else {
1259		#
1260		#  Populate %TCP{id}{}{Btime} with raw 1-way traffic by time.
1261		#  This is the master structure to store the data.
1262		#
1263		$TCP{id}{$session_id}{time}{$time}{data} .= $tcp_data;
1264		$TCP{id}{$session_id}{time}{$time}{dir} .= "B";
1265
1266		#
1267		#
1268		#  Populate %TCP{id}{}{Bseq} with client to server
1269		#  1-way raw traffic, with the TCP sequence number as
1270		#  the key (for future reassembly).
1271		#
1272		#  This is a pointer to the time structure above,
1273		#  to save on memory used (originally stored a
1274		#  duplicate copy of the data).
1275		#
1276		if ((! defined $TCP{id}{$session_id}{Bseq}{$tcp_seq}) ||
1277		 (length(${$TCP{id}{$session_id}{Bseq}{$tcp_seq}}) <
1278		 length($tcp_data))) {
1279			$TCP{id}{$session_id}{Bseq}{$tcp_seq} =
1280			 \$TCP{id}{$session_id}{time}{$time}{data};
1281		}
1282
1283		#
1284		#  Populate %Hex{TCP}{} with coloured HTML 2-way
1285		#  traffic, if needed.
1286		#
1287		if ($Arg{output_hex}) {
1288			&Process_Hex("TCP",$session_id,$tcp_data,"red");
1289		}
1290
1291	}
1292
1293}
1294
1295
1296
1297# Process_UDP_Packet - process a UDP packet and store it in memory. It takes
1298#	the raw ip data and populates the data structure %UDP.
1299#
1300sub Process_UDP_Packet {
1301
1302	my $ip_data = shift;
1303	my $ip_src = shift;
1304	my $ip_dest = shift;
1305	my $time = shift;
1306	my $drops = shift;
1307	my $copy;
1308
1309	#-------------------------------------------------------------------
1310	#
1311	#  UDP
1312	#
1313
1314	### Unpack UDP data
1315	($udp_src_port,$udp_dest_port,$udp_length,$udp_checksum,
1316	 $udp_data) = unpack('nnnna*',$ip_data);
1317
1318	#
1319	#  Generate $session_id as a unique id for this stream
1320	#  (this is built from host:port,host:port - sorting on port).
1321	#
1322	($session_id,$from_server) = &Generate_SessionID($ip_src,$udp_src_port,
1323	 $ip_dest,$udp_dest_port,"UDP");
1324
1325	#
1326	#  Flag this session as a Partial if either tcpdump or snoop
1327	#  confesses to dropping packets.
1328	#
1329	$UDP{id}{$session_id}{Partial}++ if $drops;
1330
1331	### Store size
1332	$UDP{id}{$session_id}{size} += length($udp_data);
1333
1334	### Count UDP ports seen
1335	if ($from_server) {
1336		$Count{UDPport}{$udp_src_port}++;
1337		$CountMaster{UDPport}{$udp_src_port}++;
1338	} else {
1339		$Count{UDPport}{$udp_dest_port}++;
1340		$CountMaster{UDPport}{$udp_dest_port}++;
1341	}
1342
1343	#
1344	# --- Store Stream Data in Memory ---
1345	#
1346
1347	if ($from_server) {
1348		#
1349		#  Populate %UDP{id}{}{RawA} with server to client
1350		#  1-way raw traffic
1351		#
1352		$UDP{id}{$session_id}{RawA} .= $udp_data;
1353
1354		#
1355		#  Populate %UDP{id}{}{BothHTML} with coloured HTML
1356		#  2-way traffic, blue for server to client
1357		#
1358		$copy = &Desex_HTML($udp_data);
1359		$UDP{id}{$session_id}{BothHTML} .=
1360			"<font color=\"blue\">$copy</font>";
1361
1362		#
1363		#  Populate %Hex{UDP}{} with coloured HTML 2-way
1364		#  traffic, if needed.
1365		#
1366		if ($Arg{output_hex}) {
1367			&Process_Hex("UDP",$session_id,$udp_data,"blue");
1368		}
1369
1370	} else {
1371		#
1372		#  Populate %UDP{id}{}{RawB} with client to server
1373		#  1-way raw traffic
1374		#
1375		$UDP{id}{$session_id}{RawB} .= $udp_data;
1376
1377		#
1378		#  Populate %UDP{id}{}{BothHTML} with coloured HTML
1379		#  2-way traffic, red for client to server
1380		#
1381		$copy = &Desex_HTML($udp_data);
1382		$UDP{id}{$session_id}{BothHTML} .=
1383			"<font color=\"red\">$copy</font>";
1384		#
1385		#  Populate %Hex{UDP}{} with coloured HTML 2-way
1386		#  traffic, if needed.
1387		#
1388		if ($Arg{output_hex}) {
1389			&Process_Hex("UDP",$session_id,$udp_data,"red");
1390		}
1391
1392	}
1393	#
1394	#  Populate %UDP{id}{}{time}{} with raw 1-way traffic by time
1395	#
1396	$UDP{id}{$session_id}{time}{$time} .= $udp_data;
1397
1398	### Store the packet timestamp for the first seen packet
1399	if (! defined $UDP{id}{$session_id}{StartTime}) {
1400		$UDP{id}{$session_id}{StartTime} = $time;
1401
1402		### Store other info once
1403		if ($from_server) {
1404			$UDP{id}{$session_id}{src} = $ip_dest;
1405			$UDP{id}{$session_id}{dest} = $ip_src;
1406			$UDP{id}{$session_id}{src_port} = $udp_dest_port;
1407			$UDP{id}{$session_id}{dest_port} = $udp_src_port;
1408		} else {
1409			$UDP{id}{$session_id}{src} = $ip_src;
1410			$UDP{id}{$session_id}{dest} = $ip_dest;
1411			$UDP{id}{$session_id}{src_port} = $udp_src_port;
1412			$UDP{id}{$session_id}{dest_port} = $udp_dest_port;
1413		}
1414	}
1415
1416	### Store the packet timestamp in case this is the last packet
1417	$UDP{id}{$session_id}{EndTime} = $time;
1418
1419	### Print status line
1420	printf "%6s  %-45s  %s\n",$packet,$session_id,$length
1421	 if $Arg{debug};
1422
1423}
1424
1425
1426
1427# Process_ICMP_Packet - process a ICMP packet and store it in memory. It takes
1428#	the raw ip data and populates the data structure %ICMP.
1429#	time is the session_id.
1430#
1431sub Process_ICMP_Packet {
1432
1433	my $ip_data = shift;
1434	my $ip_src = shift;
1435	my $ip_dest = shift;
1436	my $time = shift;
1437	my $drops = shift;
1438	my $ver = shift;
1439
1440	#-------------------------------------------------------------------
1441	#
1442	#  ICMP
1443	#
1444
1445	### Unpack ICMP data
1446	($icmp_type,$icmp_code,$icmp_cksum,$icmp_rest) =
1447	 unpack('CCna*',$ip_data);
1448
1449	#
1450	# --- Store ICMP data in memory ---
1451	#
1452
1453	### Store Fields
1454	$ICMP{time}{$time}{type} = $icmp_type;
1455	$ICMP{time}{$time}{code} = $icmp_code;
1456	$ICMP{time}{$time}{src} = $ip_src;
1457	$ICMP{time}{$time}{dest} = $ip_dest;
1458	$ICMP{time}{$time}{ver} = $ver;
1459
1460	#
1461	#  Flag this session as a Partial if either tcpdump or snoop
1462	#  confesses to dropping packets.
1463	#
1464	$ICMP{time}{$time}{Partial}++ if $drops;
1465
1466	#
1467	#  Save data if ICMP echo/reply
1468	#
1469	if (($icmp_type == 0) || ($icmp_type == 8) ||
1470	 ($icmp_type == 128) || ($icmp_type == 129) || 1) {
1471		### Unpack some more
1472		($icmp_type,$icmp_code,$icmp_cksum,$icmp_id,$icmp_seq,
1473		 $icmp_data) = unpack('CCnnna*',$ip_data);
1474		### Save extra fields
1475		$ICMP{time}{$time}{id} = $icmp_id;
1476		$ICMP{time}{$time}{seq} = $icmp_seq;
1477		$ICMP{time}{$time}{data} = $icmp_data;
1478	}
1479
1480	### Store size
1481	$ICMP{time}{$time}{size} += length($icmp_data);
1482
1483	if ($icmp_data ne "") {
1484		#
1485		#  Populate %ICMP{time}{}{BothHTML} with coloured HTML
1486		#  1-way traffic, blue
1487		#
1488		$copy = &Desex_HTML($icmp_data);
1489		$ICMP{time}{$time}{BothHTML} .=
1490			"<font color=\"blue\">$copy</font>";
1491	}
1492
1493	#
1494	#  Populate %Hex{ICMP}{} with coloured HTML
1495	#  traffic, if needed.
1496	#
1497	if ($Arg{output_hex}) {
1498		&Process_Hex("ICMP",$time,$icmp_data,"blue");
1499	}
1500
1501	### Print status line
1502	printf "%6s  %-45s  %s\n",$packet,"$ip_src,$ip_dest",$length
1503	 if $Arg{debug};
1504}
1505
1506
1507
1508# Process_TCP_Sessions - this subroutine processes %TCP, saving the
1509# 	sessions to various "session*" files on disk. It populates %Index
1510#	with information on files that it has created. It also checks
1511#	the application port numbers and triggers further processing -
1512#	eg telnet replay files. Min/Max size checks are also done here.
1513#
1514sub Process_TCP_Sessions {
1515
1516   my ($filename,$id_text,$id_html,$rawboth,$time,$raw);
1517   my @Time;
1518
1519   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
1520   $Bench{$BM}{text} = "Process TCP Sessions - start";
1521
1522   #
1523   #  Loop through all TCP sessions
1524   #
1525   foreach $session_id (keys %{$TCP{id}}) {
1526	$number = $Index{Sort_Lookup}{"TCP:$session_id"};
1527
1528	#
1529	#  Determine the service - usually by the lowest numbered port, eg,
1530	#  ports 51321 and 23 would give 23 (telnet).
1531	#
1532	$ip_src = $TCP{id}{$session_id}{src};
1533	$ip_dest = $TCP{id}{$session_id}{dest};
1534	$tcp_src_port = $TCP{id}{$session_id}{src_port};
1535	$tcp_dest_port = $TCP{id}{$session_id}{dest_port};
1536	($service,$client) = &Pick_Service_Port("TCP",$session_id,
1537	 $tcp_src_port,$tcp_dest_port);
1538
1539	### Fetch text name for this port
1540	$service_name = $Services_TCP{$service} || $service || "0";
1541
1542	#
1543	#  Don't actually save any files if CLI args say not to
1544	#
1545	if ($Arg{port_reject} && $Arg{Port_Rejected}{$service}) { next; }
1546	if ($Arg{port_accept} && !$Arg{Port_Accepted}{$service}) { next; }
1547	if ($Arg{ip_reject}) {
1548		if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}) {
1549			next;
1550		}
1551	}
1552	if ($Arg{ip_accept}) {
1553		unless ($Arg{IP_Accepted}{$ip_src} ||
1554		 $Arg{IP_Accepted}{$ip_dest}) {
1555			next;
1556		}
1557	}
1558
1559	#
1560	# --- Fetch RawBoth ---
1561	#
1562	# rawboth will contain the raw data in time order.
1563	$rawboth = "";
1564	foreach $time (sort {$a <=> $b}
1565	 (keys (%{$TCP{id}{$session_id}{time}}))) {
1566		$rawboth .= $TCP{id}{$session_id}{time}{$time}{data};
1567	}
1568	$length = length($rawboth);
1569
1570	#
1571	# --- Check for Min and Max size ---
1572	#
1573	next if $length < $Arg{minbytes};
1574	next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes}));
1575
1576	### Print status line
1577	$numtext = sprintf("%04d",$number);
1578	printf "%6s  %-45s  %s\n",$numtext,$session_id,$service_name
1579	 unless $Arg{quiet};
1580
1581	#
1582	# --- Save Info File to Disk ---
1583	#
1584	if ($Arg{output_info}) {
1585		$filename = "session_${numtext}.info";
1586		$firsttime = localtime($TCP{id}{$session_id}{StartTime});
1587		$lasttime = localtime($TCP{id}{$session_id}{EndTime});
1588		$duration = ($TCP{id}{$session_id}{EndTime} -
1589	 	 $TCP{id}{$session_id}{StartTime});
1590		$duration = sprintf("%.0f",$duration);
1591		if ($TCP{id}{$session_id}{Partial}) { $partial = "yes"; }
1592		 else { $partial = "no"; }
1593
1594		### Build output text
1595		$outtext = "$numtext===$session_id===$service===" .
1596		 "$service_name===$length\n\n" .
1597		 "Source addr : $ip_src\n" .
1598		 "Source port : $tcp_src_port\n" .
1599		 "Dest addr   : $ip_dest\n" .
1600		 "Dest port   : $tcp_dest_port\n" .
1601		 "Dest service: $service_name\n" .
1602		 "Length bytes: $length\n" .
1603		 "First time  : $firsttime\n" .
1604		 "Last time   : $lasttime\n" .
1605		 "Duration    : $duration seconds\n" .
1606		 "Partial     : $partial\n";
1607
1608		### Write info file
1609		open (OUT,">$filename") ||
1610		 die "ERROR11: creating $filename $!\n";
1611		print OUT $outtext;
1612		close OUT;
1613	}
1614
1615
1616	#
1617	# --- Save Index data to Memory ---
1618	#
1619
1620	## Fetch times
1621	$starttime = scalar localtime($TCP{id}{$session_id}{StartTime});
1622	$duration = ($TCP{id}{$session_id}{EndTime} -
1623	 $TCP{id}{$session_id}{StartTime});
1624	$duration = sprintf("%.0f",$duration);
1625
1626	### Generate session strings
1627	($id_text,$id_html) = &Generate_TCP_IDs($session_id);
1628
1629	### Construct HTML table row containing session data
1630	$Index{HTML}[$number] = "<tr><td><i>$number.</i></td>" .
1631	 "<td><b>$starttime</b></td><td>$duration s</td><td> " .
1632	 "<font color=\"blue\">$id_html " .
1633	 "</font></td><td> <font color=\"red\">" .
1634	 "$service_name</font></td><td> <font color=\"green\"> " .
1635	 "$length bytes</font></td><td>\n";
1636
1637	### Construct text line containing session data
1638	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number,
1639	 $id_text,"($service_name)",$length);
1640
1641	### Construct image info line (in case it is needed)
1642	$Image{HTML}[$number]{info} = "<tr><td><i>$number.</i>" .
1643	 "</td><td><b>$starttime</b></td><td> " .
1644	 "<font color=\"blue\">$id_html </font></td><td><td>\n";
1645
1646	### Construct GETPOST info line (in case it is needed)
1647	# starttime and host:port... are formatted differently so that
1648	# they are narrow and leave more room for the sub table.
1649	$GETPOST{HTML}[$number]{info} = "<tr><td><i>$number.</i>" .
1650	 "</td><td><b>$starttime</b></td><td> " .
1651	 "<font color=\"blue\">$id_html </font></td><td><td>\n";
1652
1653
1654	#
1655	# --- Save Raw Sessions to Disk ---
1656	#
1657
1658	if ($Arg{output_raw}) {
1659
1660		#
1661		#  Save ".raw" file, all raw 2-way data time-sorted.
1662		#
1663		$filename = "session_${numtext}.${service_name}.raw";
1664		open (OUT,">$filename") ||
1665		 die "ERROR12: creating $filename $!\n";
1666		binmode(OUT);		# for backward OSs
1667		print OUT $rawboth;
1668		close OUT;
1669
1670		### Update HTML index table with link
1671		$Index{HTML}[$number] .= "<li><a href=\"$filename\">raw</a> ";
1672
1673		#
1674		#  Save ".raw1" file, server->client 1-way data assembled.
1675		#
1676		$filename = "session_${numtext}.${service_name}.raw1";
1677		open (OUT,">$filename") ||
1678		 die "ERROR13: creating $filename $!\n";
1679		binmode(OUT);		# for backward OSs
1680		print OUT &TCP_Follow_RawA($session_id);
1681		close OUT;
1682
1683		### Update HTML index table with link
1684		$Index{HTML}[$number] .= "<a href=\"$filename\">raw1</a> ";
1685
1686		#
1687		#  Save ".raw2" file, client->server 1-way data assembled.
1688		#
1689		$filename = "session_${numtext}.${service_name}.raw2";
1690		open (OUT,">$filename") ||
1691		 die "ERROR14: creating $filename $!\n";
1692		binmode(OUT);		# for backward OSs
1693		print OUT &TCP_Follow_RawB($session_id);
1694		close OUT;
1695
1696		### Update HTML index table with link
1697		$Index{HTML}[$number] .= "<a href=\"$filename\">raw2</a></li> ";
1698	}
1699
1700	next unless $Arg{output_apps};
1701
1702	#
1703	# --- Save Session as HTML ---
1704	#
1705	if ($Arg{Save_As_TCP_HTML}{$service} || $Arg{output_allhtml}) {
1706		&Save_Both_HTML("TCP",$session_id,$number,$service_name,
1707		 $id_html);
1708	}
1709
1710	#
1711	# --- Save X11 Session as HTML ---
1712	#
1713	if ($Arg{Save_As_X11_HTML}{$service}) {
1714		#
1715		#  HTML Postprocessing can go here
1716		#
1717		&Generate_X11_HTML($session_id);
1718		&Process_BothHTML("TCP",$session_id,1);
1719
1720		&Save_Both_HTML("TCP",$session_id,$number,"text$service_name",
1721		 $id_html);
1722	}
1723
1724
1725	#
1726	# --- Save Hex Dump as HTML ---
1727	#
1728	if ($Arg{output_hex}) {
1729		&Process_Hex_Finish("TCP",$session_id);
1730		&Save_Hex_HTML("TCP",$session_id,$number,$service_name,
1731		 $id_html);
1732		&Save_Hex_Text("TCP",$session_id,$number,$service_name,
1733		 $id_text);
1734	}
1735
1736	#
1737	# --- Process Application Data ---
1738	#
1739
1740	if ($service == 20) {
1741		&Save_FTP_File($session_id,$number);
1742	}
1743	if ($service == 22) {
1744		&Save_Session_textSSH_files($session_id,$number,
1745		 "SSH",$id_html);
1746	}
1747	if ($Arg{keydata} && $Arg{Save_As_TCP_Playback}{$service}) {
1748		# The following is for special analysis,
1749		&Save_Session_Keydata($session_id,$number,
1750		 $service_name,$id_html);
1751	}
1752	if ($service == 25) {
1753		&Save_SMTP_Emails($session_id,$number);
1754	}
1755	if ($service == 80 or $service == 8080 or
1756	 $service == 3127 or $service == 1080)  {
1757		&Save_HTTP_Files($session_id,$number,$service_name);
1758		&Process_HTTP($session_id);
1759	}
1760
1761	if ($Arg{Save_As_X11_Playback}{$service}) {
1762		&Save_Session_XReplay($session_id,$number,$service_name);
1763	}
1764
1765	if ($Arg{Save_As_VNC_Playback}{$service}) {
1766		&Save_Session_VNCReplay_andHTML($session_id,$number,
1767		 $service_name,$id_html);
1768	}
1769
1770	$raw = &TCP_Follow_RawB($session_id);
1771	if ($raw =~ /^\200\0\0p0\211/) {
1772		&Save_NFS_File($session_id,$number);
1773	}
1774
1775	if ($Arg{Save_As_TCP_Playback}{$service}) {
1776		&Save_Session_Replay($session_id,$number,$service_name);
1777	}
1778   }
1779
1780   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
1781   $Bench{$BM}{text} = "Process TCP Sessions - end";
1782}
1783
1784
1785# Process_UDP_Streams - this subroutine processes %UDP, saving the
1786# 	sessions to various "session*" files on disk. It populates %Index
1787#	with information on the files that were created. It also checks
1788#	the application port numbers and triggers further processing -
1789#	eg DNS html output files.
1790#
1791sub Process_UDP_Streams {
1792
1793   my ($filename,$id_html,$id_text,$time,$rawboth);
1794
1795   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
1796   $Bench{$BM}{text} = "Process UDP Sessions - start";
1797
1798   #
1799   #  Loop through all UDP Streams
1800   #
1801   foreach $session_id (keys %{$UDP{id}}) {
1802	$number = $Index{Sort_Lookup}{"UDP:$session_id"};
1803
1804        #
1805        #  Determine the service - usually by the lowest numbered port, eg,
1806        #  ports 51327 and 53 would give 53 (dns). (big assumption!)
1807        #
1808	$ip_src = $UDP{id}{$session_id}{src};
1809	$ip_dest = $UDP{id}{$session_id}{dest};
1810	$udp_src_port = $UDP{id}{$session_id}{src_port};
1811	$udp_dest_port = $UDP{id}{$session_id}{dest_port};
1812	($service,$client) = &Pick_Service_Port("UDP",$session_id,
1813	 $udp_src_port,$udp_dest_port);
1814
1815	### Fetch text name for this port
1816	$service_name = $Services_UDP{$service} || $service || "0";
1817
1818        #
1819        #  Don't actually save any files if CLI args say not to
1820        #
1821	if ($Arg{port_reject} && $Arg{Port_Rejected}{$service}) { next; }
1822	if ($Arg{port_accept} && !$Arg{Port_Accepted}{$service}) { next; }
1823	if ($Arg{ip_reject}) {
1824		if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}) {
1825			next;
1826		}
1827	}
1828	if ($Arg{ip_accept}) {
1829		unless ($Arg{IP_Accepted}{$ip_src} ||
1830		 $Arg{IP_Accepted}{$ip_dest}) {
1831			next;
1832		}
1833	}
1834
1835	#
1836	# --- Fetch RawBoth ---
1837	#
1838	# rawboth will contain the raw data in time order.
1839	$rawboth = "";
1840	foreach $time (sort {$a <=> $b}
1841	 (keys (%{$UDP{id}{$session_id}{time}}))) {
1842		$rawboth .= $UDP{id}{$session_id}{time}{$time};
1843	}
1844	$length = length($rawboth);
1845
1846	#
1847	# --- Check for Min and Max Size ---
1848	#
1849	next if $length < $Arg{minbytes};
1850	next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes}));
1851
1852	### Print status line
1853	$numtext = sprintf("%04d",$number);
1854	printf "%6s  %-45s  %s\n",$numtext,$session_id,$service_name
1855	 unless $Arg{quiet};
1856
1857	#
1858	# --- Save Info File to Disk ---
1859	#
1860	if ($Arg{output_info}) {
1861		$filename = "stream_${numtext}.info";
1862                $firsttime = localtime($UDP{id}{$session_id}{StartTime});
1863                $lasttime = localtime($UDP{id}{$session_id}{EndTime});
1864                $duration = ($UDP{id}{$session_id}{EndTime} -
1865                 $UDP{id}{$session_id}{StartTime});
1866		$duration = sprintf("%.0f",$duration);
1867		if ($UDP{id}{$session_id}{Partial}) { $partial = "yes"; }
1868		 else { $partial = "no"; }
1869
1870                ### Build output text
1871                $outtext = "$numtext===$session_id===$service===" .
1872                 "$service_name===$length\n\n" .
1873                 "Source addr : $ip_src\n" .
1874                 "Source port : $udp_src_port\n" .
1875                 "Dest addr   : $ip_dest\n" .
1876                 "Dest port   : $udp_dest_port\n" .
1877                 "Dest service: $service_name\n" .
1878                 "Length bytes: $length\n" .
1879                 "First time  : $firsttime\n" .
1880                 "Last time   : $lasttime\n" .
1881                 "Duration    : $duration seconds\n" .
1882                 "Partial     : $partial\n";
1883
1884                ### Write info file
1885                open (OUT,">$filename") ||
1886                 die "ERROR15: creating $filename $!\n";
1887                print OUT $outtext;
1888                close OUT;
1889	}
1890
1891
1892	#
1893	# --- Save Index data in Memory ---
1894	#
1895
1896	### Fetch Times
1897	$starttime = scalar localtime($UDP{id}{$session_id}{StartTime});
1898	$duration = ($UDP{id}{$session_id}{EndTime} -
1899	 $UDP{id}{$session_id}{StartTime});
1900	$duration = sprintf("%.0f",$duration);
1901
1902	### Construct HTML table row containing stream data
1903	$id_html = "$ip_src:$udp_src_port &lt;-&gt; $ip_dest:$udp_dest_port";
1904	$Index{HTML}[$number] = "<tr><td><i>$number.</i></td>" .
1905	 "<td><b>$starttime</b></td><td>$duration s</td><td> " .
1906	 "<font color=\"blue\">$id_html " .
1907	 "</font></td><td> <font color=\"red\">" .
1908	 "<i>$service_name</i></font></td><td> <font color=\"green\"> " .
1909	 "$length bytes</font></td><td>\n";
1910
1911	### Construct text line containing session data
1912	$id_text = "$ip_src:$udp_src_port <-> $ip_dest:$udp_dest_port";
1913	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number,
1914	 $id_text,"($service_name)",$length);
1915
1916
1917	#
1918	# --- Save Raw Stream to Disk ---
1919	#
1920
1921	if ($Arg{output_raw}) {
1922
1923		#
1924		#  Save ".raw" file, all raw 2-way data time-sorted.
1925		#
1926		$filename = "stream_${numtext}.${service_name}.raw";
1927		open (OUT,">$filename") ||
1928		 die "ERROR16: creating $filename $!\n";
1929		binmode(OUT);		# for backward OSs
1930		print OUT $rawboth;
1931		close OUT;
1932
1933		### Update HTML index table with link
1934		$Index{HTML}[$number] .= "<li><a href=\"$filename\">raw</a> ";
1935
1936		#
1937		#  Save ".raw1" file, server->client 1-way data time-sorted.
1938		#
1939		$filename = "stream_${numtext}.${service_name}.raw1";
1940		open (OUT,">$filename") ||
1941		 die "ERROR17: creating $filename $!\n";
1942		binmode(OUT);		# for backward OSs
1943		print OUT $UDP{id}{$session_id}{RawA};
1944		close OUT;
1945
1946		### Update HTML index table with link
1947		$Index{HTML}[$number] .= "<a href=\"$filename\">raw1</a> ";
1948
1949		#
1950		#  Save ".raw2" file, client->server 1-way data time-sorted.
1951		#
1952		$filename = "stream_${numtext}.${service_name}.raw2";
1953		open (OUT,">$filename") ||
1954		 die "ERROR18: creating $filename $!\n";
1955		binmode(OUT);		# for backward OSs
1956		print OUT $UDP{id}{$session_id}{RawB};
1957		close OUT;
1958
1959		### Update HTML index table with link
1960		$Index{HTML}[$number] .= "<a href=\"$filename\">raw2</a></li> ";
1961	}
1962
1963	next unless $Arg{output_apps};
1964
1965	#
1966	# --- Save Stream as HTML ---
1967	#
1968
1969	if ($Arg{Save_As_UDP_HTML}{$service} || $Arg{output_allhtml}) {
1970		#
1971		#  HTML Postprocessing can go here
1972		#
1973		&Process_BothHTML("UDP",$session_id);
1974
1975		&Save_Both_HTML("UDP",$session_id,$number,$service_name);
1976	}
1977
1978	#
1979	# --- Save Hex Dump as HTML ---
1980	#
1981	if ($Arg{output_hex}) {
1982		&Process_Hex_Finish("UDP",$session_id);
1983		&Save_Hex_HTML("UDP",$session_id,$number,$service_name,
1984		 $id_html);
1985		&Save_Hex_Text("UDP",$session_id,$number,$service_name,
1986		 $id_text);
1987	}
1988
1989
1990	#
1991	# --- Process Application Data ---
1992	#
1993	if ($Arg{Save_As_UDP_Playback}{$service}) {
1994		&Save_Stream_Replay($session_id,$number,$service_name);
1995	}
1996
1997   }
1998
1999   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
2000   $Bench{$BM}{text} = "Process UDP Sessions - end";
2001}
2002
2003
2004
2005# Process_ICMP - this subroutine processes %ICMP.
2006#
2007sub Process_ICMP {
2008
2009   my ($filename,$id_text,$id_html);
2010
2011   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
2012   $Bench{$BM}{text} = "Process ICMP Sessions - start";
2013
2014   #
2015   #  Loop through all ICMP Streams
2016   #
2017   foreach $time (keys %{$ICMP{time}}) {
2018	$number = $Index{Sort_Lookup}{"ICMP:$time"};
2019
2020
2021	### Fetch Data
2022	$icmp_type = $ICMP{time}{$time}{type};
2023	$icmp_code = $ICMP{time}{$time}{code};
2024	$icmp_ver = $ICMP{time}{$time}{ver};
2025	$ip_src = $ICMP{time}{$time}{src};
2026	$ip_dest = $ICMP{time}{$time}{dest};
2027	$session_id = "$ip_src,$ip_dest";
2028
2029	### Fetch text name for this port
2030	$type_name = $ICMP_Types{$icmp_type} || $icmp_type || "0";
2031	$service_name = $icmp_type;
2032
2033        #
2034        #  Don't actually save any files if CLI args say not to
2035        #
2036	if ($Arg{ip_reject}) {
2037		if ($Arg{IP_Rejected}{$ip_src} || $Arg{IP_Rejected}{$ip_dest}){
2038			next;
2039		}
2040	}
2041	if ($Arg{ip_accept}) {
2042		unless ($Arg{IP_Accepted}{$ip_src} ||
2043		 $Arg{IP_Accepted}{$ip_dest}) {
2044			next;
2045		}
2046	}
2047
2048	#
2049	# --- Check for Min and Max Size ---
2050	#
2051	$length = length($ICMP{time}{$time}{data});
2052	next if $length < $Arg{minbytes};
2053	next if (($Arg{maxbytes} != 0) && ($length > $Arg{maxbytes}));
2054
2055	### Print status line
2056	$numtext = sprintf("%04d",$number);
2057	printf "%6s  %-45s  ICMP %s\n",$numtext,$session_id,$type_name
2058	 unless $Arg{quiet};
2059
2060	#
2061	# --- Save Info File to Disk ---
2062	#
2063	if (($Arg{output_info}) && ($length > 0)) {
2064		$filename = "icmp_${numtext}.${service_name}.info";
2065		if ($ICMP{time}{$time}{Partial}) { $partial = "yes"; }
2066		 else { $partial = "no"; }
2067		$starttime = scalar localtime($time);
2068
2069                ### Build output text
2070                $outtext = "$numtext===$session_id===$icmp_type===" .
2071                 "$type_name===$length\n\n" .
2072                 "Source addr : $ip_src\n" .
2073                 "Dest addr   : $ip_dest\n" .
2074                 "ICMP version: $icmp_ver\n" .
2075                 "ICMP type   : $icmp_type\n" .
2076                 "ICMP code   : $icmp_code\n" .
2077                 "ICMP name   : $type_name\n" .
2078                 "Length bytes: $length\n" .
2079                 "Time        : $starttime\n" .
2080                 "Partial     : $partial\n";
2081
2082                ### Write info file
2083                open (OUT,">$filename") ||
2084                 die "ERROR19: creating $filename $!\n";
2085                print OUT $outtext;
2086                close OUT;
2087	}
2088
2089
2090	#
2091	# --- Save Index data in Memory ---
2092	#
2093
2094	### Fetch Times
2095	$starttime = scalar localtime($time);
2096
2097	### Construct HTML table row containing stream data
2098	$id_html = "$ip_src -&gt; $ip_dest";
2099	$Index{HTML}[$number] = "<tr><td><i>$number.</i></td>" .
2100	 "<td><b>$starttime</b></td><td>0 s</td><td> " .
2101	 "<font color=\"blue\">$id_html" .
2102	 "</font></td><td> <font color=\"red\">" .
2103	 "<i>$icmp_ver</i></font></td><td> <font color=\"green\"> " .
2104	 "$length bytes</font></td><td>$type_name\n";
2105
2106	### Construct text line containing session data
2107	$id_text = "$ip_src -> $ip_dest";
2108	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",$number,
2109	 $id_text, "($icmp_ver $type_name)",$length);
2110
2111
2112	#
2113	# --- Save Raw Stream to Disk ---
2114	#
2115
2116	if (($Arg{output_raw}) && ($length > 0)) {
2117
2118		#
2119		#  Save ".raw" file, all raw 2-way data time-sorted.
2120		#
2121		$filename = "icmp_${numtext}.${service_name}.raw";
2122		open (OUT,">$filename") ||
2123		 die "ERROR20: creating $filename $!\n";
2124		binmode(OUT);		# for backward OSs
2125		print OUT $ICMP{time}{$time}{data};
2126		close OUT;
2127
2128		### Update HTML index table with link
2129		$Index{HTML}[$number] .= "<li><a href=\"$filename\">raw</a> ";
2130
2131	}
2132
2133	#
2134	# --- Save Stream as HTML ---
2135	#
2136
2137	if ($Arg{output_allhtml}) {
2138		#
2139		#  HTML Postprocessing can go here
2140		#
2141		&Process_BothHTML("ICMP",$time);
2142
2143		&Save_Both_HTML("ICMP",$time,$number,$service_name,$id_html);
2144	}
2145
2146	#
2147	# --- Save Hex Dump as HTML ---
2148	#
2149	if ($Arg{output_hex}) {
2150		&Process_Hex_Finish("ICMP",$time);
2151		&Save_Hex_HTML("ICMP",$time,$number,$service_name,$id_html);
2152		&Save_Hex_Text("ICMP",$time,$number,$service_name,$id_text);
2153	}
2154   }
2155
2156   $Bench{++$BM}{mark} = new Benchmark if $Arg{bench};
2157   $Bench{$BM}{text} = "Process ICMP Sessions - end";
2158}
2159
2160
2161# Process_HTTP - HTTP processing. Looks for GETs and POSTs, and process them
2162#		into %GETPOST. Constructs a HTTP log in %HTTPlog.
2163#
2164sub Process_HTTP {
2165	my ($junk,$var,$value,$term,$data,$request,$site,$post,$get,$reply);
2166	my ($start,$src,$num,$req,$recv,$type,$status,$time1,$duration,$dest);
2167	my @Terms;
2168	my $index = 0;
2169	my $indexA = 0;
2170	my $indexB = 0;
2171
2172	### Input
2173	my $session_id = shift;
2174
2175	$src = $TCP{id}{$session_id}{src};
2176	$dest = $TCP{id}{$session_id}{dest};
2177
2178	#
2179	#  Process
2180	#
2181
2182	### Get packet times (may need to use seqs instead)
2183	@Times = sort{$a <=> $b} (keys(%{$TCP{id}{$session_id}{time}}));
2184
2185	### Step through each packet
2186        for ($i=0; $i <= $#Times; $i++) {
2187
2188           ### Fetch data from mem
2189           $time = $Times[$i];
2190           $request = $TCP{id}{$session_id}{time}{$time}{data};
2191	   $request =~ s/^\0\0*//;
2192
2193	   #
2194	   # --- Do HTTPlog Processing ---
2195	   #
2196
2197	   next unless $request =~ /^(GET|POST)\s/; # speed
2198
2199	   ### Calc duration
2200           $time1 = $Times[$i+1] || $time;
2201	   $duration = $time1 - $time;
2202
2203	   # some magic
2204	   $reply = "";
2205	   foreach $inc (1..16) {
2206		$next = $TCP{id}{$session_id}{time}{$Times[$i+$inc]}{data};
2207		$next =~ s/^\0\0*//;
2208		if ($next =~ /^U*\0*HTTP/) {
2209			$reply = $next;
2210			$time1 = $Times[$i+$inc] || $time;
2211        		$duration = $time1 - $time;
2212			last;
2213		} else {
2214			$request .= $next;
2215		}
2216	   }
2217	   $i++; # speed
2218
2219	   if ($request =~ /^GET \S* HTTP/) {
2220
2221		### Get the site string
2222		($site) = $request =~ /^GET (\S*)\s/;
2223		if ($site =~ m:^/:) {
2224			# assume this was a http, missing the "http://host"
2225			$site = "http://${dest}$site";
2226		}
2227
2228		### Get the status and mime type from reply
2229		($status)  = $reply =~ /HTTP\/\S*\s(\S*)/s;
2230		($type) = $reply =~ /Content-Type:\s(\S*)/s;
2231		($size) = $reply =~ /Content-Length:\s(\S*)/s;
2232		$type = "-" if $type eq "";
2233		$size = 0 if $size eq "";
2234		$result = $Result_Names{$status} || "TCP_HIT";
2235
2236		### Store the log entry
2237		$HTTPlog{time}{$time} =
2238		 sprintf("%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s\n",
2239		 int($time),(($time - int($time))*1000),($duration*1000),
2240		 $src,$result,$status,$size,"GET",$site,"-","NONE","",
2241		 "-",$type);
2242		$HTTPlog{notempty} = 1;
2243
2244	   } elsif ($request =~ /^POST .* HTTP/) {
2245		### Get the site string
2246		($site) = $request =~ /^POST (\S*)\s/;
2247		if ($site =~ m:^/:) {
2248			# assume this was a http, missing the "http://host"
2249			$site = "http://${dest}$site";
2250		}
2251
2252		### Get the status and mime type
2253		($status)  = $reply =~ /HTTP\/\S*\s(\S*)/s;
2254		($type) = $reply =~ /Content-Type:\s(\S*)/s;
2255		($size) = $reply =~ /Content-Length:\s(\S*)/s;
2256		$type = "-" if $type eq "";
2257		$size = length($TCP{id}{$session_id}) if $size eq "";
2258		$result = $Result_Names{$status} || "TCP_HIT";
2259
2260		### Store the log entry
2261		$HTTPlog{time}{$time} =
2262		 sprintf("%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s\n",
2263		 int($time),(($time - int($time))*1000),($duration*1000),
2264		 $src,$result,$status,$size,"POST",$site,"-","NONE","",
2265		 "-",$type);
2266		$HTTPlog{notempty} = 1;
2267
2268	   }
2269
2270	   #
2271	   # --- Do GETPOST Processing ---
2272	   #
2273	   if ($request =~ /^GET \S*\?\S* HTTP/) {
2274
2275	     ### Get the GET string
2276	     ($site,$get) = $request =~ /^GET (\S*)\?(\S*)\s/;
2277
2278	     # check it looks like a GET,
2279	     if ($get =~ /=/) {
2280
2281		#
2282		#  Populate %GETPOST with a table containing the GET data
2283		#
2284		if (! defined $GETPOST{HTML}[$number]{query}) {
2285			$GETPOST{HTML}[$number]{info} .=
2286			 "<font color=\"red\">GET</font></td><td width=70%>";
2287			$GETPOST{notempty} = 1;
2288		} else {
2289			$GETPOST{HTML}[$number]{query} .= "<hr>\n";
2290		}
2291
2292		#
2293		#  Generate table of query key value pairs
2294		#
2295		$GETPOST{HTML}[$number]{query} .= "$site<br><table border=1>\n";
2296		@Terms = split(/&/,$get);
2297		foreach $term (@Terms) {
2298			($var,$value) = split(/=/,$term);
2299			$value =~ tr/+/ /;
2300			$value =~ s/%([a-f0-9][a-f0-9])/pack("C",hex($1))/egi;
2301			$value =~ s/</&lt;/g;
2302			$value =~ s/>/&gt;/g;
2303			$value =~ s/\n/<br>\n/g;
2304			$GETPOST{HTML}[$number]{query} .=
2305			 "<tr><td><b>$var</b></td>" .
2306			 "<td><font face=\"Courier\">$value</font></td></tr>\n";
2307		}
2308		$GETPOST{HTML}[$number]{query} .= "</table>\n";
2309	     }
2310
2311	   } elsif ($request =~ /^POST .* HTTP/) {
2312
2313	     ### Get the POST strings
2314	     ($junk,$post,$junk1) = split(/\n\n|\r\n\r\n/,$request);
2315
2316	     # check it looks like a POST
2317	     if ($post =~ /=/) {
2318
2319		#
2320		#  Populate %GETPOST with a table containing the POST data
2321		#
2322		if (! defined $GETPOST{HTML}[$number]{query}) {
2323			$GETPOST{HTML}[$number]{info} .=
2324			 "<font color=\"red\">POST</font></td><td width=70%>";
2325			$GETPOST{notempty} = 1;
2326		} else {
2327			$GETPOST{HTML}[$number]{query} .= "<hr>\n";
2328		}
2329
2330		($site) = $request =~ /^POST (\S*)\s/;
2331
2332		$post =~ s/HTTP .*//s;
2333
2334		#
2335		#  Generate table of query key value pairs
2336		#
2337		$GETPOST{HTML}[$number]{query} .= "$site<br><table border=1>\n";
2338		@Terms = split(/&/,$post);
2339		foreach $term (@Terms) {
2340			($var,$value) = split(/=/,$term);
2341			$value =~ tr/+/ /;
2342			$value =~
2343			 s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
2344			$value =~ s/</&lt;/g;
2345			$value =~ s/>/&gt;/g;
2346			$value =~ s/\n/<br>/g;
2347			$GETPOST{HTML}[$number]{query} .=
2348			 "<tr><td><b>$var</b></td>" .
2349			 "<td><font face=\"Courier\">$value</font></td></tr>\n";
2350		}
2351		$GETPOST{HTML}[$number]{query} .= "</table>\n";
2352	     }
2353	   }
2354	}
2355}
2356
2357
2358# Sort_Index - this creates a sort order for the master index.html, based
2359#	on the sort argument (defaults to sort by time).
2360#
2361sub Sort_Index {
2362
2363	if ($Arg{sort} eq "size") {
2364		&Sort_Index_By_Size();
2365	} elsif ($Arg{sort} eq "type") {
2366		&Sort_Index_By_Type();
2367	} elsif ($Arg{sort} eq "ip") {
2368		&Sort_Index_By_IP();
2369	} else {
2370		&Sort_Index_By_Time();
2371	}
2372}
2373
2374
2375# Sort_Index_By_Time - this calculates an appropriate order for the index
2376#	files based on session start time.
2377#
2378sub Sort_Index_By_Time {
2379	my ($session_id,$time,$number);
2380
2381	#
2382	#  Determine Session and Stream time order
2383	#
2384	foreach $session_id (keys %{$TCP{id}}) {
2385		$Index{Time_Order}{"TCP:$session_id"} =
2386		 $TCP{id}{$session_id}{StartTime};
2387	}
2388	foreach $session_id (keys %{$UDP{id}}) {
2389		$Index{Time_Order}{"UDP:$session_id"} =
2390		 $UDP{id}{$session_id}{StartTime};
2391	}
2392	foreach $time (keys %{$ICMP{time}}) {
2393		$Index{Time_Order}{"ICMP:$time"} = $time;
2394	}
2395	$number = 0;
2396	foreach $session (sort {$Index{Time_Order}{$a} <=>
2397	 $Index{Time_Order}{$b}} keys %{$Index{Time_Order}}) {
2398		$number++;
2399		$Index{Sort_Lookup}{$session} = $number;
2400	}
2401}
2402
2403
2404# Sort_Index_By_Size - this calculates an appropriate order for the index
2405#	files based on session size.
2406#
2407sub Sort_Index_By_Size {
2408	my ($session_id,$time,$number);
2409
2410	#
2411	#  Determine Session and Stream size order
2412	#
2413	foreach $session_id (keys %{$TCP{id}}) {
2414		$Index{Size_Order}{"TCP:$session_id"} =
2415		 $TCP{id}{$session_id}{size};
2416	}
2417	foreach $session_id (keys %{$UDP{id}}) {
2418		$Index{Size_Order}{"UDP:$session_id"} =
2419		 $UDP{id}{$session_id}{size};
2420	}
2421	foreach $time (keys %{$ICMP{time}}) {
2422		$Index{Size_Order}{"ICMP:$time"} =
2423		 $ICMP{time}{$time}{size};
2424	}
2425	$number = 0;
2426	foreach $session (sort {$Index{Size_Order}{$b} <=>
2427	 $Index{Size_Order}{$a}} keys %{$Index{Size_Order}}) {
2428		$number++;
2429		$Index{Sort_Lookup}{$session} = $number;
2430	}
2431}
2432
2433
2434# Sort_Index_By_Type - this calculates an appropriate order for the index
2435#	files based on session type, followed by time.
2436#
2437sub Sort_Index_By_Type {
2438	my ($service,$tcp_src_port,$tcp_dest_port,$client,$udp_src_port,
2439	 $udp_dest_port,$session_id,$time,$number);
2440
2441	#
2442	#  Determine Session and Stream time order
2443	#
2444	foreach $session_id (keys %{$TCP{id}}) {
2445		# Determine the service - usually by the lowest numbered port
2446		$tcp_src_port = $TCP{id}{$session_id}{src_port};
2447		$tcp_dest_port = $TCP{id}{$session_id}{dest_port};
2448		($service,$client) = &Pick_Service_Port("TCP",$session_id,
2449		 $tcp_src_port,$tcp_dest_port);
2450
2451		$Index{Type_Order}{"TCP:$session_id"}{1} = 1;
2452		$Index{Type_Order}{"TCP:$session_id"}{2} = $service;
2453		$Index{Type_Order}{"TCP:$session_id"}{3} =
2454		 $TCP{id}{$session_id}{StartTime};
2455	}
2456	foreach $session_id (keys %{$UDP{id}}) {
2457        	# Determine the service - usually by the lowest numbered port
2458		$udp_src_port = $UDP{id}{$session_id}{src_port};
2459		$udp_dest_port = $UDP{id}{$session_id}{dest_port};
2460		($service,$client) = &Pick_Service_Port("UDP",$session_id,
2461		 $udp_src_port,$udp_dest_port);
2462
2463		$Index{Type_Order}{"UDP:$session_id"}{1} = 2;
2464		$Index{Type_Order}{"UDP:$session_id"}{2} = $service;
2465		$Index{Type_Order}{"UDP:$session_id"}{3} =
2466		 $UDP{id}{$session_id}{StartTime};
2467	}
2468	foreach $time (keys %{$ICMP{time}}) {
2469		$Index{Type_Order}{"ICMP:$time"}{1} = 3;
2470		$Index{Type_Order}{"ICMP:$time"}{2} = 0;
2471		$Index{Type_Order}{"ICMP:$time"}{3} = $time;
2472	}
2473
2474	# now we sort by TCP->UDP->IP then port then time.
2475	$number = 0;
2476	foreach $session (sort {
2477		$Index{Type_Order}{$a}{1} <=> $Index{Type_Order}{$b}{1} ||
2478		$Index{Type_Order}{$a}{2} <=> $Index{Type_Order}{$b}{2} ||
2479		$Index{Type_Order}{$a}{3} <=> $Index{Type_Order}{$b}{3}
2480	 } keys %{$Index{Type_Order}}) {
2481		$number++;
2482		$Index{Sort_Lookup}{$session} = $number;
2483	}
2484}
2485
2486
2487# Sort_Index_By_IP - this calculates an appropriate order for the index
2488#	files based on client IP, followed by time.
2489#
2490sub Sort_Index_By_IP {
2491	my ($service,$ip,$ip_dest,$ip_src,$client,
2492	 $session_id,$time,$number,$text,$html,$rest);
2493	my @IP;
2494
2495	#
2496	#  Determine Session and Stream time order
2497	#
2498	foreach $session_id (keys %{$TCP{id}}) {
2499		# Determine source IP
2500		# here we use the same subroutine as the index.html
2501		# so that they match up.
2502		($text,$html) = &Generate_TCP_IDs($session_id);
2503		($ip,$rest) = split(/:/,$text,2);
2504
2505		# Split on IPv4 or IPv6
2506		$IP = ();
2507		if ($ip =~ /\./) { @IP = split(/\./,$ip); }
2508		 else { $IP[0] = $ip; }
2509
2510		$Index{Type_Order}{"TCP:$session_id"}{1} = $IP[0];
2511		$Index{Type_Order}{"TCP:$session_id"}{2} = $IP[1];
2512		$Index{Type_Order}{"TCP:$session_id"}{3} = $IP[2];
2513		$Index{Type_Order}{"TCP:$session_id"}{4} = $IP[3];
2514		$Index{Type_Order}{"TCP:$session_id"}{5} =
2515		 $TCP{id}{$session_id}{StartTime};
2516	}
2517	foreach $session_id (keys %{$UDP{id}}) {
2518		# Determine source IP
2519		$ip = $UDP{id}{$session_id}{src};
2520
2521		# Split on IPv4 or IPv6
2522		$IP = ();
2523		if ($ip =~ /\./) { @IP = split(/\./,$ip); }
2524		 else { $IP[0] = $ip; }
2525
2526		$Index{Type_Order}{"UDP:$session_id"}{1} = $IP[0];
2527		$Index{Type_Order}{"UDP:$session_id"}{2} = $IP[1];
2528		$Index{Type_Order}{"UDP:$session_id"}{3} = $IP[2];
2529		$Index{Type_Order}{"UDP:$session_id"}{4} = $IP[3];
2530		$Index{Type_Order}{"UDP:$session_id"}{5} =
2531		 $UDP{id}{$session_id}{StartTime};
2532	}
2533	foreach $time (keys %{$ICMP{time}}) {
2534		# Determine source IP
2535		$ip = $ICMP{time}{$time}{src};
2536
2537		# Split on IPv4 or IPv6
2538		$IP = ();
2539		if ($ip =~ /\./) { @IP = split(/\./,$ip); }
2540		 else { $IP[0] = $ip; }
2541
2542		$Index{Type_Order}{"ICMP:$time"}{1} = $IP[0];
2543		$Index{Type_Order}{"ICMP:$time"}{2} = $IP[1];
2544		$Index{Type_Order}{"ICMP:$time"}{3} = $IP[2];
2545		$Index{Type_Order}{"ICMP:$time"}{4} = $IP[3];
2546		$Index{Type_Order}{"ICMP:$time"}{5} = $time;
2547	}
2548
2549	# now we sort by IP then time
2550	$number = 0;
2551	foreach $session (sort {
2552		$Index{Type_Order}{$a}{1} <=> $Index{Type_Order}{$b}{1} ||
2553		$Index{Type_Order}{$a}{2} <=> $Index{Type_Order}{$b}{2} ||
2554		$Index{Type_Order}{$a}{3} <=> $Index{Type_Order}{$b}{3} ||
2555		$Index{Type_Order}{$a}{4} <=> $Index{Type_Order}{$b}{4} ||
2556		$Index{Type_Order}{$a}{1} cmp $Index{Type_Order}{$b}{1} ||
2557		$Index{Type_Order}{$a}{5} <=> $Index{Type_Order}{$b}{5}
2558	 } keys %{$Index{Type_Order}}) {
2559		$number++;
2560		$Index{Sort_Lookup}{$session} = $number;
2561	}
2562}
2563
2564
2565# Print_Welcome - print short program welcome message.
2566#
2567sub Print_Welcome {
2568	unless ($Arg{quiet}) {
2569		print "Chaosreader ver 0.94\n\n";
2570	}
2571}
2572
2573
2574# Print_Header1 - print program welcome message.
2575#
2576sub Print_Header1 {
2577	unless ($Arg{quiet}) {
2578		print "Reading $TYPE log...\n";
2579		printf "%6s  %-45s  %s\n","Packet",
2580			"Session (host:port <=> host:port)","Length";
2581	}
2582}
2583
2584
2585# Print_Header2 - print header before loading the file
2586#
2587sub Print_Header2 {
2588	print "\nCreating files...\n" unless $Arg{quiet};
2589	printf "%6s  %-45s  %s\n","Num","Session (host:port <=> host:port)",
2590	 "Service" unless $Arg{quiet};
2591}
2592
2593
2594# Print_Footer1 - print footer at end of program.
2595#
2596sub Print_Footer1 {
2597	if ($Arg{output_index}) {
2598		print "\nindex.html created.\n" unless $Arg{quiet};
2599	}
2600}
2601
2602
2603# Chdir - change directory with error
2604#
2605sub Chdir {
2606	my $dir = shift;
2607	#
2608	#  This can be invoked with $Arg{output_dir}, so $dir won't
2609	#  always be defined - which is okay.
2610	#
2611	if (defined $dir) {
2612		chdir "$dir" ||
2613		 die "ERROR21: Can't cd to $dir: $!\n";
2614	}
2615}
2616
2617
2618# Create_Index_Files - Create the HTML and text index files. This reads
2619#	%Index and creates the files on disk.
2620#
2621sub Create_Index_Files {
2622   my ($html_index,$html_line,$html_links,$image_empty,$getpost_empty);
2623   $getpost_empty = $image_empty = "";
2624
2625   if ($Arg{output_index}) {
2626
2627
2628	######################
2629	# --- index.html ---
2630
2631	$image_empty = "(Empty) " unless $Image{notempty};
2632	$getpost_empty = "(Empty) " unless $GETPOST{notempty};
2633	$httplog_empty = "(Empty) " unless $HTTPlog{notempty};
2634	#
2635	#  Create HTML Index file containing all reports
2636	#
2637	open(FILE,">index.html") || die "ERROR22: creating index: $!\n";
2638	print FILE <<END_HTML;
2639<html>
2640<head><title>Chaosreader Report, $Arg{infile}</title></head>
2641<body bgcolor="white" textcolor="black">
2642<font size=+3>Chaosreader Report</font><br>
2643<font size=+1>File: $Arg{infile}, Type: $TYPE, Created at: $the_date</font><p>
2644<a href="image.html"><font color="blue"><b>Image Report</b></font></a>
2645 $image_empty - Click here for a report on captured images.<br>
2646<a href="getpost.html"><font color="blue"><b>GET/POST Report</b></font></a>
2647 $getpost_empty - Click here for a report on HTTP GETs and POSTs.<br>
2648<a href="httplog.text"><font color="blue"><b>HTTP Proxy Log</b></font></a>
2649 $httplog_empty - Click here for a generated proxy style HTTP log.<p>
2650<font size=+2>TCP/UDP/... Sessions</font><br>
2651<table border=2>
2652END_HTML
2653	for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) {
2654		$html_line = $Index{HTML}[$html_index];
2655		next unless defined $html_line;
2656		print FILE "$html_line </td></tr>\n";
2657	}
2658	print FILE <<END_HTML;
2659</table><p>
2660<font size=+2>IP Count</font><br>
2661<table border=2>
2662END_HTML
2663	foreach $IP (sort {$Count{IP}{$b} <=> $Count{IP}{$a}}
2664	 keys %{$Count{IP}}) {
2665		print FILE "<tr><td>$IP</td><td>$Count{IP}{$IP}</td></tr>\n";
2666	}
2667	print FILE <<END_HTML;
2668</table><p>
2669<font size=+2>TCP Port Count</font><br>
2670<table border=2>
2671END_HTML
2672	foreach $port (sort {$Count{TCPport}{$b} <=> $Count{TCPport}{$a}}
2673	 keys %{$Count{TCPport}}) {
2674		$port_text = $Services_TCP{$port} || $port || "0";
2675		print FILE "<tr><td>$port_text</td><td>$Count{TCPport}{$port}" .
2676		 "</td></tr>\n";
2677	}
2678	print FILE <<END_HTML;
2679</table><p>
2680<font size=+2>UDP Port Count</font><br>
2681<table border=2>
2682END_HTML
2683	foreach $port (sort {$Count{UDPport}{$b} <=> $Count{UDPport}{$a}}
2684	 keys %{$Count{UDPport}}) {
2685		$port_text = $Services_UDP{$port} || $port || "0";
2686		print FILE "<tr><td>$port_text</td><td>$Count{UDPport}{$port}" .
2687		 "</td></tr>\n";
2688	}
2689	print FILE <<END_HTML;
2690</table><p>
2691<font size=+2>IP Protocol Count</font><br>
2692<table border=2>
2693END_HTML
2694	foreach $protocol (sort {$Count{IPprotocol}{$b} <=>
2695	 $Count{IPprotocol}{$a}} keys %{$Count{IPprotocol}}) {
2696		$protocol_text = $IP_Protocols{$protocol};
2697		print FILE "<tr><td>$protocol_text</td><td>" .
2698		 "$Count{IPprotocol}{$protocol}</td></tr>\n";
2699	}
2700	print FILE <<END_HTML;
2701</table><p>
2702<font size=+2>Ethernet Type Count</font><br>
2703<table border=2>
2704END_HTML
2705	foreach $type (sort {$Count{EtherType}{$b} <=> $Count{EtherType}{$a}}
2706	 keys %{$Count{EtherType}}) {
2707		print FILE "<tr><td>$type</td><td>$Count{EtherType}{$type}" .
2708		 "</td></tr>\n";
2709	}
2710	print FILE <<END_HTML;
2711</table>
2712</body>
2713</html>
2714END_HTML
2715
2716
2717	######################
2718	# --- index.text ---
2719
2720	#
2721	#  Create Text index file
2722	#
2723	open(FILE,">index.text") || die "ERROR23: creating index: $!\n";
2724	print FILE "TCP/UDP/... Sessions\nFile: $Arg{infile}, "
2725	 . "Type: $TYPE, Created at: $the_date\n\n";
2726	print FILE @{$Index{Text}};
2727	close FILE;
2728
2729
2730	######################
2731	# --- image.html ---
2732
2733	#
2734	#  Create HTML Image Index file to display images
2735	#
2736	open(FILE,">image.html") || die "ERROR24: creating index: $!\n";
2737	print FILE <<END_HTML;
2738<html>
2739<head><title>Chaosreader Image Report</title></head>
2740<body bgcolor="white" textcolor="black">
2741<font size=+3>Chaosreader Image Report</font><br>
2742<font size=+1>Created at: $the_date, Type: $TYPE</font><p>
2743<font size=+2>Images</font><br>
2744<table border=2>
2745END_HTML
2746	for ($html_index=0; $html_index <= $#{$Index{HTML}}; $html_index++) {
2747		$html_line = $Image{HTML}[$html_index]{info};
2748		$html_links = $Image{HTML}[$html_index]{links};
2749		next unless defined $html_links;
2750		print FILE "$html_line $html_links </td></tr>\n";
2751	}
2752	print FILE <<END_HTML;
2753</table><p>
2754</body>
2755</html>
2756END_HTML
2757
2758
2759	######################
2760	# --- getpost.html ---
2761
2762	#
2763	#  Create HTML GETPOST Index file to show HTTP GETs and POSTs
2764	#
2765	open(FILE,">getpost.html") || die "ERROR25: creating index: $!\n";
2766	print FILE <<END_HTML;
2767<html>
2768<head><title>Chaosreader GET/POST Report</title></head>
2769<body bgcolor="white" textcolor="black">
2770<font size=+3>Chaosreader GET/POST Report</font><br>
2771<font size=+1>Created at: $the_date, Type: $TYPE</font><p>
2772<font size=+2>HTTP GETs and POSTs</font><br>
2773<table border=2>
2774END_HTML
2775	for ($html_index=0; $html_index <= $#{$GETPOST{HTML}}; $html_index++) {
2776		$html_line = $GETPOST{HTML}[$html_index]{info};
2777		$html_links = $GETPOST{HTML}[$html_index]{query};
2778		next unless defined $html_links;
2779		print FILE "$html_line $html_links </td></tr>\n";
2780	}
2781	print FILE <<END_HTML;
2782</table><p>
2783</body>
2784</html>
2785END_HTML
2786
2787   }
2788}
2789
2790
2791
2792# Create_Index_Master - Create the HTML and text master index files. This
2793#	reads @Master and creates the files on disk.
2794#
2795sub Create_Index_Master {
2796
2797   my ($start,$end,$dir,$file,$index,$duration);
2798
2799   if ($Arg{output_index}) {
2800
2801	#
2802	#  Create most recent link
2803	#
2804
2805	$dir = $Master[$#Master]{dir};
2806	$recentname = "most_recent_index";
2807	unlink("$recentname");
2808	# don't die on symlink error, it's not essential
2809	symlink("$dir","$recentname");
2810
2811	#
2812	#  Create HTML Index file containing all reports
2813	#
2814	open(FILE,">index.html") || die "ERROR26: creating index: $!\n";
2815	print FILE <<END_HTML;
2816<html>
2817<head><title>Chaosreader Master Index</title></head>
2818<body bgcolor="white" textcolor="black" vlink="blue">
2819<font size=+3>Chaosreader Master Index</font><br>
2820<font size=+1>Created at: $the_date, Type: $TYPE</font><p>
2821<a href="$recentname/index.html"><font color="red">
2822<b>Most Recent Report</b></font></a>
2823 - Click here for the most recent index, and click reload for updates.<p>
2824<font size=+2>Chaosreader Reports</font><br>
2825<table border=2>
2826END_HTML
2827	for ($index=0; $index <= $#Master; $index++) {
2828		$start = $Master[$index]{starttime};
2829		$end = $Master[$index]{endtime};
2830		$dir = $Master[$index]{dir};
2831		$file = $Master[$index]{file};
2832		$size = $Master[$index]{size};
2833		$duration = $Master[$index]{duration};
2834		$html_line = "<tr><td><i>". ($index+1) . "</i></td>" .
2835		 "<td><b>$start</b></td><td><b>$end</b></td>\n" .
2836		 "<td>$duration s</td>" . "<td><font color=\"green\"> " .
2837		 "$size bytes</font></td>" .
2838		 "<td><a href=\"$dir/index.html\">$dir/$file</a></td></tr>\n";
2839		print FILE "$html_line </td></tr>\n";
2840	}
2841	print FILE <<END_HTML;
2842</table><p>
2843<font size=+2>IP Count</font><br>
2844<table border=2>
2845END_HTML
2846	foreach $IP (sort {$CountMaster{IP}{$b} <=> $CountMaster{IP}{$a}}
2847	 keys %{$CountMaster{IP}}) {
2848		print FILE "<tr><td>$IP</td><td>$CountMaster{IP}{$IP}" .
2849		 "</td></tr>\n";
2850	}
2851	print FILE <<END_HTML;
2852</table><p>
2853<font size=+2>TCP Port Count</font><br>
2854<table border=2>
2855END_HTML
2856	foreach $port (sort {$CountMaster{TCPport}{$b} <=>
2857	 $CountMaster{TCPport}{$a}} keys %{$CountMaster{TCPport}}) {
2858		$port_text = $Services_TCP{$port} || $port || "0";
2859		print FILE "<tr><td>$port_text</td><td>" .
2860		 "$CountMaster{TCPport}{$port}</td></tr>\n";
2861	}
2862	print FILE <<END_HTML;
2863</table><p>
2864<font size=+2>UDP Port Count</font><br>
2865<table border=2>
2866END_HTML
2867	foreach $port (sort {$CountMaster{UDPport}{$b} <=>
2868	 $CountMaster{UDPport}{$a}} keys %{$CountMaster{UDPport}}) {
2869		$port_text = $Services_UDP{$port} || $port || "0";
2870		print FILE "<tr><td>$port_text</td><td>" .
2871		 "$CountMaster{UDPport}{$port}</td></tr>\n";
2872	}
2873	print FILE <<END_HTML;
2874</table><p>
2875<font size=+2>IP Protocol Count</font><br>
2876<table border=2>
2877END_HTML
2878	foreach $protocol (sort {$CountMaster{IPprotocol}{$b} <=>
2879	 $CountMaster{IPprotocol}{$a}} keys %{$CountMaster{IPprotocol}}) {
2880		$protocol_text = $IP_Protocols{$protocol};
2881		print FILE "<tr><td>$protocol_text</td><td>" .
2882		 "$CountMaster{IPprotocol}{$protocol}</td></tr>\n";
2883	}
2884	print FILE <<END_HTML;
2885</table><p>
2886<font size=+2>Ethernet Type Count</font><br>
2887<table border=2>
2888END_HTML
2889	foreach $type (sort {$CountMaster{EtherType}{$b} <=>
2890	 $CountMaster{EtherType}{$a}} keys %{$CountMaster{EtherType}}) {
2891		print FILE "<tr><td>$type</td><td>" .
2892		 "$CountMaster{EtherType}{$type}</td></tr>\n";
2893	}
2894	print FILE <<END_HTML;
2895</table>
2896</body>
2897</html>
2898END_HTML
2899
2900	#
2901	#  Create Text index file
2902	#
2903	open(FILE,">index.text") || die "ERROR27: creating index: $!\n";
2904	print FILE "Master Indexes\nCreated at: $the_date, Type: $TYPE\n\n";
2905	for ($index=0; $index <= $#Master; $index++) {
2906		$start = $Master[$index]{starttime};
2907		$end = $Master[$index]{endtime};
2908		$dir = $Master[$index]{dir};
2909		$file = $Master[$index]{file};
2910		$size = $Master[$index]{size};
2911		$duration = $Master[$index]{duration};
2912		printf FILE "%-25s %3s s %8s b  %s\n",$start,$duration,
2913		 $size,"$dir/index.text";
2914	}
2915	close FILE;
2916
2917
2918	#
2919	#  Create index.file for redos
2920	#
2921	open(FILE,">index.file") || die "ERROR28: creating index: $!\n";
2922	for ($index=0; $index <= $#Master; $index++) {
2923		$dir = $Master[$index]{dir};
2924		$file = $Master[$index]{file};
2925		$start = $Master[$index]{starttime};
2926		$end = $Master[$index]{endtime};
2927		$duration = $Master[$index]{duration};
2928		print FILE "$dir\t$file\t$duration\t$start\t$end\n";
2929	}
2930	close FILE;
2931   }
2932}
2933
2934
2935# Create_Log_Files - create log files such as the HTTP log.
2936#
2937sub Create_Log_Files {
2938	#BDG some memory debug
2939	#system("pmap -x $$");
2940
2941	#
2942	#  Create HTTPlog.text
2943	#
2944	open(FILE,">httplog.text") || die "ERROR29: creating HTTP log: $!\n";
2945
2946	foreach $time (sort { $a <=> $b }(keys (%{$HTTPlog{time}}))) {
2947		print FILE $HTTPlog{time}{$time};
2948	}
2949
2950	close FILE;
2951}
2952
2953
2954
2955# File_Type - return file extension for given data, else "data".
2956#
2957sub File_Type {
2958	my $data = $_[0];
2959	my $type = "";
2960
2961	if ($data =~ /^GIF8[7-9]/) 		{ $type = "gif"; }
2962	elsif ($data =~ /^\377.....(JPEG|JFIF)/) 	{ $type = "jpeg"; }
2963	elsif ($data =~ /^PK\003\004/) 		{ $type = "zip"; }
2964	elsif ($data =~ /^\%PDF/) 		{ $type = "pdf"; }
2965	elsif ($data =~ /^\037\213/) 		{ $type = "gz"; }
2966	elsif ($data =~ /^BZh/) 		{ $type = "bz2"; }
2967	elsif ($data =~ /^\177ELF/) 		{ $type = "elf"; }
2968	elsif ($data =~ /^\%!/) 		{ $type = "ps"; }
2969	elsif ($data =~ /<html>/i) 		{ $type = "html"; }
2970	else { $type = "data"; }
2971
2972	return $type;
2973}
2974
2975
2976# Is_Image - returns true if extension is for an image.
2977#
2978sub Is_Image {
2979	my $ext = shift;
2980
2981	return 1 if ($ext eq "jpeg");
2982	return 1 if ($ext eq "gif");
2983
2984	return 0;
2985}
2986
2987
2988# Desex_HTML - Removes HTML tags ("<" and ">") from data, so that it no
2989#		longer interferes when printed as HTML.
2990#
2991sub Desex_HTML {
2992	### Input
2993	my $data = shift;
2994
2995	### Process
2996	# remove "<" and ">"s
2997	$data =~ s/</&lt;/g;
2998	$data =~ s/>/&gt;/g;
2999
3000	### Return
3001	return $data;
3002}
3003
3004
3005
3006# Process_BothHTML - Process the HTML 2-way session. Remove binary junk
3007#			that dosen't render well in a browser.
3008#
3009sub Process_BothHTML {
3010	### Input
3011	my $type = shift;
3012	my $session_id = shift;
3013	my $plain = shift;
3014	my $wrapped = "";
3015	my $index = 0;
3016	my $counter = 0;
3017	my $intag = 0;
3018	my ($char,$data);
3019
3020	if ($type eq "TCP") {
3021		$data = $TCP{id}{$session_id}{BothHTML};
3022	} elsif ($type eq "UDP") {
3023		$data = $UDP{id}{$session_id}{BothHTML};
3024	} elsif ($type eq "ICMP") {
3025		$data = $ICMP{time}{$session_id}{BothHTML};
3026	}
3027
3028	### Process (order dependant)
3029	$data =~ s/font color="red">     \0</font color="red"></g;
3030	$data =~ tr/\040-\176\n\r\f/./c;		# max 376, was 245
3031	if (defined $plain) {
3032	   # This is a plain style of line wrap
3033	   $data =~ s/([^\n\f<>]{$WRAP})/$&\n/g;
3034	} else {
3035	   # This is a fancy line wrap, a green ">" starts the wrapped lines
3036	   $data =~ s/([^\n\f<>]{$WRAP})/$&\n<font color="green">&gt;<\/font>/g;
3037	}
3038
3039	### Save
3040	if ($type eq "TCP") {
3041		$TCP{id}{$session_id}{BothHTML} = $data;
3042	} elsif ($type eq "UDP") {
3043		$UDP{id}{$session_id}{BothHTML} = $data;
3044	} elsif ($type eq "ICMP") {
3045		$ICMP{time}{$session_id}{BothHTML} = $data;
3046	}
3047
3048}
3049
3050# Process_This_HTML - Process the HTML 2-way session. Remove binary junk
3051#			that dosen't render well in a browser.
3052#
3053sub Process_This_HTML {
3054	### Input
3055	my $data = shift;
3056	my $plain = shift;
3057	my $wrapped = "";
3058	my $index = 0;
3059	my $counter = 0;
3060	my $intag = 0;
3061	my ($char);
3062
3063	### Process (order dependant)
3064	$data =~ s/font color="red">     \0</font color="red"></g;
3065	$data =~ tr/\040-\176\n\r\f/./c;		# max 376, was 245
3066	if (defined $plain) {
3067	   # This is a plain style of line wrap
3068	   $data =~ s/([^\n\f<>]{$WRAP})/$&\n/g;
3069	} else {
3070	   # This is a fancy line wrap, a green ">" starts the wrapped lines
3071	   $data =~ s/([^\n\f<>]{$WRAP})/$&\n<font color="green">&gt;<\/font>/g;
3072	}
3073
3074	return $data;
3075}
3076
3077
3078# Process_Hex - Create the coloured HTML 2-way hex dump, and a text dump.
3079#		For code reuse it uses it's own data structure %Hex.
3080#		(Originally used %TCP{id}{$session_id}{hex}).
3081#
3082sub Process_Hex {
3083	### Input
3084	my $type = shift;
3085	my $session_id = shift;
3086	my $data = shift;
3087	my $colour = shift;
3088	my $pos = $Hex{$type}{$session_id}{pos};
3089	my $offset = $Hex{$type}{$session_id}{offset};
3090	my $hexhtml = $Hex{$type}{$session_id}{hexhtml};
3091	my $viewhtml = $Hex{$type}{$session_id}{viewhtml};
3092	my $hextext = $Hex{$type}{$session_id}{hextext};
3093	my $viewtext = $Hex{$type}{$session_id}{viewtext};
3094	my (@Bytes,$byte,$view,$view2);
3095
3096
3097	$pos = 1 unless defined $pos;
3098	$offset = 0 unless defined $offset;
3099	$hexhtml .= "<font color=\"$colour\">";
3100	$viewhtml .= "<font color=\"$colour\">";
3101
3102	### Process
3103	@Bytes = unpack("C*",$data);
3104	foreach $byte (@Bytes) {
3105		$view = chr($byte);
3106		$view =~ tr/\040-\176/./c;
3107		$view2 = $view;
3108		$view2 =~ s/</&lt;/g;
3109		$view2 =~ s/>/&gt;/g;
3110		$viewhtml .= $view2;
3111		$viewtext .= $view;
3112		$hexhtml .= sprintf("%2.2x",$byte);
3113		$hextext .= sprintf("%2.2x",$byte);
3114		$pos++;
3115		if ($pos > 16) {
3116			### Save text version
3117			$Hex{$type}{$session_id}{text} .=
3118			 sprintf("%6.08x",$offset) . "  $hextext  $viewtext\n";
3119
3120			### Save HTML version
3121			$Hex{$type}{$session_id}{HTML} .=
3122			 '<font color="green">' . sprintf("%6.08x",$offset) .
3123			 "</font>  $hexhtml  $viewhtml\n";
3124
3125			$pos = 1;
3126			$offset += 16;
3127			$hexhtml = "</font><font color=\"$colour\">";
3128			$viewhtml = "</font><font color=\"$colour\">";
3129			$hextext = $viewtext = "";
3130		}
3131		if ( ($pos != 1) && (($pos %2) == 1) ) {
3132			$hexhtml .= " ";
3133			$hextext .= " ";
3134		}
3135	}
3136	$hexhtml .= "</font>";
3137	$viewhtml .= "</font>";
3138
3139	$Hex{$type}{$session_id}{pos} = $pos;
3140	$Hex{$type}{$session_id}{offset} = $offset;
3141	$Hex{$type}{$session_id}{hexhtml} = $hexhtml;
3142	$Hex{$type}{$session_id}{viewhtml} = $viewhtml;
3143	$Hex{$type}{$session_id}{hextext} = $hextext;
3144	$Hex{$type}{$session_id}{viewtext} = $viewtext;
3145}
3146
3147
3148
3149# Process_Hex_Finish - Finish the hex dumps.
3150#
3151sub Process_Hex_Finish {
3152	### Input
3153	my $type = shift;
3154	my $session_id = shift;
3155	my $pos = $Hex{$type}{$session_id}{pos};
3156	my $offset = $Hex{$type}{$session_id}{offset};
3157	my $hexhtml = $Hex{$type}{$session_id}{hexhtml};
3158	my $viewhtml = $Hex{$type}{$session_id}{viewhtml};
3159	my $hextext = $Hex{$type}{$session_id}{hextext};
3160	my $viewtext = $Hex{$type}{$session_id}{viewtext};
3161	my ($short);
3162
3163	return unless defined $pos;
3164	return if $pos == 1;
3165
3166	$short = 39 - length($hextext);
3167	$hexhtml .= " " x $short;
3168	$hextext .= " " x $short;
3169
3170	### Save text version
3171	$Hex{$type}{$session_id}{text} .=
3172	 sprintf("%6.08x",$offset) . "  $hextext  $viewtext\n";
3173
3174	### Save HTML version
3175	$Hex{$type}{$session_id}{HTML} .=
3176	 '<font color="green">' . sprintf("%6.08x",$offset) .
3177	 "</font>  $hexhtml  $viewhtml\n";
3178
3179}
3180
3181
3182# Generate_X11_HTML - fetch the text from an X11 session and save
3183# 	as bidirectional 2-way coloured HTML.
3184#
3185# Todo: check if a text or keypress event can be split during
3186#  transmission and add code similar to X11 replay to handle this.
3187#
3188sub Generate_X11_HTML {
3189	my ($filename,$data,$copy,$xcode,$xbyte,$xlength,$xrest,$d,
3190	 $xlv,$xvalue,$pad,$y,$yold,$chars,$colour,$session_data,
3191	 $service_name,$colourold,$store,$keytype,$gotsome);
3192	my @Times;
3193
3194	$session_data = "";
3195
3196	### Input
3197	my $session_id = shift;
3198	$data = "";
3199	$service_name = "X11";
3200
3201	### Processing
3202	my $session_text = $session_id;
3203	$session_text =~ s/,/ <-> /;
3204
3205	### Fetch raw data
3206	$xserver = &TCP_Follow_RawA($session_id);
3207
3208	#
3209	#  Determine endian of this transfer.
3210	#
3211	($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver);
3212	#
3213	#  Create aliases for "n" and "N".
3214	#
3215	if ($xvalue < 256) {
3216		$n = "n"; $N = "N";
3217	} else {
3218		$n = "v"; $N = "V";
3219	}
3220	#
3221	#  Determine keymap style - see &Set_X11_KeyCodes()
3222	#
3223	if ($xserver =~
3224	 /q...Q.*w...W.*e...E.*r...R.*t...T.*y...Y.*u...U.*i...I.*o...O.*p/) {
3225		$keytype = "linux";
3226	} else {
3227		$keytype = "sun";
3228	}
3229
3230	#
3231	#  Fetch data from both directions, sorting on timestamps
3232	#
3233	@Times = sort{$a <=> $b} (keys %{$TCP{id}{$session_id}{time}});
3234
3235	#
3236	# --- Main Loop ---
3237	#
3238	# (this needs to be a for loop!)
3239	for ($i=0; $i <= $#Times; $i++) {
3240	   $time = $Times[$i];
3241
3242	   ### Fetch X11 data and direction as a colour
3243	   if (defined $TCP{id}{$session_id}{time}{$time}{dir}) {
3244		$copy = $TCP{id}{$session_id}{time}{$time}{data};
3245		if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") {
3246			$colour = "red";
3247		} else {
3248			$colour = "blue";
3249		}
3250	   }
3251
3252	   $xrest = $copy;
3253	   #
3254	   #  Process through X11 codes
3255	   #
3256	   while (length($xrest) > 0) {
3257		### Fetch xcode and other values
3258		($xcode,$xbyte,$xlength,$xrest) = unpack("CC${n}a*",$xrest);
3259		$chars = "";
3260
3261		#
3262		#  Fetch code values from $xrest, and trim
3263		#  $xrest. For most requests, the value length
3264		#  is a field (bytes 3,4) except for XErrors
3265		#  (code 0) where the total length is always 32.
3266		#
3267		if ($xcode == 0) {
3268			$xlv = 28;
3269		} else {
3270			$xlv = ($xlength - 1) * 4;
3271			$xlv = -$xlv if $xlv < 0;
3272		}
3273
3274		### Fetch values for this xcode
3275		($xvalue,$xrest) = unpack("a${xlv}a*",$xrest);
3276
3277		$store = 0;
3278
3279		#
3280		#  Process a draw text event (76, 77)
3281		#
3282		if (($colour eq "blue") && (($xcode == 76)||($xcode == 77))) {
3283			# Check if this is a xImageText16Req
3284			if ($xcode == 77) { $xbyte *= 2; }
3285
3286			($pad,$y,$chars) = unpack("a10${n}a$xbyte",$xvalue);
3287			if ($yold != $y) { $chars = "\n$chars"; }
3288			$chars =~ s/\0//g;
3289
3290			$store = 1;
3291			$yold = $y;
3292		}
3293
3294		#
3295		#  Process a key pressed event (2)
3296		#
3297		if (($colour eq "red") && ($xcode = "2")) {
3298			($pad,$caps,$pad) = unpack("a24${n}a*",$xvalue);
3299
3300			#
3301			#  Translate the X11 KeyCode to the actual char
3302			#  (try "xmodmap -pke")
3303			#
3304			$chars = $KeyCode{$keytype}{$caps}{$xbyte};
3305
3306			### Don't keep red \n's for neatness (keep blue ones)
3307			unless ($chars eq "\n") {
3308				$store = 1;
3309			}
3310		}
3311
3312		#
3313		#  Process a text scroll event (by using 62 - copy area)
3314		#
3315		if (($colour eq "blue") && ($xcode == 62)) {
3316			$chars = "\n";
3317			$store = 1;
3318		}
3319
3320		### Store data
3321		if ($store) {
3322			if ($colour ne $colourold) {
3323				$session_data .=
3324				 "</font><font color=\"$colour\">$chars";
3325			} else {
3326				$session_data .= $chars;
3327			}
3328			$colourold = $colour;
3329		}
3330	   }
3331	}
3332
3333	$TCP{id}{$session_id}{BothHTML} = $session_data;
3334}
3335
3336
3337# Save_Both_HTML - Save bidirectional (coloured) data into a html file.
3338#
3339sub Save_Both_HTML {
3340	my ($filename);
3341
3342	### Input
3343	my $type = shift;
3344	my $session_id = shift;
3345	my $number = shift;
3346	my $service_name = shift;
3347	my $session_text = shift;
3348	my $numtext = sprintf("%04d",$number);
3349	my ($base,$raw);
3350
3351	$session_text = $session_id unless defined $session_text;
3352
3353	### Processing
3354	$session_text =~ s/,/ <-> /;
3355
3356	### Checks
3357	$ext = "";
3358	$session_data = "";
3359	if ($type eq "TCP") {
3360	   $base = "session";
3361	   #
3362	   # Note, the following is similar code for TCP, UDP and ICMP.
3363	   # However UDP and ICMP use a simple strategy to store and fetch
3364	   # the processed HTML; whereas TCP uses a complex yet memory
3365	   # efficient strategy. This is intentional - the way TCP has
3366	   # been stored has been tuned to reduce memory usage, as TCP has
3367	   # the bulk of the data (and the bulk of the memory problem). This
3368	   # has not been necessary with UDP and ICMP (yet).
3369	   #
3370	   if ($TCP{id}{$session_id}{BothHTML} ne "") {
3371		#
3372		#  If the BothHTML report has already been calculated, fetch
3373		#
3374		$session_data = $TCP{id}{$session_id}{BothHTML};
3375	   } else {
3376		#
3377		#  Generate a BothHTML report by following packets by time
3378		#
3379		foreach $time (sort {$a <=> $b}
3380		 (keys (%{$TCP{id}{$session_id}{time}}))) {
3381		   $raw = $TCP{id}{$session_id}{time}{$time}{data};
3382		   $raw = &Desex_HTML($raw);
3383		   next unless length($raw);
3384		   if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") {
3385			$session_data .= "<font color=\"blue\">$raw</font>";
3386		   } else {
3387			$session_data .= "<font color=\"red\">$raw</font>";
3388		   }
3389	   	}
3390	   	$session_data = &Process_This_HTML($session_data);
3391	   	$base = "session";
3392	   	if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3393	   }
3394
3395	} elsif ($type eq "UDP") {
3396		$base = "stream";
3397		$session_data = $UDP{id}{$session_id}{BothHTML};
3398		if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3399	} elsif ($type eq "ICMP") {
3400		$base = "icmp";
3401		$session_data = $ICMP{time}{$session_id}{BothHTML};
3402		if ($ICMP{time}{$session_id}{Partial}) { $ext = ".partial"; }
3403	} else {
3404		$base = "are_belong_to_us";
3405	}
3406
3407	### Do nothing if there is no data ("26" is mostly due to colour tags)
3408	return unless ((defined $session_data)&&(length($session_data) > 26));
3409
3410	### Output
3411        $filename = "${base}_${numtext}.${service_name}${ext}.html";
3412	open (OUT,">$filename") || die "ERROR30: file create, $filename: $!\n";
3413	binmode(OUT);
3414	print OUT "<HTML>\n<HEAD><TITLE>$number</TITLE></HEAD>" .
3415	 "<BODY bgcolor=\"white\">\n" .
3416	 "<H1>$service_name: $session_text</H1>\n" .
3417	 "<H2>File $Arg{infile}, Session $number</H2>\n" .
3418	 "<PRE WRAP=\"virtual\">\n" .
3419         $session_data . "</PRE>\n</BODY>\n</HTML>\n";
3420        close OUT;
3421
3422	### Global Vars
3423	my $length = length($session_data);
3424	$Index{HTML}[$number] .= "<li><a href=\"$filename\">as_html</a></li>\n";
3425	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
3426	 '"' , "   $filename","",$length);
3427}
3428
3429
3430
3431# Save_Hex_HTML - Save bidirectional (coloured) hex data into a html file.
3432#
3433sub Save_Hex_HTML {
3434	my ($filename);
3435
3436	### Input
3437	my $type = shift;
3438	my $session_id = shift;
3439	my $number = shift;
3440	my $service_name = shift;
3441	my $session_text = shift;
3442	my $session_data = $Hex{$type}{$session_id}{HTML};
3443	my $numtext = sprintf("%04d",$number);
3444	my ($base);
3445
3446	$session_text = $session_id unless defined $session_text;
3447	$session_data = "" unless defined $session_data;
3448
3449
3450	### Processing
3451	$session_text =~ s/,/ <-> /;
3452
3453	### Checks
3454	$ext = "";
3455	if ($type eq "TCP") {
3456		$base = "session";
3457		if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3458	} elsif ($type eq "UDP") {
3459		$base = "stream";
3460		if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3461	} elsif ($type eq "ICMP") {
3462		$base = "icmp";
3463		if ($ICMP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3464	}
3465
3466	### Output
3467        $filename = "${base}_${numtext}.${service_name}${ext}.hex.html";
3468	open (OUT,">$filename") || die "ERROR31: file create, $filename: $!\n";
3469	binmode(OUT);
3470	print OUT "<HTML>\n<HEAD><TITLE>$number</TITLE></HEAD>" .
3471	 "<BODY bgcolor=\"white\">\n" .
3472	 "<H1>$service_name: $session_text</H1>\n" .
3473	 "<H2>File $Arg{infile}, Session $number</H2>\n" .
3474	 "<PRE WRAP=\"virtual\">\n" .
3475         $session_data . "</PRE>\n</BODY>\n</HTML>\n";
3476        close OUT;
3477
3478	### Global Vars
3479	my $length = length($session_data);
3480	$Index{HTML}[$number] .= "<li>";
3481	$Index{HTML}[$number] .= "<a href=\"$filename\">hex</a></li>\n";
3482	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
3483	 '"' , "   $filename","",$length);
3484}
3485
3486
3487
3488# Save_Hex_Text - Save bidirectional hex data into a text file.
3489#
3490sub Save_Hex_Text {
3491	my ($filename);
3492
3493	### Input
3494	my $type = shift;
3495	my $session_id = shift;
3496	my $number = shift;
3497	my $session_text = shift;
3498	my $session_data = $Hex{$type}{$session_id}{text};
3499	my $numtext = sprintf("%04d",$number);
3500	my ($base);
3501
3502	$session_text = $session_id unless defined $session_text;
3503	$session_data = "" unless defined $session_data;
3504
3505	### Processing
3506	$session_text =~ s/,/ <-> /;
3507
3508	### Checks
3509	$ext = "";
3510	if ($type eq "TCP") {
3511		$base = "session";
3512		if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3513	} elsif ($type eq "UDP") {
3514		$base = "stream";
3515		if ($UDP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3516	} elsif ($type eq "ICMP") {
3517		$base = "icmp";
3518		if ($ICMP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3519	}
3520
3521	### Output
3522        $filename = "${base}_${numtext}.${service_name}${ext}.hex.text";
3523	open (OUT,">$filename") || die "ERROR32: file create, $filename: $!\n";
3524	binmode(OUT);
3525	print OUT "$service_name: $session_text\n" .
3526	 "File $Arg{infile}, Session $number\n\n$session_data\n";
3527        close OUT;
3528
3529	### Global Vars
3530	my $length = length($session_data);
3531	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
3532	 '"' , "   $filename","",$length);
3533}
3534
3535
3536# Save_FTP_File - Save files from an active FTP session.
3537#
3538sub Save_FTP_File {
3539	my ($filename,$ftp_data,$length);
3540	my $session_id = shift;
3541	my $number = shift;
3542	my $numtext = sprintf("%04d",$number);
3543	my $service_name = "ftp-data";
3544
3545	### Input
3546	$ftp_data = &TCP_Follow_RawB($session_id);
3547	if (! defined $ftp_data) {
3548		$ftp_data = &TCP_Follow_RawA($session_id);
3549	}
3550
3551	### Checks
3552	$ftp_type = &File_Type($ftp_data);
3553	if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
3554	 else { $ext = ""; }
3555
3556	### Output
3557        $filename = "session_${numtext}.part_01.$service_name${ext}.$ftp_type";
3558        open (OUT,">$filename") || die "ERROR33: file create, $filename: $!\n";
3559	binmode(OUT);		# for backward OSs
3560        print OUT $ftp_data;
3561        close OUT;
3562
3563	### Global Vars
3564	$length = length($ftp_data);
3565	$Index{HTML}[$number] .=
3566	 "<li><a href=\"$filename\">$filename</a> $length bytes</li>\n";
3567	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
3568	 '"' , "   $filename","",$length);
3569	if (&Is_Image($ftp_type)) {
3570		$Image{HTML}[$number]{links} .=
3571		 "<img src=\"$filename\"> ";
3572		$Image{notempty} = 1;
3573	}
3574}
3575
3576# NOTE On Replays
3577#
3578# The essence of these is to playback the client/server data so that
3579# the original session can be replayed. There are two styles,
3580#
3581# Text Replays. These playback the text component to the application
3582# data to the screen. These usally work well. The actual text data is not
3583# cleaned up in any way, so to preserve escape sequences necessary to
3584# redisplay in the original style. Eg, telnet.
3585#
3586# GUI Replays, or Server/Client Replays. These often use TCP/IP to send
3587# the data back to the server or client to playback the session. These
3588# are less robust, mainly becuase negotiation can occur slightly differently
3589# causing nothing to be displayed. There is code here to redo the
3590# negotiation - but it is very difficult for this to be 100% robust.
3591# The main reasons the GUI replays fail are colour depth mismatch
3592# and dropped packets. Eg, X11.
3593#
3594# Both styles print the binary data within single quotes ' '. This
3595# creates perl programs that can't be "cat" (use cat -vet), or edited
3596# in vi (use vim) due to the raw binary data. A neater style would be to
3597# translate the binary data into octal or hex text streams, eg
3598# 'print "\015\012\087\012"'... Currently this is not used, as it would
3599# roughly increase the file size by a factor of 4. However plopping
3600# data in the middle of perl programs creates problems of it's own
3601# (see the unusual seds). At some point I may opt for the easier,
3602# although lengthier, method.
3603
3604
3605# Save_Session_Replay - Save a replay program for this session. eg, telnet.
3606#
3607sub Save_Session_Replay {
3608	my ($filename,$duration,$time);
3609	my $session_id = shift;
3610	my $number = shift;
3611	my $service_name = shift;
3612	my $numtext = sprintf("%04d",$number);
3613
3614	### Output
3615	$filename = "session_${numtext}.${service_name}.replay";
3616	$duration = ($TCP{id}{$session_id}{EndTime} -
3617	 $TCP{id}{$session_id}{StartTime});
3618	$duration = sprintf("%.0f",$duration);
3619	open (REPLAY,">$filename") ||
3620	 die "ERROR34: creating $filename $!\n";
3621	binmode(REPLAY);	# for backward OSs
3622
3623	#
3624	#  Create a perl program, that when run itself will print out
3625	#  the contents of the server 1-way stream, with pauses based on
3626	#  the packet arrival times (replay the session in realtime).
3627	#
3628	print REPLAY "#!$PERL\n";
3629	print REPLAY <<'END';
3630#
3631# This is a telnet/login replay program. It will replay a session using
3632# the timestamps from the packet log.
3633#
3634# USAGE: run the script as normal. You can provide a factor as an
3635#	argument, eg "2" to run twice as fast, or "0.5" to run
3636#	at half time. eg,
3637# 		./session_0002.telnet.replay 2
3638#
3639# Auto generated by Chaosreader.
3640#
3641$| = 1;
3642$factor = $ARGV[0] || 1;
3643sub ms {
3644	$ms = shift;
3645	$ms = $ms / $factor;
3646	select(undef, undef, undef, $ms);
3647}
3648END
3649
3650	#
3651	#  Sort the data on the timestamps, calculating timestamp differences
3652	#  to record in the replay program.
3653	#
3654	@Times = ();
3655	foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
3656		if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") {
3657			push(@Times,$time)
3658		}
3659	}
3660	@Times = sort { $a <=> $b } @Times;
3661
3662	for ($i=0; $i <= $#Times; $i++) {		# required
3663
3664		### Calculate time diff if possible
3665		if ($i == $#Times) {
3666			$timediff = 0;
3667		} else {
3668			$timediff = $Times[$i+1] - $Times[$i];
3669			if ($timediff < 0) { $timediff = 0; }
3670		}
3671		$time = $Times[$i];
3672
3673		### Fetch data from mem
3674		$data = $TCP{id}{$session_id}{time}{$time}{data};
3675
3676		#
3677		#  Clean the data a little (order important)
3678		#
3679		$data =~ s/\\/\\\\/g;	# backslash the backslashes
3680		$data =~ s/'/\\'/g;	# backslash single quotes
3681
3682		#
3683		#  Now output the data in the replay program
3684		#
3685		print REPLAY "print '" . $data . "';\n";
3686
3687		#
3688		#  This causes the replay program to pause
3689		#
3690		print REPLAY "ms($timediff);\n";
3691	}
3692	close REPLAY;
3693
3694	### Better make it executable
3695	chmod (0755, "$filename");
3696
3697	### Global Vars
3698	$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
3699	 "</a> $duration seconds</li>\n";
3700	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
3701	 '"' , "   $filename","",$duration);
3702}
3703
3704
3705# Save_Session_textSSH_files - Save a replay program to display the SSH
3706#	session in a text format, a html form of this, and a key delay
3707#	data file.
3708#
3709# The program "sshkeydata" will take the key delay data file and estimate
3710# the original typed commands. (It also needs a key delay data file
3711# from a plaintext session such as telnet, which is generated by the
3712# Save_Session_Keydata subroutine).
3713#
3714# This has been designed with SSH ver 2 in mind.
3715#
3716sub Save_Session_textSSH_files {
3717	my ($filename1,$filename2,$filename3,$duration,$time,$data,$length,
3718	 $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2,
3719	 $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize,$datah,
3720	 $data00);
3721	my $session_id = shift;
3722	my $number = shift;
3723	my $service_name = shift;
3724	my $session_text = shift;
3725	my $numtext = sprintf("%04d",$number);
3726	my $delay = ""; 		# a text list of key delays
3727	my $html = "";			# a html form of output
3728	my $bytes = 0;			# data bytes of the connection
3729	my $minsize;			# The min client packet size
3730	my $state;
3731
3732	$duration = ($TCP{id}{$session_id}{EndTime} -
3733	 $TCP{id}{$session_id}{StartTime});
3734	$duration2 = sprintf("%.2f",$duration);
3735	$duration = sprintf("%.0f",$duration);
3736
3737	### Output
3738	$filename1 = "session_${numtext}.text${service_name}.replay";
3739	open (REPLAY,">$filename1") ||
3740	 die "ERROR35: creating $filename1 $!\n";
3741	binmode(REPLAY);	# for backward OSs
3742
3743	#
3744	#  Create a perl program that replays details of the original
3745	#  SSH session. We print the direction of traffic and size,
3746	#  paused using the original delays.
3747	#
3748	print REPLAY "#!$PERL\n";
3749	print REPLAY <<'END';
3750#
3751# This is a text SSH replay program. It will replay details of the
3752# original SSH session using timestamps from the packet capture log.
3753#
3754# USAGE: run the script as normal. You can provide a factor as an
3755#	argument, eg "2" to run twice as fast, or "0.5" to run
3756#	at half time. eg,
3757# 		./session_0002.textSSH.replay 2
3758#
3759# Auto generated by Chaosreader.
3760#
3761$| = 1;
3762$factor = $ARGV[0] || 1;
3763sub ms {
3764	$ms = shift;
3765	$ms = $ms / $factor;
3766	select(undef, undef, undef, $ms);
3767}
3768print <<'SUBEND';
3769SSH text analysis replay
3770------------------------
3771"*" is client traffic (including keystrokes), "." is the return text.
3772A number is a multiple of the previous char, eg ".32" is 32 return chars.
3773
3774SUBEND
3775END
3776
3777	#
3778	#  Sort the data on the timestamps, calculating timestamp differences
3779	#  to record in the replay program.
3780	#
3781	@Times = ();
3782	%PacketSize = ();
3783	foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
3784		if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) {
3785			next;
3786		}
3787		push(@Times,$time);
3788		if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") {
3789			### Frequency count sent sizes
3790			$data = $TCP{id}{$session_id}{time}{$time}{data};
3791			$length = length($data);
3792			$PacketSize{$length}++ if $length < 100;
3793		}
3794	}
3795	@Times = sort { $a <=> $b } @Times;
3796	$outtime = $Times[0];
3797	$outsize = 0;
3798
3799	#
3800	#  Determine the client min size - this is the minimum length of
3801	#  a data packet, eg a keystroke.
3802	#
3803	foreach $length (sort {$PacketSize{$b} <=> $PacketSize{$a}}
3804	 (keys(%PacketSize))) {
3805		$minsize = $length;
3806		last;
3807	}
3808
3809	# The very first packet
3810	$data00 = $TCP{id}{$session_id}{time}{$Times[0]}{data};
3811
3812	### Process data
3813	for ($i=0; $i <= $#Times; $i++) {		# required
3814
3815	   ### Calculate time diff if possible
3816	   $time0 = $Times[$i];
3817	   $time1 = $Times[$i+1];
3818	   $time2 = $Times[$i+2];
3819	   $time3 = $Times[$i+3];
3820	   if ($i == $#Times) {
3821		$timediff1 = 0;
3822		$timediff2 = 0;
3823	   } else {
3824	   	$timediff1 = $time1 - $time0;
3825	   	$timediff2 = $time2 - $time0;
3826	   	if ($timediff1 < 0) { $timediff1 = 0; }
3827	   }
3828
3829	   ### Fetch data from mem, "0" is this packet...
3830	   $data0 = $TCP{id}{$session_id}{time}{$time0}{data};
3831	   $data1 = $TCP{id}{$session_id}{time}{$time1}{data};
3832	   $data2 = $TCP{id}{$session_id}{time}{$time2}{data};
3833	   $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir};
3834	   $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir};
3835	   $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir};
3836	   $dir3 = $TCP{id}{$session_id}{time}{$time3}{dir};
3837	   $length0 = length($data0);
3838	   $length1 = length($data1);
3839	   $length2 = length($data2);
3840
3841	   # working variables
3842	   $bytes += $length0;
3843	   $length = $length0;
3844	   $data = $data0;
3845
3846	   ##################
3847	   #  Process Data
3848	   #
3849	   #  This is designed for a command line SSH session and
3850	   #  the calculations are based on many assumptions.
3851	   #
3852	   #  For example: if the client sends a small packet (which
3853	   #  we'll assume is a keystroke) and the server responds
3854	   #  with large packets (beyond merely echoing the keystroke),
3855	   #  then we can assume that this keystroke was the enter key,
3856	   #  and the large response was the output of the command.
3857	   #
3858	   #  There are two states - keystrokes and output text.
3859	   #
3860	   #  The follow code works well most of the time, and provides
3861	   #  meaningful results for non command line sessions.
3862	   #
3863
3864	   #
3865	   # --- Server to Client ---
3866	   #
3867	   if ($dir0 eq "A") {
3868		if ($i > 3 || $data00 !~ /^ssh/i) {
3869			# a "." represents an encrypted server to client packet
3870			$data = ".";
3871			$html .= '<font color="blue">' . $data;
3872		} else {
3873			### Process initial plaintext negotiation
3874
3875			# first we clean up the data,
3876			$data =~ tr/\040-\176/./c;
3877			$data =~ s/\\/\\\\/g;
3878			$data =~ s/'/\\'/g;
3879			$data .= "\n";
3880			$hdata = $data;
3881			$hdata = &Desex_HTML($hdata);
3882
3883	   		# This is a fancy line wrap, adds a green ">"
3884	   		$hdata =~
3885		   s/([^\n\f<>]{$WRAP})/$&\n<font color="green">&gt;<\/font>/g;
3886			$html .= '<font color="blue">' . $hdata;
3887		}
3888
3889		if ($state eq "output") {
3890		   if ($length0 > $minsize && $i > 3) {
3891			# This prints the length in the replay files
3892			# as a number following the symbol,
3893			# eg ".60" would mean a "." with length 60.
3894			# length actually means size beyond minsize.
3895			$length -= $minsize;
3896			$data .= "$length";
3897			$html .= "$length";
3898			$outsize += $length;
3899		   }
3900
3901		### Data -> Keystrokes
3902		if ($dir1 eq "B" && $length1 == $minsize) {
3903			# Process the transition from command output back
3904			# to keystrokes.
3905			$data .= "\n";
3906			$html .= "\n";
3907			$delay .= "s $outsize\n";
3908			$delay .= sprintf("t %.6f\n",$time0 - $outtime);
3909			$delay .= "  \n";	# command delimiter
3910			$outsize = 0;
3911			$outtime = $time0;
3912			$state = "key";
3913		   }
3914		}
3915		$html .= '</font>';
3916	   }
3917
3918	   #
3919	   # --- Client to Server ---
3920	   #
3921	   else {
3922		if ($i == 1) {
3923		   # PuTTY appears to have an unusual way to send keystrokes
3924		   # to the server, that differs to OpenSSH and Sun's SSH.
3925		   # Remember if this is a PuTTY session.
3926		   $sshtype = "putty" if $data =~ /PuTTY/;
3927		}
3928
3929		### Keystroke
3930		if ($sshtype eq "") {
3931		   # If the client is sending a minsize packet and the server
3932		   # then responds, we assume this is a keystroke.
3933		   if ($length0 == $minsize && $dir1 eq "A") {
3934			$delay .= "k \n";
3935		   }
3936		} elsif ($sshtype eq "putty") {
3937		   # if the client is sending a minsize packet, followed by
3938		   # another packet, then a reply packet, and then a server
3939		   # response; we assume that this is a keystroke.
3940		   # (This processes PuTTY's doubled keystrokes).
3941		   if ($length0 == $minsize && $dir1 eq "B" && $dir2 eq "A") {
3942			$delay .= "k \n";
3943		   }
3944		}
3945
3946		### Process initial plaintext negotiation
3947		if ($i > 3 || $data00 !~ /^ssh/i) {
3948			# a "*" represents an encrypted client to server packet
3949			$data = "*";
3950			$html .= '<font color="red">' . $data;
3951		} else {
3952			### Process initial plaintext negotiation
3953
3954			# first we clean up the data,
3955			$data =~ tr/\040-\176/*/c;
3956			$data =~ s/\\/\\\\/g;
3957			$data =~ s/'/\\'/g;
3958			$data .= "\n";
3959			$hdata = $data;
3960			$hdata = &Desex_HTML($hdata);
3961
3962   			# This is a fancy line wrap, adds a green ">"
3963   			$hdata =~
3964		   s/([^\n\f<>]{$WRAP})/$&\n<font color="green">&gt;<\/font>/g;
3965			$html .= '<font color="red">' . $hdata;
3966		}
3967
3968		### Keystroke -> Keystroke delay
3969	 	if ($sshtype eq "") {
3970		   if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" &&
3971		    $length2 == $minsize) {
3972			# If this is a keystroke packet, and the next packet
3973			# is a response, and then another keystroke packet
3974			# is sent; then measure the keystroke delay.
3975			$timediff2 = $time2 - $time0;
3976			$delay .= sprintf("d %.6f\n",$timediff2);
3977			$outsize = 0;
3978			$outtime = $time0;
3979		   }
3980		} elsif ($sshtype eq "putty") {
3981		   if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" &&
3982		    $length2 == $minsize && $dir3 eq "B") {
3983			# This is the same idea as the above, but processes
3984			# PuTTY's doubled keystrokes.
3985			$timediff2 = $time2 - $time0;
3986			$delay .= sprintf("d %.6f\n",$timediff2);
3987			$outsize = 0;
3988			$outtime = $time0;
3989		   }
3990		}
3991
3992		if ($length0 > $minsize && $i > 3) {
3993			#
3994			# This prints the length in the replay files
3995			# as a number following the symbol,
3996			# eg ".60" would mean a "." with length 60.
3997			# length actually means size beyond minsize.
3998			$length -= $minsize;
3999			$data .= "$length";
4000			$html .= "$length";
4001		}
4002		$html .= '</font>';
4003
4004		### Keystrokes -> Data
4005		if ( ($length0 == $minsize &&
4006		 (($length1 + $length2) > ($minsize * 2))) ||
4007		 ($dir1 eq "A" && $dir2 eq "A") ) {
4008			$data .= "\n";
4009			$html .= "\n";
4010			#
4011			# "r" describes the response packet. This value
4012			# may or may not be meaningful depending on the
4013			# SSH software.
4014			if ($length1 > $minsize) {
4015			   $delay .= "r 1\n";
4016			   $delay .= sprintf("p %.6f\n",$timediff1);
4017			} else {
4018			   $delay .= "r 2\n";
4019			   $delay .= sprintf("p %.6f\n",$timediff2);
4020			}
4021			$state = "output";
4022		}
4023	   }
4024
4025	   ### Now output the data in the replay program
4026	   print REPLAY "print '" . $data . "';\n";
4027
4028	   ### This causes the replay program to pause
4029	   print REPLAY "ms($timediff1);\n";
4030	}
4031	$duration = 0.01 if $duration == 0;	# avoid divide by 0,
4032	$speed = sprintf("%.2f",$bytes / (1024 * $duration));
4033	print REPLAY "print \"\n\n" .
4034	 "Summary: $duration2 seconds, $bytes bytes, $speed Kb/sec\\n\";";
4035	close REPLAY;
4036
4037	### Better make it executable
4038	chmod (0755, "$filename1");
4039
4040	#
4041	#  HTML version of the replay script
4042	#
4043	$filename2 = "session_${numtext}.text${service_name}.html";
4044	open (HTML,">$filename2") ||
4045	 die "ERROR36: Can't write to file, $filename2 $!\n";
4046	$html = "<html><head><title>SSH text analysis</title></head>\n" .
4047	"<body bgcolor=\"white\">" .
4048	"<H1>$service_name: $session_text</H1>\n" .
4049	"<H2>File $Arg{infile}, Session $number</H2>\n" .
4050	"<h3>$duration2 seconds, $bytes bytes, $speed Kb/sec</h3>\n" .
4051	'"*" is client traffic (including ' .
4052	'keystrokes), "." is the return ' .
4053	'text.<br>A number is a multiple of the previous char, eg ".32" ' .
4054	'is 32 return chars.<br>' .
4055	"\n<b><pre>$html</pre></b>\n</body>\n</html>\n";
4056	print HTML $html;
4057	close HTML;
4058
4059	#
4060	#  Text Database of time delays between possible keystrokes
4061	#
4062	$filename3 = "session_${numtext}.text${service_name}.keydata";
4063	open (DELAY,">$filename3") ||
4064	 die "ERROR37: Can't write keydata file: $filename3 $!\n";
4065	$delay = "$delay  \n";
4066	print DELAY $delay;
4067	close DELAY;
4068
4069	#
4070	#  Update Global Vars to remember new filenames
4071	#
4072	$Index{HTML}[$number] .= "<li><a href=\"$filename1\">$filename1" .
4073	 "</a> $duration seconds</li>\n";
4074	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
4075	 '"' , "   $filename1","",$duration);
4076	$Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
4077	 "</a> </li>\n";
4078	$length = length($html);
4079	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
4080	 '"' , "   $filename2","",$length);
4081	$Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" .
4082	 "</a> </li>\n";
4083	$length = length($delay);
4084	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
4085	 '"' , "   $filename3","",$length);
4086}
4087
4088
4089# Save_Session_Keydata - Save a key delay data file to assist SSH analysis.
4090#
4091# This code is intentionally designed to be similar to the SSH processing
4092# code, so that both their outputs can be compared. As a standalone
4093# subroutine this wouldn't make too much sense; instead bear in mind that
4094# I'd like the processing to mimic how SSH was processed. That way we
4095# run this on plenty of known text (telnet) and become familiar with
4096# exactly what will happen for the unknown text (SSH).
4097#
4098sub Save_Session_Keydata {
4099	my ($filename1,$filename2,$filename3,$duration,$time,$data,$length,
4100	 $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2,
4101	 $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize);
4102	my $session_id = shift;
4103	my $number = shift;
4104	my $service_name = shift;
4105	my $session_text = shift;
4106	my $numtext = sprintf("%04d",$number);
4107	my $delay = ""; 		# a text list of key delays
4108	my $minsize;			# The min client packet size
4109	my $state = "key";
4110
4111	### Sort the data by timestamps
4112	@Times = ();
4113	%PacketSize = ();
4114	foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
4115		if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) {
4116			next;
4117		}
4118		push(@Times,$time);
4119	}
4120	@Times = sort { $a <=> $b } @Times;
4121	$outtime = $Times[0];
4122	$outsize = 0;
4123	$minsize = 1;				# known for telnet
4124
4125	### Process data
4126	for ($i=0; $i <= $#Times; $i++) {		# required
4127
4128	   ### Calculate time diff if possible
4129	   $time0 = $Times[$i];
4130	   $time1 = $Times[$i+1];
4131	   $time2 = $Times[$i+2];
4132	   if ($i == $#Times) {
4133		$timediff1 = 0;
4134		$timediff2 = 0;
4135	   } else {
4136		$timediff1 = $time1 - $time0;
4137		$timediff2 = $time2 - $time0;
4138		if ($timediff1 < 0) { $timediff1 = 0; }
4139	   }
4140
4141	   ### Fetch data from mem, "0" is this packet...
4142	   $data0 = $TCP{id}{$session_id}{time}{$time0}{data};
4143	   $data1 = $TCP{id}{$session_id}{time}{$time1}{data};
4144	   $data2 = $TCP{id}{$session_id}{time}{$time2}{data};
4145	   $data0 = "\n" if $data0 eq "\r\n";
4146	   $data1 = "\n" if $data1 eq "\r\n";
4147	   $data2 = "\n" if $data2 eq "\r\n";
4148	   $data0 = "\n" if $data0 =~ /\r./;
4149	   $data1 = "\n" if $data1 =~ /\r./;
4150	   $data2 = "\n" if $data2 =~ /\r./;
4151	   $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir};
4152	   $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir};
4153	   $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir};
4154	   $length0 = length($data0);
4155	   $length1 = length($data1);
4156	   $length2 = length($data2);
4157
4158	   $length = $length0;
4159	   $data = $data0;
4160
4161	   #
4162	   #  Process Data
4163	   #
4164	   if ($dir0 eq "A") {
4165		if ($state eq "output") {
4166			if ($length0 > $minsize) {
4167				$length -= $minsize;
4168				$outsize += $length;
4169			}
4170
4171			### Data -> Keystrokes
4172			if ($dir1 eq "B" && $length1 == $minsize) {
4173				$delay .= "s $outsize\n";
4174				$delay .= sprintf("t %.6f\n",$time0 - $outtime);
4175				$delay .= "  \n";
4176				$outsize = 0;
4177				$outtime = $time0;
4178				$state = "key";
4179			}
4180		}
4181	   } else {
4182		### Keystroke
4183		if ($length0 == $minsize) {
4184			if ($data0 eq "\n") {
4185				$delay .= "k \\n\n";
4186			} else {
4187				$delay .= "k $data0\n";
4188			}
4189		}
4190		### Keystroke -> Keystroke delay
4191		if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" &&
4192		 $length2 == $minsize) {
4193			$timediff2 = $time2 - $time0;
4194			$delay .= sprintf("d %.6f\n",$timediff2);
4195			$outsize = 0;
4196			$outtime = $time0;
4197		}
4198
4199		if ($length0 > $minsize) {
4200			$length -= $minsize;
4201		}
4202
4203		### Keystrokes -> Data
4204		if ( ($length0 == $minsize &&
4205		 (($length1 + $length2) > ($minsize * 2))) ||
4206		 ($dir1 eq "A" && $dir2 eq "A") ) {
4207			if ($length1 > $minsize) {
4208			   $delay .= "r 1\n";
4209			   $delay .= sprintf("p %.6f\n",$timediff1);
4210			} else {
4211			   $delay .= "r 2\n";
4212			   $delay .= sprintf("p %.6f\n",$timediff2);
4213			}
4214			$state = "output";
4215		}
4216	   }
4217	}
4218
4219	#
4220	#  Text Database of time delays between possible keystrokes
4221	#
4222	$filename3 = "session_${numtext}.${service_name}.keydata";
4223	open (DELAY,">$filename3") ||
4224	 die "ERROR38: A pink jelly hits you. You die. $filename3 $!\n";
4225	print DELAY "$delay  \n";
4226	close DELAY;
4227
4228	#
4229	#  Update Global Vars to remember new filenames
4230	#
4231	$Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" .
4232	 "</a> </li>\n";
4233	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s\n",
4234	 '"' , "   $filename3","","");
4235}
4236
4237
4238# Save_Stream_Replay - Save a replay program for this stream. eg, dns.
4239#
4240sub Save_Stream_Replay {
4241	my ($filename,$duration);
4242	my $session_id = shift;
4243	my $number = shift;
4244	my $service_name = shift;
4245	my $numtext = sprintf("%04d",$number);
4246
4247	### Output
4248	$filename = "stream_${numtext}.${service_name}.replay";
4249	$duration = ($UDP{id}{$session_id}{EndTime} -
4250	 $UDP{id}{$session_id}{StartTime});
4251	$duration = sprintf("%.0f",$duration);
4252	open (REPLAY,">$filename") ||
4253	 die "ERROR39: creating $filename $!\n";
4254	binmode(REPLAY);	# for backward OSs
4255
4256	#
4257	#  Create a perl program, that when run itself will print out
4258	#  the contents of the server 1-way stream, with pauses based on
4259	#  the packet arrival times (replay the stream in realtime).
4260	#
4261	print REPLAY "#!$PERL\n";
4262	print REPLAY <<'END';
4263#
4264# This is a UDP replay program. It will replay a stream using
4265# the timestamps from the packet log.
4266#
4267# USAGE: run the script as normal. You can provide a factor as an
4268#	argument, eg "2" to run twice as fast, or "0.5" to run
4269#	at half time. eg,
4270# 		./stream_0002.telnet.replay 2
4271#
4272# Auto generated by Chaosreader.
4273#
4274$| = 1;
4275$factor = $ARGV[0] || 1;
4276sub ms {
4277	$ms = shift;
4278	$ms = $ms / $factor;
4279	select(undef, undef, undef, $ms);
4280}
4281END
4282
4283	#
4284	#  Sort the data on the timestamps, calculating timestamp differences
4285	#  to record in the replay program.
4286	#
4287	@Times = keys (%{$UDP{id}{$session_id}{time}});
4288	@Times = sort { $a <=> $b } @Times;
4289
4290	for ($i=0; $i <= $#Times; $i++) {	# required
4291
4292		### Calculate time diff if possible
4293		if ($i == $#Times) {
4294			$timediff = 0;
4295		} else {
4296			$timediff = $Times[$i+1] - $Times[$i];
4297			if ($timediff < 0) { $timediff = 0; }
4298		}
4299		$time = $Times[$i];
4300
4301		### Fetch data from mem
4302		$data = $UDP{id}{$session_id}{time}{$time};
4303		delete $UDP{id}{$session_id}{time}{$time};
4304
4305		#
4306		#  Clean the data a little (order important)
4307		#
4308		$data =~ s/\\/\\\\/g;	# backslash the backslashes
4309		$data =~ s/'/\\'/g;	# backslash single quotes
4310
4311		#
4312		#  Now output the data in the replay program
4313		#
4314		print REPLAY "print '" . $data . "';\n";
4315
4316		#
4317		#  This causes the replay program to pause
4318		#
4319		print REPLAY "ms($timediff);\n";
4320	}
4321	close REPLAY;
4322
4323	### Better make it executable
4324	chmod (0755, "$filename");
4325
4326	### Global Vars
4327	$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
4328	 "</a> $duration seconds</li>\n";
4329	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
4330	 '"' , "   $filename","",$duration);
4331}
4332
4333
4334# Save_Session_XReplay - Save a replay program for this session. eg, X11.
4335#	This processes far more of the X11 protocol than I was hoping.
4336#	(xscope and ethereal were used to analyse X11).
4337#
4338sub Save_Session_XReplay {
4339	my $session_id = shift;
4340	my $number = shift;
4341	my $service_name = shift;
4342	my $numtext = sprintf("%04d",$number);
4343	my ($filename,$duration,$xcode,$xres_old,$xrest,$xwnum,$xdiff,
4344	 $xlength,$xmsb,$xstart,$xjunk,$xvalue,$readnow,$data,$newdata,
4345	 $n,$N,$chars,$y,$timediff,$texttimediff,$checkdepth,$filename2,
4346	 $x11type);
4347	my @xWords;
4348
4349	### Initials
4350	$xmsb = "";
4351	$readnow = 0;
4352	$xres_old = -1;
4353	$checkdepth = 0;
4354
4355	#
4356	#  Output - Main X11 replay program
4357	#
4358	$filename = "session_${numtext}.${service_name}.replay";
4359	$duration = ($TCP{id}{$session_id}{EndTime} -
4360	 $TCP{id}{$session_id}{StartTime});
4361	$duration = sprintf("%.0f",$duration);
4362	open (REPLAY,">$filename") ||
4363	 die "ERROR40: creating $filename $!\n";
4364	binmode(REPLAY);	# for backward OSs
4365
4366	#
4367	#  Output - Text (keystroke replay)
4368	#
4369	$filename2 = "session_${numtext}.text${service_name}.replay";
4370	open (REPLAY2,">$filename2") ||
4371	 die "ERROR41: creating $filename2 $!\n";
4372	binmode(REPLAY2);	# for backward OSs
4373
4374
4375	#  --- textX11 ---
4376	#
4377	#  Create a perl program, that when run itself will print out
4378	#  the contents of the server 1-way stream, with pauses based on
4379	#  the packet arrival times (replay the session in realtime).
4380	#
4381	print REPLAY2 "#!$PERL\n";
4382	print REPLAY2 <<'END';
4383#
4384# This is an X11 text replay program. It will replay keystrokes and text
4385# of an X11 session using the timestamps from the packet log.
4386#
4387# USAGE: run the script as normal. You can provide a factor as an
4388#	argument, eg "2" to run twice as fast, or "0.5" to run
4389#	at half time. eg,
4390# 		./session_0002.textX11.replay 2
4391#
4392# Auto generated by Chaosreader.
4393#
4394$| = 1;
4395$factor = $ARGV[0] || 1;
4396sub ms {
4397	$ms = shift;
4398	$ms = $ms / $factor;
4399	select(undef, undef, undef, $ms);
4400}
4401END
4402
4403
4404	#  --- X11 ---
4405	#
4406	#  Create a perl program, that when run itself will print out
4407	#  the contents of the server 1-way stream, with pauses based on
4408	#  the packet arrival times (replay the session in realtime).
4409	#
4410	print REPLAY "#!$PERL\n";
4411	print REPLAY <<'END';
4412#
4413# This is a X11 replay program. It will replay a session using
4414# the timestamps from the packet log, and transpose the X11 protocol so
4415# that it can be redisplayed. You must have captured from the start
4416# of the connection for this to work.
4417#
4418# USAGE: ./session_0001.X11.replay [-d destination host] [-p port] factor
4419#
4420#	just run the script as normal. You can provide a factor as an
4421#	argument, eg "2" to run twice as fast, or "0.5" to run
4422#	at half time. eg,
4423# 		./session_0002.X11.replay 2
4424#	a different host and port can be specified if needed. eg,
4425#		./session_0002.X11.replay -d 192.168.1.5 -p 6001
4426#
4427# PROBLEMS: you may need to authorise this connection to the X11 server
4428#	before it works. You could run "xhost +hostname" beforehand.
4429# 	The playback needs to have captured the start of the connection.
4430#	Check you support the same colour depth as the playback. And check
4431#	the playback file simply isn't too big! (more than 500 Kb is
4432#	currently problematic).
4433#
4434#
4435# Auto generated by Chaosreader.
4436#
4437
4438use IO::Socket;
4439use Getopt::Std;
4440
4441if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); }
4442
4443# Try fetching values from $DISPLAY
4444($hostdef,$portdef) = $ENV{DISPLAY} =~ /([^:]*):(\d*)/;
4445$hostdef = "127.0.0.1" if $hostdef eq "";
4446$portdef += 6000;
4447
4448# Command line options take preference
4449&getopts('d:p:');
4450if (defined $opt_d) { $host = $opt_d; } else { $host = $hostdef; }
4451if (defined $opt_p) { $port = $opt_p; } else { $port = $portdef; }
4452$factor = $ARGV[0] || 1;
4453$DEBUG = 0;
4454$| = 1;
4455
4456print "Chaosreader X11 Replay (experimental)\n\n";
4457print "Connecting to $host:$port\n";
4458print "(problems? try running \"xhost +hostname\" first).\n\n";
4459
4460
4461# --- Open Socket ---
4462#
4463$remote = IO::Socket::INET->new( Proto => "tcp",
4464				PeerAddr  => $host,
4465				PeerPort  => $port,
4466				);
4467unless ($remote) { die "ERROR42: Can't connect to X11 daemon on $host:$port"; }
4468$remote->autoflush(1);
4469
4470
4471# --- Subroutines ---
4472#
4473
4474# ms - sleeps for specified milliseconds
4475#
4476sub ms {
4477	$ms = shift;
4478	$ms = $ms / $factor;
4479	select(undef, undef, undef, $ms);
4480}
4481# help - print help
4482#
4483sub help {
4484        open (MYSELF,"$0") || die "ERROR43: I can't see myself: $!\n";
4485        @Myself = <MYSELF>;
4486        close MYSELF;
4487        ### Print comment from top of code
4488        foreach $line (@Myself) {
4489                last if $line !~ /^#/;
4490                next if $line =~ m:^#!/usr/bin/perl:;
4491                $line =~ s/^#/ /;
4492                print $line;
4493        }
4494        print "\n";
4495        exit(0);
4496}
4497# R - recalculates and prints a resourse setting
4498#     The single character subroutine name saves on file space below.
4499#
4500sub R {
4501	#$offset = shift;
4502	#$new = $res + $offset;
4503	my $rid = shift;
4504	my $new;
4505
4506	# final checks
4507	$diff = $rid - $ridbaseold;
4508	$diff = -$diff if $diff < 0;
4509	if ((($rid < $ridbaseold) && ($rid < 8196)) || ($diff > 8196)) {
4510	   if ($msb) { return pack('N',$rid); }
4511		else { return pack('V',$rid); }
4512	}
4513
4514	$new = $rid & $ridmaskold;
4515	$new = $new | $ridbase;
4516	if ($msb) { return pack('N',$new); }
4517	     else { return pack('V',$new); }
4518}
4519# D - prints the new Drawable, usually the rootid.
4520#
4521sub D {
4522	my $rid = shift;
4523
4524	# final checks
4525	if ($rid >= $ridbaseold) {
4526		# return mapped resource id
4527		return R($rid);
4528	}
4529	# return rootid
4530	if ($msb) { return pack('N',$rootid); }
4531	     else { return pack('V',$rootid); }
4532}
4533# C - prints the new Colour map.
4534#
4535sub C {
4536	my $rid = shift;
4537
4538	# final checks
4539	if ($rid >= $ridbaseold) {
4540		# return mapped resource id
4541		return R($rid);
4542	}
4543	# return colour map
4544	if ($msb) { return pack('N',$colour); }
4545	     else { return pack('V',$colour); }
4546}
4547# M - Returns a generic mapped id. Can be rootid, colour, or resource.
4548#     These are used in Xcodes involving a mask.
4549#
4550sub M {
4551	my $rid = shift;
4552
4553	# final checks
4554	if ($rid >= $ridbaseold) {
4555		# return mapped resource id
4556		return R($rid);
4557	}
4558	# return rootid map
4559	if ($rid == $rootidold) {
4560	   if ($msb) { return pack('N',$rootid); }
4561		else { return pack('V',$rootid); }
4562	}
4563	# return colour map
4564	if ($rid == $colourold) {
4565	   if ($msb) { return pack('N',$colour); }
4566		else { return pack('V',$colour); }
4567	}
4568	# return other
4569	if ($msb) { return pack('N',$rid); }
4570		else { return pack('V',$rid); }
4571}
4572# P - Check depth pixels, print warning if there is a mismatch.
4573#
4574sub P {
4575	my $depth = shift;
4576	if (! defined $Depth{$depth}) {
4577		print "\nWARNING: requested depth $depth may not be ".
4578		 "supported by the server?\n";
4579	}
4580}
4581# debug - print out a value
4582#
4583sub debug {
4584	my $word = shift;
4585	my $num = shift;
4586	my $pack = pack("N",$num);
4587	print "$word: $num ",
4588	 sprintf("%2.2x%2.2x%2.2x%2.2x\n",unpack("C*",$pack));
4589}
4590
4591
4592# --- MAIN ---
4593#
4594print "Sending X11 traffic:";
4595END
4596	### Fetch raw data
4597	$xserver = &TCP_Follow_RawA($session_id);
4598
4599	#
4600	#  Determine endian of this transfer. Reading the
4601	#  second short on MSB gives 11, and on LSB 2816
4602	#  (at least in testing). We split the difference
4603	#  on 256 (is case there is a little variation).
4604	#
4605	($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver);
4606	#
4607	#  Create aliases for "n" and "N" so I can think
4608	#  in big endian.
4609	#
4610	if ($xvalue < 256) {
4611		$xmsb = 1;
4612		$n = "n";
4613		$N = "N";
4614	} else {
4615		$xmsb = 0;
4616		$n = "v";
4617		$N = "V";
4618	}
4619	my ($success,$major,$minor,$length,$release,$ridbase,
4620	 $ridmask,$mbsize,$vendor,$reqmax,$roots,$formats,$ibo,
4621	 $bbo,$bslu,$bslp,$keymin,$keymax,$pad,$rest) =
4622	 unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$xserver);
4623
4624	($x11type,$rest) = unpack("a${vendor}a*",$rest);
4625	$pad = ((4 - ($vendor % 4)) % 4);
4626	($junk,$rest) = unpack("a${pad}a*",$rest);
4627
4628	foreach $i (1..$formats) {
4629		($junk,$rest) = unpack("a8a*",$rest);
4630	}
4631	($rootid,$colour,$junk) = unpack("$N${N}a*",$rest);
4632
4633	#
4634	#  Sort the data on the timestamps, calculating timestamp differences
4635	#  to record in the replay program.
4636	#
4637	@Times = ();
4638	foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
4639		if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") {
4640			push(@Times,$time)
4641		}
4642	}
4643	@Times = sort { $a <=> $b } @Times;
4644
4645	#
4646	# --- Main Loop ---
4647	#
4648	# (this needs to be a for loop!)
4649	for ($i=0; $i <= $#Times; $i++) {
4650
4651		### Calculate time diff if possible
4652		if ($i == $#Times) {
4653			$timediff = 0;
4654		} else {
4655			$timediff = $Times[$i+1] - $Times[$i];
4656			# just in case,
4657			if ($timediff < 0) { $timediff = 0; }
4658		}
4659		$time = $Times[$i];
4660		$texttimediff += $timediff;
4661
4662		### Fetch data from mem
4663		$data = $TCP{id}{$session_id}{time}{$time}{data};
4664
4665		### If initial request was fetched,
4666		if ($readnow == 0) {
4667			### Populate $xstart with initial request
4668			$xstart .= $data;
4669
4670			#
4671			#  This triggers the replay program to ask the X11
4672			#  server for the connection data - which
4673			#  needs to be processed so that various
4674			#  resource offsets can be used later on.
4675			#
4676			if (length($xstart) >= 12)  {
4677			   $readnow = 1;
4678			}
4679
4680		} else {
4681		   #
4682		   #  Change resource offsets
4683		   #  (reads $data and writes to $data)
4684		   #
4685		   $xrest = $data;
4686		   $data = "";		# output stream of data & subs
4687
4688		   #
4689		   #  Process through X11 codes
4690		   #
4691		   while (length($xrest) > 0) {
4692			($xcode,$xbyte,$xlength,$xrest) =
4693			 unpack("CC${n}a*",$xrest);
4694
4695			### Add xcode to output stream $data
4696			$d = pack("CC${n}",$xcode,$xbyte,$xlength);
4697			# the unusual seds
4698			$d =~ s/\\/\\\\/g;
4699			$d =~ s/'/\\'/g;
4700			$d =~ s/\015\012/'."\\015\\012".'/gs;
4701			$data .= $d;
4702
4703			#
4704			#  Fetch code values from $xrest, and trim
4705			#  $xrest. For most requests, the value length
4706			#  is a field (bytes 3,4) except for XErrors
4707			#  (code 0) where the total length is always 32.
4708			#
4709			if ($xcode == 0) {
4710				$xlv = 28;
4711			} else {
4712				$xlv = ($xlength - 1) * 4;
4713				$xlv = -$xlv if $xlv < 0;
4714			}
4715			while (length($xrest) < $xlv) {
4716				# some more magic
4717				$i++;
4718				last if ($i > $#Times);
4719
4720				$next = $Times[$i];
4721
4722				### Fetch data from mem
4723				$xrest .=
4724				 $TCP{id}{$session_id}{time}{$next}{data};
4725			}
4726
4727			($xvalue,$xrest) = unpack("a${xlv}a*",$xrest);
4728
4729			#$format = "%2.2x%2.2x " x ($xlv/2);
4730			#printf("X$xcode: $xbyte,$xlength $format\n",
4731			# unpack("C*",$xvalue));   ### Debug
4732
4733			$xwnum = 0;
4734			@xWords = unpack("${N}*",$xvalue);
4735
4736			#
4737			#  If this is a text event, save the text to the
4738			#  textX11 replay program.
4739			#
4740			if (($xcode == 76) || ($xcode == 77)) {
4741
4742				# Check if this is a xImageText16Req
4743				if ($xcode == 77) { $xbyte *= 2; }
4744
4745				($pad,$y,$chars) =
4746				 unpack("a10${n}a$xbyte",$xvalue);
4747				if ($yold != $y) { $chars = "\n$chars"; }
4748
4749				### Clean the data a little (order important)
4750				$chars =~ s/\\/\\\\/g;
4751				$chars =~ s/'/\\'/g;
4752				$chars =~ s/\0//g;
4753
4754				### Now output the data in the replay program
4755				print REPLAY2 "print '" . $chars . "';\n";
4756
4757				### This causes the replay program to pause
4758				print REPLAY2 "ms($texttimediff);\n"
4759		 		 unless $texttimediff < 0.002;
4760
4761				$yold = $y;
4762				$texttimediff = 0;
4763			}
4764			#
4765			#  Process a text scroll event (by using 62 - copy area)
4766			#
4767			if ($xcode == 62) {
4768				print REPLAY2 "print \"\\n\";\n";
4769				$chars = "\n";
4770			}
4771
4772
4773			#
4774			#  If this is a create window event, check the depth.
4775			#
4776			if (($xcode == 1) && ($checkdepth == 0)) {
4777				$data .= "',P($xbyte),'";
4778				$checkdepth = 1;
4779			}
4780
4781			#
4782			#  Print the X11 data with embedded subroutines
4783			#  to transpose the resource IDs.
4784			#
4785			foreach $xw (@xWords) {
4786				$xwnum++;
4787				if ($X11_Codes[$xcode][$xwnum] == 1) {
4788					$data .= "',R($xw),'";
4789					#print "XCODER: $xcode, $xwnum\n";
4790				} elsif ($X11_Codes[$xcode][$xwnum] == 2) {
4791					$data .= "',D($xw),'";
4792					#print "XCODED: $xcode, $xwnum\n";
4793				} elsif ($X11_Codes[$xcode][$xwnum] == 3) {
4794					$data .= "',C($xw),'";
4795					#print "XCODEC: $xcode, $xwnum\n";
4796				} elsif ($X11_Codes[$xcode][$xwnum] == 4) {
4797					$data .= "',M($xw),'";
4798					#print "XCODEM: $xcode, $xwnum\n";
4799				} else {
4800					$d = pack("$N",$xw);
4801					$d =~ s/\\/\\\\/g;
4802					$d =~ s/'/\\'/g;
4803					$d =~ s/\015\012/'."\\015\\012".'/gs;
4804					$data .= $d;
4805				}
4806			}
4807		   }
4808		}
4809
4810		#
4811		#  Now output the data in the replay program
4812		#
4813		print REPLAY "print '.';\n";
4814		print REPLAY "print \$remote '" . $data . "';\n";
4815
4816		if ($readnow == 1) {
4817			$readnow = 2;
4818			print REPLAY "\$msb = $xmsb;\n";
4819			print REPLAY "\$ridbaseold = $ridbase;\n";
4820			print REPLAY "\$ridmaskold = $ridmask;\n";
4821			print REPLAY "\$rootidold = $rootid;\n";
4822			print REPLAY "\$colourold = $colour;\n";
4823			#
4824			#  The following code implements the client to
4825			#  server connection - we need to read the
4826			#  resource and window IDs which are necessary
4827			#  when transposing the replay traffic to
4828			#  these new values.
4829			#
4830			print REPLAY <<'END';
4831if ($msb) {
4832	$n = "n";
4833	$N = "N";
4834} else {
4835	$n = "v";
4836	$N = "V";
4837}
4838
4839
4840read($remote,$in,40);		# (xConnSetup)
4841($success,$major,$minor,$length,$release,$ridbase,$ridmask,$mbsize,$vendor,
4842$reqmax,$roots,$formats,$ibo,$bbo,$bslu,$bslp,$keymin,$keymax,$pad) =
4843unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$in);
4844
4845read($remote,$in,$vendor);
4846print "\nX11 Server Type: $in\n";
4847read($remote,$in,((4 - ($vendor % 4)) % 4));
4848
4849foreach $i (1..$formats) {
4850	read($remote,$in,8);	# (xPixmapFormat)
4851	($depth,$junk) = unpack("Ca*",$in);
4852	$Depth{$depth} = 1;
4853	next if $depth == 1;
4854	print "X11 server supports $depth bit resolution\n";
4855}
4856read($remote,$in,8);	# (xWindowRoot)
4857($rootid,$colour,$junk) = unpack("$N$N",$in) unless defined $rootid;
4858
4859if ($DEBUG) {
4860	debug("Resource ID new: ",$ridbase);
4861	debug("Resource ID old: ",$ridbaseold);
4862	debug("Root ID new: ",$rootid);
4863	debug("Root ID old: ",$rootidold);
4864	debug("Colour map new: ",$colour);
4865	debug("Colour map old: ",$colourold);
4866}
4867END
4868		}
4869
4870		#
4871		#  This causes the replay program to pause
4872		#
4873		print REPLAY "ms($timediff);\n"
4874		 unless $timediff < 0.002;	# (efficiency).
4875	}
4876	print REPLAY "print \"\n\";\n";
4877	print REPLAY "close \$remote;\n";
4878	close REPLAY;
4879
4880	### Better make it executable
4881	chmod (0755, "$filename");
4882
4883	close REPLAY2;
4884	### Better make it executable
4885	chmod (0755, "$filename2");
4886
4887	### Global Vars
4888	$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
4889	 "</a> $duration seconds</li>\n";
4890	$Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
4891	 "</a> $duration seconds</li>\n";
4892	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
4893	 '"' , "   $filename","",$duration);
4894	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
4895	 '"' , "   $filename2","",$duration);
4896}
4897
4898
4899
4900# Save_Session_VNCReplay_andHTML - Save a replay program for this session.
4901#	This creates a program that is used in conjunction with vncviewer.
4902#	It also saves the HTML version (it would have been redundant to
4903# 	create a seperate subroutine for that).
4904#
4905sub Save_Session_VNCReplay_andHTML {
4906	my $session_id = shift;
4907	my $number = shift;
4908	my $service_name = shift;
4909	my $session_text = shift;
4910	my $numtext = sprintf("%04d",$number);
4911	my ($filename,$filename2,$filename3,$duration,$code,$rest,$extra,
4912	 $length,$start,$junk,$down,$value,$data,$oldtimediff,$printed,$chars,
4913	 $char,$timediff,$checkdepth,$html);
4914	my @xWords;
4915
4916	$oldtimediff = 0;
4917	$printed = 0;
4918	$html = "";
4919
4920
4921	#
4922	#  Output - Text (keystroke replay)
4923	#
4924	$filename2 = "session_${numtext}.text${service_name}.replay";
4925	open (REPLAY2,">$filename2") ||
4926	 die "ERROR44: creating $filename2 $!\n";
4927	binmode(REPLAY2);	# for backward OSs
4928
4929	#
4930	#  --- textVNC ---
4931	#
4932	#  Create a perl program, that when run itself will print out
4933	#  the contents of the client 1-way stream, with pauses based on
4934	#  the packet arrival times (replay the session in realtime).
4935	#
4936	print REPLAY2 "#!$PERL\n";
4937	print REPLAY2 <<'END';
4938#
4939# This is an VNC text replay program. It will replay keystrokes from
4940# a VNC session using the timestamps from the packet log.
4941#
4942# USAGE: run the script as normal. You can provide a factor as an
4943#	argument, eg "2" to run twice as fast, or "0.5" to run
4944#	at half time. eg,
4945# 		./session_0002.textVNC.replay 2
4946#
4947# Auto generated by Chaosreader.
4948#
4949$| = 1;
4950$factor = $ARGV[0] || 1;
4951sub ms {
4952	$ms = shift;
4953	$ms = $ms / $factor;
4954	select(undef, undef, undef, $ms);
4955}
4956END
4957
4958	#
4959	#  Sort the data on the timestamps, calculating timestamp differences
4960	#  to record in the replay program.
4961	#
4962	@Times = ();
4963	foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
4964		if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") {
4965			push(@Times,$time)
4966		}
4967	}
4968	@Times = sort { $a <=> $b } @Times;
4969
4970	#
4971	# --- Main Loop ---
4972	#
4973	# (this needs to be a for loop!)
4974	for ($i=0; $i <= $#Times; $i++) {
4975
4976		### Calculate time diff if possible
4977		if ($i == $#Times) {
4978			$timediff = 0;
4979		} else {
4980			$timediff = $Times[$i+1] - $Times[$i];
4981			# just in case,
4982			if ($timediff < 0) { $timediff = 0; }
4983		}
4984		$time = $Times[$i];
4985
4986		### Fetch data from mem
4987		$data = $TCP{id}{$session_id}{time}{$time}{data};
4988		($code) = unpack("C",$data);
4989
4990		$chars = "";
4991
4992		# skip code 0's
4993		if ($code > 0) {
4994		   #
4995		   #  Process through VNC client codes
4996		   #
4997		   $chars = "";
4998		   while (length($data) > 0) {
4999			($code) = unpack("C",$data);
5000			$length = $VNC_Code_Size{$code};
5001			$length--;
5002			last if $length <= 0;
5003
5004			# Fetch this code only
5005			($code,$value,$data) = unpack("Ca${length}a*",$data);
5006
5007			### Process Key Pressed
5008			if ($code == 4) {
5009			   ($down,$junk,$extra,$char) = unpack("Ca4Ca",$value);
5010
5011			   next if $down == 0;		# record key-ups
5012
5013			   if ($extra == 0) {
5014				$chars .= $char;
5015			   } else {
5016				if (defined $KeyCode{vnc}{0}{$char}) {
5017				   $chars .= $KeyCode{vnc}{0}{$char};
5018				}
5019			   }
5020			   $html .= $chars;
5021			}
5022		   }
5023
5024		}
5025
5026		$chars =~ s/\\/\\\\/g;
5027		$chars =~ s/'/\\'/g;
5028
5029		### Now output the data in the replay program
5030		unless (length($chars) == 0) {
5031			print REPLAY2 "ms($oldtimediff);\n"
5032	 		 unless $oldtimediff < 0.002;
5033
5034			### Print the data
5035			print REPLAY2 "print '" . $chars . "';\n";
5036
5037			# these counters are for efficiency, otherwise
5038			# we print too many sequiential sleeps
5039			$printed = 1;
5040			$oldtimediff = 0;
5041		} else {
5042			$printed = 0;
5043			$oldtimediff += $timediff;
5044			next;
5045		}
5046
5047		### This causes the replay program to pause
5048		print REPLAY2 "ms($timediff);\n"
5049 		 unless $timediff < 0.002;
5050	}
5051	close REPLAY2;
5052
5053	### Better make it executable
5054	chmod (0755, "$filename2");
5055
5056
5057	#  --- HTML ---
5058	#
5059	#  Create a HTML page showing the keystrokes
5060
5061	### Clean up html
5062	$html = &Desex_HTML($html);
5063
5064	### Output
5065        $filename3 = "session_${numtext}.text${service_name}${ext}.html";
5066	open (OUT,">$filename3") ||die "ERROR45: file create, $filename3: $!\n";
5067	binmode(OUT);
5068	print OUT "<HTML>\n<BODY bgcolor=\"white\">\n" .
5069	 "<H1>$service_name: $session_text</H1>\n" .
5070	 "<H2>File $Arg{infile}, Session $number</H2>\n" .
5071	 "<PRE WRAP=\"virtual\">\n" .
5072         "<font color=\"red\">" .$html. "</font></PRE>\n</BODY>\n</HTML>\n";
5073        close OUT;
5074
5075	### Global Vars
5076	$length = length($html);
5077	$Index{HTML}[$number] .=
5078	 "<li><a href=\"$filename3\">keystrokes</a></li>\n";
5079	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5080	 '"' , "   $filename3","",$length);
5081
5082
5083	#
5084	#  Output - Main VNC replay program
5085	#
5086	$filename = "session_${numtext}.${service_name}.replay";
5087	$duration = ($TCP{id}{$session_id}{EndTime} -
5088	 $TCP{id}{$session_id}{StartTime});
5089	$duration = sprintf("%.0f",$duration);
5090	open (REPLAY,">$filename") ||
5091	 die "ERROR46: creating $filename $!\n";
5092	binmode(REPLAY);	# for backward OSs
5093
5094	#
5095	#  --- VNC ---
5096	#
5097	#  Create a perl program, that when run itself will create a
5098	#  playback VNC server that listens on a port. When a vncviewer
5099	#  connects, the contents of the server 1-way stream arew played back,
5100	#  with pauses.
5101	#
5102	print REPLAY "#!$PERL\n";
5103	print REPLAY <<'END';
5104#
5105# This is a VNC replay program. This runs as a server and listens on a port,
5106# then vncviewer is run to connect to that port - at which point the playback
5107# commences.
5108#
5109# USAGE: ./session_0001.VNC.replay [-p port] factor
5110#
5111#	just run the script as normal. You can provide a factor as an
5112#	argument, eg "2" to run twice as fast, or "0.5" to run
5113#	at half time. eg,
5114# 		./session_0002.VNC.replay 2
5115#	a different host and port can be specified if needed. eg,
5116#		./session_0002.VNC.replay -p 5925
5117#
5118#	After the script is running, connect using vncviewer. eg,
5119#		vncviewer -viewonly localhost:25
5120#
5121# PROBLEMS: The playback needs to have captured the start of the connection,
5122# 	you need to be at the same colour depth as the playback (or more may
5123#	work), and your screen should be at least as big as the playback
5124#	resolution. Newer versions of vncviewer may be tuned to match the
5125#	playback (eg "-8bit").
5126#
5127# Auto generated by Chaosreader.
5128#
5129
5130use IO::Socket;
5131use Getopt::Std;
5132use Net::hostent;
5133
5134$| = 1;
5135
5136if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); }
5137
5138# Command line options take preference
5139&getopts('p:');
5140if (defined $opt_p) { $port = $opt_p; } else { $port = 5921; }
5141$vncport = $port - 5900;
5142if ($vncport < 0) { die "ERROR47: Port $port too low, use at least 5901.\n"; }
5143$factor = $ARGV[0] || 1;
5144$DEBUG = 0;
5145
5146print "Chaosreader VNC Replay (experimental)\n\n";
5147print "Listening on port $port...\n";
5148
5149
5150# --- Open Socket ---
5151#
5152$server = IO::Socket::INET->new( Proto     => 'tcp',
5153                                 LocalPort => $port,
5154                                 Listen    => SOMAXCONN,
5155                                 Reuse     => 1);
5156
5157die "can't setup server" unless $server;
5158unless ($server) {
5159	die "ERROR48: Can't open port $port. Try a different port.";
5160}
5161
5162print <<WELCOME;
5163Port opened successfully.
5164
5165Now run vncviewer and connect to this port. eg,
5166	vncviewer -viewonly localhost:$vncport
5167
5168If you are prompted for a password, type any character and hit enter.
5169Waiting for connection...
5170WELCOME
5171
5172
5173# --- Subroutines ---
5174#
5175
5176# ms - sleeps for specified milliseconds
5177#
5178sub ms {
5179	$ms = shift;
5180	$ms = $ms / $factor;
5181	select(undef, undef, undef, $ms);
5182}
5183# help - print help
5184#
5185sub help {
5186        open (MYSELF,"$0") || die "ERROR49: I can't see myself: $!\n";
5187        @Myself = <MYSELF>;
5188        close MYSELF;
5189        ### Print comment from top of code
5190        foreach $line (@Myself) {
5191                last if $line !~ /^#/;
5192                next if $line =~ m:^#!/usr/bin/perl:;
5193                $line =~ s/^#/ /;
5194                print $line;
5195        }
5196        print "\n";
5197        exit(0);
5198}
5199
5200
5201#
5202# --- MAIN ---
5203#
5204
5205### Wait for connection
5206$client = $server->accept();
5207$client->autoflush(1);
5208
5209print "Sending VNC traffic:";
5210
5211END
5212
5213	#
5214	#  Sort the data on the timestamps, calculating timestamp differences
5215	#  to record in the replay program.
5216	#
5217	@Times = ();
5218	foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
5219		if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") {
5220			push(@Times,$time)
5221		}
5222	}
5223	@Times = sort { $a <=> $b } @Times;
5224
5225	#
5226	# --- Main Loop ---
5227	#
5228	# (this needs to be a for loop!)
5229	for ($i=0; $i <= $#Times; $i++) {
5230
5231		### Calculate time diff if possible
5232		if ($i == $#Times) {
5233			$timediff = 0;
5234		} else {
5235			$timediff = $Times[$i+1] - $Times[$i];
5236			# just in case,
5237			if ($timediff < 0) { $timediff = 0; }
5238		}
5239		$time = $Times[$i];
5240
5241		### Fetch data from mem
5242		$data = $TCP{id}{$session_id}{time}{$time}{data};
5243
5244		$data =~ s/\\/\\\\/g;
5245		$data =~ s/'/\\'/g;
5246		$data =~ s/\015\012/'."\\015\\012".'/gs;
5247
5248		#
5249		#  Now output the data in the replay program
5250		#
5251		print REPLAY "print '.';\n";
5252		print REPLAY "print \$client '" . $data . "';\n";
5253
5254		#
5255		#  This causes the replay program to pause
5256		#
5257		print REPLAY "ms($timediff);\n"
5258		 unless $timediff < 0.002;	# (efficiency).
5259	}
5260	print REPLAY "print \"\n\";\n";
5261	print REPLAY "close \$client;\n";
5262	close REPLAY;
5263
5264	### Better make it executable
5265	chmod (0755, "$filename");
5266
5267	### Global Vars
5268	$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5269	 "</a> $duration seconds</li>\n";
5270	$Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
5271	 "</a> $duration seconds</li>\n";
5272	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
5273	 '"' , "   $filename","",$duration);
5274	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
5275	 '"' , "   $filename2","",$duration);
5276}
5277
5278
5279
5280# Save_SMTP_Emails - Save emails from an SMTP session.
5281#
5282sub Save_SMTP_Emails {
5283	my ($filename);
5284	my $session_id = shift;
5285	my $number = shift;
5286	my $service_name = "smtp";
5287	my $numtext = sprintf("%04d",$number);
5288
5289
5290	### Full - Input
5291	$snmp_data = &TCP_Follow_RawB($session_id);
5292
5293	### Full - Processing
5294	@Snmp_parts = split(/\r\n\.\r\n|\n\.\n/,$snmp_data);
5295
5296	### LOOP
5297	$partnum = 0;
5298	foreach $snmp_part (@Snmp_parts) {
5299
5300		next unless $snmp_part =~ /DATA/;
5301		$partnum++;
5302		$parttext = sprintf("%02d",$partnum);
5303
5304		### Part - Processing
5305		$snmp_part =~ s/^.*DATA\r?\n//s;	# '/s;' is new perl5,
5306							# else '/;' with $* = 1
5307
5308		### Part - Output
5309		if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
5310		 else { $ext = ""; }
5311	        $filename = "session_${numtext}.part_${parttext}." .
5312		 "${service_name}${ext}.email";
5313	        open (OUT,">$filename") ||
5314		 die "ERROR50: file create, $filename: $!\n";
5315		binmode(OUT);		# for backward OSs
5316	        print OUT $snmp_part;
5317	        close OUT;
5318
5319		### Part - Global Vars
5320		my $length = length($snmp_part);
5321		$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5322		 "</a> $length bytes</li>\n";
5323		$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5324		 '"' , "   $filename","",$length);
5325	}
5326}
5327
5328
5329# Save_HTTP_Files - Save HTTP components.
5330#
5331sub Save_HTTP_Files {
5332	my ($filename);
5333	my $session_id = shift;
5334	my $number = shift;
5335	my $service_name = shift;
5336	my $numtext = sprintf("%04d",$number);
5337
5338	### Full - Input
5339	$http_session = &TCP_Follow_RawA($session_id);
5340
5341	### Full - Processing
5342	@HttpParts = split(/HTTP\/[0-9.]* /,$http_session);
5343
5344	### LOOP
5345	$partnum = 0;
5346	foreach $http_part (@HttpParts) {
5347
5348		### Part - Processing
5349		($http_header,$http_data) = split(/\r\n\r\n|\n\n/,$http_part,2);
5350		next if $http_data eq "";
5351		next if length($http_data) < 8;
5352		$partnum++;
5353		$parttext = sprintf("%02d",$partnum);
5354
5355		### Part - Checks
5356		$http_type = &File_Type($http_data);
5357		if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
5358		 else { $ext = ""; }
5359
5360		### Part - Output
5361	        $filename = "session_${numtext}.part_$parttext${ext}." .
5362		 "$http_type";
5363	        open (OUT,">$filename") ||
5364		 die "ERROR51: file create, $filename: $!\n";
5365		binmode(OUT);		# for backward OSs
5366	        print OUT $http_data;
5367	        close OUT;
5368
5369		### Part - Global Vars
5370		my $length = length($http_data);
5371		$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5372		 "</a> $length bytes</li>\n";
5373		$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5374		 '"' , "   $filename","",$length);
5375		if (&Is_Image($http_type)) {
5376			$Image{HTML}[$number]{links} .=
5377			 "<img src=\"$filename\"> ";
5378			$Image{notempty} = 1;
5379		}
5380	}
5381}
5382
5383
5384# Save_NFS_File - Save NFS file. Only works well for some files, if the NFS
5385#		header can't be processed, a "*.nfs.raw" file is created.
5386#
5387sub Save_NFS_File {
5388	my ($filename);
5389	my $session_id = shift;
5390	my $number = shift;
5391	my $service_name = "nfs";
5392	my $numtext = sprintf("%04d",$number);
5393
5394	### Input
5395	my $nfs_raw = &TCP_Follow_RawB($session_id);
5396
5397	### Processing
5398	($nfs_start,$nfs_size,$nfs_end) = unpack('a56a4a*',$nfs_raw);
5399	$nfs_sizeint = unpack("N",$nfs_size);
5400	($nfs_start,$nfs_data) = split(/$nfs_size....$nfs_size/,$nfs_end,2);
5401
5402	### Checks
5403	if (($nfs_sizeint > 4) && (length($nfs_data) >= $nfs_sizeint)) {
5404		$nfs_type = &File_Type($nfs_data);
5405		if ($nfs_sizeint < length($nfs_data)) {
5406			$nfs_data = unpack("a${nfs_sizeint}a*",$nfs_data);
5407		}
5408	} else {
5409		$nfs_type = "raw";
5410		$nfs_data = $nfs_raw;
5411	}
5412	if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
5413	 else { $ext = ""; }
5414
5415	### Output
5416        $filename = "session_${numtext}.part_01.${service_name}${ext}.nfs." .
5417	 "$nfs_type";
5418	open (OUT,">$filename") || die "ERROR52: file create, $filename: $!\n";
5419	binmode(OUT);		# for backward OSs
5420	print OUT $nfs_data;
5421	close OUT;
5422
5423	### Global Vars
5424	my $length = length($nfs_data);
5425	$Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename</a>" .
5426	 " $length bytes</li>\n";
5427	$Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5428	 '"' , "   $filename","",$length);
5429}
5430
5431
5432# TCP_Follow_RawA - process session by TCP Seq numbers 1-way.
5433#			(TCP ASSEMBLY)
5434#
5435sub TCP_Follow_RawA {
5436	my $session_id = shift;
5437	my $raw = "";
5438
5439	#
5440	#  Assemble TCP Sessions. Each hash contains session_ids as keys,
5441	#  and the value points to another hash of sequence numbers and data.
5442	#  %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output.
5443	#
5444	@Seqs = keys (%{$TCP{id}{$session_id}{Aseq}});
5445	foreach $seq (sort { $a <=> $b } @Seqs) {
5446		$raw .= ${$TCP{id}{$session_id}{Aseq}{$seq}};
5447	}
5448
5449	return $raw;
5450}
5451
5452
5453# TCP_Follow_RawB - process session by TCP Seq numbers 1-way.
5454#			(TCP ASSEMBLY)
5455#
5456sub TCP_Follow_RawB {
5457	my $session_id = shift;
5458	my $raw = "";
5459
5460	#
5461	#  Assemble TCP Sessions. Each hash contains session_ids as keys,
5462	#  and the value points to another hash of sequence numbers and data.
5463	#  %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output.
5464	#
5465	@Seqs = keys (%{$TCP{id}{$session_id}{Bseq}});
5466	foreach $seq (sort { $a <=> $b } @Seqs) {
5467		$raw .= ${$TCP{id}{$session_id}{Bseq}{$seq}};
5468	}
5469
5470	return $raw;
5471}
5472
5473
5474# Pick_Service_Port - pick which port is the server. Usually is the lower
5475#	number, however check if the direction is already known (eg SYN).
5476#	The port arguments will not often be needed.
5477#
5478# NOTE: This code is different to Generate_TCP_IPs - which does the "<->"'s
5479#
5480sub Pick_Service_Port {
5481	my $type = shift;
5482	my $id = shift;
5483	my $porta = shift;
5484	my $portb = shift;
5485	my $from_server = 0;
5486	my ($hi,$low);
5487
5488	# Catch active FTP, etc.
5489	($low,$hi) = sort { $a <=> $b } ($porta,$portb);
5490	if ($low < 100) {
5491		return ($low,$hi);
5492	}
5493
5494	if ($type eq "TCP") {
5495	   if (defined $TCP{id}{$id}{source}) {
5496		if ($TCP{id}{$id}{source} eq $TCP{id}{$id}{src}) {
5497		   return ($TCP{id}{$id}{dest_port},$TCP{id}{$id}{src_port});
5498		} else {
5499		   return ($TCP{id}{$id}{src_port},$TCP{id}{$id}{dest_port});
5500		}
5501	   }
5502	} elsif ($type eq "UDP") {
5503	   return ($UDP{id}{$id}{dest_port},$UDP{id}{$id}{src_port});
5504	}
5505
5506	# resort to a sort
5507	return sort { $a <=> $b } ($porta,$portb);
5508}
5509
5510
5511# Generate_SessionID - input source and dest IPs and ports, and generate
5512# 	a unique session_id based on them. this is done by sorting on
5513#	ports and then IPs. Also returns a flag if the packet may be
5514#	assumed to be from_server - where the lowest port is assumed to
5515#	be the server (unless TCP SYNs have been observed).
5516#
5517sub Generate_SessionID {
5518	my $ip_src = shift;
5519	my $tcp_src_port = shift;
5520	my $ip_dest = shift;
5521	my $tcp_dest_port = shift;
5522	my $type = shift;
5523	my $from_server = 0;
5524	my $session_id;
5525
5526	#
5527	#  Generate session_id string using host:port,host:port sorted on
5528	#  port (low port last).
5529	#
5530	if ($tcp_src_port < $tcp_dest_port) {
5531		$session_id = "$ip_dest:$tcp_dest_port,$ip_src:$tcp_src_port";
5532		$from_server = 1;
5533	} elsif ($tcp_src_port > $tcp_dest_port) {
5534		$session_id = "$ip_src:$tcp_src_port,$ip_dest:$tcp_dest_port";
5535		$from_server = 0;
5536	} else {
5537		$session_id =join(",",sort("$ip_src:$tcp_src_port",
5538					"$ip_dest:$tcp_dest_port"));
5539		$from_server = 1;
5540	}
5541
5542	if ($type eq "TCP") {
5543		if (defined $TCP{id}{$session_id}{source}) {
5544			if ($TCP{id}{$session_id}{source} eq $ip_dest) {
5545				$from_server = 1;
5546			} else {
5547				$from_server = 0;
5548			}
5549		}
5550	}
5551	return ($session_id,$from_server);
5552}
5553
5554
5555
5556# Generate_TCP_IDs - generate a text and html version of the session ID, that
5557#		displays direction of the TCP session if SYNs and ACKs were
5558#		observed, else uses a "<->" symbol to represent unknown
5559#		direction. TCP only.
5560#
5561sub Generate_TCP_IDs {
5562	my $session_id = shift;
5563	my ($ip_src,$tcp_src_port,$ip_dest,$tcp_dest_port,$text,$html);
5564
5565	# try this direction,
5566	$ip_src = $TCP{id}{$session_id}{src};
5567	$ip_dest = $TCP{id}{$session_id}{dest};
5568	$tcp_src_port = $TCP{id}{$session_id}{src_port};
5569	$tcp_dest_port = $TCP{id}{$session_id}{dest_port};
5570
5571	if (defined $TCP{id}{$session_id}{source}) {
5572		if ($TCP{id}{$session_id}{source} eq $ip_dest) {
5573			# nope, switch ends
5574			$ip_src = $TCP{id}{$session_id}{dest};
5575			$ip_dest = $TCP{id}{$session_id}{src};
5576			$tcp_src_port = $TCP{id}{$session_id}{dest_port};
5577			$tcp_dest_port = $TCP{id}{$session_id}{src_port};
5578		}
5579		$text = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port";
5580		$html = "$ip_src:$tcp_src_port -&gt; $ip_dest:$tcp_dest_port";
5581	} else {
5582		$text = "$ip_src:$tcp_src_port <-> $ip_dest:$tcp_dest_port";
5583		$html = "$ip_src:$tcp_src_port &lt;-&gt; " .
5584		 "$ip_dest:$tcp_dest_port";
5585	}
5586
5587	return ($text,$html);
5588}
5589
5590
5591
5592# Generate_IP_ID - input source IP, dest IP and ident, and generate a
5593#		unique ip_id based on them. This is necessary for IP
5594#		fragmentation reassembely. Normally we would assume that
5595#		the IP_ident was unique - however this program could
5596#		process traffic from many different hosts over a long
5597#		period of time - idents alone could clash.
5598#
5599sub Generate_IP_ID {
5600	my $ip_src = shift;
5601	my $ip_dest = shift;
5602	my $ip_ident = shift;
5603	my $ip_id;
5604
5605	#
5606	#  Generate ip_id string using host:host:ident sorted on IP.
5607	#
5608	#
5609	$ip_id = join(",",sort("$ip_src","$ip_dest")) . ",$ip_ident";
5610
5611	return $ip_id;
5612}
5613
5614
5615
5616# Read_Tcpdump_Record - Read the next tcpdump record, will "last" if
5617#			there are no more records.
5618#
5619sub Read_Tcpdump_Record {
5620	my $more;
5621
5622	### Fetch record header
5623	$length = read(INFILE,$header_rec,($integerSize * 2 + 8));
5624
5625	### Quit main loop if at end of file
5626	last if $length < 16;
5627
5628	### Throw out extra info in tcpdump/modified1 format
5629	if ($STYLE =~ /^modified/) {
5630		$length = read(INFILE,$more,8);
5631	}
5632
5633	$frame++;
5634
5635	## Unpack header, endian sensitive
5636	if ($STYLE =~ /1$/) {
5637		($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length,
5638		 $tcpdump_length_orig)
5639		 = unpack('NNNN',$header_rec);
5640	} else {
5641		($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length,
5642		 $tcpdump_length_orig)
5643		 = unpack('VVVV',$header_rec);
5644	}
5645	$length = read(INFILE,$tcpdump_data,$tcpdump_length);
5646	$tcpdump_drops = $tcpdump_length_orig - $tcpdump_length;
5647}
5648
5649
5650# Read_Snoop_Record - Read the next snoop record, will "last" if
5651#			there are no more records.
5652#
5653sub Read_Snoop_Record {
5654	### Fetch record header
5655	$length = read(INFILE,$header_rec,24);
5656
5657	### Quit main loop if at end of file
5658	last if $length < 24;
5659
5660	$frame++;
5661
5662	### Unpack header
5663	($snoop_length_orig,$snoop_length_inc,$snoop_length_rec,$snoop_drops,
5664		$snoop_seconds,$snoop_msecs) = unpack('NNNNNN',$header_rec);
5665	$length = read(INFILE,$snoop_data,$snoop_length_inc);
5666	$skip = read(INFILE,$pad,($snoop_length_rec - $snoop_length_inc - 24));
5667}
5668
5669
5670# Load_Index_File - Load the master index file "index.file" into @Master
5671#
5672sub Load_Index_File {
5673
5674	my ($path,$dir,$file,$start,$end,$duration,$index);
5675
5676	#
5677	#  Load index.file lines into memory
5678	#
5679	open (FILES,"index.file") || die "ERROR53: Can't read index.file: $!\n"
5680	 ."Standalone mode needs to have run recently from this directory.\n\n";
5681
5682	chomp(@Files = <FILES>);
5683	close FILES;
5684
5685	#
5686	#  Populate @Master
5687	#
5688	$index = 0;
5689	foreach $path (@Files) {
5690		($dir,$file,$duration,$start,$end) = split(/\t/,$path);
5691		$Master[$index]{starttime} = $start;
5692		$Master[$index]{endtime} = $end;
5693		$Master[$index]{dir} = $dir;
5694		$Master[$index]{file} = $file;
5695		$Master[$index]{duration} = $duration;
5696		$Master[$index]{size} = -s "$dir/$file";
5697		$index++;
5698	}
5699}
5700
5701
5702# Load_Etc_Services - load /etc/services lookup table into memory,
5703#			into %Services_TCP and %Services_UDP.
5704#
5705sub Load_Etc_Services {
5706	my ($line,$name,$service);
5707
5708	### Hardcoded
5709	%Services_TCP = (20 => "ftp-data",
5710		         21 => "ftp",
5711		         23 => "telnet",
5712		         25 => "smtp",
5713		         80 => "web",
5714		         109 => "pop2",
5715		         110 => "pop3",
5716		         143 => "imap",
5717		         513 => "login",
5718		         514 => "shell",
5719		         3128 => "web",
5720		         4110 => "irc4110",
5721		         5000 => "irc5000",
5722		         6000 => "X11",
5723		         6660 => "irc",
5724		         6665 => "irc",
5725		         6666 => "irc",
5726		         6667 => "irc",
5727		         6668 => "irc",
5728		         6669 => "irc",
5729		         7000 => "irc7000",
5730		         8000 => "irc8000",
5731		         8080 => "web",
5732		         9000 => "irc9000");
5733	# non standard IRC ports include the number in their name
5734
5735	foreach (@Save_As_X11_Playback_Ports) {
5736		$Services_TCP{$_} = "X11";
5737	}
5738
5739	foreach (@Save_As_VNC_Playback_Ports) {
5740		$Services_TCP{$_} = "VNC";
5741	}
5742
5743	%Services_UDP = (53 => "dns");
5744
5745	### File input
5746	open(SERVICES,"/etc/services") || return;
5747	while ($line = <SERVICES>) {
5748		next if $line =~ /^#|^\s*$/;    # skip comments, blank lines.
5749		if ($line =~ /\d\/tcp/) {
5750			$is_tcp = 1;
5751		} else {
5752			$is_tcp = 0;
5753		}
5754		$line =~ s:/.*::;
5755		($name,$port) = split(' ',$line);
5756		if ($is_tcp) {
5757			$Services_TCP{$port} = $name;
5758		} else {
5759			$Services_UDP{$port} = $name;
5760		}
5761
5762	}
5763	close SERVICES;
5764}
5765
5766
5767# Set_IP_Protocols - Set a lookup hash for IP Protocols to names.
5768# 		RFC790, RFC1700.
5769#
5770sub Set_IP_Protocols {
5771	%IP_Protocols = (0 => "Reserved",
5772			1 => "ICMP",
5773			2 => "Unassigned",
5774			3 => "Gateway-to-Gateway",
5775			4 => "CCMC Gateway Monitoring Message",
5776			5 => "ST",
5777			6 => "TCP",
5778			7 => "UCL",
5779			8 => "Unassigned",
5780			9 => "Secure",
5781			10 => "BBN RCC Monitoring",
5782			11 => "NVP",
5783			12 => "PUP",
5784			13 => "Pluribus",
5785			14 => "Telenet",
5786			15 => "XNET",
5787			16 => "Chaos",
5788			17 => "UDP",
5789			18 => "Multiplexing",
5790			19 => "DCN",
5791			20 => "TAC Monitoring",
5792			37 => "DDP",
5793			41 => "SIP",
5794			42 => "SDRP",
5795			44 => "IPv6 Frag",
5796			50 => "SIPP-ESP",
5797			51 => "SIPP-AH",
5798			53 => "SWIPE",
5799			50 => "SDRP",
5800			58 => "ICMPv6",
5801			88 => "IGRP",
5802			94 => "IPIP"
5803	);
5804}
5805
5806# Set_ICMP_Types - Set a lookup hash for ICMP Types. RFC792.
5807#
5808sub Set_ICMP_Types {
5809	%ICMP_Types = (0 => "Echo Reply",
5810			3 => "Destination Unreachable",
5811			4 => "Source Quench",
5812			5 => "Redirect",
5813			8 => "Echo",
5814			11 => "Time Exceeded",
5815			12 => "Parameter Problem",
5816			13 => "Timestamp",
5817			14 => "Timestamp Reply",
5818			15 => "Information Request",
5819			16 => "Information Reply",
5820			128 => "Echo",
5821			129 => "Echo Reply",
5822			135 => "Neighbor solicitation",
5823			136 => "Neighbor advertisement"
5824	);
5825}
5826
5827# Set_Result_Names - Set a lookup hash for squid result codes.
5828#		(This needs some fine tuning).
5829#
5830sub Set_Result_Names {
5831	%Result_Names = ("" => "TCP_MISS",
5832			000 => "TCP_MISS",
5833			200 => "TCP_HIT",
5834			302 => "TCP_HIT",
5835			304 => "TCP_REFRESH_HIT",
5836			404 => "TCP_NEGATIVE_HIT"
5837	);
5838}
5839
5840# Set_X11_Codes - creates a lookup hash needed for X11 transposing.
5841#
5842sub Set_X11_Codes {
5843	#
5844	#  This has a row per X11 code, the row describing the 16 bit
5845	#  words that make up the values. "1" means resource id.
5846	#  (some values are 8 bit, but are fortunately padded).
5847	#
5848
5849	@X11_Codes = (
5850[ 0 ],	# X_Error entry
5851[ 0, 2, 2, 0, 0, 0, 1, 0,4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateWindow 1
5852[ 0, 1, 0 ], # X_ChangeWindowAttributes
5853[ 0, 1 ], # X_GetWindowAttributes
5854[ 0 ], # X_DestroyWindow?
5855[ 0 ], # X_DestroySubwindows?
5856[ 0, 1 ], # X_ChangeSaveSet
5857[ 0, 1, 1, 0 ], # X_ReparentWindow
5858[ 0, 1 ], # X_MapWindow
5859[ 0, 1 ], # X_MapSubwindows
5860[ 0, 1 ], # X_UnmapWindow 10
5861[ 0, 1 ], # X_UnmapSubwindows
5862[ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ConfigureWindow
5863[ 0, 1 ], # X_CirculateWindow
5864[ 0, 2 ], # X_GetGeometry
5865[ 0, 1 ], # X_QueryTree
5866[ 0, 1 ], # X_InternAtom (? else 0,0)
5867[ 0 ], # X_GetAtomName?
5868[ 0, 1, 0, 0, 1, 0 ], # X_ChangeProperty (? else 0,1,0,0,0,0)
5869[ 0, 1, 0 ], # X_DeleteProperty
5870[ 0, 2, 0, 0, 0, 0 ], # X_GetProperty 20
5871[ 0 ], # X_ListProperties?
5872[ 0, 1, 0, 0 ], # X_SetSelectionOwner
5873[ 0 ], # X_GetSelectionOwner
5874[ 0, 1, 0, 0, 0, 0 ], # X_ConvertSelection
5875[ 0, 1, 0 ], # X_SendEvent
5876[ 0, 1, 0, 1, 0, 0 ], # X_GrabPointer
5877[ 0, 1, 0 ], # X_UngrabPointer?
5878[ 0, 1, 0, 1, 0, 0 ], # X_GrabButton
5879[ 0, 1, 0 ], # X_UngrabButton
5880[ 0, 1, 0, 0 ], # X_ChangeActivePointerGrab 30
5881[ 0, 1, 0, 0 ], # X_GrabKeyboard
5882[ 0, 1, 0 ], # X_UngrabKeyboard?
5883[ 0, 1, 0, 0 ], # X_GrabKey
5884[ 0, 1, 0 ], # X_UngrabKey
5885[ 0, 0, 0 ], # X_AllowEvents
5886[ 0 ], # X_GrabServer?
5887[ 0 ], # X_UngrabServer?
5888[ 0 ], # X_QueryPointer?
5889[ 0, 1, 0, 0 ], # X_GetMotionEvents
5890[ 0, 1, 1, 0 ], # X_TranslateCoords 40
5891[ 0, 1, 1, 0, 0, 0 ], # X_WarpPointer
5892[ 0, 1, 0 ], # X_SetInputFocus
5893[ 0 ], # X_GetInputFocus?
5894[ 0 ], # X_QueryKeymap?
5895[ 0, 1, 0 ], # X_OpenFont
5896[ 0, 1 ], # X_CloseFont
5897[ 0, 1 ], # X_QueryFont
5898[ 0, 1 ], # X_QueryTextExtents
5899[ 0, 0 ], # X_ListFonts
5900[ 0, 0 ], # X_ListFontsWithInfo 50
5901[ 0, 0 ], # X_SetFontPath
5902[ 0 ], # X_GetFontPath?
5903[ 0, 1, 2, 0 ], # X_CreatePixmap
5904[ 0 ], # X_FreePixmap?
5905[ 0, 1, 2, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateGC ?(else 0,1,1,0)
5906[ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeGC
5907[ 0, 1, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CopyGC
5908[ 0, 1, 0 ], # X_SetDashes
5909[ 0, 1, 0 ], # X_SetClipRectangles
5910[ 0, 1 ], # X_FreeGC? 60
5911[ 0, 1, 0, 0 ], # X_ClearArea
5912[ 0, 2, 2, 1, 0, 0, 0 ], # X_CopyArea
5913[ 0, 2, 2, 1, 0, 0, 0, 0 ], # X_CopyPlane
5914[ 0, 2, 1 ], # X_PolyPoint
5915[ 0, 2, 1 ], # X_PolyLine
5916[ 0, 2, 1 ], # X_PolySegment
5917[ 0, 2, 1 ], # X_PolyRectangle
5918[ 0, 2, 1 ], # X_PolyArc
5919[ 0, 2, 1, 0 ], # X_FillPoly
5920[ 0, 2, 1 ], # X_PolyFillRectangle 70
5921[ 0, 2, 1 ], # X_PolyFillArc
5922[ 0, 2, 1, 0, 0, 0 ], # X_PutImage
5923[ 0, 2, 0, 0, 0 ], # X_GetImage
5924[ 0, 2, 1, 0 ], # X_PolyText8
5925[ 0, 2, 1, 0 ], # X_PolyText16
5926[ 0, 2, 1, 0 ], # X_ImageText8
5927[ 0, 2, 1, 0 ], # X_ImageText16
5928[ 0, 3, 1, 1 ], # X_CreateColormap
5929[ 0 ], # X_FreeColormap?
5930[ 0, 3, 3 ], # X_CopyColormapAndFree 80
5931[ 0 ], # X_InstallColormap?
5932[ 0 ], # X_UninstallColormap?
5933[ 0 ], # X_ListInstalledColormaps?
5934[ 0, 3, 0, 0 ], # X_AllocColor
5935[ 0, 3, 0 ], # X_AllocNamedColor
5936[ 0, 3, 0 ], # X_AllocColorCells
5937[ 0, 3, 0, 0 ], # X_AllocColorPlanes
5938[ 0, 3, 0 ], # X_FreeColors
5939[ 0, 3 ], # X_StoreColors
5940[ 0, 3, 0, 0 ], # X_StoreNamedColor 90
5941[ 0, 3 ], # X_QueryColors
5942[ 0, 3, 0 ], # X_LookupColor
5943[ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateCursor
5944[ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateGlyphCursor
5945[ 0 ], # X_FreeCursor?
5946[ 0, 1, 0, 0, 0 ], # X_RecolorCursor
5947[ 0, 2, 0 ], # X_QueryBestSize
5948[ 0, 1 ], # X_QueryExtension (? else 0,0)
5949[ 0, 0, 0 ], # X_ListExtensions?
5950[ 0, 1, 0 ], # X_ChangeKeyboardMapping 100
5951[ 0, 1, 0 ], # X_GetKeyboardMapping
5952[ 0, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeKeyboardControl
5953[ 0, 0, 0 ], # X_GetKeyboardControl?
5954[ 0 ], # X_Bell
5955[ 0, 0, 0 ], # X_ChangePointerControl
5956[ 0, 0, 0 ], # X_GetPointerControl?
5957[ 0, 0, 0 ], # X_SetScreenSaver
5958[ 0, 0, 0 ], # X_GetScreenSaver?
5959[ 0, 0 ], # X_ChangeHosts
5960[ 0 ], # X_ListHosts 110
5961[ 0 ], # X_SetAccessControl
5962[ 0 ], # X_SetCloseDownMode
5963[ 0, 0, 0 ], # X_KillClient?
5964[ 0, 1, 0 ], # X_RotateProperties
5965[ 0 ], # X_ForceScreenSaver
5966[ 0 ], # X_SetPointerMapping
5967[ 0, 0, 0 ], # X_GetPointerMapping?
5968[ 0 ], # X_SetModifierMapping
5969[ 0, 0, 0 ], # X_GetModifierMapping?
5970[ 0 ], # undef 120
5971[ 0 ], # undef
5972[ 0 ], # undef
5973[ 0 ], # undef
5974[ 0 ], # undef
5975[ 0 ], # undef
5976[ 0 ], # undef
5977[ 0, 0, 0 ] # X_NoOperation 127
5978	);
5979
5980}
5981
5982# Set_X11_KeyCodes - creates a lookup hash of X11 Key codes needed
5983#	to generate coloured 2-way HTML X11 reports.
5984#
5985sub Set_X11_KeyCodes {
5986	my ($junk,$code,$char1,$char2,$line,
5987	 $sun_xmodmap_pke,$linux_xmodmap_pke);
5988	my %Alias;
5989
5990	#
5991	# These are generated using "xmodmap -pke" (and trimmed a little).
5992	#
5993	$sun_xmodmap_pke = <<END;
5994keycode   8 = Control_L
5995keycode   9 = Control_R
5996keycode  10 = Shift_L
5997keycode  11 = Shift_R
5998keycode  12 = Meta_L
5999keycode  13 = Meta_R
6000keycode  14 = Alt_L
6001keycode  15 = Alt_R
6002keycode  16 = space
6003keycode  17 = 0 parenright
6004keycode  18 = 1 exclam
6005keycode  19 = 2 at
6006keycode  20 = 3 numbersign
6007keycode  21 = 4 dollar
6008keycode  22 = 5 percent
6009keycode  23 = 6 asciicircum
6010keycode  24 = 7 ampersand
6011keycode  25 = 8 asterisk
6012keycode  26 = 9 parenleft
6013keycode  27 = minus underscore
6014keycode  28 = equal plus
6015keycode  29 = bracketleft braceleft
6016keycode  30 = bracketright braceright
6017keycode  31 = semicolon colon
6018keycode  32 = apostrophe quotedbl
6019keycode  33 = grave asciitilde
6020keycode  34 = comma less
6021keycode  35 = period greater
6022keycode  36 = slash question
6023keycode  37 = backslash bar
6024keycode  38 = a A
6025keycode  39 = b B
6026keycode  40 = c C
6027keycode  41 = d D
6028keycode  42 = e E
6029keycode  43 = f F
6030keycode  44 = g G
6031keycode  45 = h H
6032keycode  46 = i I
6033keycode  47 = j J
6034keycode  48 = k K
6035keycode  49 = l L
6036keycode  50 = m M
6037keycode  51 = n N
6038keycode  52 = o O
6039keycode  53 = p P
6040keycode  54 = q Q
6041keycode  55 = r R
6042keycode  56 = s S
6043keycode  57 = t T
6044keycode  58 = u U
6045keycode  59 = v V
6046keycode  60 = w W
6047keycode  61 = x X
6048keycode  62 = y Y
6049keycode  63 = z Z
6050keycode  64 = BackSpace
6051keycode  65 = Return
6052keycode  66 = Tab
6053keycode  67 = Escape
6054keycode  68 = Delete
6055END
6056
6057	#
6058	# These are generated using "xmodmap -pke" (and trimmed a little).
6059	#
6060	$linux_xmodmap_pke = <<END;
6061keycode   8 =
6062keycode   9 = Escape
6063keycode  10 = 1 exclam
6064keycode  11 = 2 at
6065keycode  12 = 3 numbersign
6066keycode  13 = 4 dollar
6067keycode  14 = 5 percent
6068keycode  15 = 6 asciicircum
6069keycode  16 = 7 ampersand
6070keycode  17 = 8 asterisk
6071keycode  18 = 9 parenleft
6072keycode  19 = 0 parenright
6073keycode  20 = minus underscore
6074keycode  21 = equal plus
6075keycode  22 = BackSpace Terminate_Server
6076keycode  23 = Tab ISO_Left_Tab
6077keycode  24 = q Q
6078keycode  25 = w W
6079keycode  26 = e E
6080keycode  27 = r R
6081keycode  28 = t T
6082keycode  29 = y Y
6083keycode  30 = u U
6084keycode  31 = i I
6085keycode  32 = o O
6086keycode  33 = p P
6087keycode  34 = bracketleft braceleft
6088keycode  35 = bracketright braceright
6089keycode  36 = Return
6090keycode  37 = Control_L
6091keycode  38 = a A
6092keycode  39 = s S
6093keycode  40 = d D
6094keycode  41 = f F
6095keycode  42 = g G
6096keycode  43 = h H
6097keycode  44 = j J
6098keycode  45 = k K
6099keycode  46 = l L
6100keycode  47 = semicolon colon
6101keycode  48 = apostrophe quotedbl
6102keycode  49 = grave asciitilde
6103keycode  50 = Shift_L
6104keycode  51 = backslash bar
6105keycode  52 = z Z
6106keycode  53 = x X
6107keycode  54 = c C
6108keycode  55 = v V
6109keycode  56 = b B
6110keycode  57 = n N
6111keycode  58 = m M
6112keycode  59 = comma less
6113keycode  60 = period greater
6114keycode  61 = slash question
6115keycode  62 = Shift_R
6116keycode  64 = Alt_L Meta_L
6117keycode  65 = space
6118keycode  94 = less greater
6119END
6120	%Alias = qw(exclam ! at @ dollar $ percent %
6121	 asciicircum ^ ampersand & asterisk * minus - underscore _
6122	 equal = plus + bracketleft [ bracketright ] braceleft {
6123	 braceright } semicolon ; colon : apostrophe ' quotedbl "
6124	 grave ` asciitilde ~ backslash \ bar | less <
6125	 period . greater > slash / question ?);
6126
6127	# naughty chatacrers (some of these generate warnings)
6128	@Alias{"parenleft","parenright","space"} = ("(",")"," ");
6129	@Alias{"Tab","Return","numbersign","comma"} = ("\t","\n","#",",");
6130
6131
6132	#
6133	#  Populate KeyCode aliase
6134	#
6135	foreach $line (split(/\n/,$sun_xmodmap_pke)) {
6136		($junk,$code,$junk,$char1,$char2) = split(' ',$line);
6137		if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; }
6138		if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; }
6139		if (length($char1) > 1) { $char1 = "."; }
6140		if (length($char2) > 1) { $char2 = "."; }
6141		$KeyCode{sun}{0}{$code} = $char1;
6142		$KeyCode{sun}{1}{$code} = $char2;
6143	}
6144	foreach $line (split(/\n/,$linux_xmodmap_pke)) {
6145		($junk,$code,$junk,$char1,$char2) = split(' ',$line);
6146		if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; }
6147		if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; }
6148		if (length($char1) > 1) { $char1 = "."; }
6149		if (length($char2) > 1) { $char2 = "."; }
6150		$KeyCode{linux}{0}{$code} = $char1;
6151		$KeyCode{linux}{1}{$code} = $char2;
6152	}
6153
6154}
6155
6156
6157# Set_VNC_Codes - set globals for VNC.
6158#
6159sub Set_VNC_Codes  {
6160
6161	### set client code to request size hash.
6162	%VNC_Code_Size = ( 0 => 20,
6163			   1 => 6,
6164			   2 => 4,
6165			   3 => 10,
6166			   4 => 8,
6167			   5 => 6,
6168			   6 => 8 );
6169
6170	### Some essential keysyms
6171	$KeyCode{vnc}{0}{"\010"} = "\b";
6172	$KeyCode{vnc}{0}{"\011"} = "\t";
6173	$KeyCode{vnc}{0}{"\015"} = "\n";
6174
6175}
6176
6177
6178
6179# Touch_Vars - This is stops perl -w warnings about vars used only once.
6180#		Part of my todo list is to cull this list.
6181#
6182#
6183sub Touch_Vars {
6184	#
6185	#  Perl < 5.6 code
6186	#
6187	#use vars qw($ip_ttl $udp_checksum $ip_ident $tcp_length_data
6188	#$ip_tos $tcp_options $opt_A $opt_D $tcp_header_rest $opt_J
6189	#$opt_P $opt_U $opt_X $opt_e $opt_h $opt_i $pad $opt_j
6190	#$snoop_length_orig $http_header $opt_p $opt_q $opt_r
6191	#$header_rest $tcp_ack $ether_dest $ether_src $skip
6192	#$ip_length $udp_length $ip_options $ip_checksum
6193	#$opt_b $opt_B $opt_l $opt_L $ip_rest $ip_hop $ip_reserved
6194	#$ip_flow $icmp_rest $opt_f $opt_z);
6195	#
6196	#  Perl 5.6 code
6197	#
6198	#our ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data,
6199	#$ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J,
6200	#$opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j,
6201	#$snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r,
6202	#$header_rest,$tcp_ack,$ether_dest,$ether_src,$skip,
6203	#$ip_length,$udp_length,$ip_options,$ip_checksum,
6204	#$opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved,
6205	#$ip_flow,$icmp_rest,$opt_f,$opt_z);
6206	#
6207	#  Perl < 5.6 and 5.6 code (but not elegant)
6208	#
6209	@Once_is_okay = ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data,
6210	$ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J,
6211	$opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j,
6212	$snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r,
6213	$header_rest,$tcp_ack,$ether_dest,$ether_src,$skip,
6214	$ip_length,$udp_length,$ip_options,$ip_checksum,$tcp_rst,$tcp_fin,
6215	$opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved,
6216	$ip_flow,$icmp_rest,$opt_f,$opt_z,$junk1,$opt_H,$opt_I,$opt_R);
6217}
6218
6219
6220# Check_Command - check which is the network sniffing command and save
6221#		it to $command.
6222#
6223sub Check_Command {
6224
6225	#
6226	#  Check which OS we are on, die if it looks incompatible
6227	#
6228	if ($^O eq "linux") {
6229		#
6230		#  The "-s9999" tells tcpdump to keep a packet up to this
6231		#  size, otherwise the default is 68 bytes. Some versions of
6232		#  tcpdump allow using "-s0" for unlimited.
6233		#
6234		$command = "tcpdump -s9999 -w";
6235	} elsif ($^O eq "solaris") {
6236		$command = "snoop -o";
6237	} else {
6238		die "ERROR54: Can't find the sniffer command for \"$^O\".\n" .
6239		 "\t Please use log mode instead.\n";
6240	}
6241
6242	#
6243	#  Check username
6244	#
6245	if ($ENV{LOGNAME} ne "root") {
6246		print STDERR "WARNING: Are you root? If not, this probably "
6247		 . "won't work. Trying anyway...\n";
6248	}
6249}
6250
6251
6252# Process_Command_Line_Arguments - this process the command line arguments
6253#	and sets various globals which are kept in %Arg. It also prints
6254#	usage and exists if need be.
6255#
6256sub Process_Command_Line_Arguments {
6257	my $result;
6258
6259	#
6260	#  Process Global Defaults into %Arg
6261	#
6262	foreach (@Save_As_HTML_TCP_Ports) {
6263		$Arg{Save_As_TCP_HTML}{$_} = 1;
6264	}
6265	foreach (@Save_As_HTML_UDP_Ports) {
6266		$Arg{Save_As_UDP_HTML}{$_} = 1;
6267	}
6268	foreach (@Save_As_TCP_Playback_Ports) {
6269		$Arg{Save_As_TCP_Playback}{$_} = 1;
6270	}
6271	foreach (@Save_As_UDP_Playback_Ports) {
6272		$Arg{Save_As_UDP_Playback}{$_} = 1;
6273	}
6274	foreach (@Save_As_X11_Playback_Ports) {
6275		$Arg{Save_As_X11_Playback}{$_} = 1;
6276	}
6277	foreach (@Save_As_HTML_X11_Ports) {
6278		$Arg{Save_As_X11_HTML}{$_} = 1;
6279	}
6280	foreach (@Save_As_VNC_Playback_Ports) {
6281		$Arg{Save_As_VNC_Playback}{$_} = 1;
6282	}
6283
6284	if (defined $ARGV[0]) {
6285		### Dump full help if asked
6286		&Usage_Full if $ARGV[0] eq "--help";
6287
6288		### Dump massive help if asked
6289		&Usage_Massive if $ARGV[0] eq "--help2";
6290	}
6291
6292	#
6293	#  Command Line Defaults
6294	#
6295	$Arg{output_raw} = 0;
6296	$Arg{output_hex} = 0;
6297	$Arg{output_UDP} = 1;
6298	$Arg{output_TCP} = 1;
6299	$Arg{output_ICMP} = 1;
6300	$Arg{output_info} = 0;
6301	$Arg{output_apps} = 1;
6302	$Arg{output_index} = 1;
6303	$Arg{keydata} = 0;
6304	$Arg{debug} = 0;
6305
6306	#
6307	#  Check correct switches were used
6308	#
6309	Getopt::Long::Configure ("bundling");
6310	$result = GetOptions (
6311				"application!" => \$opt_a,
6312				"a" => \$opt_a,
6313				"e|everything" => \$opt_e,
6314				"h" => \$opt_h,
6315				"info!" => \$opt_i,
6316				"i" => \$opt_i,
6317				"q|quiet" => \$opt_q,
6318				"raw!" => \$opt_r,
6319				"r" => \$opt_r,
6320				"v|verbose" => \$opt_v,
6321				"index!" => \$opt_x,
6322				"x" => \$opt_x,
6323				"A" => \$opt_A,
6324				"H|hex" => \$opt_H,
6325				"I" => \$opt_I,
6326				"R" => \$opt_R,
6327				"U|noudp" => \$opt_U,
6328				"T|notcp" => \$opt_T,
6329				"Y|noicmp" => \$opt_Y,
6330				"X" => \$opt_X,
6331				"D|dir=s" => \$opt_D,
6332				"b|playtcp=s" => \$opt_b,
6333				"B|playudp=s" => \$opt_B,
6334				"l|htmltcp=s" => \$opt_l,
6335				"L|htmludp=s" => \$opt_L,
6336				"m|min=s" => \$opt_m,
6337				"M|max=s" => \$opt_M,
6338				"o|sort=s" => \$opt_o,
6339				"p|port=s" => \$opt_p,
6340				"P|noport=s" => \$opt_P,
6341				"j|ipaddr=s" => \$opt_j,
6342				"J|noipaddr=s" => \$opt_J,
6343				"s|runonce=s" => \$opt_s,
6344				"S|runmany=s" => \$opt_S,
6345				"z|runredo" => \$opt_z,
6346				"f|filter=s" => \$opt_f,
6347				"k|keydata" => \$opt_k,
6348				"debug" => \$opt_debug,
6349				"bench" => \$opt_bench
6350	 );
6351
6352	#
6353	#  Process switches
6354	#
6355	&Usage() if ($opt_h || ! $result);
6356	$Arg{output_raw} = 1 if $opt_r or $opt_v;
6357	$Arg{output_hex} = 1 if $opt_H or $opt_e;
6358	$Arg{output_info} = 1 if $opt_i or $opt_v;
6359	$Arg{quiet} = 1 if $opt_q;
6360	$Arg{output_UDP} = 0 if $opt_U;
6361	$Arg{output_TCP} = 0 if $opt_T;
6362	$Arg{output_ICMP} = 0 if $opt_Y;
6363	$Arg{output_apps} = 0 if ($opt_A || (defined $opt_a && $opt_a eq "0"));
6364	$Arg{output_index} = 0 if ($opt_X || (defined $opt_x && $opt_x eq "0"));
6365	$Arg{output_allhtml} = 1 if $opt_e;
6366	my $extra_TCPplayback = $opt_b;
6367	my $extra_UDPplayback = $opt_B;
6368	my $extra_TCPhtml = $opt_l;
6369	my $extra_UDPhtml = $opt_L;
6370	my $ports_accepted = $opt_p;
6371	my $ports_rejected = $opt_P;
6372	my $ips_accepted = $opt_j;
6373	my $ips_rejected = $opt_J;
6374	$Arg{output_dir} = $opt_D;
6375	$Arg{filter} = $opt_f || "";
6376	$Arg{minbytes} = 0;
6377	$Arg{maxbytes} = 0;
6378	$Arg{sort} = "time";
6379	$Arg{keydata} = 1 if $opt_k;
6380	$Arg{debug} = 1 if $opt_debug;
6381	$Arg{bench} = 1 if $opt_bench;
6382
6383	#
6384	#  Check for min/max bytes
6385	#
6386	if (defined $opt_m) {
6387		if ($opt_m =~ /k$/) {
6388			$opt_m =~ s/k$//;
6389			$opt_m *= 1024;
6390		}
6391		$Arg{minbytes} = $opt_m;
6392	}
6393	if (defined $opt_M) {
6394		if ($opt_M =~ /k$/) {
6395			$opt_M =~ s/k$//;
6396			$opt_M *= 1024;
6397		}
6398		$Arg{maxbytes} = $opt_M;
6399	}
6400
6401	#
6402	#  Check for sort option
6403	#
6404	if (defined $opt_o) {
6405		if ($opt_o !~ /^(time|size|type|ip)$/) {
6406			print STDERR "ERROR55: Sort must be \"time\", " .
6407			 "\"size\", \"type\" or \"ip\".\n";
6408			&Usage();
6409		}
6410		$Arg{sort} = $opt_o;
6411	}
6412
6413	#
6414	#  Check for standalone redo mode
6415	#
6416	if (defined $opt_z) {
6417		$Arg{redo} = 1;
6418		if (defined $Arg{output_dir}) {
6419			# bad luck
6420			die "ERROR56: Can't use an output dir "
6421			 . "$Arg{output_dir} in redo mode.\n\n";
6422		}
6423	}
6424
6425	#
6426	#  Check for standalone mode
6427	#
6428	elsif (defined $opt_s || defined $opt_S) {
6429		$Arg{standalone} = 1;
6430		if (defined $opt_s) {
6431			if ($opt_s =~ /,/) {
6432				die "ERROR57: Unexpected comma found in " .
6433				 "\"-s$opt_s\" (did you mean \"-S$opt_s\"?)\n";
6434			}
6435			$Arg{mins} = $opt_s;
6436			$Arg{count} = 1;
6437		} elsif (defined $opt_S) {
6438			my ($mins,$count) = split(/,/,$opt_S);
6439			$Arg{mins} = $mins;
6440			### -1 means endless
6441			$Arg{count} = $count || -1;
6442		}
6443	}
6444
6445	#
6446	#  This is normal mode
6447	#
6448	else {
6449		$Arg{normal} = 1;
6450	}
6451
6452	#
6453	#  Build accepted or rejected port list as %Arg{Port_Accepted},...
6454	#
6455	if (defined $ports_accepted) {
6456		$Arg{port_accept} = 1;
6457		foreach $port (split(/,/,$ports_accepted)) {
6458			$Arg{Port_Accepted}{$port} = 1;
6459		}
6460	}
6461	if (defined $ports_rejected) {
6462		$Arg{port_reject} = 1;
6463		foreach $port (split(/,/,$ports_rejected)) {
6464			$Arg{Port_Rejected}{$port} = 1;
6465		}
6466	}
6467
6468	#
6469	#  Build accepted or rejected IP list as %Arg{IP_Accepted},...
6470	#
6471	if (defined $ips_accepted) {
6472		$Arg{ip_accept} = 1;
6473		foreach $ip (split(/,/,$ips_accepted)) {
6474			$Arg{IP_Accepted}{$ip} = 1;
6475		}
6476	}
6477	if (defined $ips_rejected) {
6478		$Arg{ip_reject} = 1;
6479		foreach $ip (split(/,/,$ips_rejected)) {
6480			$Arg{IP_Rejected}{$ip} = 1;
6481		}
6482	}
6483
6484	#
6485	#  Add extra ports to playback or HTML
6486	#
6487	if (defined $extra_TCPplayback) {
6488		foreach $port (split(/,/,$extra_TCPplayback)) {
6489			$Arg{Save_As_TCP_Playback}{$port} = 1;
6490		}
6491	}
6492	if (defined $extra_UDPplayback) {
6493		foreach $port (split(/,/,$extra_UDPplayback)) {
6494			$Arg{Save_As_UDP_Playback}{$port} = 1;
6495		}
6496	}
6497	if (defined $extra_TCPhtml) {
6498		foreach $port (split(/,/,$extra_TCPhtml)) {
6499			$Arg{Save_As_TCP_HTML}{$port} = 1;
6500		}
6501	}
6502	if (defined $extra_UDPhtml) {
6503		foreach $port (split(/,/,$extra_UDPhtml)) {
6504			$Arg{Save_As_UDP_HTML}{$port} = 1;
6505		}
6506	}
6507
6508	#
6509	#  Check infile was provided, or print usage
6510	#
6511	if (! defined $ARGV[0] && ! ($Arg{standalone} || $Arg{redo})) {
6512		&Usage();
6513	}
6514	@{$Arg{infiles}} = @ARGV;
6515}
6516
6517
6518# Usage - print command usage and exit.
6519#
6520sub Usage {
6521        print <<END;
6522USAGE: chaosreader [-aehikqrvxAHIRTUXY] [-D dir]
6523	           [-b port[,...]] [-B port[,...]]
6524	           [-j IPaddr[,...]] [-J IPaddr[,...]]
6525 	           [-l port[,...]] [-L port[,...]] [-m bytes[k]]
6526	           [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"]
6527                   [-p port[,...]] [-P port[,...]]
6528	           infile [infile2 ...]
6529       chaosreader -s [mins] | -S [mins[,count]]
6530	           [-z] [-f 'filter']
6531   eg,
6532      chaosreader infile      # Create application session files, indexes
6533      chaosreader -v infile   # Verbose - Create ALL files
6534      chaosreader -i infile   # Create info files
6535      chaosreader -r infile   # Create raw files
6536      chaosreader -S 2,5      # Standalone - sniff network 5 times by 2 mins.
6537      chaosreader -h          # Print a brief help (this)
6538      chaosreader --help      # Print verbose help and version
6539      chaosreader --help2     # Print massive help
6540END
6541        exit(0);
6542}
6543
6544
6545# Usage Full - print command usage and exit.
6546#
6547sub Usage_Full {
6548        print "Version 0.94, 01-May-2004
6549
6550USAGE: chaosreader [-aehikqrvxAHIRTUXY] [-D dir]
6551                   [-b port[,...]] [-B port[,...]]
6552                   [-j IPaddr[,...]] [-J IPaddr[,...]]
6553                   [-l port[,...]] [-L port[,...]] [-m bytes[k]]
6554                   [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"]
6555                   [-p port[,...]] [-P port[,...]]
6556                   infile [infile2 ...]
6557
6558       chaosreader -s [mins] | -S [mins[,count]]
6559	           [-z] [-f 'filter']
6560
6561   chaosreader           # Create application session files, indexes
6562
6563   -a, --application     # Create application session files (default)
6564   -e, --everything      # Create HTML 2-way & hex files for everything
6565   -h                    # Print a brief help
6566   --help                # Print verbose help (this) and version
6567   --help2               # Print massive help
6568   -i, --info            # Create info file
6569   -q, --quiet           # Quiet, no output to screen
6570   -r, --raw             # Create raw files
6571   -v, --verbose         # Verbose - Create ALL files .. (except -e)
6572   -x, --index           # Create index files (default)
6573   -A, --noapplication   # Exclude application session files
6574   -H, --hex             # Include hex dumps (slow)
6575   -I, --noinfo          # Exclude info files
6576   -R, --noraw           # Exclude raw files
6577   -T, --notcp           # Exclude TCP traffic
6578   -U, --noudp           # Exclude UDP traffic
6579   -Y, --noicmp          # Exclude ICMP traffic
6580   -X, --noindex         # Exclude index files
6581   -k, --keydata         # Create extra files for keystroke analysis
6582   -D dir    --dir dir        # Output all files to this directory
6583   -b 25,79  --playtcp 25,79  # replay these TCP ports as well (playback)
6584   -B 36,42  --playudp 36,42  # replay these UDP ports as well (playback)
6585   -l 7,79   --htmltcp 7,79   # Create HTML for these TCP ports as well
6586   -L 7,123  --htmludp 7,123  # Create HTML for these UDP ports as well
6587   -m 1k     --min 1k         # Min size of connection to save (\"k\" for Kb)
6588   -M 1024k  --max 1k         # Max size of connection to save (\"k\" for Kb)
6589   -o size   --sort size      # sort Order: time/size/type/ip (Default time)
6590   -p 21,23  --port 21,23     # Only examine these ports (TCP & UDP)
6591   -P 80,81  --noport 80,81   # Exclude these ports (TCP & UDP)
6592   -s 5      --runonce 5      # Standalone. Run tcpdump/snoop for 5 mins.
6593   -S 5,10   --runmany 5,10   # Standalone, many. 10 samples of 5 mins each.
6594   -S 5      --runmany 5      # Standalone, endless. 5 min samples forever.
6595   -z        --runredo        # Standalone, redo. Rereads last run's logs.
6596   -j 10.1.2.1  --ipaddr 10.1.2.1    # Only examine these IPs
6597   -J 10.1.2.1  --noipaddr 10.1.2.1  # Exclude these IPs
6598   -f 'port 7'  --filter 'port 7'    # With standalone, use this dump filter.
6599
6600eg1,
6601     tcpdump -s9000 -w output1          # create tcpdump capture file
6602     chaosreader output1                # extract recognised sessions, or,
6603     chaosreader -ve output1            # gimme everything, or,
6604     chaosreader -p 20,21,23 output1    # only ftp and telnet...
6605eg2,
6606     snoop -o output1                   # create snoop capture file instead
6607     chaosreader output1                # extract recognised sessions...
6608eg3,
6609     chaosreader -S 2,5		# Standalone, sniff network 5 times for 2 mins
6610				# each. View index.html for progress (or .text)
6611";
6612        exit(0);
6613}
6614
6615
6616# Usage_Massive - print massive help. Actually strip it from the comments
6617#		at the top of the code.
6618#
6619sub Usage_Massive {
6620	open (MYSELF,"$0") || die "ERROR58: I can't see myself: $!\n";
6621	@Myself = <MYSELF>;
6622	close MYSELF;
6623
6624	### Print comment from top of code
6625	foreach $line (@Myself) {
6626		last if $line !~ /^#/;
6627		last if $line =~ /^# Todo:/;
6628		next if $line =~ m:^#!/usr/bin/perl:;
6629		$line =~ s/^#/ /;
6630		print $line;
6631	}
6632	print "\n";
6633
6634        exit(0);
6635}
6636
6637
6638
6639__END__
6640
6641Reminders for myself
6642====================
6643/s for multiline match
6644
6645
6646Comments style:
6647
6648# Micro comment
6649
6650### Tiny Comment
6651
6652#
6653#  Small comment
6654#
6655
6656#
6657# --- Meduim Comment ---
6658#
6659
6660#########################
6661# --- Large Comment ---
6662#
6663
6664########################
6665# --- Huge Comment --- #
6666########################
6667
6668
6669Error message style
6670===================
6671
6672die "ERROR#: message: $!\n";
6673
6674
6675
6676Data types,
6677===========
6678	%Arg
6679		-> @infiles
6680		-> output_raw
6681		-> output_hex
6682		-> output_UDP
6683		-> output_info
6684		-> output_apps
6685		-> output_index
6686		-> output_allhtml
6687		-> Save_As_TCP_HTML
6688			-> $port
6689		-> Save_As_UDP_HTML
6690			-> $port
6691		-> Save_As_TCP_Playback
6692			-> $port
6693		-> Save_As_UDP_Playback
6694			-> $port
6695		-> Port_Accepted
6696			-> $port
6697		-> Port_Rejected
6698			-> $port
6699		-> ip_accept
6700		-> ip_reject
6701		-> IP_Accepted
6702			-> $ip
6703		-> IP_Rejected
6704			-> $ip
6705		-> debug
6706		-> standalone
6707		-> redo
6708		-> normal
6709		-> mins
6710		-> count
6711		-> output_dir
6712		-> quiet
6713		-> infile
6714		-> minbytes
6715		-> maxbytes
6716
6717	%IP
6718		-> time
6719			-> $packet_time
6720				-> ver
6721				-> src
6722				-> dest
6723				-> protocol
6724				-> frag
6725					-> $ip_frag
6726				-> fragged
6727				-> drops
6728		-> id
6729			-> $ip_id
6730				-> StartTime
6731
6732	%TCP
6733		-> id
6734			-> $session_id
6735				-> src
6736				-> dest
6737				-> source	# SYN seen
6738				-> src_port
6739				-> dest_port
6740				-> Aseq
6741					-> $$tcp_seq
6742				-> Bseq
6743					-> $$tcp_seq
6744				-> time
6745					-> $time
6746						-> dir
6747						-> data
6748				-> BothHTML
6749				-> StartTime
6750				-> EndTime
6751				-> size
6752				-> knowndir
6753
6754	%UDP
6755		-> id
6756			-> $session_id
6757				-> src
6758				-> dest
6759				-> src_port
6760				-> dest_port
6761				-> RawA
6762				-> RawB
6763				-> time
6764					-> $time
6765				-> BothHTML
6766				-> StartTime
6767				-> EndTime
6768				-> size
6769
6770	%ICMP
6771		-> time
6772			-> type
6773			-> code
6774			-> src
6775			-> dest
6776			-> Partial
6777			-> ver
6778			-> size
6779
6780	%Count
6781		-> IP
6782		-> IPprotocols
6783		-> TCPports
6784		-> UDPports
6785		-> EtherType
6786
6787	%CountMaster
6788		(as above)
6789
6790	%Index
6791		-> @HTML
6792		-> @Text
6793		-> Time_Order
6794			-> $session_timeid
6795		-> Sort_Lookup
6796			-> $session_timeid
6797
6798	%Image
6799		-> @HTML
6800			-> links
6801			-> info
6802		-> notempty
6803
6804	%GETPOST
6805		-> @HTML
6806			-> query
6807			-> info
6808		-> notempty
6809
6810	%Hex
6811		-> $type
6812			-> $session_id
6813				-> offset
6814				-> pos
6815				-> hextext
6816				-> hexhtml
6817				-> viewtext
6818				-> viewhtml
6819
6820	%Filenames
6821		-> $time
6822			-> filename
6823			-> service
6824			-> session_id
6825
6826	@Master
6827		-> starttime
6828		-> endtime
6829		-> duration
6830		-> size
6831		-> dir
6832		-> file
6833
6834
6835
6836
6837