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