· 6 years ago · Nov 18, 2019, 07:36 PM
1#!/usr/bin/perl
2#
3# Chaosreader can trace TCP/UDP/... sessions and fetch application data
4# from tcpdump or snoop logs. This is like an "any-snarf" program, it will
5# fetch telnet sessions, FTP files, HTTP transfers (HTML, GIF, JPEG, ...),
6# SMTP emails, etc ... from the captured data inside the network traffic
7# logs. It creates a html index file that links to all the session details,
8# including realtime replay programs for telnet, rlogin or IRC sessions;
9# and reports such as image reports and HTTP GET/POST content reports.
10# It also creates replay programs for telnet sessions, so that you can
11# play them back in realtime (or even different speeds).
12#
13# Chaosreader can also run in standalone mode - where it invokes tcpdump or
14# snoop (if they are available) to create the log files and then processes
15# them.
16#
17# 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 doesn'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 <-> $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 -> $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/</</g;
2477 $value =~ s/>/>/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/</</g;
2520 $value =~ s/>/>/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/</</g;
3279 $data =~ s/>/>/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">><\/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">><\/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/</</g;
3382 $view2 =~ s/>/>/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">><\/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">><\/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 $speed = sprintf("%.2f",$bytes / (1024 * $duration));
4279 print REPLAY "print \"\n\n" .
4280 "Summary: $duration2 seconds, $bytes bytes, $speed Kb/sec\\n\";";
4281 close REPLAY;
4282
4283 ### Better make it executable
4284 chmod (0755, "$filename1");
4285
4286 #
4287 # HTML version of the replay script
4288 #
4289 $filename2 = "session_${numtext}.text${service_name}.html";
4290 open (HTML,">$filename2") ||
4291 die "ERROR36: Can't write to file, $filename2 $!\n";
4292 $html = "<html><head><title>SSH text analysis</title></head>\n" .
4293 "<body bgcolor=\"white\">" .
4294 "<H1>$service_name: $session_text</H1>\n" .
4295 "<H2>File $Arg{infile}, Session $number</H2>\n" .
4296 "<h3>$duration2 seconds, $bytes bytes, $speed Kb/sec</h3>\n" .
4297 '"*" is client traffic (including ' .
4298 'keystrokes), "." is the return ' .
4299 'text.<br>A number is a multiple of the previous char, eg ".32" ' .
4300 'is 32 return chars.<br>' .
4301 "\n<b><pre>$html</pre></b>\n</body>\n</html>\n";
4302 print HTML $html;
4303 close HTML;
4304
4305 #
4306 # Text Database of time delays between possible keystrokes
4307 #
4308 $filename3 = "session_${numtext}.text${service_name}.keydata";
4309 open (DELAY,">$filename3") ||
4310 die "ERROR37: Can't write keydata file: $filename3 $!\n";
4311 $delay = "$delay \n";
4312 print DELAY $delay;
4313 close DELAY;
4314
4315 #
4316 # Update Global Vars to remember new filenames
4317 #
4318 $Index{HTML}[$number] .= "<li><a href=\"$filename1\">$filename1" .
4319 "</a> $duration seconds</li>\n";
4320 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
4321 '"' , " $filename1","",$duration);
4322 $Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
4323 "</a> </li>\n";
4324 $length = length($html);
4325 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
4326 '"' , " $filename2","",$length);
4327 $Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" .
4328 "</a> </li>\n";
4329 $length = length($delay);
4330 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
4331 '"' , " $filename3","",$length);
4332}
4333
4334
4335# Save_Session_Keydata - Save a key delay data file to assist SSH analysis.
4336#
4337# This code is intentionally designed to be similar to the SSH processing
4338# code, so that both their outputs can be compared. As a standalone
4339# subroutine this wouldn't make too much sense; instead bear in mind that
4340# I'd like the processing to mimic how SSH was processed. That way we
4341# run this on plenty of known text (telnet) and become familiar with
4342# exactly what will happen for the unknown text (SSH).
4343#
4344sub Save_Session_Keydata {
4345 my ($filename1,$filename2,$filename3,$duration,$time,$data,$length,
4346 $time0,$time1,$time2,$data0,$data1,$data2,$length0,$length1,$length2,
4347 $dir0,$dir1,$dir2,$timediff,$timediff2,$outtime,$outsize);
4348 my $session_id = shift;
4349 my $number = shift;
4350 my $service_name = shift;
4351 my $session_text = shift;
4352 my $numtext = sprintf("%04d",$number);
4353 my $delay = ""; # a text list of key delays
4354 my $minsize; # The min client packet size
4355 my $state = "key";
4356
4357 ### Sort the data by timestamps
4358 @Times = ();
4359 %PacketSize = ();
4360 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
4361 if (length($TCP{id}{$session_id}{time}{$time}{data}) == 0) {
4362 next;
4363 }
4364 push(@Times,$time);
4365 }
4366 @Times = sort { $a <=> $b } @Times;
4367 $outtime = $Times[0];
4368 $outsize = 0;
4369 $minsize = 1; # known for telnet
4370
4371 ### Process data
4372 for ($i=0; $i <= $#Times; $i++) { # required
4373
4374 ### Calculate time diff if possible
4375 $time0 = $Times[$i];
4376 $time1 = $Times[$i+1];
4377 $time2 = $Times[$i+2];
4378 if ($i == $#Times) {
4379 $timediff1 = 0;
4380 $timediff2 = 0;
4381 } else {
4382 $timediff1 = $time1 - $time0;
4383 $timediff2 = $time2 - $time0;
4384 if ($timediff1 < 0) { $timediff1 = 0; }
4385 }
4386
4387 ### Fetch data from mem, "0" is this packet...
4388 $data0 = $TCP{id}{$session_id}{time}{$time0}{data};
4389 $data1 = $TCP{id}{$session_id}{time}{$time1}{data};
4390 $data2 = $TCP{id}{$session_id}{time}{$time2}{data};
4391 $data0 = "\n" if $data0 eq "\r\n";
4392 $data1 = "\n" if $data1 eq "\r\n";
4393 $data2 = "\n" if $data2 eq "\r\n";
4394 $data0 = "\n" if $data0 =~ /\r./;
4395 $data1 = "\n" if $data1 =~ /\r./;
4396 $data2 = "\n" if $data2 =~ /\r./;
4397 $dir0 = $TCP{id}{$session_id}{time}{$time0}{dir};
4398 $dir1 = $TCP{id}{$session_id}{time}{$time1}{dir};
4399 $dir2 = $TCP{id}{$session_id}{time}{$time2}{dir};
4400 $length0 = length($data0);
4401 $length1 = length($data1);
4402 $length2 = length($data2);
4403
4404 $length = $length0;
4405 $data = $data0;
4406
4407 #
4408 # Process Data
4409 #
4410 if ($dir0 eq "A") {
4411 if ($state eq "output") {
4412 if ($length0 > $minsize) {
4413 $length -= $minsize;
4414 $outsize += $length;
4415 }
4416
4417 ### Data -> Keystrokes
4418 if ($dir1 eq "B" && $length1 == $minsize) {
4419 $delay .= "s $outsize\n";
4420 $delay .= sprintf("t %.6f\n",$time0 - $outtime);
4421 $delay .= " \n";
4422 $outsize = 0;
4423 $outtime = $time0;
4424 $state = "key";
4425 }
4426 }
4427 } else {
4428 ### Keystroke
4429 if ($length0 == $minsize) {
4430 if ($data0 eq "\n") {
4431 $delay .= "k \\n\n";
4432 } else {
4433 $delay .= "k $data0\n";
4434 }
4435 }
4436 ### Keystroke -> Keystroke delay
4437 if ($length0 == $minsize && $dir1 eq "A" && $dir2 eq "B" &&
4438 $length2 == $minsize) {
4439 $timediff2 = $time2 - $time0;
4440 $delay .= sprintf("d %.6f\n",$timediff2);
4441 $outsize = 0;
4442 $outtime = $time0;
4443 }
4444
4445 if ($length0 > $minsize) {
4446 $length -= $minsize;
4447 }
4448
4449 ### Keystrokes -> Data
4450 if ( ($length0 == $minsize &&
4451 (($length1 + $length2) > ($minsize * 2))) ||
4452 ($dir1 eq "A" && $dir2 eq "A") ) {
4453 if ($length1 > $minsize) {
4454 $delay .= "r 1\n";
4455 $delay .= sprintf("p %.6f\n",$timediff1);
4456 } else {
4457 $delay .= "r 2\n";
4458 $delay .= sprintf("p %.6f\n",$timediff2);
4459 }
4460 $state = "output";
4461 }
4462 }
4463 }
4464
4465 #
4466 # Text Database of time delays between possible keystrokes
4467 #
4468 $filename3 = "session_${numtext}.${service_name}.keydata";
4469 open (DELAY,">$filename3") ||
4470 die "ERROR38: A pink jelly hits you. You die. $filename3 $!\n";
4471 print DELAY "$delay \n";
4472 close DELAY;
4473
4474 #
4475 # Update Global Vars to remember new filenames
4476 #
4477 $Index{HTML}[$number] .= "<li><a href=\"$filename3\">$filename3" .
4478 "</a> </li>\n";
4479 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s\n",
4480 '"' , " $filename3","","");
4481}
4482
4483
4484# Save_Stream_Replay - Save a replay program for this stream. eg, dns.
4485#
4486sub Save_Stream_Replay {
4487 my ($filename,$duration);
4488 my $session_id = shift;
4489 my $number = shift;
4490 my $service_name = shift;
4491 my $numtext = sprintf("%04d",$number);
4492
4493 ### Output
4494 $filename = "stream_${numtext}.${service_name}.replay";
4495 $duration = ($UDP{id}{$session_id}{EndTime} -
4496 $UDP{id}{$session_id}{StartTime});
4497 $duration = sprintf("%.0f",$duration);
4498 open (REPLAY,">$filename") ||
4499 die "ERROR39: creating $filename $!\n";
4500 binmode(REPLAY); # for backward OSs
4501
4502 #
4503 # Create a perl program, that when run itself will print out
4504 # the contents of the server 1-way stream, with pauses based on
4505 # the packet arrival times (replay the stream in realtime).
4506 #
4507 print REPLAY "#!$PERL\n";
4508 print REPLAY <<'END';
4509#
4510# This is a UDP replay program. It will replay a stream using
4511# the timestamps from the packet log.
4512#
4513# USAGE: run the script as normal. You can provide a factor as an
4514# argument, eg "2" to run twice as fast, or "0.5" to run
4515# at half time. eg,
4516# ./stream_0002.telnet.replay 2
4517#
4518# Auto generated by Chaosreader.
4519#
4520$| = 1;
4521$factor = $ARGV[0] || 1;
4522sub ms {
4523 $ms = shift;
4524 $ms = $ms / $factor;
4525 select(undef, undef, undef, $ms);
4526}
4527END
4528
4529 #
4530 # Sort the data on the timestamps, calculating timestamp differences
4531 # to record in the replay program.
4532 #
4533 @Times = keys (%{$UDP{id}{$session_id}{time}});
4534 @Times = sort { $a <=> $b } @Times;
4535
4536 for ($i=0; $i <= $#Times; $i++) { # required
4537
4538 ### Calculate time diff if possible
4539 if ($i == $#Times) {
4540 $timediff = 0;
4541 } else {
4542 $timediff = $Times[$i+1] - $Times[$i];
4543 if ($timediff < 0) { $timediff = 0; }
4544 }
4545 $time = $Times[$i];
4546
4547 ### Fetch data from mem
4548 $data = $UDP{id}{$session_id}{time}{$time};
4549 delete $UDP{id}{$session_id}{time}{$time};
4550
4551 #
4552 # Clean the data a little (order important)
4553 #
4554 $data =~ s/\\/\\\\/g; # backslash the backslashes
4555 $data =~ s/'/\\'/g; # backslash single quotes
4556
4557 #
4558 # Now output the data in the replay program
4559 #
4560 print REPLAY "print '" . $data . "';\n";
4561
4562 #
4563 # This causes the replay program to pause
4564 #
4565 print REPLAY "ms($timediff);\n";
4566 }
4567 close REPLAY;
4568
4569 ### Better make it executable
4570 chmod (0755, "$filename");
4571
4572 ### Global Vars
4573 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
4574 "</a> $duration seconds</li>\n";
4575 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
4576 '"' , " $filename","",$duration);
4577}
4578
4579
4580# Save_Session_XReplay - Save a replay program for this session. eg, X11.
4581# This processes far more of the X11 protocol than I was hoping.
4582# (xscope and ethereal were used to analyse X11).
4583#
4584sub Save_Session_XReplay {
4585 my $session_id = shift;
4586 my $number = shift;
4587 my $service_name = shift;
4588 my $numtext = sprintf("%04d",$number);
4589 my ($filename,$duration,$xcode,$xres_old,$xrest,$xwnum,$xdiff,
4590 $xlength,$xmsb,$xstart,$xjunk,$xvalue,$readnow,$data,$newdata,
4591 $n,$N,$chars,$y,$timediff,$texttimediff,$checkdepth,$filename2,
4592 $x11type);
4593 my @xWords;
4594
4595 ### Initials
4596 $xmsb = "";
4597 $readnow = 0;
4598 $xres_old = -1;
4599 $checkdepth = 0;
4600
4601 #
4602 # Output - Main X11 replay program
4603 #
4604 $filename = "session_${numtext}.${service_name}.replay";
4605 $duration = ($TCP{id}{$session_id}{EndTime} -
4606 $TCP{id}{$session_id}{StartTime});
4607 $duration = sprintf("%.0f",$duration);
4608 open (REPLAY,">$filename") ||
4609 die "ERROR40: creating $filename $!\n";
4610 binmode(REPLAY); # for backward OSs
4611
4612 #
4613 # Output - Text (keystroke replay)
4614 #
4615 $filename2 = "session_${numtext}.text${service_name}.replay";
4616 open (REPLAY2,">$filename2") ||
4617 die "ERROR41: creating $filename2 $!\n";
4618 binmode(REPLAY2); # for backward OSs
4619
4620
4621 # --- textX11 ---
4622 #
4623 # Create a perl program, that when run itself will print out
4624 # the contents of the server 1-way stream, with pauses based on
4625 # the packet arrival times (replay the session in realtime).
4626 #
4627 print REPLAY2 "#!$PERL\n";
4628 print REPLAY2 <<'END';
4629#
4630# This is an X11 text replay program. It will replay keystrokes and text
4631# of an X11 session using the timestamps from the packet log.
4632#
4633# USAGE: run the script as normal. You can provide a factor as an
4634# argument, eg "2" to run twice as fast, or "0.5" to run
4635# at half time. eg,
4636# ./session_0002.textX11.replay 2
4637#
4638# Auto generated by Chaosreader.
4639#
4640$| = 1;
4641$factor = $ARGV[0] || 1;
4642sub ms {
4643 $ms = shift;
4644 $ms = $ms / $factor;
4645 select(undef, undef, undef, $ms);
4646}
4647END
4648
4649
4650 # --- X11 ---
4651 #
4652 # Create a perl program, that when run itself will print out
4653 # the contents of the server 1-way stream, with pauses based on
4654 # the packet arrival times (replay the session in realtime).
4655 #
4656 print REPLAY "#!$PERL\n";
4657 print REPLAY <<'END';
4658#
4659# This is a X11 replay program. It will replay a session using
4660# the timestamps from the packet log, and transpose the X11 protocol so
4661# that it can be redisplayed. You must have captured from the start
4662# of the connection for this to work.
4663#
4664# USAGE: ./session_0001.X11.replay [-d destination host] [-p port] factor
4665#
4666# just run the script as normal. You can provide a factor as an
4667# argument, eg "2" to run twice as fast, or "0.5" to run
4668# at half time. eg,
4669# ./session_0002.X11.replay 2
4670# a different host and port can be specified if needed. eg,
4671# ./session_0002.X11.replay -d 192.168.1.5 -p 6001
4672#
4673# PROBLEMS: you may need to authorise this connection to the X11 server
4674# before it works. You could run "xhost +hostname" beforehand.
4675# The playback needs to have captured the start of the connection.
4676# Check you support the same colour depth as the playback. And check
4677# the playback file simply isn't too big! (more than 500 Kb is
4678# currently problematic).
4679#
4680#
4681# Auto generated by Chaosreader.
4682#
4683
4684use IO::Socket;
4685use Getopt::Std;
4686
4687if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); }
4688
4689# Try fetching values from $DISPLAY
4690($hostdef,$portdef) = $ENV{DISPLAY} =~ /([^:]*):(\d*)/;
4691$hostdef = "127.0.0.1" if $hostdef eq "";
4692$portdef += 6000;
4693
4694# Command line options take preference
4695&getopts('d:p:');
4696if (defined $opt_d) { $host = $opt_d; } else { $host = $hostdef; }
4697if (defined $opt_p) { $port = $opt_p; } else { $port = $portdef; }
4698$factor = $ARGV[0] || 1;
4699$DEBUG = 0;
4700$| = 1;
4701
4702print "Chaosreader X11 Replay (experimental)\n\n";
4703print "Connecting to $host:$port\n";
4704print "(problems? try running \"xhost +hostname\" first).\n\n";
4705
4706
4707# --- Open Socket ---
4708#
4709$remote = IO::Socket::INET->new( Proto => "tcp",
4710 PeerAddr => $host,
4711 PeerPort => $port,
4712 );
4713unless ($remote) { die "ERROR42: Can't connect to X11 daemon on $host:$port"; }
4714$remote->autoflush(1);
4715
4716
4717# --- Subroutines ---
4718#
4719
4720# ms - sleeps for specified milliseconds
4721#
4722sub ms {
4723 $ms = shift;
4724 $ms = $ms / $factor;
4725 select(undef, undef, undef, $ms);
4726}
4727# help - print help
4728#
4729sub help {
4730 open (MYSELF,"$0") || die "ERROR43: I can't see myself: $!\n";
4731 @Myself = <MYSELF>;
4732 close MYSELF;
4733 ### Print comment from top of code
4734 foreach $line (@Myself) {
4735 last if $line !~ /^#/;
4736 next if $line =~ m:^#!/usr/bin/perl:;
4737 $line =~ s/^#/ /;
4738 print $line;
4739 }
4740 print "\n";
4741 exit(0);
4742}
4743# R - recalculates and prints a resourse setting
4744# The single character subroutine name saves on file space below.
4745#
4746sub R {
4747 #$offset = shift;
4748 #$new = $res + $offset;
4749 my $rid = shift;
4750 my $new;
4751
4752 # final checks
4753 $diff = $rid - $ridbaseold;
4754 $diff = -$diff if $diff < 0;
4755 if ((($rid < $ridbaseold) && ($rid < 8196)) || ($diff > 8196)) {
4756 if ($msb) { return pack('N',$rid); }
4757 else { return pack('V',$rid); }
4758 }
4759
4760 $new = $rid & $ridmaskold;
4761 $new = $new | $ridbase;
4762 if ($msb) { return pack('N',$new); }
4763 else { return pack('V',$new); }
4764}
4765# D - prints the new Drawable, usually the rootid.
4766#
4767sub D {
4768 my $rid = shift;
4769
4770 # final checks
4771 if ($rid >= $ridbaseold) {
4772 # return mapped resource id
4773 return R($rid);
4774 }
4775 # return rootid
4776 if ($msb) { return pack('N',$rootid); }
4777 else { return pack('V',$rootid); }
4778}
4779# C - prints the new Colour map.
4780#
4781sub C {
4782 my $rid = shift;
4783
4784 # final checks
4785 if ($rid >= $ridbaseold) {
4786 # return mapped resource id
4787 return R($rid);
4788 }
4789 # return colour map
4790 if ($msb) { return pack('N',$colour); }
4791 else { return pack('V',$colour); }
4792}
4793# M - Returns a generic mapped id. Can be rootid, colour, or resource.
4794# These are used in Xcodes involving a mask.
4795#
4796sub M {
4797 my $rid = shift;
4798
4799 # final checks
4800 if ($rid >= $ridbaseold) {
4801 # return mapped resource id
4802 return R($rid);
4803 }
4804 # return rootid map
4805 if ($rid == $rootidold) {
4806 if ($msb) { return pack('N',$rootid); }
4807 else { return pack('V',$rootid); }
4808 }
4809 # return colour map
4810 if ($rid == $colourold) {
4811 if ($msb) { return pack('N',$colour); }
4812 else { return pack('V',$colour); }
4813 }
4814 # return other
4815 if ($msb) { return pack('N',$rid); }
4816 else { return pack('V',$rid); }
4817}
4818# P - Check depth pixels, print warning if there is a mismatch.
4819#
4820sub P {
4821 my $depth = shift;
4822 if (! defined $Depth{$depth}) {
4823 print "\nWARNING: requested depth $depth may not be ".
4824 "supported by the server?\n";
4825 }
4826}
4827# debug - print out a value
4828#
4829sub debug {
4830 my $word = shift;
4831 my $num = shift;
4832 my $pack = pack("N",$num);
4833 print "$word: $num ",
4834 sprintf("%2.2x%2.2x%2.2x%2.2x\n",unpack("C*",$pack));
4835}
4836
4837
4838# --- MAIN ---
4839#
4840print "Sending X11 traffic:";
4841END
4842 ### Fetch raw data
4843 $xserver = &TCP_Follow_RawA($session_id);
4844
4845 #
4846 # Determine endian of this transfer. Reading the
4847 # second short on MSB gives 11, and on LSB 2816
4848 # (at least in testing). We split the difference
4849 # on 256 (is case there is a little variation).
4850 #
4851 ($xjunk,$xvalue,$xjunk) = unpack('nna*',$xserver);
4852 #
4853 # Create aliases for "n" and "N" so I can think
4854 # in big endian.
4855 #
4856 if ($xvalue < 256) {
4857 $xmsb = 1;
4858 $n = "n";
4859 $N = "N";
4860 } else {
4861 $xmsb = 0;
4862 $n = "v";
4863 $N = "V";
4864 }
4865 my ($success,$major,$minor,$length,$release,$ridbase,
4866 $ridmask,$mbsize,$vendor,$reqmax,$roots,$formats,$ibo,
4867 $bbo,$bslu,$bslp,$keymin,$keymax,$pad,$rest) =
4868 unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$xserver);
4869
4870 ($x11type,$rest) = unpack("a${vendor}a*",$rest);
4871 $pad = ((4 - ($vendor % 4)) % 4);
4872 ($junk,$rest) = unpack("a${pad}a*",$rest);
4873
4874 foreach $i (1..$formats) {
4875 ($junk,$rest) = unpack("a8a*",$rest);
4876 }
4877 ($rootid,$colour,$junk) = unpack("$N${N}a*",$rest);
4878
4879 #
4880 # Sort the data on the timestamps, calculating timestamp differences
4881 # to record in the replay program.
4882 #
4883 @Times = ();
4884 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
4885 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") {
4886 push(@Times,$time)
4887 }
4888 }
4889 @Times = sort { $a <=> $b } @Times;
4890
4891 #
4892 # --- Main Loop ---
4893 #
4894 # (this needs to be a for loop!)
4895 for ($i=0; $i <= $#Times; $i++) {
4896
4897 ### Calculate time diff if possible
4898 if ($i == $#Times) {
4899 $timediff = 0;
4900 } else {
4901 $timediff = $Times[$i+1] - $Times[$i];
4902 # just in case,
4903 if ($timediff < 0) { $timediff = 0; }
4904 }
4905 $time = $Times[$i];
4906 $texttimediff += $timediff;
4907
4908 ### Fetch data from mem
4909 $data = $TCP{id}{$session_id}{time}{$time}{data};
4910
4911 ### If initial request was fetched,
4912 if ($readnow == 0) {
4913 ### Populate $xstart with initial request
4914 $xstart .= $data;
4915
4916 #
4917 # This triggers the replay program to ask the X11
4918 # server for the connection data - which
4919 # needs to be processed so that various
4920 # resource offsets can be used later on.
4921 #
4922 if (length($xstart) >= 12) {
4923 $readnow = 1;
4924 }
4925
4926 } else {
4927 #
4928 # Change resource offsets
4929 # (reads $data and writes to $data)
4930 #
4931 $xrest = $data;
4932 $data = ""; # output stream of data & subs
4933
4934 #
4935 # Process through X11 codes
4936 #
4937 while (length($xrest) > 0) {
4938 ($xcode,$xbyte,$xlength,$xrest) =
4939 unpack("CC${n}a*",$xrest);
4940
4941 ### Add xcode to output stream $data
4942 $d = pack("CC${n}",$xcode,$xbyte,$xlength);
4943 # the unusual seds
4944 $d =~ s/\\/\\\\/g;
4945 $d =~ s/'/\\'/g;
4946 $d =~ s/\015\012/'."\\015\\012".'/gs;
4947 $data .= $d;
4948
4949 #
4950 # Fetch code values from $xrest, and trim
4951 # $xrest. For most requests, the value length
4952 # is a field (bytes 3,4) except for XErrors
4953 # (code 0) where the total length is always 32.
4954 #
4955 if ($xcode == 0) {
4956 $xlv = 28;
4957 } else {
4958 $xlv = ($xlength - 1) * 4;
4959 $xlv = -$xlv if $xlv < 0;
4960 }
4961 while (length($xrest) < $xlv) {
4962 # some more magic
4963 $i++;
4964 last if ($i > $#Times);
4965
4966 $next = $Times[$i];
4967
4968 ### Fetch data from mem
4969 $xrest .=
4970 $TCP{id}{$session_id}{time}{$next}{data};
4971 }
4972
4973 ($xvalue,$xrest) = unpack("a${xlv}a*",$xrest);
4974
4975 #$format = "%2.2x%2.2x " x ($xlv/2);
4976 #printf("X$xcode: $xbyte,$xlength $format\n",
4977 # unpack("C*",$xvalue)); ### Debug
4978
4979 $xwnum = 0;
4980 @xWords = unpack("${N}*",$xvalue);
4981
4982 #
4983 # If this is a text event, save the text to the
4984 # textX11 replay program.
4985 #
4986 if (($xcode == 76) || ($xcode == 77)) {
4987
4988 # Check if this is a xImageText16Req
4989 if ($xcode == 77) { $xbyte *= 2; }
4990
4991 ($pad,$y,$chars) =
4992 unpack("a10${n}a$xbyte",$xvalue);
4993 if ($yold != $y) { $chars = "\n$chars"; }
4994
4995 ### Clean the data a little (order important)
4996 $chars =~ s/\\/\\\\/g;
4997 $chars =~ s/'/\\'/g;
4998 $chars =~ s/\0//g;
4999
5000 ### Now output the data in the replay program
5001 print REPLAY2 "print '" . $chars . "';\n";
5002
5003 ### This causes the replay program to pause
5004 print REPLAY2 "ms($texttimediff);\n"
5005 unless $texttimediff < 0.002;
5006
5007 $yold = $y;
5008 $texttimediff = 0;
5009 }
5010 #
5011 # Process a text scroll event (by using 62 - copy area)
5012 #
5013 if ($xcode == 62) {
5014 print REPLAY2 "print \"\\n\";\n";
5015 $chars = "\n";
5016 }
5017
5018
5019 #
5020 # If this is a create window event, check the depth.
5021 #
5022 if (($xcode == 1) && ($checkdepth == 0)) {
5023 $data .= "',P($xbyte),'";
5024 $checkdepth = 1;
5025 }
5026
5027 #
5028 # Print the X11 data with embedded subroutines
5029 # to transpose the resource IDs.
5030 #
5031 foreach $xw (@xWords) {
5032 $xwnum++;
5033 if ($X11_Codes[$xcode][$xwnum] == 1) {
5034 $data .= "',R($xw),'";
5035 #print "XCODER: $xcode, $xwnum\n";
5036 } elsif ($X11_Codes[$xcode][$xwnum] == 2) {
5037 $data .= "',D($xw),'";
5038 #print "XCODED: $xcode, $xwnum\n";
5039 } elsif ($X11_Codes[$xcode][$xwnum] == 3) {
5040 $data .= "',C($xw),'";
5041 #print "XCODEC: $xcode, $xwnum\n";
5042 } elsif ($X11_Codes[$xcode][$xwnum] == 4) {
5043 $data .= "',M($xw),'";
5044 #print "XCODEM: $xcode, $xwnum\n";
5045 } else {
5046 $d = pack("$N",$xw);
5047 $d =~ s/\\/\\\\/g;
5048 $d =~ s/'/\\'/g;
5049 $d =~ s/\015\012/'."\\015\\012".'/gs;
5050 $data .= $d;
5051 }
5052 }
5053 }
5054 }
5055
5056 #
5057 # Now output the data in the replay program
5058 #
5059 print REPLAY "print '.';\n";
5060 print REPLAY "print \$remote '" . $data . "';\n";
5061
5062 if ($readnow == 1) {
5063 $readnow = 2;
5064 print REPLAY "\$msb = $xmsb;\n";
5065 print REPLAY "\$ridbaseold = $ridbase;\n";
5066 print REPLAY "\$ridmaskold = $ridmask;\n";
5067 print REPLAY "\$rootidold = $rootid;\n";
5068 print REPLAY "\$colourold = $colour;\n";
5069 #
5070 # The following code implements the client to
5071 # server connection - we need to read the
5072 # resource and window IDs which are necessary
5073 # when transposing the replay traffic to
5074 # these new values.
5075 #
5076 print REPLAY <<'END';
5077if ($msb) {
5078 $n = "n";
5079 $N = "N";
5080} else {
5081 $n = "v";
5082 $N = "V";
5083}
5084
5085
5086read($remote,$in,40); # (xConnSetup)
5087($success,$major,$minor,$length,$release,$ridbase,$ridmask,$mbsize,$vendor,
5088$reqmax,$roots,$formats,$ibo,$bbo,$bslu,$bslp,$keymin,$keymax,$pad) =
5089unpack("a2$n$n$n$N$N$N$N$n${n}CCCCCCCC${N}a*",$in);
5090
5091read($remote,$in,$vendor);
5092print "\nX11 Server Type: $in\n";
5093read($remote,$in,((4 - ($vendor % 4)) % 4));
5094
5095foreach $i (1..$formats) {
5096 read($remote,$in,8); # (xPixmapFormat)
5097 ($depth,$junk) = unpack("Ca*",$in);
5098 $Depth{$depth} = 1;
5099 next if $depth == 1;
5100 print "X11 server supports $depth bit resolution\n";
5101}
5102read($remote,$in,8); # (xWindowRoot)
5103($rootid,$colour,$junk) = unpack("$N$N",$in) unless defined $rootid;
5104
5105if ($DEBUG) {
5106 debug("Resource ID new: ",$ridbase);
5107 debug("Resource ID old: ",$ridbaseold);
5108 debug("Root ID new: ",$rootid);
5109 debug("Root ID old: ",$rootidold);
5110 debug("Colour map new: ",$colour);
5111 debug("Colour map old: ",$colourold);
5112}
5113END
5114 }
5115
5116 #
5117 # This causes the replay program to pause
5118 #
5119 print REPLAY "ms($timediff);\n"
5120 unless $timediff < 0.002; # (efficiency).
5121 }
5122 print REPLAY "print \"\n\";\n";
5123 print REPLAY "close \$remote;\n";
5124 close REPLAY;
5125
5126 ### Better make it executable
5127 chmod (0755, "$filename");
5128
5129 close REPLAY2;
5130 ### Better make it executable
5131 chmod (0755, "$filename2");
5132
5133 ### Global Vars
5134 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5135 "</a> $duration seconds</li>\n";
5136 $Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
5137 "</a> $duration seconds</li>\n";
5138 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
5139 '"' , " $filename","",$duration);
5140 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
5141 '"' , " $filename2","",$duration);
5142}
5143
5144
5145
5146# Save_Session_VNCReplay_andHTML - Save a replay program for this session.
5147# This creates a program that is used in conjunction with vncviewer.
5148# It also saves the HTML version (it would have been redundant to
5149# create a seperate subroutine for that).
5150#
5151sub Save_Session_VNCReplay_andHTML {
5152 my $session_id = shift;
5153 my $number = shift;
5154 my $service_name = shift;
5155 my $session_text = shift;
5156 my $numtext = sprintf("%04d",$number);
5157 my ($filename,$filename2,$filename3,$duration,$code,$rest,$extra,
5158 $length,$start,$junk,$down,$value,$data,$oldtimediff,$printed,$chars,
5159 $char,$timediff,$checkdepth,$html);
5160 my @xWords;
5161
5162 $oldtimediff = 0;
5163 $printed = 0;
5164 $html = "";
5165
5166
5167 #
5168 # Output - Text (keystroke replay)
5169 #
5170 $filename2 = "session_${numtext}.text${service_name}.replay";
5171 open (REPLAY2,">$filename2") ||
5172 die "ERROR44: creating $filename2 $!\n";
5173 binmode(REPLAY2); # for backward OSs
5174
5175 #
5176 # --- textVNC ---
5177 #
5178 # Create a perl program, that when run itself will print out
5179 # the contents of the client 1-way stream, with pauses based on
5180 # the packet arrival times (replay the session in realtime).
5181 #
5182 print REPLAY2 "#!$PERL\n";
5183 print REPLAY2 <<'END';
5184#
5185# This is an VNC text replay program. It will replay keystrokes from
5186# a VNC session using the timestamps from the packet log.
5187#
5188# USAGE: run the script as normal. You can provide a factor as an
5189# argument, eg "2" to run twice as fast, or "0.5" to run
5190# at half time. eg,
5191# ./session_0002.textVNC.replay 2
5192#
5193# Auto generated by Chaosreader.
5194#
5195$| = 1;
5196$factor = $ARGV[0] || 1;
5197sub ms {
5198 $ms = shift;
5199 $ms = $ms / $factor;
5200 select(undef, undef, undef, $ms);
5201}
5202END
5203
5204 #
5205 # Sort the data on the timestamps, calculating timestamp differences
5206 # to record in the replay program.
5207 #
5208 @Times = ();
5209 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
5210 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "B") {
5211 push(@Times,$time)
5212 }
5213 }
5214 @Times = sort { $a <=> $b } @Times;
5215
5216 #
5217 # --- Main Loop ---
5218 #
5219 # (this needs to be a for loop!)
5220 for ($i=0; $i <= $#Times; $i++) {
5221
5222 ### Calculate time diff if possible
5223 if ($i == $#Times) {
5224 $timediff = 0;
5225 } else {
5226 $timediff = $Times[$i+1] - $Times[$i];
5227 # just in case,
5228 if ($timediff < 0) { $timediff = 0; }
5229 }
5230 $time = $Times[$i];
5231
5232 ### Fetch data from mem
5233 $data = $TCP{id}{$session_id}{time}{$time}{data};
5234 ($code) = unpack("C",$data);
5235
5236 $chars = "";
5237
5238 # skip code 0's
5239 if ($code > 0) {
5240 #
5241 # Process through VNC client codes
5242 #
5243 $chars = "";
5244 while (length($data) > 0) {
5245 ($code) = unpack("C",$data);
5246 $length = $VNC_Code_Size{$code};
5247 $length--;
5248 last if $length <= 0;
5249
5250 # Fetch this code only
5251 ($code,$value,$data) = unpack("Ca${length}a*",$data);
5252
5253 ### Process Key Pressed
5254 if ($code == 4) {
5255 ($down,$junk,$extra,$char) = unpack("Ca4Ca",$value);
5256
5257 next if $down == 0; # record key-ups
5258
5259 if ($extra == 0) {
5260 $chars .= $char;
5261 } else {
5262 if (defined $KeyCode{vnc}{0}{$char}) {
5263 $chars .= $KeyCode{vnc}{0}{$char};
5264 }
5265 }
5266 $html .= $chars;
5267 }
5268 }
5269
5270 }
5271
5272 $chars =~ s/\\/\\\\/g;
5273 $chars =~ s/'/\\'/g;
5274
5275 ### Now output the data in the replay program
5276 unless (length($chars) == 0) {
5277 print REPLAY2 "ms($oldtimediff);\n"
5278 unless $oldtimediff < 0.002;
5279
5280 ### Print the data
5281 print REPLAY2 "print '" . $chars . "';\n";
5282
5283 # these counters are for efficiency, otherwise
5284 # we print too many sequiential sleeps
5285 $printed = 1;
5286 $oldtimediff = 0;
5287 } else {
5288 $printed = 0;
5289 $oldtimediff += $timediff;
5290 next;
5291 }
5292
5293 ### This causes the replay program to pause
5294 print REPLAY2 "ms($timediff);\n"
5295 unless $timediff < 0.002;
5296 }
5297 close REPLAY2;
5298
5299 ### Better make it executable
5300 chmod (0755, "$filename2");
5301
5302
5303 # --- HTML ---
5304 #
5305 # Create a HTML page showing the keystrokes
5306
5307 ### Clean up html
5308 $html = &Desex_HTML($html);
5309
5310 ### Output
5311 $filename3 = "session_${numtext}.text${service_name}${ext}.html";
5312 open (OUT,">$filename3") ||die "ERROR45: file create, $filename3: $!\n";
5313 binmode(OUT);
5314 print OUT "<HTML>\n<BODY bgcolor=\"white\">\n" .
5315 "<H1>$service_name: $session_text</H1>\n" .
5316 "<H2>File $Arg{infile}, Session $number</H2>\n" .
5317 "<PRE WRAP=\"virtual\">\n" .
5318 "<font color=\"red\">" .$html. "</font></PRE>\n</BODY>\n</HTML>\n";
5319 close OUT;
5320
5321 ### Global Vars
5322 $length = length($html);
5323 $Index{HTML}[$number] .=
5324 "<li><a href=\"$filename3\">keystrokes</a></li>\n";
5325 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5326 '"' , " $filename3","",$length);
5327
5328
5329 #
5330 # Output - Main VNC replay program
5331 #
5332 $filename = "session_${numtext}.${service_name}.replay";
5333 $duration = ($TCP{id}{$session_id}{EndTime} -
5334 $TCP{id}{$session_id}{StartTime});
5335 $duration = sprintf("%.0f",$duration);
5336 open (REPLAY,">$filename") ||
5337 die "ERROR46: creating $filename $!\n";
5338 binmode(REPLAY); # for backward OSs
5339
5340 #
5341 # --- VNC ---
5342 #
5343 # Create a perl program, that when run itself will create a
5344 # playback VNC server that listens on a port. When a vncviewer
5345 # connects, the contents of the server 1-way stream arew played back,
5346 # with pauses.
5347 #
5348 print REPLAY "#!$PERL\n";
5349 print REPLAY <<'END';
5350#
5351# This is a VNC replay program. This runs as a server and listens on a port,
5352# then vncviewer is run to connect to that port - at which point the playback
5353# commences.
5354#
5355# USAGE: ./session_0001.VNC.replay [-p port] factor
5356#
5357# just run the script as normal. You can provide a factor as an
5358# argument, eg "2" to run twice as fast, or "0.5" to run
5359# at half time. eg,
5360# ./session_0002.VNC.replay 2
5361# a different host and port can be specified if needed. eg,
5362# ./session_0002.VNC.replay -p 5925
5363#
5364# After the script is running, connect using vncviewer. eg,
5365# vncviewer -viewonly localhost:25
5366#
5367# PROBLEMS: The playback needs to have captured the start of the connection,
5368# you need to be at the same colour depth as the playback (or more may
5369# work), and your screen should be at least as big as the playback
5370# resolution. Newer versions of vncviewer may be tuned to match the
5371# playback (eg "-8bit").
5372#
5373# Auto generated by Chaosreader.
5374#
5375
5376use IO::Socket;
5377use Getopt::Std;
5378use Net::hostent;
5379
5380$| = 1;
5381
5382if ($ARGV[0] =~ /^-h$|^--help$/) { &help(); }
5383
5384# Command line options take preference
5385&getopts('p:');
5386if (defined $opt_p) { $port = $opt_p; } else { $port = 5921; }
5387$vncport = $port - 5900;
5388if ($vncport < 0) { die "ERROR47: Port $port too low, use at least 5901.\n"; }
5389$factor = $ARGV[0] || 1;
5390$DEBUG = 0;
5391
5392print "Chaosreader VNC Replay (experimental)\n\n";
5393print "Listening on port $port...\n";
5394
5395
5396# --- Open Socket ---
5397#
5398$server = IO::Socket::INET->new( Proto => 'tcp',
5399 LocalPort => $port,
5400 Listen => SOMAXCONN,
5401 Reuse => 1);
5402
5403die "can't setup server" unless $server;
5404unless ($server) {
5405 die "ERROR48: Can't open port $port. Try a different port.";
5406}
5407
5408print <<WELCOME;
5409Port opened successfully.
5410
5411Now run vncviewer and connect to this port. eg,
5412 vncviewer -viewonly localhost:$vncport
5413
5414If you are prompted for a password, type any character and hit enter.
5415Waiting for connection...
5416WELCOME
5417
5418
5419# --- Subroutines ---
5420#
5421
5422# ms - sleeps for specified milliseconds
5423#
5424sub ms {
5425 $ms = shift;
5426 $ms = $ms / $factor;
5427 select(undef, undef, undef, $ms);
5428}
5429# help - print help
5430#
5431sub help {
5432 open (MYSELF,"$0") || die "ERROR49: I can't see myself: $!\n";
5433 @Myself = <MYSELF>;
5434 close MYSELF;
5435 ### Print comment from top of code
5436 foreach $line (@Myself) {
5437 last if $line !~ /^#/;
5438 next if $line =~ m:^#!/usr/bin/perl:;
5439 $line =~ s/^#/ /;
5440 print $line;
5441 }
5442 print "\n";
5443 exit(0);
5444}
5445
5446
5447#
5448# --- MAIN ---
5449#
5450
5451### Wait for connection
5452$client = $server->accept();
5453$client->autoflush(1);
5454
5455print "Sending VNC traffic:";
5456
5457END
5458
5459 #
5460 # Sort the data on the timestamps, calculating timestamp differences
5461 # to record in the replay program.
5462 #
5463 @Times = ();
5464 foreach $time (keys (%{$TCP{id}{$session_id}{time}})) {
5465 if ($TCP{id}{$session_id}{time}{$time}{dir} eq "A") {
5466 push(@Times,$time)
5467 }
5468 }
5469 @Times = sort { $a <=> $b } @Times;
5470
5471 #
5472 # --- Main Loop ---
5473 #
5474 # (this needs to be a for loop!)
5475 for ($i=0; $i <= $#Times; $i++) {
5476
5477 ### Calculate time diff if possible
5478 if ($i == $#Times) {
5479 $timediff = 0;
5480 } else {
5481 $timediff = $Times[$i+1] - $Times[$i];
5482 # just in case,
5483 if ($timediff < 0) { $timediff = 0; }
5484 }
5485 $time = $Times[$i];
5486
5487 ### Fetch data from mem
5488 $data = $TCP{id}{$session_id}{time}{$time}{data};
5489
5490 $data =~ s/\\/\\\\/g;
5491 $data =~ s/'/\\'/g;
5492 $data =~ s/\015\012/'."\\015\\012".'/gs;
5493
5494 #
5495 # Now output the data in the replay program
5496 #
5497 print REPLAY "print '.';\n";
5498 print REPLAY "print \$client '" . $data . "';\n";
5499
5500 #
5501 # This causes the replay program to pause
5502 #
5503 print REPLAY "ms($timediff);\n"
5504 unless $timediff < 0.002; # (efficiency).
5505 }
5506 print REPLAY "print \"\n\";\n";
5507 print REPLAY "close \$client;\n";
5508 close REPLAY;
5509
5510 ### Better make it executable
5511 chmod (0755, "$filename");
5512
5513 ### Global Vars
5514 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5515 "</a> $duration seconds</li>\n";
5516 $Index{HTML}[$number] .= "<li><a href=\"$filename2\">$filename2" .
5517 "</a> $duration seconds</li>\n";
5518 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
5519 '"' , " $filename","",$duration);
5520 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s seconds\n",
5521 '"' , " $filename2","",$duration);
5522}
5523
5524
5525
5526# Save_SMTP_Emails - Save emails from an SMTP session.
5527#
5528sub Save_SMTP_Emails {
5529 my ($filename);
5530 my $session_id = shift;
5531 my $number = shift;
5532 my $service_name = "smtp";
5533 my $numtext = sprintf("%04d",$number);
5534
5535
5536 ### Full - Input
5537 $snmp_data = &TCP_Follow_RawB($session_id);
5538
5539 ### Full - Processing
5540 @Snmp_parts = split(/\r\n\.\r\n|\n\.\n/,$snmp_data);
5541
5542 ### LOOP
5543 $partnum = 0;
5544 foreach $snmp_part (@Snmp_parts) {
5545
5546 next unless $snmp_part =~ /DATA/;
5547 $partnum++;
5548 $parttext = sprintf("%02d",$partnum);
5549
5550 ### Part - Processing
5551 $snmp_part =~ s/^.*DATA\r?\n//s; # '/s;' is new perl5,
5552 # else '/;' with $* = 1
5553
5554 ### Part - Output
5555 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
5556 else { $ext = ""; }
5557 $filename = "session_${numtext}.part_${parttext}." .
5558 "${service_name}${ext}.email";
5559 open (OUT,">$filename") ||
5560 die "ERROR50: file create, $filename: $!\n";
5561 binmode(OUT); # for backward OSs
5562 print OUT $snmp_part;
5563 close OUT;
5564
5565 ### Part - Global Vars
5566 my $length = length($snmp_part);
5567 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5568 "</a> $length bytes</li>\n";
5569 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5570 '"' , " $filename","",$length);
5571 }
5572}
5573
5574
5575# Save_HTTP_Files - Save HTTP components.
5576#
5577sub Save_HTTP_Files {
5578 my ($filename);
5579 my $session_id = shift;
5580 my $number = shift;
5581 my $service_name = shift;
5582 my $numtext = sprintf("%04d",$number);
5583
5584 ### Full - Input
5585 $http_session = &TCP_Follow_RawA($session_id);
5586
5587 ### Full - Processing
5588 @HttpParts = split(/HTTP\/[0-9.]* /,$http_session);
5589
5590 ### LOOP
5591 $partnum = 0;
5592 foreach $http_part (@HttpParts) {
5593
5594 # JL. I want to see all parts, in partcular empty ones
5595 # resulting from 304 (Not Modified). Thus, the original
5596 # check below on $http_data is too strong.
5597 next if $http_part eq "";
5598
5599 ### Part - Processing
5600 ($http_header,$http_data) = split(/\r\n\r\n|\n\n/,$http_part,2);
5601 # next if $http_data eq "";
5602 # next if length($http_data) < 8;
5603 $partnum++;
5604 $parttext = sprintf("%02d",$partnum);
5605
5606 ### JL: Chunk Check, patch from http://refrequelate.blogspot.com/2008/07/more-de-chunking-chaosreader-patch.html
5607 if ( $http_header =~ /Transfer-Encoding: chunked/ ) {
5608 my $new_http_data="";
5609 my $chunksize=-1;
5610 my $pos=0;
5611 until ($chunksize==0) {
5612 my $eolpos=index($http_data,"\r\n",$pos);
5613 $chunksize=hex(substr($http_data,$pos,$eolpos - $pos));
5614 $pos=($eolpos+2);
5615 if ($chunksize > 0) {
5616 $new_http_data.=substr($http_data,$pos,$chunksize);
5617 }
5618 $pos+=($chunksize+2);
5619 }
5620 $http_data=$new_http_data;
5621 }
5622
5623 ### Part - Checks
5624 my $http_type = &File_Type($http_data);
5625 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
5626 else { $ext = ""; }
5627
5628 ### JL: Check for known MIME type in Content-Type
5629 my ($content_type) = $http_header =~ /Content-Type:\s+(\S*)/is;
5630 my $file_extension = "";
5631 if ($content_type ne "") {
5632 for my $pattern ( keys %mime_types ) {
5633 my $value = $mime_types{$pattern};
5634 if ( $content_type =~ /$pattern/i ) {
5635 $file_extension = $value;
5636 last;
5637 }
5638 }
5639 if (($file_extension eq "bin")
5640 or ($file_extension eq "asc")) {
5641 # Not too specific. Some HTTP servers return images
5642 # with Content-Type: application/octet-stream, others
5643 # with Content-Type: text/plain
5644 # Prefer http_type then...
5645 if ($http_type ne "data") {
5646 $file_extension = $http_type;
5647 }
5648 }
5649 elsif ($file_extension eq "") {
5650 print "Unkown Content-Type $content_type.";
5651 print " May want to extend MIME types.\n";
5652 }
5653 }
5654
5655 ### Part - Output
5656 # JL: Create filename based on Content-Type
5657 my $filename = "session_${numtext}.part_$parttext${ext}";
5658 if ($file_extension ne "") {
5659 $filename .= ".$file_extension";
5660 }
5661 if ( ($file_extension eq "") or ($http_type eq "gz") ) {
5662 $filename .= ".$http_type";
5663 }
5664 open (OUT,">$filename") ||
5665 die "ERROR51: file create, $filename: $!\n";
5666 binmode(OUT); # for backward OSs
5667 print OUT $http_data;
5668 close OUT;
5669
5670 ### JL: gz decompressing
5671 if ( $http_type eq "gz" ) {
5672 my $gunzipped = substr($filename, 0, length($filename) - 3);
5673 my $gunzip_failed = 0;
5674 gunzip $filename => $gunzipped
5675 or $gunzip_failed = 1;
5676 if ( $gunzip_failed == 0 ) {
5677 $filename = $gunzipped;
5678 }
5679 }
5680 # Pex:Deflate
5681 elsif ( $http_type eq "deflate") {
5682 #print "inflating " . $http_type ;
5683 my $inflated = substr($filename, 0, length($filename) - 4) . "inflated.html";
5684 my $status = IO::Uncompress::Inflate::inflate($filename, $inflated, Transparent => 0);
5685 my $error = $IO::Uncompress::Inflate::InflateError;
5686 if ($status) {
5687 #Succesful inflate
5688 $filename = $inflated;
5689 }
5690 else {
5691 my $status = IO::Uncompress::RawInflate::rawinflate($filename, $inflated);
5692 my $error = $IO::Uncompress::RawInflate::RawInflateError;
5693 if ($status) {
5694 #Succesful raw inflate
5695 $filename = $inflated;
5696 }
5697 elsif ($error eq "expected end of file"){
5698 # End of file, might been succesful
5699 $filename = $inflated;
5700 }
5701 else {
5702 #failed inflate
5703 #print "failed inflate";
5704 }
5705 }
5706 }
5707
5708 ### Part - Global Vars
5709 my $length = length($http_data);
5710 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename" .
5711 "</a> $length bytes</li>\n";
5712 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5713 '"' , " $filename","",$length);
5714 if (&Is_Image($http_type) or
5715 &Is_Image($file_extension)) { # JL: Also check file ext.
5716 $Image{HTML}[$number]{links} .= "<img src=\"$filename\"> ";
5717 $Image{notempty} = 1;
5718 # JL: Remember this part as image.
5719 $ExtImage{HTML}[$number]{parts}[$partnum] = 1;
5720 }
5721 }
5722}
5723
5724
5725# Save_NFS_File - Save NFS file. Only works well for some files, if the NFS
5726# header can't be processed, a "*.nfs.raw" file is created.
5727#
5728sub Save_NFS_File {
5729 my ($filename);
5730 my $session_id = shift;
5731 my $number = shift;
5732 my $service_name = "nfs";
5733 my $numtext = sprintf("%04d",$number);
5734
5735 ### Input
5736 my $nfs_raw = &TCP_Follow_RawB($session_id);
5737
5738 ### Processing
5739 ($nfs_start,$nfs_size,$nfs_end) = unpack('a56a4a*',$nfs_raw);
5740 $nfs_sizeint = unpack("N",$nfs_size);
5741 ($nfs_start,$nfs_data) = split(/$nfs_size....$nfs_size/,$nfs_end,2);
5742
5743 ### Checks
5744 if (($nfs_sizeint > 4) && (length($nfs_data) >= $nfs_sizeint)) {
5745 $nfs_type = &File_Type($nfs_data);
5746 if ($nfs_sizeint < length($nfs_data)) {
5747 $nfs_data = unpack("a${nfs_sizeint}a*",$nfs_data);
5748 }
5749 } else {
5750 $nfs_type = "raw";
5751 $nfs_data = $nfs_raw;
5752 }
5753 if ($TCP{id}{$session_id}{Partial}) { $ext = ".partial"; }
5754 else { $ext = ""; }
5755
5756 ### Output
5757 $filename = "session_${numtext}.part_01.${service_name}${ext}.nfs." .
5758 "$nfs_type";
5759 open (OUT,">$filename") || die "ERROR52: file create, $filename: $!\n";
5760 binmode(OUT); # for backward OSs
5761 print OUT $nfs_data;
5762 close OUT;
5763
5764 ### Global Vars
5765 my $length = length($nfs_data);
5766 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename</a>" .
5767 " $length bytes</li>\n";
5768 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5769 '"' , " $filename","",$length);
5770}
5771
5772
5773# Save_DNS_File - Save DNS file.
5774#
5775sub Save_DNS_File {
5776 my ($filename);
5777 my $session_id = shift;
5778 my $number = shift;
5779 my $numtext = sprintf("%04d",$number);
5780 my $text = $UDP{id}{$session_id}{DNS};
5781
5782 ### Output
5783 $filename = "session_${numtext}.domain.txt";
5784 open (OUT,">$filename") || die "ERROR52: file create, $filename: $!\n";
5785 print OUT $text;
5786 close OUT;
5787
5788 ### Global Vars
5789 my $length = length($text);
5790 $Index{HTML}[$number] .= "<li><a href=\"$filename\">$filename</a>" .
5791 " $length bytes</li>\n";
5792 $Index{Text}[$number] .= sprintf("%-4s %-45s %-10s %8s bytes\n",
5793 '"' , " $filename","",$length);
5794}
5795
5796
5797# TCP_Follow_RawA - process session by TCP Seq numbers 1-way.
5798# (TCP ASSEMBLY)
5799#
5800sub TCP_Follow_RawA {
5801 my $session_id = shift;
5802 my $raw = "";
5803
5804 #
5805 # Assemble TCP Sessions. Each hash contains session_ids as keys,
5806 # and the value points to another hash of sequence numbers and data.
5807 # %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output.
5808 #
5809 @Seqs = keys (%{$TCP{id}{$session_id}{Aseq}});
5810 foreach $seq (sort { $a <=> $b } @Seqs) {
5811 $raw .= ${$TCP{id}{$session_id}{Aseq}{$seq}};
5812 }
5813
5814 return $raw;
5815}
5816
5817
5818# TCP_Follow_RawB - process session by TCP Seq numbers 1-way.
5819# (TCP ASSEMBLY)
5820#
5821sub TCP_Follow_RawB {
5822 my $session_id = shift;
5823 my $raw = "";
5824
5825 #
5826 # Assemble TCP Sessions. Each hash contains session_ids as keys,
5827 # and the value points to another hash of sequence numbers and data.
5828 # %TCP{id}{}{Aseq} is input, and %TCP{id}{}{RawA} is output.
5829 #
5830 @Seqs = keys (%{$TCP{id}{$session_id}{Bseq}});
5831 foreach $seq (sort { $a <=> $b } @Seqs) {
5832 $raw .= ${$TCP{id}{$session_id}{Bseq}{$seq}};
5833 }
5834
5835 return $raw;
5836}
5837
5838
5839# Pick_Service_Port - pick which port is the server. Usually is the lower
5840# number, however check if the direction is already known (eg SYN).
5841# The port arguments will not often be needed.
5842#
5843# NOTE: This code is different to Generate_TCP_IPs - which does the "<->"'s
5844#
5845sub Pick_Service_Port {
5846 my $type = shift;
5847 my $id = shift;
5848 my $porta = shift;
5849 my $portb = shift;
5850 my $from_server = 0;
5851 my ($hi,$low);
5852
5853 # Catch active FTP, etc.
5854 ($low,$hi) = sort { $a <=> $b } ($porta,$portb);
5855 if ($low < 100) {
5856 return ($low,$hi);
5857 }
5858
5859 if ($type eq "TCP") {
5860 if (defined $TCP{id}{$id}{source}) {
5861 if ($TCP{id}{$id}{source} eq $TCP{id}{$id}{src}) {
5862 return ($TCP{id}{$id}{dest_port},$TCP{id}{$id}{src_port});
5863 } else {
5864 return ($TCP{id}{$id}{src_port},$TCP{id}{$id}{dest_port});
5865 }
5866 }
5867 } elsif ($type eq "UDP") {
5868 return ($UDP{id}{$id}{dest_port},$UDP{id}{$id}{src_port});
5869 }
5870
5871 # resort to a sort
5872 return sort { $a <=> $b } ($porta,$portb);
5873}
5874
5875# Retrieve DNS name for IP address based on DNS traffic of this capture.
5876# If possible, retrieve the original name for a CNAME of the IP address.
5877#
5878sub Get_Name_For_IP {
5879 my $ip_addr = shift;
5880 my $result = $ip_addr;
5881 if (defined $DNS{$ip_addr}) {
5882 $result = $DNS{$ip_addr};
5883 while (defined $DNS{$result}) {
5884 $result = $DNS{$result};
5885 }
5886 }
5887 return $result;
5888}
5889
5890# Generate_SessionID - input source and dest IPs and ports, and generate
5891# a unique session_id based on them. this is done by sorting on
5892# ports and then IPs. Also returns a flag if the packet may be
5893# assumed to be from_server - where the lowest port is assumed to
5894# be the server (unless TCP SYNs have been observed).
5895#
5896sub Generate_SessionID {
5897 my $ip_src = shift;
5898 my $tcp_src_port = shift;
5899 my $ip_dest = shift;
5900 my $tcp_dest_port = shift;
5901 my $type = shift;
5902 my $from_server = 0;
5903 my $session_id;
5904
5905 #
5906 # Generate session_id string using host:port,host:port sorted on
5907 # port (low port last).
5908 #
5909 if ($tcp_src_port < $tcp_dest_port) {
5910 $session_id = "$ip_dest:$tcp_dest_port,$ip_src:$tcp_src_port";
5911 $from_server = 1;
5912 } elsif ($tcp_src_port > $tcp_dest_port) {
5913 $session_id = "$ip_src:$tcp_src_port,$ip_dest:$tcp_dest_port";
5914 $from_server = 0;
5915 } else {
5916 $session_id =join(",",sort("$ip_src:$tcp_src_port",
5917 "$ip_dest:$tcp_dest_port"));
5918 $from_server = 1;
5919 }
5920
5921 if ($type eq "TCP") {
5922 if (defined $TCP{id}{$session_id}{source}) {
5923 if ($TCP{id}{$session_id}{source} eq $ip_dest
5924 # JL: Also look at the port as ip_src and ip_dest
5925 # may be the same (e.g., 127.0.0.1)
5926 # Also in Generate_TCP_IDs below.
5927 && $TCP{id}{$session_id}{source_port} eq $tcp_dest_port) {
5928 $from_server = 1;
5929 } else {
5930 $from_server = 0;
5931 }
5932 }
5933 }
5934 return ($session_id,$from_server);
5935}
5936
5937
5938
5939# Generate_TCP_IDs - generate a text and html version of the session ID, that
5940# displays direction of the TCP session if SYNs and ACKs were
5941# observed, else uses a "<->" symbol to represent unknown
5942# direction. TCP only.
5943#
5944sub Generate_TCP_IDs {
5945 my $session_id = shift;
5946 my ($ip_src,$tcp_src_port,$ip_dest,$tcp_dest_port,$text,$html);
5947
5948 # try this direction,
5949 $ip_src = $TCP{id}{$session_id}{src};
5950 $ip_dest = $TCP{id}{$session_id}{dest};
5951 $tcp_src_port = $TCP{id}{$session_id}{src_port};
5952 $tcp_dest_port = $TCP{id}{$session_id}{dest_port};
5953
5954 if (defined $TCP{id}{$session_id}{source}) {
5955 if ($TCP{id}{$session_id}{source} eq $ip_dest
5956 && $TCP{id}{$session_id}{source_port} eq $tcp_dest_port) {
5957 # nope, switch ends
5958 $ip_src = $TCP{id}{$session_id}{dest};
5959 $ip_dest = $TCP{id}{$session_id}{src};
5960 $tcp_src_port = $TCP{id}{$session_id}{dest_port};
5961 $tcp_dest_port = $TCP{id}{$session_id}{src_port};
5962 }
5963 if ($Arg{prefer_dns}) {
5964 $ip_src = &Get_Name_For_IP($ip_src);
5965 $ip_dest = &Get_Name_For_IP($ip_dest);
5966 }
5967 $text = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port";
5968 $html = "$ip_src:$tcp_src_port -> $ip_dest:$tcp_dest_port";
5969 } else {
5970 if ($Arg{prefer_dns}) {
5971 $ip_src = &Get_Name_For_IP($ip_src);
5972 $ip_dest = &Get_Name_For_IP($ip_dest);
5973 }
5974 $text = "$ip_src:$tcp_src_port <-> $ip_dest:$tcp_dest_port";
5975 $html = "$ip_src:$tcp_src_port <-> " .
5976 "$ip_dest:$tcp_dest_port";
5977 }
5978
5979 return ($text,$html);
5980}
5981
5982
5983
5984# Generate_IP_ID - input source IP, dest IP and ident, and generate a
5985# unique ip_id based on them. This is necessary for IP
5986# fragmentation reassembely. Normally we would assume that
5987# the IP_ident was unique - however this program could
5988# process traffic from many different hosts over a long
5989# period of time - idents alone could clash.
5990#
5991sub Generate_IP_ID {
5992 my $ip_src = shift;
5993 my $ip_dest = shift;
5994 my $ip_ident = shift;
5995 my $ip_id;
5996
5997 #
5998 # Generate ip_id string using host:host:ident sorted on IP.
5999 #
6000 #
6001 $ip_id = join(",",sort("$ip_src","$ip_dest")) . ",$ip_ident";
6002
6003 return $ip_id;
6004}
6005
6006
6007
6008# Read_Tcpdump_Record - Read the next tcpdump record, will "last" if
6009# there are no more records.
6010#
6011sub Read_Tcpdump_Record {
6012 my $more;
6013
6014 ### Fetch record header
6015 $length = read(INFILE,$header_rec,($integerSize * 2 + 8));
6016
6017 ### Quit main loop if at end of file
6018 last if $length < 16;
6019
6020 ### Throw out extra info in tcpdump/modified1 format
6021 if ($STYLE =~ /^modified/) {
6022 $length = read(INFILE,$more,8);
6023 }
6024
6025 $frame++;
6026
6027 ## Unpack header, endian sensitive
6028 if ($STYLE =~ /1$/) {
6029 ($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length,
6030 $tcpdump_length_orig)
6031 = unpack('NNNN',$header_rec);
6032 } else {
6033 ($tcpdump_seconds,$tcpdump_msecs,$tcpdump_length,
6034 $tcpdump_length_orig)
6035 = unpack('VVVV',$header_rec);
6036 }
6037 $length = read(INFILE,$tcpdump_data,$tcpdump_length);
6038 $tcpdump_drops = $tcpdump_length_orig - $tcpdump_length;
6039}
6040
6041
6042# Read_Snoop_Record - Read the next snoop record, will "last" if
6043# there are no more records.
6044#
6045sub Read_Snoop_Record {
6046 ### Fetch record header
6047 $length = read(INFILE,$header_rec,24);
6048
6049 ### Quit main loop if at end of file
6050 last if $length < 24;
6051
6052 $frame++;
6053
6054 ### Unpack header
6055 ($snoop_length_orig,$snoop_length_inc,$snoop_length_rec,$snoop_drops,
6056 $snoop_seconds,$snoop_msecs) = unpack('NNNNNN',$header_rec);
6057 $length = read(INFILE,$snoop_data,$snoop_length_inc);
6058 $skip = read(INFILE,$pad,($snoop_length_rec - $snoop_length_inc - 24));
6059}
6060
6061
6062# Load_Index_File - Load the master index file "index.file" into @Master
6063#
6064sub Load_Index_File {
6065
6066 my ($path,$dir,$file,$start,$end,$duration,$index);
6067
6068 #
6069 # Load index.file lines into memory
6070 #
6071 open (FILES,"index.file") || die "ERROR53: Can't read index.file: $!\n"
6072 ."Standalone mode needs to have run recently from this directory.\n\n";
6073
6074 chomp(@Files = <FILES>);
6075 close FILES;
6076
6077 #
6078 # Populate @Master
6079 #
6080 $index = 0;
6081 foreach $path (@Files) {
6082 ($dir,$file,$duration,$start,$end) = split(/\t/,$path);
6083 $Master[$index]{starttime} = $start;
6084 $Master[$index]{endtime} = $end;
6085 $Master[$index]{dir} = $dir;
6086 $Master[$index]{file} = $file;
6087 $Master[$index]{duration} = $duration;
6088 $Master[$index]{size} = -s "$dir/$file";
6089 $index++;
6090 }
6091}
6092
6093
6094# Load_Etc_Services - load /etc/services lookup table into memory,
6095# into %Services_TCP and %Services_UDP.
6096#
6097sub Load_Etc_Services {
6098 my ($line,$name,$service);
6099
6100 ### Hardcoded
6101 %Services_TCP = (20 => "ftp-data",
6102 21 => "ftp",
6103 23 => "telnet",
6104 25 => "smtp",
6105 80 => "web",
6106 109 => "pop2",
6107 110 => "pop3",
6108 143 => "imap",
6109 513 => "login",
6110 514 => "shell",
6111 3128 => "web",
6112 4110 => "irc4110",
6113 5000 => "irc5000",
6114 6000 => "X11",
6115 6660 => "irc",
6116 6665 => "irc",
6117 6666 => "irc",
6118 6667 => "irc",
6119 6668 => "irc",
6120 6669 => "irc",
6121 7000 => "irc7000",
6122 8000 => "irc8000",
6123 8080 => "web",
6124 9000 => "irc9000");
6125 # non standard IRC ports include the number in their name
6126
6127 foreach (@Save_As_X11_Playback_Ports) {
6128 $Services_TCP{$_} = "X11";
6129 }
6130
6131 foreach (@Save_As_VNC_Playback_Ports) {
6132 $Services_TCP{$_} = "VNC";
6133 }
6134
6135 %Services_UDP = (53 => "dns");
6136
6137 ### File input
6138 open(SERVICES,"/etc/services") || return;
6139 while ($line = <SERVICES>) {
6140 next if $line =~ /^#|^\s*$/; # skip comments, blank lines.
6141 if ($line =~ /\d\/tcp/) {
6142 $is_tcp = 1;
6143 } else {
6144 $is_tcp = 0;
6145 }
6146 $line =~ s:/.*::;
6147 ($name,$port) = split(' ',$line);
6148 if ($is_tcp) {
6149 $Services_TCP{$port} = $name;
6150 } else {
6151 $Services_UDP{$port} = $name;
6152 }
6153
6154 }
6155 close SERVICES;
6156}
6157
6158
6159# Set_IP_Protocols - Set a lookup hash for IP Protocols to names.
6160# RFC790, RFC1700.
6161#
6162sub Set_IP_Protocols {
6163 %IP_Protocols = (0 => "Reserved",
6164 1 => "ICMP",
6165 2 => "Unassigned",
6166 3 => "Gateway-to-Gateway",
6167 4 => "CCMC Gateway Monitoring Message",
6168 5 => "ST",
6169 6 => "TCP",
6170 7 => "UCL",
6171 8 => "Unassigned",
6172 9 => "Secure",
6173 10 => "BBN RCC Monitoring",
6174 11 => "NVP",
6175 12 => "PUP",
6176 13 => "Pluribus",
6177 14 => "Telenet",
6178 15 => "XNET",
6179 16 => "Chaos",
6180 17 => "UDP",
6181 18 => "Multiplexing",
6182 19 => "DCN",
6183 20 => "TAC Monitoring",
6184 37 => "DDP",
6185 41 => "SIP",
6186 42 => "SDRP",
6187 44 => "IPv6 Frag",
6188 50 => "SIPP-ESP",
6189 51 => "SIPP-AH",
6190 53 => "SWIPE",
6191 50 => "SDRP",
6192 58 => "ICMPv6",
6193 88 => "IGRP",
6194 94 => "IPIP"
6195 );
6196}
6197
6198# Set_ICMP_Types - Set a lookup hash for ICMP Types. RFC792.
6199#
6200sub Set_ICMP_Types {
6201 %ICMP_Types = (0 => "Echo Reply",
6202 3 => "Destination Unreachable",
6203 4 => "Source Quench",
6204 5 => "Redirect",
6205 8 => "Echo",
6206 11 => "Time Exceeded",
6207 12 => "Parameter Problem",
6208 13 => "Timestamp",
6209 14 => "Timestamp Reply",
6210 15 => "Information Request",
6211 16 => "Information Reply",
6212 128 => "Echo",
6213 129 => "Echo Reply",
6214 135 => "Neighbor solicitation",
6215 136 => "Neighbor advertisement"
6216 );
6217}
6218
6219# Set_Result_Names - Set a lookup hash for squid result codes.
6220# (This needs some fine tuning).
6221#
6222sub Set_Result_Names {
6223 %Result_Names = ("" => "TCP_MISS",
6224 000 => "TCP_MISS",
6225 200 => "TCP_HIT",
6226 302 => "TCP_HIT",
6227 304 => "TCP_REFRESH_HIT",
6228 404 => "TCP_NEGATIVE_HIT"
6229 );
6230}
6231
6232# Set_X11_Codes - creates a lookup hash needed for X11 transposing.
6233#
6234sub Set_X11_Codes {
6235 #
6236 # This has a row per X11 code, the row describing the 16 bit
6237 # words that make up the values. "1" means resource id.
6238 # (some values are 8 bit, but are fortunately padded).
6239 #
6240
6241 @X11_Codes = (
6242[ 0 ], # X_Error entry
6243[ 0, 2, 2, 0, 0, 0, 1, 0,4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateWindow 1
6244[ 0, 1, 0 ], # X_ChangeWindowAttributes
6245[ 0, 1 ], # X_GetWindowAttributes
6246[ 0 ], # X_DestroyWindow?
6247[ 0 ], # X_DestroySubwindows?
6248[ 0, 1 ], # X_ChangeSaveSet
6249[ 0, 1, 1, 0 ], # X_ReparentWindow
6250[ 0, 1 ], # X_MapWindow
6251[ 0, 1 ], # X_MapSubwindows
6252[ 0, 1 ], # X_UnmapWindow 10
6253[ 0, 1 ], # X_UnmapSubwindows
6254[ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ConfigureWindow
6255[ 0, 1 ], # X_CirculateWindow
6256[ 0, 2 ], # X_GetGeometry
6257[ 0, 1 ], # X_QueryTree
6258[ 0, 1 ], # X_InternAtom (? else 0,0)
6259[ 0 ], # X_GetAtomName?
6260[ 0, 1, 0, 0, 1, 0 ], # X_ChangeProperty (? else 0,1,0,0,0,0)
6261[ 0, 1, 0 ], # X_DeleteProperty
6262[ 0, 2, 0, 0, 0, 0 ], # X_GetProperty 20
6263[ 0 ], # X_ListProperties?
6264[ 0, 1, 0, 0 ], # X_SetSelectionOwner
6265[ 0 ], # X_GetSelectionOwner
6266[ 0, 1, 0, 0, 0, 0 ], # X_ConvertSelection
6267[ 0, 1, 0 ], # X_SendEvent
6268[ 0, 1, 0, 1, 0, 0 ], # X_GrabPointer
6269[ 0, 1, 0 ], # X_UngrabPointer?
6270[ 0, 1, 0, 1, 0, 0 ], # X_GrabButton
6271[ 0, 1, 0 ], # X_UngrabButton
6272[ 0, 1, 0, 0 ], # X_ChangeActivePointerGrab 30
6273[ 0, 1, 0, 0 ], # X_GrabKeyboard
6274[ 0, 1, 0 ], # X_UngrabKeyboard?
6275[ 0, 1, 0, 0 ], # X_GrabKey
6276[ 0, 1, 0 ], # X_UngrabKey
6277[ 0, 0, 0 ], # X_AllowEvents
6278[ 0 ], # X_GrabServer?
6279[ 0 ], # X_UngrabServer?
6280[ 0 ], # X_QueryPointer?
6281[ 0, 1, 0, 0 ], # X_GetMotionEvents
6282[ 0, 1, 1, 0 ], # X_TranslateCoords 40
6283[ 0, 1, 1, 0, 0, 0 ], # X_WarpPointer
6284[ 0, 1, 0 ], # X_SetInputFocus
6285[ 0 ], # X_GetInputFocus?
6286[ 0 ], # X_QueryKeymap?
6287[ 0, 1, 0 ], # X_OpenFont
6288[ 0, 1 ], # X_CloseFont
6289[ 0, 1 ], # X_QueryFont
6290[ 0, 1 ], # X_QueryTextExtents
6291[ 0, 0 ], # X_ListFonts
6292[ 0, 0 ], # X_ListFontsWithInfo 50
6293[ 0, 0 ], # X_SetFontPath
6294[ 0 ], # X_GetFontPath?
6295[ 0, 1, 2, 0 ], # X_CreatePixmap
6296[ 0 ], # X_FreePixmap?
6297[ 0, 1, 2, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CreateGC ?(else 0,1,1,0)
6298[ 0, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeGC
6299[ 0, 1, 1, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_CopyGC
6300[ 0, 1, 0 ], # X_SetDashes
6301[ 0, 1, 0 ], # X_SetClipRectangles
6302[ 0, 1 ], # X_FreeGC? 60
6303[ 0, 1, 0, 0 ], # X_ClearArea
6304[ 0, 2, 2, 1, 0, 0, 0 ], # X_CopyArea
6305[ 0, 2, 2, 1, 0, 0, 0, 0 ], # X_CopyPlane
6306[ 0, 2, 1 ], # X_PolyPoint
6307[ 0, 2, 1 ], # X_PolyLine
6308[ 0, 2, 1 ], # X_PolySegment
6309[ 0, 2, 1 ], # X_PolyRectangle
6310[ 0, 2, 1 ], # X_PolyArc
6311[ 0, 2, 1, 0 ], # X_FillPoly
6312[ 0, 2, 1 ], # X_PolyFillRectangle 70
6313[ 0, 2, 1 ], # X_PolyFillArc
6314[ 0, 2, 1, 0, 0, 0 ], # X_PutImage
6315[ 0, 2, 0, 0, 0 ], # X_GetImage
6316[ 0, 2, 1, 0 ], # X_PolyText8
6317[ 0, 2, 1, 0 ], # X_PolyText16
6318[ 0, 2, 1, 0 ], # X_ImageText8
6319[ 0, 2, 1, 0 ], # X_ImageText16
6320[ 0, 3, 1, 1 ], # X_CreateColormap
6321[ 0 ], # X_FreeColormap?
6322[ 0, 3, 3 ], # X_CopyColormapAndFree 80
6323[ 0 ], # X_InstallColormap?
6324[ 0 ], # X_UninstallColormap?
6325[ 0 ], # X_ListInstalledColormaps?
6326[ 0, 3, 0, 0 ], # X_AllocColor
6327[ 0, 3, 0 ], # X_AllocNamedColor
6328[ 0, 3, 0 ], # X_AllocColorCells
6329[ 0, 3, 0, 0 ], # X_AllocColorPlanes
6330[ 0, 3, 0 ], # X_FreeColors
6331[ 0, 3 ], # X_StoreColors
6332[ 0, 3, 0, 0 ], # X_StoreNamedColor 90
6333[ 0, 3 ], # X_QueryColors
6334[ 0, 3, 0 ], # X_LookupColor
6335[ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateCursor
6336[ 0, 1, 1, 1, 0, 0, 0, 0 ], # X_CreateGlyphCursor
6337[ 0 ], # X_FreeCursor?
6338[ 0, 1, 0, 0, 0 ], # X_RecolorCursor
6339[ 0, 2, 0 ], # X_QueryBestSize
6340[ 0, 1 ], # X_QueryExtension (? else 0,0)
6341[ 0, 0, 0 ], # X_ListExtensions?
6342[ 0, 1, 0 ], # X_ChangeKeyboardMapping 100
6343[ 0, 1, 0 ], # X_GetKeyboardMapping
6344[ 0, 0, 4,4,4,4,4,4,4,4,4,4,4,4 ], # X_ChangeKeyboardControl
6345[ 0, 0, 0 ], # X_GetKeyboardControl?
6346[ 0 ], # X_Bell
6347[ 0, 0, 0 ], # X_ChangePointerControl
6348[ 0, 0, 0 ], # X_GetPointerControl?
6349[ 0, 0, 0 ], # X_SetScreenSaver
6350[ 0, 0, 0 ], # X_GetScreenSaver?
6351[ 0, 0 ], # X_ChangeHosts
6352[ 0 ], # X_ListHosts 110
6353[ 0 ], # X_SetAccessControl
6354[ 0 ], # X_SetCloseDownMode
6355[ 0, 0, 0 ], # X_KillClient?
6356[ 0, 1, 0 ], # X_RotateProperties
6357[ 0 ], # X_ForceScreenSaver
6358[ 0 ], # X_SetPointerMapping
6359[ 0, 0, 0 ], # X_GetPointerMapping?
6360[ 0 ], # X_SetModifierMapping
6361[ 0, 0, 0 ], # X_GetModifierMapping?
6362[ 0 ], # undef 120
6363[ 0 ], # undef
6364[ 0 ], # undef
6365[ 0 ], # undef
6366[ 0 ], # undef
6367[ 0 ], # undef
6368[ 0 ], # undef
6369[ 0, 0, 0 ] # X_NoOperation 127
6370 );
6371
6372}
6373
6374# Set_X11_KeyCodes - creates a lookup hash of X11 Key codes needed
6375# to generate coloured 2-way HTML X11 reports.
6376#
6377sub Set_X11_KeyCodes {
6378 my ($junk,$code,$char1,$char2,$line,
6379 $sun_xmodmap_pke,$linux_xmodmap_pke);
6380 my %Alias;
6381
6382 #
6383 # These are generated using "xmodmap -pke" (and trimmed a little).
6384 #
6385 $sun_xmodmap_pke = <<END;
6386keycode 8 = Control_L
6387keycode 9 = Control_R
6388keycode 10 = Shift_L
6389keycode 11 = Shift_R
6390keycode 12 = Meta_L
6391keycode 13 = Meta_R
6392keycode 14 = Alt_L
6393keycode 15 = Alt_R
6394keycode 16 = space
6395keycode 17 = 0 parenright
6396keycode 18 = 1 exclam
6397keycode 19 = 2 at
6398keycode 20 = 3 numbersign
6399keycode 21 = 4 dollar
6400keycode 22 = 5 percent
6401keycode 23 = 6 asciicircum
6402keycode 24 = 7 ampersand
6403keycode 25 = 8 asterisk
6404keycode 26 = 9 parenleft
6405keycode 27 = minus underscore
6406keycode 28 = equal plus
6407keycode 29 = bracketleft braceleft
6408keycode 30 = bracketright braceright
6409keycode 31 = semicolon colon
6410keycode 32 = apostrophe quotedbl
6411keycode 33 = grave asciitilde
6412keycode 34 = comma less
6413keycode 35 = period greater
6414keycode 36 = slash question
6415keycode 37 = backslash bar
6416keycode 38 = a A
6417keycode 39 = b B
6418keycode 40 = c C
6419keycode 41 = d D
6420keycode 42 = e E
6421keycode 43 = f F
6422keycode 44 = g G
6423keycode 45 = h H
6424keycode 46 = i I
6425keycode 47 = j J
6426keycode 48 = k K
6427keycode 49 = l L
6428keycode 50 = m M
6429keycode 51 = n N
6430keycode 52 = o O
6431keycode 53 = p P
6432keycode 54 = q Q
6433keycode 55 = r R
6434keycode 56 = s S
6435keycode 57 = t T
6436keycode 58 = u U
6437keycode 59 = v V
6438keycode 60 = w W
6439keycode 61 = x X
6440keycode 62 = y Y
6441keycode 63 = z Z
6442keycode 64 = BackSpace
6443keycode 65 = Return
6444keycode 66 = Tab
6445keycode 67 = Escape
6446keycode 68 = Delete
6447END
6448
6449 #
6450 # These are generated using "xmodmap -pke" (and trimmed a little).
6451 #
6452 $linux_xmodmap_pke = <<END;
6453keycode 8 =
6454keycode 9 = Escape
6455keycode 10 = 1 exclam
6456keycode 11 = 2 at
6457keycode 12 = 3 numbersign
6458keycode 13 = 4 dollar
6459keycode 14 = 5 percent
6460keycode 15 = 6 asciicircum
6461keycode 16 = 7 ampersand
6462keycode 17 = 8 asterisk
6463keycode 18 = 9 parenleft
6464keycode 19 = 0 parenright
6465keycode 20 = minus underscore
6466keycode 21 = equal plus
6467keycode 22 = BackSpace Terminate_Server
6468keycode 23 = Tab ISO_Left_Tab
6469keycode 24 = q Q
6470keycode 25 = w W
6471keycode 26 = e E
6472keycode 27 = r R
6473keycode 28 = t T
6474keycode 29 = y Y
6475keycode 30 = u U
6476keycode 31 = i I
6477keycode 32 = o O
6478keycode 33 = p P
6479keycode 34 = bracketleft braceleft
6480keycode 35 = bracketright braceright
6481keycode 36 = Return
6482keycode 37 = Control_L
6483keycode 38 = a A
6484keycode 39 = s S
6485keycode 40 = d D
6486keycode 41 = f F
6487keycode 42 = g G
6488keycode 43 = h H
6489keycode 44 = j J
6490keycode 45 = k K
6491keycode 46 = l L
6492keycode 47 = semicolon colon
6493keycode 48 = apostrophe quotedbl
6494keycode 49 = grave asciitilde
6495keycode 50 = Shift_L
6496keycode 51 = backslash bar
6497keycode 52 = z Z
6498keycode 53 = x X
6499keycode 54 = c C
6500keycode 55 = v V
6501keycode 56 = b B
6502keycode 57 = n N
6503keycode 58 = m M
6504keycode 59 = comma less
6505keycode 60 = period greater
6506keycode 61 = slash question
6507keycode 62 = Shift_R
6508keycode 64 = Alt_L Meta_L
6509keycode 65 = space
6510keycode 94 = less greater
6511END
6512 %Alias = qw(exclam ! at @ dollar $ percent %
6513 asciicircum ^ ampersand & asterisk * minus - underscore _
6514 equal = plus + bracketleft [ bracketright ] braceleft {
6515 braceright } semicolon ; colon : apostrophe ' quotedbl "
6516 grave ` asciitilde ~ backslash \ bar | less <
6517 period . greater > slash / question ?);
6518
6519 # naughty chatacrers (some of these generate warnings)
6520 @Alias{"parenleft","parenright","space"} = ("(",")"," ");
6521 @Alias{"Tab","Return","numbersign","comma"} = ("\t","\n","#",",");
6522
6523
6524 #
6525 # Populate KeyCode aliase
6526 #
6527 foreach $line (split(/\n/,$sun_xmodmap_pke)) {
6528 ($junk,$code,$junk,$char1,$char2) = split(' ',$line);
6529 if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; }
6530 if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; }
6531 if (length($char1) > 1) { $char1 = "."; }
6532 if (length($char2) > 1) { $char2 = "."; }
6533 $KeyCode{sun}{0}{$code} = $char1;
6534 $KeyCode{sun}{1}{$code} = $char2;
6535 }
6536 foreach $line (split(/\n/,$linux_xmodmap_pke)) {
6537 ($junk,$code,$junk,$char1,$char2) = split(' ',$line);
6538 if (defined $Alias{$char1}) { $char1 = $Alias{$char1}; }
6539 if (defined $Alias{$char2}) { $char2 = $Alias{$char2}; }
6540 if (length($char1) > 1) { $char1 = "."; }
6541 if (length($char2) > 1) { $char2 = "."; }
6542 $KeyCode{linux}{0}{$code} = $char1;
6543 $KeyCode{linux}{1}{$code} = $char2;
6544 }
6545
6546}
6547
6548
6549# Set_VNC_Codes - set globals for VNC.
6550#
6551sub Set_VNC_Codes {
6552
6553 ### set client code to request size hash.
6554 %VNC_Code_Size = ( 0 => 20,
6555 1 => 6,
6556 2 => 4,
6557 3 => 10,
6558 4 => 8,
6559 5 => 6,
6560 6 => 8 );
6561
6562 ### Some essential keysyms
6563 $KeyCode{vnc}{0}{"\010"} = "\b";
6564 $KeyCode{vnc}{0}{"\011"} = "\t";
6565 $KeyCode{vnc}{0}{"\015"} = "\n";
6566
6567}
6568
6569
6570# JL: Initialize Hostnames for IP addresses
6571sub Set_DNS {
6572 %DNS = (
6573 "192.168.178.1" => "fritz.box"
6574 )
6575}
6576
6577# JL: Set_MIME_Types - create hash of MIME types and file extensions.
6578sub Set_MIME_Types {
6579 # Initialize with types seen in the wild but not covered below.
6580 %mime_types = (
6581 "application/binary" => "binary",
6582 "application/ocsp-response" => "ocsp",
6583 "application/pln" => "pln", # bahn.de schedule
6584 "application/vnd.google.safebrowsing-update" => "safebrowsing-update",
6585 "application/vnd.google.safebrowsing-chunk" => "safebrowsing-chunk",
6586 "application/vnd.ms-sync.wbxml" => "mssync", # MS ActiveSync
6587 "application/x-protobuffer" => "protobuffer", # google
6588 "application/x-smd" => "smd", # MapDroyd boundary
6589 "(application|text)/((x-)?javascript|(x-)?js)" => "js",
6590 "(application|text)/json" => "json",
6591 "application/x-amf" => ".amf",
6592 "application/x-zip-compressed" => ".zip",
6593 "image/bmp" => "bmp",
6594 "image/vnd.microsoft.icon" => "ico",
6595 "image/x-gif" => "gif",
6596 "(image/x-)?jp(e)?g" => "jpeg",
6597 "image/x-png" => "png",
6598 "text/xml" => "xml",
6599 "application/x-bzip2" => "bz2",
6600 "application/x-css" => "css",
6601 "font/woff" => "woff",
6602 "application/font-woff" => "woff");
6603 # Following created with:
6604 # grep -o -P "^(text|application|audio|image|video).*\t+([a-z0-9]+)" /etc/mime.types > mime.types
6605 $raw_mime_types = <<END;
6606application/andrew-inset ez
6607application/annodex anx
6608application/atom+xml atom
6609application/atomcat+xml atomcat
6610application/atomserv+xml atomsrv
6611application/bbolin lin
6612application/cap cap
6613application/cu-seeme cu
6614application/davmount+xml davmount
6615application/dsptype tsp
6616application/ecmascript es
6617application/futuresplash spl
6618application/hta hta
6619application/java-archive jar
6620application/java-serialized-object ser
6621application/java-vm class
6622application/javascript js
6623application/m3g m3g
6624application/mac-binhex40 hqx
6625application/mac-compactpro cpt
6626application/mathematica nb
6627application/msaccess mdb
6628application/msword doc
6629application/mxf mxf
6630application/octet-stream bin
6631application/oda oda
6632application/ogg ogx
6633application/pdf pdf
6634application/pgp-keys key
6635application/pgp-signature pgp
6636application/pics-rules prf
6637application/postscript ps
6638application/rar rar
6639application/rdf+xml rdf
6640application/rss+xml rss
6641application/rtf rtf
6642application/smil smi
6643application/xhtml+xml xhtml
6644application/xml xml
6645application/xspf+xml xspf
6646application/zip zip
6647application/vnd.android.package-archive apk
6648application/vnd.cinderella cdy
6649application/vnd.google-earth.kml+xml kml
6650application/vnd.google-earth.kmz kmz
6651application/vnd.mozilla.xul+xml xul
6652application/vnd.ms-excel xls
6653application/vnd.ms-pki.seccat cat
6654application/vnd.ms-pki.stl stl
6655application/vnd.ms-powerpoint ppt
6656application/vnd.oasis.opendocument.chart odc
6657application/vnd.oasis.opendocument.database odb
6658application/vnd.oasis.opendocument.formula odf
6659application/vnd.oasis.opendocument.graphics odg
6660application/vnd.oasis.opendocument.graphics-template otg
6661application/vnd.oasis.opendocument.image odi
6662application/vnd.oasis.opendocument.presentation odp
6663application/vnd.oasis.opendocument.presentation-template otp
6664application/vnd.oasis.opendocument.spreadsheet ods
6665application/vnd.oasis.opendocument.spreadsheet-template ots
6666application/vnd.oasis.opendocument.text odt
6667application/vnd.oasis.opendocument.text-master odm
6668application/vnd.oasis.opendocument.text-template ott
6669application/vnd.oasis.opendocument.text-web oth
6670application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
6671application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx
6672application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
6673application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx
6674application/vnd.openxmlformats-officedocument.presentationml.template potx
6675application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
6676application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx
6677application/vnd.rim.cod cod
6678application/vnd.smaf mmf
6679application/vnd.stardivision.calc sdc
6680application/vnd.stardivision.chart sds
6681application/vnd.stardivision.draw sda
6682application/vnd.stardivision.impress sdd
6683application/vnd.stardivision.math sdf
6684application/vnd.stardivision.writer sdw
6685application/vnd.stardivision.writer-global sgl
6686application/vnd.sun.xml.calc sxc
6687application/vnd.sun.xml.calc.template stc
6688application/vnd.sun.xml.draw sxd
6689application/vnd.sun.xml.draw.template std
6690application/vnd.sun.xml.impress sxi
6691application/vnd.sun.xml.impress.template sti
6692application/vnd.sun.xml.math sxm
6693application/vnd.sun.xml.writer sxw
6694application/vnd.sun.xml.writer.global sxg
6695application/vnd.sun.xml.writer.template stw
6696application/vnd.symbian.install sis
6697application/vnd.visio vsd
6698application/vnd.wap.wbxml wbxml
6699application/vnd.wap.wmlc wmlc
6700application/vnd.wap.wmlscriptc wmlsc
6701application/vnd.wordperfect wpd
6702application/vnd.wordperfect5.1 wp5
6703application/x-123 wk
6704application/x-7z-compressed 7z
6705application/x-abiword abw
6706application/x-apple-diskimage dmg
6707application/x-bcpio bcpio
6708application/x-bittorrent torrent
6709application/x-cab cab
6710application/x-cbr cbr
6711application/x-cbz cbz
6712application/x-cdf cdf
6713application/x-cdlink vcd
6714application/x-chess-pgn pgn
6715application/x-cpio cpio
6716application/x-csh csh
6717application/x-debian-package deb
6718application/x-director dcr
6719application/x-dms dms
6720application/x-doom wad
6721application/x-dvi dvi
6722application/x-httpd-eruby rhtml
6723application/x-font pfa
6724application/x-freemind mm
6725application/x-futuresplash spl
6726application/x-gnumeric gnumeric
6727application/x-go-sgf sgf
6728application/x-graphing-calculator gcf
6729application/x-gtar gtar
6730application/x-hdf hdf
6731application/x-httpd-php phtml
6732application/x-httpd-php-source phps
6733application/x-httpd-php3 php3
6734application/x-httpd-php3-preprocessed php3p
6735application/x-httpd-php4 php4
6736application/x-httpd-php5 php5
6737application/x-ica ica
6738application/x-info info
6739application/x-internet-signup ins
6740application/x-iphone iii
6741application/x-iso9660-image iso
6742application/x-jam jam
6743application/x-java-jnlp-file jnlp
6744application/x-jmol jmz
6745application/x-kchart chrt
6746application/x-killustrator kil
6747application/x-koan skp
6748application/x-kpresenter kpr
6749application/x-kspread ksp
6750application/x-kword kwd
6751application/x-latex latex
6752application/x-lha lha
6753application/x-lyx lyx
6754application/x-lzh lzh
6755application/x-lzx lzx
6756application/x-maker frm
6757application/x-mif mif
6758application/x-ms-wmd wmd
6759application/x-ms-wmz wmz
6760application/x-msdos-program com
6761application/x-msi msi
6762application/x-netcdf nc
6763application/x-ns-proxy-autoconfig pac
6764application/x-nwc nwc
6765application/x-object o
6766application/x-oz-application oza
6767application/x-pkcs7-certreqresp p7r
6768application/x-pkcs7-crl crl
6769application/x-python-code pyc
6770application/x-qgis qgs
6771application/x-quicktimeplayer qtl
6772application/x-redhat-package-manager rpm
6773application/x-ruby rb
6774application/x-sh sh
6775application/x-shar shar
6776application/x-shockwave-flash swf
6777application/x-silverlight scr
6778application/x-stuffit sit
6779application/x-sv4cpio sv4cpio
6780application/x-sv4crc sv4crc
6781application/x-tar tar
6782application/x-tcl tcl
6783application/x-tex-gf gf
6784application/x-tex-pk pk
6785application/x-texinfo texinfo
6786application/x-troff t
6787application/x-troff-man man
6788application/x-troff-me me
6789application/x-troff-ms ms
6790application/x-ustar ustar
6791application/x-wais-source src
6792application/x-wingz wz
6793application/x-x509-ca-cert crt
6794application/x-xcf xcf
6795application/x-xfig fig
6796application/x-xpinstall xpi
6797audio/amr amr
6798audio/amr-wb awb
6799audio/amr amr
6800audio/amr-wb awb
6801audio/annodex axa
6802audio/basic au
6803audio/flac flac
6804audio/midi mid
6805audio/mpeg mpga
6806audio/mpegurl m3u
6807audio/ogg oga
6808audio/prs.sid sid
6809audio/x-aiff aif
6810audio/x-gsm gsm
6811audio/x-mpegurl m3u
6812audio/x-ms-wma wma
6813audio/x-ms-wax wax
6814audio/x-pn-realaudio ra
6815audio/x-realaudio ra
6816audio/x-scpls pls
6817audio/x-sd2 sd2
6818audio/x-wav wav
6819image/gif gif
6820image/ief ief
6821image/jpeg jpeg
6822image/pcx pcx
6823image/png png
6824image/svg+xml svg
6825image/tiff tiff
6826image/vnd.djvu djvu
6827image/vnd.wap.wbmp wbmp
6828image/x-canon-cr2 cr2
6829image/x-canon-crw crw
6830image/x-cmu-raster ras
6831image/x-coreldraw cdr
6832image/x-coreldrawpattern pat
6833image/x-coreldrawtemplate cdt
6834image/x-corelphotopaint cpt
6835image/x-epson-erf erf
6836image/x-icon ico
6837image/x-jg art
6838image/x-jng jng
6839image/x-ms-bmp bmp
6840image/x-nikon-nef nef
6841image/x-olympus-orf orf
6842image/x-photoshop psd
6843image/x-portable-anymap pnm
6844image/x-portable-bitmap pbm
6845image/x-portable-graymap pgm
6846image/x-portable-pixmap ppm
6847image/x-rgb rgb
6848image/x-xbitmap xbm
6849image/x-xpixmap xpm
6850image/x-xwindowdump xwd
6851text/cache-manifest manifest
6852text/calendar ics
6853text/css css
6854text/csv csv
6855text/h323 323
6856text/html html
6857text/iuls uls
6858text/mathml mml
6859text/plain asc
6860text/richtext rtx
6861text/scriptlet sct
6862text/texmacs tm
6863text/tab-separated-values tsv
6864text/vnd.sun.j2me.app-descriptor jad
6865text/vnd.wap.wml wml
6866text/vnd.wap.wmlscript wmls
6867text/x-bibtex bib
6868text/x-boo boo
6869text/x-c++hdr h
6870text/x-c++src c
6871text/x-chdr h
6872text/x-component htc
6873text/x-csh csh
6874text/x-csrc c
6875text/x-dsrc d
6876text/x-diff diff
6877text/x-haskell hs
6878text/x-java java
6879text/x-literate-haskell lhs
6880text/x-moc moc
6881text/x-pascal p
6882text/x-pcs-gcd gcd
6883text/x-perl pl
6884text/x-python py
6885text/x-scala scala
6886text/x-setext etx
6887text/x-sh sh
6888text/x-tcl tcl
6889text/x-tex tex
6890text/x-vcalendar vcs
6891text/x-vcard vcf
6892video/3gpp 3gp
6893video/annodex axv
6894video/dl dl
6895video/dv dif
6896video/fli fli
6897video/gl gl
6898video/mpeg mpeg
6899video/mp4 mp4
6900video/quicktime qt
6901video/ogg ogv
6902video/vnd.mpegurl mxu
6903video/x-flv flv
6904video/x-la-asf lsf
6905video/x-mng mng
6906video/x-ms-asf asf
6907video/x-ms-wm wm
6908video/x-ms-wmv wmv
6909video/x-ms-wmx wmx
6910video/x-ms-wvx wvx
6911video/x-msvideo avi
6912video/x-sgi-movie movie
6913video/x-matroska mpv
6914END
6915 %ext_types = ();
6916 foreach $line (split(/\n/, $raw_mime_types)) {
6917 my ($mime_type, $extension) = split(/\t+/, $line);
6918 # Beware. "+" needs to be escaped in patterns.
6919 $mime_type =~ s/\+/\\\+/g;
6920 $mime_types{$mime_type} = $extension;
6921 my ($type, $sub_type) = split(/\//, $mime_type);
6922 # We check whether we already have a value for this extension.
6923 # We don't care if application is overwritten.
6924 if ((defined $ext_types{$extension}) &&
6925 ($ext_types{$extension} ne $type) &&
6926 ($ext_types{$extension} ne "application")) {
6927 print "$extension already has $ext_types{$extension}.";
6928 print "Should now become $type. Fix mime.types?\n"
6929 }
6930 else {
6931 $ext_types{$extension} = $type;
6932 }
6933 }
6934}
6935
6936
6937
6938# Touch_Vars - This is stops perl -w warnings about vars used only once.
6939# Part of my todo list is to cull this list.
6940#
6941#
6942sub Touch_Vars {
6943 #
6944 # Perl < 5.6 code
6945 #
6946 #use vars qw($ip_ttl $udp_checksum $ip_ident $tcp_length_data
6947 #$ip_tos $tcp_options $opt_A $opt_D $tcp_header_rest $opt_J
6948 #$opt_P $opt_U $opt_X $opt_e $opt_h $opt_i $pad $opt_j
6949 #$snoop_length_orig $http_header $opt_p $opt_q $opt_r
6950 #$header_rest $tcp_ack $ether_dest $ether_src $skip
6951 #$ip_length $udp_length $ip_options $ip_checksum
6952 #$opt_b $opt_B $opt_l $opt_L $ip_rest $ip_hop $ip_reserved
6953 #$ip_flow $icmp_rest $opt_f $opt_z);
6954 #
6955 # Perl 5.6 code
6956 #
6957 #our ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data,
6958 #$ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J,
6959 #$opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j,
6960 #$snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r,
6961 #$header_rest,$tcp_ack,$ether_dest,$ether_src,$skip,
6962 #$ip_length,$udp_length,$ip_options,$ip_checksum,
6963 #$opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved,
6964 #$ip_flow,$icmp_rest,$opt_f,$opt_z);
6965 #
6966 # Perl < 5.6 and 5.6 code (but not elegant)
6967 #
6968 @Once_is_okay = ($ip_ttl,$udp_checksum,$ip_ident,$tcp_length_data,
6969 $ip_tos,$tcp_options,$opt_A,$opt_D,$tcp_header_rest,$opt_J,
6970 $opt_P,$opt_U,$opt_X,$opt_e,$opt_h,$opt_i,$pad,$opt_j,
6971 $snoop_length_orig,$http_header,$opt_p,$opt_q,$opt_r,
6972 $header_rest,$tcp_ack,$ether_dest,$ether_src,$skip,
6973 $ip_length,$udp_length,$ip_options,$ip_checksum,$tcp_rst,$tcp_fin,
6974 $opt_b,$opt_B,$opt_l,$opt_L,$ip_rest,$ip_hop,$ip_reserved,
6975 $ip_flow,$icmp_rest,$opt_f,$opt_z,$junk1,$opt_H,$opt_I,$opt_R);
6976}
6977
6978
6979# Check_Command - check which is the network sniffing command and save
6980# it to $command.
6981#
6982sub Check_Command {
6983
6984 #
6985 # Check which OS we are on, die if it looks incompatible
6986 #
6987 if ($^O eq "linux") {
6988 #
6989 # The "-s9999" tells tcpdump to keep a packet up to this
6990 # size, otherwise the default is 68 bytes. Some versions of
6991 # tcpdump allow using "-s0" for unlimited.
6992 #
6993 $command = "tcpdump -s9999 -w";
6994 } elsif ($^O eq "solaris") {
6995 $command = "snoop -o";
6996 } elsif ($^O eq "darwin") {
6997 $command = "tcpdump -s0 -w";
6998 } else {
6999 die "ERROR54: Can't find the sniffer command for \"$^O\".\n" .
7000 "\t Please use log mode instead.\n";
7001 }
7002
7003 #
7004 # Check username
7005 #
7006 if ($ENV{LOGNAME} ne "root") {
7007 print STDERR "WARNING: Are you root? If not, this probably "
7008 . "won't work. Trying anyway...\n";
7009 }
7010}
7011
7012
7013# Process_Command_Line_Arguments - this process the command line arguments
7014# and sets various globals which are kept in %Arg. It also prints
7015# usage and exists if need be.
7016#
7017sub Process_Command_Line_Arguments {
7018 my $result;
7019
7020 #
7021 # Process Global Defaults into %Arg
7022 #
7023 foreach (@Save_As_HTML_TCP_Ports) {
7024 $Arg{Save_As_TCP_HTML}{$_} = 1;
7025 }
7026 foreach (@Save_As_HTML_UDP_Ports) {
7027 $Arg{Save_As_UDP_HTML}{$_} = 1;
7028 }
7029 foreach (@Save_As_TCP_Playback_Ports) {
7030 $Arg{Save_As_TCP_Playback}{$_} = 1;
7031 }
7032 foreach (@Save_As_UDP_Playback_Ports) {
7033 $Arg{Save_As_UDP_Playback}{$_} = 1;
7034 }
7035 foreach (@Save_As_X11_Playback_Ports) {
7036 $Arg{Save_As_X11_Playback}{$_} = 1;
7037 }
7038 foreach (@Save_As_HTML_X11_Ports) {
7039 $Arg{Save_As_X11_HTML}{$_} = 1;
7040 }
7041 foreach (@Save_As_VNC_Playback_Ports) {
7042 $Arg{Save_As_VNC_Playback}{$_} = 1;
7043 }
7044
7045 if (defined $ARGV[0]) {
7046 ### Dump full help if asked
7047 &Usage_Full if $ARGV[0] eq "--help";
7048
7049 ### Dump massive help if asked
7050 &Usage_Massive if $ARGV[0] eq "--help2";
7051 }
7052
7053 #
7054 # Command Line Defaults
7055 #
7056 $Arg{output_raw} = 0;
7057 $Arg{output_hex} = 0;
7058 $Arg{output_UDP} = 1;
7059 $Arg{output_TCP} = 1;
7060 $Arg{output_ICMP} = 1;
7061 $Arg{output_info} = 0;
7062 $Arg{output_apps} = 1;
7063 $Arg{output_index} = 1;
7064 $Arg{prefer_dns} = 0; # JL: Prefer DNS names over IP addresses?
7065 $Arg{httplog_html} = 0; # JL: Should we create HTTPlog in HTML?
7066 $Arg{httplog_name} = "httplog.text"; # JL: Old default as variable
7067 $Arg{httplog_txt} = "httplog.txt"; # JL: New text format
7068 $Arg{keydata} = 0;
7069 $Arg{debug} = 0;
7070
7071 #
7072 # Check correct switches were used
7073 #
7074 Getopt::Long::Configure ("bundling");
7075 $result = GetOptions (
7076 "application!" => \$opt_a,
7077 "a" => \$opt_a,
7078 "d|preferdns" => \$opt_d, # JL: new option
7079 "e|everything" => \$opt_e,
7080 "h" => \$opt_h,
7081 "info!" => \$opt_i,
7082 "i" => \$opt_i,
7083 "n|names" => \$opt_n, # JL: new option
7084 "q|quiet" => \$opt_q,
7085 "raw!" => \$opt_r,
7086 "r" => \$opt_r,
7087 "v|verbose" => \$opt_v,
7088 "index!" => \$opt_x,
7089 "x" => \$opt_x,
7090 "A" => \$opt_A,
7091 "H|hex" => \$opt_H,
7092 "I" => \$opt_I,
7093 "R" => \$opt_R,
7094 "U|noudp" => \$opt_U,
7095 "T|notcp" => \$opt_T,
7096 "Y|noicmp" => \$opt_Y,
7097 "X" => \$opt_X,
7098 "D|dir=s" => \$opt_D,
7099 "b|playtcp=s" => \$opt_b,
7100 "B|playudp=s" => \$opt_B,
7101 "l|htmltcp=s" => \$opt_l,
7102 "L|htmludp=s" => \$opt_L,
7103 "m|min=s" => \$opt_m,
7104 "M|max=s" => \$opt_M,
7105 "o|sort=s" => \$opt_o,
7106 "p|port=s" => \$opt_p,
7107 "P|noport=s" => \$opt_P,
7108 "j|ipaddr=s" => \$opt_j,
7109 "J|noipaddr=s" => \$opt_J,
7110 "s|runonce=s" => \$opt_s,
7111 "S|runmany=s" => \$opt_S,
7112 "z|runredo" => \$opt_z,
7113 "f|filter=s" => \$opt_f,
7114 "k|keydata" => \$opt_k,
7115 "debug" => \$opt_debug,
7116 "bench" => \$opt_bench
7117 );
7118
7119 #
7120 # Process switches
7121 #
7122 &Usage() if ($opt_h || ! $result);
7123 $Arg{output_raw} = 1 if $opt_r or $opt_v;
7124 $Arg{output_hex} = 1 if $opt_H or $opt_e;
7125 $Arg{output_info} = 1 if $opt_i or $opt_v;
7126 $Arg{quiet} = 1 if $opt_q;
7127 $Arg{output_UDP} = 0 if $opt_U;
7128 $Arg{output_TCP} = 0 if $opt_T;
7129 $Arg{output_ICMP} = 0 if $opt_Y;
7130 $Arg{output_apps} = 0 if ($opt_A || (defined $opt_a && $opt_a eq "0"));
7131 $Arg{output_index} = 0 if ($opt_X || (defined $opt_x && $opt_x eq "0"));
7132 $Arg{output_allhtml} = 1 if $opt_e;
7133 $Arg{prefer_dns} = 1 if $opt_d;
7134 $Arg{httplog_html} = 1 if $opt_n;
7135 $Arg{httplog_name} = "httplog.html" if $opt_n;
7136 my $extra_TCPplayback = $opt_b;
7137 my $extra_UDPplayback = $opt_B;
7138 my $extra_TCPhtml = $opt_l;
7139 my $extra_UDPhtml = $opt_L;
7140 my $ports_accepted = $opt_p;
7141 my $ports_rejected = $opt_P;
7142 my $ips_accepted = $opt_j;
7143 my $ips_rejected = $opt_J;
7144 $Arg{output_dir} = $opt_D;
7145 $Arg{filter} = $opt_f || "";
7146 $Arg{minbytes} = 0;
7147 $Arg{maxbytes} = 0;
7148 $Arg{sort} = "time";
7149 $Arg{keydata} = 1 if $opt_k;
7150 $Arg{debug} = 1 if $opt_debug;
7151 $Arg{bench} = 1 if $opt_bench;
7152
7153 mkdir $Arg{output_dir};
7154
7155 #
7156 # Check for min/max bytes
7157 #
7158 if (defined $opt_m) {
7159 if ($opt_m =~ /k$/) {
7160 $opt_m =~ s/k$//;
7161 $opt_m *= 1024;
7162 }
7163 $Arg{minbytes} = $opt_m;
7164 }
7165 if (defined $opt_M) {
7166 if ($opt_M =~ /k$/) {
7167 $opt_M =~ s/k$//;
7168 $opt_M *= 1024;
7169 }
7170 $Arg{maxbytes} = $opt_M;
7171 }
7172
7173 #
7174 # Check for sort option
7175 #
7176 if (defined $opt_o) {
7177 if ($opt_o !~ /^(time|size|type|ip)$/) {
7178 print STDERR "ERROR55: Sort must be \"time\", " .
7179 "\"size\", \"type\" or \"ip\".\n";
7180 &Usage();
7181 }
7182 $Arg{sort} = $opt_o;
7183 }
7184
7185 #
7186 # Check for standalone redo mode
7187 #
7188 if (defined $opt_z) {
7189 $Arg{redo} = 1;
7190 if (defined $Arg{output_dir}) {
7191 # bad luck
7192 die "ERROR56: Can't use an output dir "
7193 . "$Arg{output_dir} in redo mode.\n\n";
7194 }
7195 }
7196
7197 #
7198 # Check for standalone mode
7199 #
7200 elsif (defined $opt_s || defined $opt_S) {
7201 $Arg{standalone} = 1;
7202 if (defined $opt_s) {
7203 if ($opt_s =~ /,/) {
7204 die "ERROR57: Unexpected comma found in " .
7205 "\"-s$opt_s\" (did you mean \"-S$opt_s\"?)\n";
7206 }
7207 $Arg{mins} = $opt_s;
7208 $Arg{count} = 1;
7209 } elsif (defined $opt_S) {
7210 my ($mins,$count) = split(/,/,$opt_S);
7211 $Arg{mins} = $mins;
7212 ### -1 means endless
7213 $Arg{count} = $count || -1;
7214 }
7215 }
7216
7217 #
7218 # This is normal mode
7219 #
7220 else {
7221 $Arg{normal} = 1;
7222 }
7223
7224 #
7225 # Build accepted or rejected port list as %Arg{Port_Accepted},...
7226 #
7227 if (defined $ports_accepted) {
7228 $Arg{port_accept} = 1;
7229 foreach $port (split(/,/,$ports_accepted)) {
7230 $Arg{Port_Accepted}{$port} = 1;
7231 }
7232 }
7233 if (defined $ports_rejected) {
7234 $Arg{port_reject} = 1;
7235 foreach $port (split(/,/,$ports_rejected)) {
7236 $Arg{Port_Rejected}{$port} = 1;
7237 }
7238 }
7239
7240 #
7241 # Build accepted or rejected IP list as %Arg{IP_Accepted},...
7242 #
7243 if (defined $ips_accepted) {
7244 $Arg{ip_accept} = 1;
7245 foreach $ip (split(/,/,$ips_accepted)) {
7246 $Arg{IP_Accepted}{$ip} = 1;
7247 }
7248 }
7249 if (defined $ips_rejected) {
7250 $Arg{ip_reject} = 1;
7251 foreach $ip (split(/,/,$ips_rejected)) {
7252 $Arg{IP_Rejected}{$ip} = 1;
7253 }
7254 }
7255
7256 #
7257 # Add extra ports to playback or HTML
7258 #
7259 if (defined $extra_TCPplayback) {
7260 foreach $port (split(/,/,$extra_TCPplayback)) {
7261 $Arg{Save_As_TCP_Playback}{$port} = 1;
7262 }
7263 }
7264 if (defined $extra_UDPplayback) {
7265 foreach $port (split(/,/,$extra_UDPplayback)) {
7266 $Arg{Save_As_UDP_Playback}{$port} = 1;
7267 }
7268 }
7269 if (defined $extra_TCPhtml) {
7270 foreach $port (split(/,/,$extra_TCPhtml)) {
7271 $Arg{Save_As_TCP_HTML}{$port} = 1;
7272 }
7273 }
7274 if (defined $extra_UDPhtml) {
7275 foreach $port (split(/,/,$extra_UDPhtml)) {
7276 $Arg{Save_As_UDP_HTML}{$port} = 1;
7277 }
7278 }
7279
7280 #
7281 # Check infile was provided, or print usage
7282 #
7283 if (! defined $ARGV[0] && ! ($Arg{standalone} || $Arg{redo})) {
7284 &Usage();
7285 }
7286 @{$Arg{infiles}} = @ARGV;
7287}
7288
7289
7290# Usage - print command usage and exit.
7291#
7292sub Usage {
7293 print "USAGE: chaosreader [-adehiknqrvxAEHIRTUXY] [-D dir]
7294 [-b port[,...]] [-B port[,...]]
7295 [-j IPaddr[,...]] [-J IPaddr[,...]]
7296 [-l port[,...]] [-L port[,...]] [-m bytes[k]]
7297 [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"]
7298 [-p port[,...]] [-P port[,...]]
7299 infile [infile2 ...]
7300 chaosreader -s [mins] | -S [mins[,count]]
7301 [-z] [-f 'filter']
7302 eg, chaosreader infile # Create application session files, indexes
7303 chaosreader -v infile # Verbose - Create ALL files
7304 chaosreader -i infile # Create info files
7305 chaosreader -r infile # Create raw files
7306 chaosreader -S 2,5 # Standalone - sniff network 5 times by 2 mins.
7307 chaosreader -h # Print a brief help (this)
7308 chaosreader --help # Print verbose help and version
7309 chaosreader --help2 # Print massive help\n\n";
7310 exit(0);
7311}
7312
7313
7314# Usage Full - print command usage and exit.
7315#
7316sub Usage_Full {
7317 print "Version 0.95i, 14-Apr-2014
7318
7319USAGE: chaosreader [-adehiknqrvxAHIRTUXY] [-D dir]
7320 [-b port[,...]] [-B port[,...]]
7321 [-j IPaddr[,...]] [-J IPaddr[,...]]
7322 [-l port[,...]] [-L port[,...]] [-m bytes[k]]
7323 [-M bytes[k]] [-o \"time\"|\"size\"|\"type\"|\"ip\"]
7324 [-p port[,...]] [-P port[,...]]
7325 infile [infile2 ...]
7326
7327 chaosreader -s [mins] | -S [mins[,count]]
7328 [-z] [-f 'filter']
7329
7330 chaosreader # Create application session files, indexes
7331
7332 -a, --application # Create application session files (default)
7333 -d, --preferdns # Show DNS names instead of IP addresses
7334 -e, --everything # Create HTML 2-way & hex files for everything
7335 -h # Print a brief help
7336 --help # Print verbose help (this) and version
7337 --help2 # Print massive help
7338 -i, --info # Create info file
7339 -q, --quiet # Quiet, no output to screen
7340 -r, --raw # Create raw files
7341 -v, --verbose # Verbose - Create ALL files .. (except -e)
7342 -x, --index # Create index files (default)
7343 -A, --noapplication # Exclude application session files
7344 -H, --hex # Include hex dumps (slow)
7345 -I, --noinfo # Exclude info files
7346 -R, --noraw # Exclude raw files
7347 -T, --notcp # Exclude TCP traffic
7348 -U, --noudp # Exclude UDP traffic
7349 -Y, --noicmp # Exclude ICMP traffic
7350 -X, --noindex # Exclude index files
7351 -k, --keydata # Create extra files for keystroke analysis
7352 -n, --names # Include hostnames in hyperlinked HTTPlog (HTML)
7353 -D dir --dir dir # Output all files to this directory
7354 -b 25,79 --playtcp 25,79 # replay these TCP ports as well (playback)
7355 -B 36,42 --playudp 36,42 # replay these UDP ports as well (playback)
7356 -l 7,79 --htmltcp 7,79 # Create HTML for these TCP ports as well
7357 -L 7,123 --htmludp 7,123 # Create HTML for these UDP ports as well
7358 -m 1k --min 1k # Min size of connection to save (\"k\" for Kb)
7359 -M 1024k --max 1k # Max size of connection to save (\"k\" for Kb)
7360 -o size --sort size # sort Order: time/size/type/ip (Default time)
7361 -p 21,23 --port 21,23 # Only examine these ports (TCP & UDP)
7362 -P 80,81 --noport 80,81 # Exclude these ports (TCP & UDP)
7363 -s 5 --runonce 5 # Standalone. Run tcpdump/snoop for 5 mins.
7364 -S 5,10 --runmany 5,10 # Standalone, many. 10 samples of 5 mins each.
7365 -S 5 --runmany 5 # Standalone, endless. 5 min samples forever.
7366 -z --runredo # Standalone, redo. Rereads last run's logs.
7367 -j 10.1.2.1 --ipaddr 10.1.2.1 # Only examine these IPs
7368 -J 10.1.2.1 --noipaddr 10.1.2.1 # Exclude these IPs
7369 -f 'port 7' --filter 'port 7' # With standalone, use this dump filter.
7370
7371eg1,
7372 tcpdump -s9000 -w output1 # create tcpdump capture file
7373 chaosreader output1 # extract recognised sessions, or,
7374 chaosreader -ve output1 # gimme everything, or,
7375 chaosreader -p 20,21,23 output1 # only ftp and telnet...
7376eg2,
7377 snoop -o output1 # create snoop capture file instead
7378 chaosreader output1 # extract recognised sessions...
7379eg3,
7380 chaosreader -S 2,5 # Standalone, sniff network 5 times for 2 mins
7381 # each. View index.html for progress (or .text)
7382";
7383 exit(0);
7384}
7385
7386
7387# Usage_Massive - print massive help. Actually strip it from the comments
7388# at the top of the code.
7389#
7390sub Usage_Massive {
7391 open (MYSELF,"$0") || die "ERROR58: I can't see myself: $!\n";
7392 @Myself = <MYSELF>;
7393 close MYSELF;
7394
7395 ### Print comment from top of code
7396 foreach $line (@Myself) {
7397 last if $line !~ /^#/;
7398 last if $line =~ /^# Todo:/;
7399 next if $line =~ m:^#!/usr/bin/perl:;
7400 $line =~ s/^#/ /;
7401 print $line;
7402 }
7403 print "\n";
7404
7405 exit(0);
7406}
7407
7408
7409
7410__END__
7411
7412Reminders for myself
7413====================
7414/s for multiline match
7415
7416
7417Comments style:
7418
7419# Micro comment
7420
7421### Tiny Comment
7422
7423#
7424# Small comment
7425#
7426
7427#
7428# --- Meduim Comment ---
7429#
7430
7431#########################
7432# --- Large Comment ---
7433#
7434
7435########################
7436# --- Huge Comment --- #
7437########################
7438
7439
7440Error message style
7441===================
7442
7443die "ERROR#: message: $!\n";
7444
7445
7446
7447Data types,
7448===========
7449 %Arg
7450 -> @infiles
7451 -> output_raw
7452 -> output_hex
7453 -> output_UDP
7454 -> output_info
7455 -> output_apps
7456 -> output_index
7457 -> output_allhtml
7458 -> Save_As_TCP_HTML
7459 -> $port
7460 -> Save_As_UDP_HTML
7461 -> $port
7462 -> Save_As_TCP_Playback
7463 -> $port
7464 -> Save_As_UDP_Playback
7465 -> $port
7466 -> Port_Accepted
7467 -> $port
7468 -> Port_Rejected
7469 -> $port
7470 -> ip_accept
7471 -> ip_reject
7472 -> IP_Accepted
7473 -> $ip
7474 -> IP_Rejected
7475 -> $ip
7476 -> debug
7477 -> standalone
7478 -> redo
7479 -> normal
7480 -> mins
7481 -> count
7482 -> output_dir
7483 -> quiet
7484 -> infile
7485 -> minbytes
7486 -> maxbytes
7487
7488 %IP
7489 -> time
7490 -> $packet_time
7491 -> ver
7492 -> src
7493 -> dest
7494 -> protocol
7495 -> frag
7496 -> $ip_frag
7497 -> fragged
7498 -> drops
7499 -> id
7500 -> $ip_id
7501 -> StartTime
7502
7503 %TCP
7504 -> id
7505 -> $session_id
7506 -> src
7507 -> dest
7508 -> source # SYN seen
7509 -> src_port
7510 -> dest_port
7511 -> Aseq
7512 -> $$tcp_seq
7513 -> Bseq
7514 -> $$tcp_seq
7515 -> time
7516 -> $time
7517 -> dir
7518 -> data
7519 -> BothHTML
7520 -> StartTime
7521 -> EndTime
7522 -> size
7523 -> knowndir
7524
7525 %UDP
7526 -> id
7527 -> $session_id
7528 -> src
7529 -> dest
7530 -> src_port
7531 -> dest_port
7532 -> RawA
7533 -> RawB
7534 -> time
7535 -> $time
7536 -> BothHTML
7537 -> StartTime
7538 -> EndTime
7539 -> size
7540
7541 %ICMP
7542 -> time
7543 -> type
7544 -> code
7545 -> src
7546 -> dest
7547 -> Partial
7548 -> ver
7549 -> size
7550
7551 %Count
7552 -> IP
7553 -> IPprotocols
7554 -> TCPports
7555 -> UDPports
7556 -> EtherType
7557
7558 %CountMaster
7559 (as above)
7560
7561 %Index
7562 -> @HTML
7563 -> @Text
7564 -> Time_Order
7565 -> $session_timeid
7566 -> Sort_Lookup
7567 -> $session_timeid
7568
7569 %Image
7570 -> @HTML
7571 -> links
7572 -> info
7573 -> notempty
7574
7575 %GETPOST
7576 -> @HTML
7577 -> query
7578 -> info
7579 -> notempty
7580
7581 %Hex
7582 -> $type
7583 -> $session_id
7584 -> [ $from_server, data ]
7585 %Filenames
7586 -> $time
7587 -> filename
7588 -> service
7589 -> session_id
7590
7591 @Master
7592 -> starttime
7593 -> endtime
7594 -> duration
7595 -> size
7596 -> dir
7597 -> file