#!/usr/bin/perl -T

# $Author: hso $
# $Date: 2001/06/17 06:00:36 $
# $Revision: 1.23 $
# $RCSfile: snifob,v $
# $Name: v0_9_2 $

use vars qw( 
             @default_color_set @mac_addr_color_set @src_ip_color_set
             @dst_ip_color_set @src_port_color_set @dst_port_color_set
             @time_color_set @hex_p_color_set @hex_np_color_set
	     @keyword_color_set

	     %foreground %background %intensity $reset $escape
             $snort_hex_re $snoop_hex_re $hex_X_re $hex_x_re
	     $hex_smb_re $snort_timestamp_re $dt_timestamp_re
	     $tcpdump_epoch_timestamp_re $snoop_dr_timestamp_re
	     $num_ip_addr_re $aip_addr_re
	     $hostname_re $fqdn_re $alphnum_hn_port_re
	     $num_port_re $alphnum_port_re $mac_addr_re

	     $sniffer $sniffer_options $sniffer_dump
             $sniffer_options_dump

	     $using_snifoutfh

	     $rel_version

	     $opt_Debug $opt_Help $opt_Nocolor $opt_Nodecode $opt_Run
             $opt_Drun
             

	     $eol
           );

use strict;
use Getopt::Long;

## begin user configurable

# color sets
@default_color_set = qw(dark cyan black);
@mac_addr_color_set = qw(dark cyan blue);
@src_ip_color_set = qw(bright magenta black);
@dst_ip_color_set = qw(bright yellow black);
@src_port_color_set = qw(dark white blue);
@dst_port_color_set = qw(dark black green);
@time_color_set = qw(dark green black);
@hex_p_color_set = qw(bright green black);
@hex_np_color_set = qw(dark blue black);
@keyword_color_set = qw(bright cyan blue);

$sniffer = "Not configured";
$sniffer_options = "Not configured";

#$sniffer = "/usr/local/bin/sudo /usr/local/bin/snort";
#$sniffer_options = "-A none -axvBi eth0";

#$sniffer = "/usr/local/bin/sudo /usr/local/sbin/tcpdump";
#$sniffer_options = "-vveln";

# the following should include an option or options to dump the
#  packet payload
$sniffer_dump = "Not configured";
$sniffer_options_dump = "Not configured";

#$sniffer_dump = "/usr/local/bin/sudo /usr/local/bin/snort";
#$sniffer_options_dump = "-A none -axvdBi eth0";

#$sniffer_dump = "/usr/local/bin/sudo /usr/local/sbin/tcpdump";
#$sniffer_options_dump = "-Xvvelns 1518";

## begin potentially evil security hole
#
# *NOTE*: users could set their environment to anything they
# want and this program *will* run that code. This is bad
# if you are using sudo with a policy for a user to only
# run snifob as root. You could tighten the de-tainting
# regex: /(.*)/ to fit what you're trying to accomplish to
# help.
#
#$sniffer = $ENV{sniffer};
#$sniffer_options = $ENV{sniffer_options};
#
#if ($sniffer =~ /(.*)/) {
#  $sniffer = $1;
#}
#if ($sniffer_options =~ /(.*)/) {
#  $sniffer_options = $1;
#}
#
## end potentially evil security hole

## end user configurable

## begin non user configurable ( certainly user hackable, however ;) )
##  use the source  l |_| |< 3  !!

# clean path for perl -T
$ENV{PATH} = "";
$ENV{ENV} = "";

# signal handlers to clean up colorization
$SIG{INT} = \&sig_exit_cleanup_handler;
$SIG{QUIT} = \&sig_exit_cleanup_handler;
$SIG{STOP} = \&sig_stop_cleanup_handler;

# colors
%foreground = (
		black => 30,
		red => 31,
		green => 32,
		yellow => 33,
		blue => 34,
		magenta => 35,
		cyan => 36,
		white => 37
	      );

%background = (
		black => 40,
		red => 41,
		green => 42,
		yellow => 43,
		blue => 44,
		magenta => 45,
		cyan => 46,
		white => 47
	      );

%intensity =  (
		dark => 0,
		bright => 1
	      );

# escape sequences
$escape = "\033[";
$reset = "00m";


# can all the ( become (?:
# patterns
$snort_hex_re = '(?:0x[\dA-Fa-f]{4}:\s)?(?:[\dA-F]{2}\s){1,16}\s+.{1,16}';
$snoop_hex_re = '\s{1,11}\d+:\s(?:[\dA-Fa-f]{2,4}\s)+';
$hex_X_re = '0x[\dA-Fa-f]{4}\s+[\dA-Fa-f]+';
$hex_x_re = '\s+([\dA-Fa-f]{2,4} )+';
$hex_smb_re = '\[[A-Fa-f\d]{3}\]( [A-Fa-f\d]{2})+';

$snort_timestamp_re = '\d{2}\/\d{2}-\d{2}:\d{2}:\d{2}\.\d{6}';
$dt_timestamp_re = '(\d{1,2}:){2}\d{1,2}\.\d+';
$tcpdump_epoch_timestamp_re = '\d+\.\d{6}';
$snoop_dr_timestamp_re = '\d+\.\d{5}';

$num_ip_addr_re = '(((2([0-4]\d|5[0-5])|[01]?\d{1,2}))\.){3}((2([0-4]\d|5[0-5])|[01]?\d{1,2}))';
# hostname_re and fqdn_re shouldn't accept (see RFC 1035) '_' (found in \w); Since some people
#  have used '_' in their labels/hostnames....
$hostname_re = '(([a-zA-z][A-Za-z\d_-]{1,61}[A-Za-z\d])|([a-zA-z][A-Za-z\d])|[a-zA-z])';
$fqdn_re = '((([a-zA-z][A-Za-z\d_-]{1,61}[A-Za-z\d])|([a-zA-z][A-Za-z\d])|[a-zA-z])\.)+(([a-zA-z][A-Za-z\d_-]{1,61}[A-Za-z\d])|([a-zA-z][A-Za-z\d])|[a-zA-z])';

$num_port_re = '\d{1,5}';
$alphnum_port_re = '[\w-]+';
$mac_addr_re = '([\dA-Fa-f]{1,2}:){5}[\dA-Fa-f]{1,2}';


# global variables
$eol = $/;
$rel_version = "0.9.2";
$using_snifoutfh = 0;

# immediately flush output buffer when data is written
$| = 1;

GetOptions(
  "debug:i" => \$opt_Debug,
  "help" => \$opt_Help,
  "nocolor" => \$opt_Nocolor,
  "nodecode" => \$opt_Nodecode,
  "run" => \$opt_Run,
  "drun" => \$opt_Drun
);

#begin "main"
{

  my $fh_to_read = ();

  &reset_ansi();

  if ($opt_Help) {
    print "\n";
    print "Options available for $0 v$rel_version:\n";
    print "\n";
    print "    --help\tshow this information\n";
    print "    --nocolor\tdo not colorize output\n";
    print "    --nodecode\tdo not de-hexify tcpdump -x output\n";
    print "    --debug\tjust like it sounds\n";
    print "    --run\trun \"$sniffer\" with options \"$sniffer_options\" &\n";
    print "      \t\tsnag stdout (doesn't dump packet payload)\n";
    print "    --drun\trun \"$sniffer_dump\" with options \"$sniffer_options_dump\" &\n";
    print "      \t\tsnag stdout (dumps packet payload)\n";
    print "\n";
    print "    For additional information, run perldoc on $0\n";
    print "\n";
    exit 1;
  }

  if (defined($opt_Debug)) {
    if ($opt_Debug < 1) {
      $opt_Debug = 1;
    }
  }

  if ($opt_Run) {
    if ($sniffer eq "Not configured" || $sniffer_options eq "Not configured") {
      print "\nEdit $0 and configure \$sniffer and \$sniffer_options\n\n";
      exit 1;
    }
    else {
      &debug_msg("Opening |$sniffer $sniffer_options\n") if $opt_Debug;
      open(SNIFOUTFH, "$sniffer $sniffer_options|") || 
      die "Can't start \"$sniffer $sniffer_options\". error: $?";
      $using_snifoutfh = 1;
      print "$0 v$rel_version processing SNIFOUTFH.\n";
    }
  }
  elsif ($opt_Drun) {
    if ($sniffer_dump eq "Not configured" || $sniffer_options_dump eq "Not configured") {
      print "\nEdit $0 and configure \$sniffer_dump and \$sniffer_options_dump\n\n";
      exit 1;
    }
    else {
      &debug_msg("Opening |$sniffer_dump $sniffer_options_dump\n") if $opt_Debug;
      open(SNIFOUTFH, "$sniffer_dump $sniffer_options_dump|") || 
      die "Can't start \"$sniffer_dump $sniffer_options_dump\". error: $?";
      $using_snifoutfh = 1;
      print "$0 v$rel_version processing SNIFOUTFH.\n";
    }
  }
  else {
    print "\n\n$0 v$rel_version processing stdin.\n";
  }

  &colorize(\@default_color_set);

  ## begin main loop
  if ($opt_Run || $opt_Drun) {
    while (<SNIFOUTFH>) {
      &match_line($_);
    }
  }
  else {
    while(<STDIN>) {
      &match_line($_);
    }
  }
  ## end main loop

  if ($opt_Run || $opt_Drun) {
    close(SNIFOUTFH);
  }
}
#end "main"

sub match_line () {
    
    my $line_matched = 0;
    my $line = ();

    $line = &multi_chomp($_[0]);

    #&debug_msg("Called: match_line (snort)\n") if $opt_Debug;
    &debug_msg("Called: match_line\n") if $opt_Debug;

    if (!$line_matched && !$opt_Nodecode && $line =~ /^$hex_x_re/) {
      &old_hex($line);
      $line_matched = 1;
    }

    if (!$line_matched && $line =~ /^$hex_X_re/) {
      &new_hex($line);
      $line_matched = 1;
    }

    if (!$line_matched && $line =~ /^$hex_smb_re/) {
      &smb_hex($line);
      $line_matched = 1;
    }

    if (!$line_matched && $line =~ /^$snoop_hex_re/) {
      &new_hex($line);
      $line_matched = 1;
    }

    # tcpdump info lines
    if (!$line_matched && $line =~ /^arp (who-has|reply)/) {
      &arp_info_line($line);
      $line_matched = 1;
    }

    if ( !$line_matched && (
	($line =~ /^$dt_timestamp_re/) ||
	($line =~ /^$tcpdump_epoch_timestamp_re/) ||
	($line =~ /^(  )?$snoop_dr_timestamp_re/)
       ) ) {
      &time_info_line($line);
      $line_matched = 1;
    }

    if ( !$line_matched && (
	($line =~ /^$num_ip_addr_re\.($num_port_re|$alphnum_port_re) > /) || 
	($line =~ /^$fqdn_re\.($num_port_re|$alphnum_port_re) > /) ||
	($line =~ /^$hostname_re\.($num_port_re|$alphnum_port_re) > /)
       ) ) {
      if(&addr_port_info_line($line) == 1) {
	$line_matched = 1;
      }
    }

    if ( !$line_matched && (
        (
	  ($line =~ /^ ?$num_ip_addr_re > /) ||
	  ($line =~ /^ ?$fqdn_re > /) ||
	  ($line =~ /^ ?$hostname_re > /)
        ) ||
        ( (
	  ($line =~ /^ ?$num_ip_addr_re -> /) ||
	  ($line =~ /^ ?$fqdn_re -> /) ||
	  ($line =~ /^ ?$hostname_re -> /) ) && (index($line, "=") >= 0)
        )
       ) ) {
      &addr_info_line($line);
      $line_matched = 1;
    }

    if (!$line_matched && $line =~ /^$mac_addr_re/) {
      &mac_info_line($line);
      $line_matched = 1;
    }

    # snort lines
    if ( !$line_matched && (
	($line =~/^$snort_timestamp_re/)
       ) ) {
      &s_time_info_line($line);
      $line_matched = 1;
    }

    if ( !$line_matched && (
	($line =~/^$snort_hex_re/)
      ) ) {
      &s_hex_line($line);
      $line_matched = 1;
    }

    if ( !$line_matched && (
	($line =~/^(?: )?$num_ip_addr_re(?::$num_port_re)? -> $num_ip_addr_re(?::$num_port_re)?/) ||
	($line =~/^(?: )?$num_ip_addr_re(?::$num_port_re)? -> $num_ip_addr_re(?::$num_port_re)?/)
       ) ) {
      &s_ipaddr_line($line);
      $line_matched = 1;
    }

    if ( !$line_matched && (
	($line =~/^(ICMP|TCP|UDP)[^:]/)
      ) ) {
      my @tmp_line_array = split(/\ /, $line);
      &s_process_rest_of_line(\@tmp_line_array);
      $line_matched = 1;
    }

    # snoop -v
    if ( !$line_matched && (
	($line =~/^[A-Z]+:/)
      ) ) {
      my @tmp_line_array = split(/\ /, $line);
      &snoopv_process_rest_of_line(\@tmp_line_array);
      $line_matched = 1;
    }

    # what if dos/win32, do we need \r\n ?
    if (! $line_matched) {
      print ("$line$eol");
    }

    $line_matched = 0;
}

# snort functions
sub s_time_info_line() {

  my $wrk_line = $_[0];
  my @wrk_line_array = ();

  my $date = ();
  my $time = ();

  &debug_msg("Called: s_time_info_line (snort)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  ($date, $time) = split(/-/, shift(@wrk_line_array));

  &colorize(\@time_color_set);
  print "$date";

  &colorize(\@default_color_set);
  print "-";

  &colorize(\@time_color_set);
  print "$time";

  &colorize(\@default_color_set);

  &s_process_rest_of_line(\@wrk_line_array);

}

sub s_ipaddr_line() {

  my $wrk_line = $_[0];
  my @wrk_line_array = ();

  my $addr = ();
  my $port = ();

  &debug_msg("Called: s_ipaddr_line (snort)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  $addr = shift(@wrk_line_array);

  if (index($addr, ":") >= 0) {
    ($addr, $port) = split (/:/, $addr);

    &colorize(\@src_ip_color_set);
    print "$addr";

    &colorize(\@default_color_set);
    print ":";

    &colorize(\@src_port_color_set);
    print "$port";

    &colorize(\@default_color_set);
  }
  else {
    &colorize(\@src_ip_color_set);
    print "$addr";

    &colorize(\@default_color_set);
  }

  &s_process_rest_of_line(\@wrk_line_array);

}

sub s_hex_line() {

  my $wrk_line = $_[0];

  my $preamble = ();

  my $beg_hex_chunk = ();
  my $end_hex_chunk = ();

  my @lhs_hex_array = ();
  my @rhs_hex_array = ();

  my $l_ele = ();
  my $r_ele = ();

  my $index = 0;
  my $padding = 0;

  &debug_msg("Called: s_hex_line (snort)\n") if $opt_Debug;

  if ($wrk_line =~ /(0x[\dA-Fa-f]{4}:\s)?(([A-Fa-f\d]{2} ){1,16})(\s+)(.{1,16})/) {
    if ($opt_Debug) {
      if ($opt_Debug == 4) {
	print "\$&: $&\n";
	print "\$`: $`\n";
	print "\$': $'\n";
	print "\$1: $1\n";
	print "\$2: $2\n";
	print "\$3: $3\n";
	print "\$4: $4\n";
	print "\$5: $5\n";
	print "\$6: $6\n";
	print "\$7: $7\n";
	print "\$8: $8\n";
	print "\$9: $9\n";
      }
    }
    $preamble = $1 if defined($1);
    $beg_hex_chunk = $2;
    $padding = $4;
    $end_hex_chunk = $5;
  }

  @lhs_hex_array = split(/\ /, $beg_hex_chunk);
  @rhs_hex_array = split(/ */, $end_hex_chunk);

  print "$preamble" if defined($1);

  foreach $l_ele (@lhs_hex_array) {
    if ($l_ele ge "20" && $l_ele le "7E") {
      &colorize(\@hex_p_color_set);
      print "$l_ele";

      &colorize(\@default_color_set);
      if ($index <= $#lhs_hex_array) {
	print " ";
      }
    }
    else {
      &colorize(\@hex_np_color_set);
      print "$l_ele";

      $rhs_hex_array[$index] = "\0";

      if ($index <= $#lhs_hex_array) {
	print " ";
      }
    }
    $index++;
  }

  print $padding;

# fixme:
# 0x0000: 00 50 DA 30 97 05 00 50 DA 30 57 FA 08 00 45 10  .P.0...P.0W...E.
# 0x0010: 00 D0 DB 8B 40 00 40 06 E3 31 C0 A8 FD 03 C0 A8  ....@.@..1......
# 0x0020: FD 05 07 FA 00 8B AA 81 92 94 00 06 34 96 50 18  ............4.P.
# 0x0030: 7D 74 25 B1 00 00 00 00 00 A4 FF 53 4D 42 72 00  }t%........SMBr.
# 0x0040: 00 00 00 08 01 00 00 00 00 00 00 00 00 00 00 00  ................
# 0x0050: 00 00 00 00 B7 79 00 00 01 00 00 81 00 02 50 43  .....y........PC
# 0x0060: 20 4E 45 54 57 4F 52 4B 20 50 52 4F 47 52 41 4D   NETWORKPROGRAM
# 0x0070: 20 31 2E 30 00 02 4D 49 43 52 4F 53 4F 46 54 20   1.0...ICROSOFT
# 0x0080: 4E 45 54 57 4F 52 4B 53 20 31 2E 30 33 00 02 4D  NETWORKS1.03...
# 0x0090: 49 43 52 4F 53 4F 46 54 20 4E 45 54 57 4F 52 4B  ICROSOFTNETWORK
# 0x00A0: 53 20 33 2E 30 00 02 4C 41 4E 4D 41 4E 31 2E 30  S3.0...ANMAN1.0
# 0x00B0: 00 02 4C 4D 31 2E 32 58 30 30 32 00 02 53 61 6D  ..LM1.2X002..Sam
# 0x00C0: 62 61 00 02 4E 54 20 4C 41 4E 4D 41 4E 20 31 2E  ba..NTLANMAN1.
# 0x00D0: 30 00 02 4E 54 20 4C 4D 20 30 2E 31 32 00        0..NTLM0.12.Use of uninitialized value at /home/hso/proj/snifob/snifob line 530, <STDIN> chunk 782.
# ..

  foreach $r_ele (@rhs_hex_array) {
    if ($r_ele ge "!" && $r_ele le "~") {
      &colorize(\@hex_p_color_set);
      print "$r_ele";

      &colorize(\@default_color_set);
    }
    else {
      &colorize(\@hex_np_color_set);
      print ".";
    }
  }

  &colorize(\@default_color_set);
  print "\n";

}

sub s_process_rest_of_line() {
  my $wrk_line_array_ref = $_[0];

  my $index = 0;
  my $curr_victim = ();

  &debug_msg("Called: s_process_rest_of_line (snort)\n") if $opt_Debug;

  for($index = 0; $index <= $#$wrk_line_array_ref; $index++) {
    my $did_print = 0;
    my $look_forward = 0;
    my $look_backward = 0;
    my $continue = 0;

   
    $curr_victim = $$wrk_line_array_ref[$index];

    if ($opt_Debug) {
      if ($opt_Debug == 3) {
	&debug_msg("loop pass: $index, \$current_victim: $curr_victim");
      }
    }

    $look_backward = 1 if ($index > 0);
    $look_forward = 1 if ($index < $#$wrk_line_array_ref);

    if ($continue == 0 && $look_backward) {
      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "->");

      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "who-has" && 
                        $$wrk_line_array_ref[$index + 1] eq "tell");

      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "tell" &&
                        $$wrk_line_array_ref[$index - 3] eq "who-has");

      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "reply" &&
                        $$wrk_line_array_ref[$index + 1] eq "is-at");
    }
    if ($continue == 0 && $look_forward) {
      $continue = 1 if ($$wrk_line_array_ref[$index + 1] eq "->");
    }

    if ($did_print == 0) {
      if ( $continue && ($curr_victim =~ /^$num_ip_addr_re(:$num_port_re)?/)) {

      print " ";

      my $addr = ();
      my $port = ();

      if (index($curr_victim, ":") >= 0) {
	($addr, $port) = split(/:/, $curr_victim);
      }
      else {
	$addr = $curr_victim;
      }

      if (
	  $$wrk_line_array_ref[$index-1] eq "->" || 
	  $$wrk_line_array_ref[$index-1] eq "who-has"
	 ) {
	&colorize(\@dst_ip_color_set);
      }
      else {
	&colorize(\@src_ip_color_set);
      }
      print "$addr";

      &colorize(\@default_color_set);

      if (defined($port)) {
	if (
	    $$wrk_line_array_ref[$index-1] eq "->" || 
	    $$wrk_line_array_ref[$index-1] eq "who-has"
	   ) {
	  print ":";
	  &colorize(\@dst_port_color_set);
	  print "$port";
	}
	else {
	  print ":";
	  &colorize(\@src_port_color_set);
	  print "$port";
	}
      }
      &colorize(\@default_color_set);

      $did_print = 1;
      $continue = 0;
    }
    }

    if ($did_print == 0) {
      if ($curr_victim =~ /$mac_addr_re/) {

	print " ";

	&colorize(\@mac_addr_color_set);
	print "$curr_victim";

	&colorize(\@default_color_set);

	$did_print = 1;
      }
    }
 
    if ($did_print == 0) {
      if (
	  ($curr_victim eq "ARP") ||
	  ($curr_victim eq "ICMP") ||
	  ($curr_victim eq "TCP" && ($$wrk_line_array_ref[$index + 1] ne "Options")) ||
	  ($curr_victim eq "UDP")
	 ) {

	if ( 
	    ($$wrk_line_array_ref[0] ne "ICMP") &&
	    ($$wrk_line_array_ref[0] ne "TCP") &&
	    ($$wrk_line_array_ref[0] ne "UDP")
	   ) {
	  print " ";
	}

	if ($opt_Debug) {
	  if ($opt_Debug == 3) {
	    print &debug_msg("matched snort keyword");
	  }
	}

	&colorize(\@keyword_color_set);
	print "$curr_victim";

	&colorize(\@default_color_set);

	$did_print = 1;
      }
    }

    if ($did_print == 0) {
      if ($curr_victim =~ /$mac_addr_re/) {

	print " ";

	&colorize(\@mac_addr_color_set);
	print "$curr_victim";

	&colorize(\@default_color_set);

	$did_print = 1;
      }
    }
  
    if (!$did_print) {
      print " $curr_victim";
    }
    else {
      $did_print = 0;
    }

  }
  
  print "\n";
}

# snoopv functions
sub snoopv_process_rest_of_line() {
  my $wl_ref = $_[0];

  my $ele = ();
  my $index = ();

  &debug_msg("Called: snoopv_process_rest_of_line");

  for ($index = 0; $index <= $#$wl_ref; $index++) {
    my $found_comma = ();

    $ele = $$wl_ref[$index];

    $found_comma = chop($ele) if ($ele =~ /.*,$/);

    print " ";

    if ($#$wl_ref >= 3) {
      &colorize(\@src_port_color_set) if ($$wl_ref[$index - 2] eq "port" && $$wl_ref[$index - 3] eq "Source");
      &colorize(\@dst_port_color_set) if ($$wl_ref[$index - 2] eq "port" && $$wl_ref[$index - 3] eq "Destination");

      &colorize(\@src_ip_color_set) if (($$wl_ref[$index - 2] eq "address" && $$wl_ref[$index - 3] eq "Source")
					&& $ele ne "=");
      &colorize(\@dst_ip_color_set) if (($$wl_ref[$index - 2] eq "address" && $$wl_ref[$index - 3] eq "Destination")
					&& $ele ne "=");
    }

    if ($#$wl_ref >= 4) {
      &colorize(\@src_ip_color_set) if ($$wl_ref[$index - 3] eq "address" && $$wl_ref[$index - 4] eq "Source");
      &colorize(\@dst_ip_color_set) if ($$wl_ref[$index - 3] eq "address" && $$wl_ref[$index - 4] eq "Destination");
    }

    &colorize(\@time_color_set) if ($ele =~ /$dt_timestamp_re/);
    &colorize(\@mac_addr_color_set) if ($ele =~ /$mac_addr_re/);
    
    print "$ele";

    &colorize(\@default_color_set);

    print "," if defined($found_comma);
  }

  print "\n";
}

# tcpdump (works w/ some of snoop too) functions
sub new_hex () {
  my $wrk_line = $_[0];

  my $preamble = ();

  my $lhs_hex_chunk = ();
  my $rhs_hex_chunk = ();

  my @lhs_hex_array = ();
  my @rhs_hex_array = ();

  my $padding = ();
  my $index = ();

  my $wl_re = ();

  my $l_ele = ();
  my $r_ele = ();

  &debug_msg("Called: new_hex (tcpdump/snoop)\n") if $opt_Debug;

# fixme:
#           0: 00b0 d078 9de8 0800 20f2 8996 0800 4510            ...x............
#          16: 0264 2592 4000 ff06 f934 ac14 014b ac14           .d%.@....4...K..
#          32: 0149 0016 04c6 3dbd 3cdb 5a9f 3db0 8018           .I....=.<.Z.=...
#          48: 2798 cc85 0000 0101 080a 0978 0b13 002a           '..........x...*
#          64: 50b4 0000 0209 5a86 1d43 e8aa 1a91 595b           P.....Z..C....Y[
#          80: 0c7b 3fb0 06c5 9694 b6b0 5f11 3353 3e84           .{?......._.3S>.
#          96: a0d8 f207 1333 bb2f 61b6 7757 9583 430e           .....3./a.wW..C.
#         112: ad95 88ed b0bc 39f6 28a6 fde2 66fe ecfe           ......9.(...f...
#         128: cf35 9e21 cbaa b4d7 9656 12f4 8278 a91c           .5.!.....V...x..
#         144: 59ff 2b44 3217 0b23 7a9d 336e 7b33 625a           Y.+D2..#z.3n{3bZ
#         160: 0f23 370c 835b 29c4 dddc f44a 8829 f8c0           .#7..[)....J.)..
#         176: 2beb 4ec8 b357 4f78 17d7 97d8 9401 c415           +.N..WOx........
#         192: 7823 e194 68a6 c187 c8be e4d6 e552 10bc           x#..h........R..
#         208: b7d1 7d09 6a6c b081 b4e0 f66e 41b9 76c6           ..}.jl.....nA.v.
#         224: f1b3 572a 6683 8a85 574c ead7 da0a eb37           ..W*f...WL.....7
#         240: 124e 1b43 84e1 c9fa a30a 461f f359 b61e           .N.C......F..Y..
#         256: ff9a 060a 20ee ef41 251e 7451 153e 292b            .......%..Q..)+
#         272: 2b20 30fa fcee 3428 4369 395a d51c 4b27            +0....(Ci9Z...'
#         288: c5ba 6124 ded6 8620 b515 f1ad 63f2 209d             ..a$..........Use of uninitialized value in string ge at ./snifob line 848, <STDIN> line 2632.
#..
#         304: 7559 7a44 94b3 3a67 6c5d 7074 5554 cbea           uYzD..:gl]ptUT..
#         320: 3f90 1704 a3bc 62c5 7455 edb5 fcfc 3e38           ?.....b.tU....>8
#         336: 1979 187b 76a1 1916 26fa 3387 dac7 1153           .y.{v...&.3....S
#         352: 1447 f447 4d34 a3f1 14ed 39cf 43e8 36ec           .G.GM4....9.C.6.
#         368: 7e4f 9761 83a9 e707 9d7a f3fc 8697 7b31           ~O.a.....z....{1
#         384: 60b2 1137 88ad d3bb a1fb 39c1 d960 4ccc           `..7......9..`L.
#         400: c39a dce0 39fd 7f18 8feb f2fa b0d3 b348           ....9..........H
#         416: 5d17 e412 316a 514c 522c 4eff ea0b 44af           ]...1jQLR,N...D.
#         432: 51f8 8aca 2b73 2c03 8dd3 12cd bcb1 2f8b           Q...+s,......./.
#         448: 597e 6a29 ccbb 3991 e30a ffa8 f33f 0310           Y~j)..9......?..
#         464: 0bb8 3153 5752 2220 b612 7574 b278 3c9c            ..1SWR"...t..<..
#         480: 0159 bdf6 d9f4 592d b544 aabf eccf 1dc3           .Y....Y-.D......
#         496: ff9c 839e 3806 6282 5603 3541 78c2 5fc7           ....8.b.V.5Ax._.
#         512: 0faf bfad 7146 b9cd e5fd 4815 fa03 562d           ....qF....H...V-
#         528: 1a6d 75e5 e248 20a6 7a1f 7c96 8094 4175            .mu..H........u
#         544: a4dc 4fb7 94db ab8c bcd7 7929 885d d8ba           ..O.......y).]..
#         560: 97ea 962e 3385 db9f 3327 e541 2293 df64           ....3...3'.A"..d
#         576: 3e89 c064 59c5 ad28 9e41 c935 6fee df80           >..dY..(.A.5o...
#         592: f8b5 6a53 8375 0000 0012 525e c73c 75d7           ..jS.u....R^.<u.
#         608: d771 ee39 1ce6 9585 df90 cfbf e81e e776           .q.9...........v
#         624: 7a66                                              zf


  # really should be 2 or 4 not 2,4
  $wl_re = '^(((?:0x[A-Fa-f\d]{4})|(?:\s{1,11}\d+:))\s{1,3})((([A-Fa-f\d]{2,4}\s){1,7})?([A-Fa-f\d]{2,4}))(\s{1,45})(.{1,16})';
  if ($wrk_line =~ /$wl_re/) {
    $preamble = $1;
    $lhs_hex_chunk = $3;
    $rhs_hex_chunk = $8;
    if ($opt_Debug) {
      if ($opt_Debug == 4) {
	print "\$&: $&\n";
	print "\$`: $`\n";
	print "\$': $'\n";
	print "\$1: $1\n";
	print "\$2: $2\n";
	print "\$3: $3\n";
	print "\$4: $4\n";
	print "\$5: $5\n";
	print "\$6: $6\n";
	print "\$7: $7\n";
	print "\$8: $8\n";
	print "\$9: $9\n";
      }
    }
  }

  #$lhs_hex_chunk =~ s/([a-f\d])([a-f\d])([a-f\d])([a-f\d]) ?/$1$2 $3$4 /g;
  $lhs_hex_chunk =~ s/([a-f\d])([a-f\d])([a-f\d])([a-f\d]) ?/$1$2 $3$4 /g;
  @lhs_hex_array = split(/\ /, $lhs_hex_chunk);
  @rhs_hex_array = split(/ */, $rhs_hex_chunk);

  $padding = length($wrk_line) - ((($#lhs_hex_array + 1) * 2) + ((($#lhs_hex_array + 1) / 2) - 1) + ($#rhs_hex_array + 1));
  print "$preamble";

  $index = 0;
  foreach $l_ele (@lhs_hex_array) {
    if ( uc($l_ele) ge "20" && uc($l_ele) le "7E") {
      &colorize(\@hex_p_color_set);
      print "$l_ele";

      &colorize(\@default_color_set);
    }
    else {
      &colorize(\@hex_np_color_set);
      print "$l_ele";

      $rhs_hex_array[$index] = "\0";
    }
    if ($index < $#lhs_hex_array && (($index + 1) % 2 == 0)) {
      print " ";
    }
    $index++;
  }

  print " " x $padding;

  foreach $r_ele (@rhs_hex_array) {
    if ( $r_ele ge "!" && $r_ele le "~") {
      &colorize(\@hex_p_color_set);
      print "$r_ele";

      &colorize(\@default_color_set);
    }
    else {
      &colorize(\@hex_np_color_set);
      print ".";
    }
  }

  &colorize(\@default_color_set);
  print "\n";

}

sub old_hex () {
  my $wrk_line = $_[0];

  # these really should be globals set by cmd line args
  my $sub_dot = 1;

  &debug_msg("Called: old_hex (tcpdump)\n") if $opt_Debug;

  chomp($wrk_line);

  print("\t\t\t  ");
  my @hex_blob = split("\ ", $wrk_line);

  my $space = 1;
  foreach my $ele (@hex_blob) {
    my @data_bytes = unpack ("C2",pack("H4", $ele));        

    foreach my $byte (@data_bytes) {
      if ($sub_dot) {
	if (($byte > 31) && ($byte < 127)){
	  &colorize(\@hex_p_color_set);
	  print chr($byte);
	  &colorize(\@default_color_set);
	}
	else {
	  # note that '.' is == to 2e in hex, so if you
	  #  see a '.' with a 2e below it really is '.'
	  &colorize(\@hex_np_color_set);
	  print ".";
	  &colorize(\@default_color_set);
	}
      }
      else {
	print chr($byte);
      }
      print " ";
      if ($space == 2) {
	$space = 0;
	print " ";
      }
      $space++;
    }

  }

  print "\n$wrk_line\n";

}

sub smb_hex () {
  my $wrk_line = $_[0];

  my $preamble = ();

  my $lhs_hex_chunk = ();
  my $rhs_hex_chunk = ();

  my @lhs_hex_array = ();
  my @rhs_hex_array = ();

  my $padding = ();
  my $index = ();

  my $wl_re = ();

  my $l_ele = ();
  my $r_ele = ();

  &debug_msg("Called: smb_hex (tcpdump)\n") if $opt_Debug;

  $wl_re = '^(\[[\dA-Fa-f]+\]\s)((([A-Fa-f\d]{2}\s{1,2}){1,15})?([A-Fa-f\d]{2}))(\s{1,48})(.{1,8}(\s.{1,8})?)';
  if ($wrk_line =~ /$wl_re/) {
    $preamble = $1;
    $lhs_hex_chunk = $2;
    $rhs_hex_chunk = $7;
    $padding = $6;
  }

  $lhs_hex_chunk =~ s/  / /;
  @lhs_hex_array = split(/\ /, $lhs_hex_chunk);
  @rhs_hex_array = split(/ */, $rhs_hex_chunk);

  print "$preamble";

  $index = 0;
  foreach $l_ele (@lhs_hex_array) {
    if ( uc($l_ele) ge "20" && uc($l_ele) le "7E") {
      &colorize(\@hex_p_color_set);
      print "$l_ele";

      &colorize(\@default_color_set);
    }
    else {
      &colorize(\@hex_np_color_set);
      print "$l_ele";

      $rhs_hex_array[$index] = "\0";
    }
    if ($index < $#lhs_hex_array) {
      print " ";
      if (($index + 1) % 8 == 0) {
	print " ";
      }
    }
    $index++;
  }

  print $padding;

  $index = 0;
  foreach $r_ele (@rhs_hex_array) {
    if ( $r_ele ge " " && $r_ele le "~") {
      &colorize(\@hex_p_color_set);
      print "$r_ele";

      &colorize(\@default_color_set);
    }
    else {
      &colorize(\@hex_np_color_set);
      print ".";
    }

    print " " if ($index == 7);
    $index++;
  }

  &colorize(\@default_color_set);
  print "\n";
  
}

sub time_info_line() {
  my $wrk_line = $_[0];

  my @wrk_line_array = ();
  my $time = ();

  &debug_msg("Called: time_info_line (tcpdump)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  $time = shift(@wrk_line_array);

  &colorize(\@time_color_set);

  print "$time";

  &colorize(\@default_color_set);

  &process_rest_of_line(\@wrk_line_array);
}

sub addr_info_line() {

  my $wrk_line = $_[0];
  my @wrk_line_array = ();
  my $addr = ();
  my $colon_found = ();

  &debug_msg("Called: addr_info_line (tcpdump)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  $addr = shift(@wrk_line_array);

  &colorize(\@src_ip_color_set);

  print "$addr";

  &colorize(\@default_color_set);

  &process_rest_of_line(\@wrk_line_array);

}

sub addr_port_info_line() {
  my $wrk_line = $_[0];

  my @wrk_line_array = ();
  my $addr_port = ();
  my $addr = ();
  my $port = ();
  my $colon_found = ();

  &debug_msg("Called: addr_port_info_line (tcpdump)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  $addr_port = shift(@wrk_line_array);

  ($addr, $port) = &proc_addr_port(\$addr_port);

  if (!defined($addr) && !defined($port)) {
    return (0);
  }

  &colorize(\@src_ip_color_set);
  print "$addr";

  &colorize(\@default_color_set);
  print ".";

  &colorize(\@src_port_color_set);
  print "$port";

  &colorize(\@default_color_set);

  &process_rest_of_line(\@wrk_line_array);
  
  return(1);
}

sub arp_info_line() {
  my $wrk_line = $_[0];

  my @wrk_line_array = ();
  my $first_arp_ele = ();

  &debug_msg("Called: arp_info_line (tcpdump)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  $first_arp_ele = shift(@wrk_line_array);

  print "$first_arp_ele";

  &process_rest_of_line(\@wrk_line_array);
}

sub mac_info_line() {
  my $wrk_line = $_[0];

  my @wrk_line_array = ();
  my $mac_addr = ();

  &debug_msg("Called: mac_info_line (tcpdump)\n") if $opt_Debug;

  @wrk_line_array = split("\ ", $wrk_line);

  $mac_addr = shift(@wrk_line_array);

  &colorize(\@mac_addr_color_set);

  print "$mac_addr";

  &colorize(\@default_color_set);

  &process_rest_of_line(\@wrk_line_array);
}

sub proc_addr_port() {
  my $addr_port_ref = $_[0];

  my @addr_port_rev = ();

  my $lport = ();
  my $laddr = ();
  
  &debug_msg("Called: proc_addr_port\n") if $opt_Debug;

  @addr_port_rev = split(/\./, reverse($$addr_port_ref));

  $lport = reverse(shift (@addr_port_rev));

  $laddr = reverse(join('.', @addr_port_rev));

  if ($lport =~ /[A-Za-z_-]+/) {
    if (
	! defined(getservbyname("$lport", "tcp")) &&
	! defined(getservbyname("$lport", "udp"))
       ) {
      $lport = ();
      $laddr = ();
    }
  }

  return ($laddr, $lport);

}

sub process_rest_of_line() {
  my $wrk_line_array_ref = $_[0];

  my $index = 0;
  my $curr_victim = ();

  &debug_msg("Called: process_rest_of_line (tcpdump)\n") if $opt_Debug;

  for($index = 0; $index <= $#$wrk_line_array_ref; $index++) {
    my $did_print = 0;
    my $look_forward = 0;
    my $look_backward = 0;
    my $continue = 0;
   
    $curr_victim = $$wrk_line_array_ref[$index];

    # addrs w/ ports
    $look_backward = 1 if ($index > 0);
    $look_forward = 1 if ($index < $#$wrk_line_array_ref);
  
# fixme: 
    # think about look forward/backward issue
#one.area.com > home3: icmp: echo replyUse of uninitialized value at ./snifob line 1046, <STDIN> chunk 85.
# (DF)
#crux.domain > home3.1024: 18535* 1/2/2 (164)
#home3 > one.area.com icmp: echo request
#one.area.com > home3: icmp: echo replyUse of uninitialized value at ./snifob line 1046, <STDIN> chunk 88.
# (DF)
#home3 > one.area.com icmp: echo request
#one.area.com > home3: icmp: echo replyUse of uninitialized value at ./snifob line 1046, <STDIN> chunk 90.
# (DF)
 
    # $look_forward = 0 if ($index == $#$wrk_line_array_ref);

    if ($continue == 0 && $look_backward) {
      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq ">");
      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "->");

      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "who-has" && 
                        (
			  $$wrk_line_array_ref[$index + 1] eq "tell" ||
			  $$wrk_line_array_ref[$index + 2] eq "tell"
                        )
                       );

      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "tell" &&
                        (
                         $$wrk_line_array_ref[$index - 3] eq "who-has" || 
                         $$wrk_line_array_ref[$index - 4] eq "who-has"
                        )
                       );

      $continue = 1 if ($$wrk_line_array_ref[$index - 1] eq "reply" &&
                        $$wrk_line_array_ref[$index + 1] eq "is-at");
    }
    if ($continue == 0 && $look_forward) {
      $continue = 1 if ($$wrk_line_array_ref[$index + 1] eq ">");
      $continue = 1 if ($$wrk_line_array_ref[$index + 1] eq "->");
    }

    if ($did_print == 0) {
      if ( $continue && (
	  ($curr_victim =~ /$num_ip_addr_re\.($num_port_re|$alphnum_port_re):?$/) ||
	  ($curr_victim =~ /$fqdn_re\.($num_port_re|$alphnum_port_re):?$/)  ||
	  ($curr_victim =~ /$hostname_re\.($num_port_re|$alphnum_port_re):?$/)
       ) ) {

	my $colon_found = ();
	my $addr = ();
	my $port = ();

	($curr_victim, $colon_found) = &was_there_colon($curr_victim);

	($addr, $port) = &proc_addr_port(\$curr_victim);

	if (defined($addr) && defined($port)) {
	  if (
	      $$wrk_line_array_ref[$index-1] eq ">" ||
	      $$wrk_line_array_ref[$index-1] eq "->"
	     ) {
	    &colorize(\@dst_ip_color_set);
	    print " $addr";
	    &colorize(\@default_color_set);
	    print ".";
	    &colorize(\@dst_port_color_set);
	    print "$port";
	  }
	  else {
	    &colorize(\@src_ip_color_set);
	    print " $addr";
	    &colorize(\@default_color_set);
	    print ".";
	    &colorize(\@src_port_color_set);
	    print "$port";
	  }
	  &colorize(\@default_color_set);
	  if ($colon_found) {
	    print ":";
	    $colon_found = 0;
	  }

	  $did_print = 1;
	  $continue = 0;
	}
      }
    }

    # addrs w/o ports
    if ($did_print == 0) {
      if ( $continue && 
	   (
	    ($curr_victim =~ /^$num_ip_addr_re:?$/) ||
	    ($curr_victim =~ /^$fqdn_re:?$/) ||
	    ($curr_victim =~ /$hostname_re:?$/)
	   )
         ) {

	my $colon_found = ();
	($curr_victim, $colon_found) = &was_there_colon($curr_victim);


	if (
	    $$wrk_line_array_ref[$index-1] eq "->" || 
	    $$wrk_line_array_ref[$index-1] eq ">" || 
	    $$wrk_line_array_ref[$index-1] eq "who-has"
	   ) {
	  &colorize(\@dst_ip_color_set);
	}
	else {
	  &colorize(\@src_ip_color_set);
	}
	print " $curr_victim";
	&colorize(\@default_color_set);
	if ($colon_found) {
	  print ":";
	  $colon_found = 0;
	}

	$did_print = 1;
	$continue = 0;
      }
    }

    # ports, snoop style
    if ($did_print == 0) {
      if ($curr_victim =~ /(D|S|port)=($num_port_re)/ && 
          (
	    ($$wrk_line_array_ref[$index-1] eq "TCP" ||
	     $$wrk_line_array_ref[$index-2] eq "TCP" )
	    ||
	    ($$wrk_line_array_ref[$index-1] eq "UDP" ||
	     $$wrk_line_array_ref[$index-2] eq "UDP" )
          )
         ) {
	print " $1=";
	&colorize(\@dst_ip_color_set) if ($1 eq "D");
	&colorize(\@src_ip_color_set) if ($1 eq "S" || $1 eq "port");
	print "$2";
	&colorize(\@default_color_set);
	$did_print = 1;
      }
    }

    # mac addrs
    if ($did_print == 0) {
      if ($curr_victim =~ /$mac_addr_re/) {
	print " ";
	&colorize(\@mac_addr_color_set);
	print "$curr_victim";
	&colorize(\@default_color_set);
	$did_print = 1;
      }
    }

    if (! $did_print) {
      print " $curr_victim";
    }
    else {
      $did_print = 0;
    }
  }

  print "\n";
}

sub was_there_colon() {
  my $dotted_tuple = $_[0];

  &debug_msg("Called: was_there_colon\n") if $opt_Debug;

  if ($dotted_tuple =~ /.*:$/) {
    chop($dotted_tuple);
    return ($dotted_tuple, 1);
  }
  else {
    return ($dotted_tuple, 0);
  }
}

# support functions
sub sig_exit_cleanup_handler() {
  $SIG{INT} = \&sig_exit_cleanup_handler;
  $SIG{QUIT} = \&sig_exit_cleanup_handler;

  &reset_ansi();

  if ($using_snifoutfh) {
    close(SNIFOUTFH);
  }

  exit;
}

sub sig_stop_cleanup_handler() {
  $SIG{STOP} = \&sig_stop_cleanup_handler;

  &reset_ansi;
}

sub multi_chomp () {
  my $temp_line = $_[0];

  &debug_msg("Called: multichomp\n") if $opt_Debug;

  $temp_line =~ s/$eol$//;

  return $temp_line;
}

sub colorize() {

  my $color_set_ref = $_[0];
  my $setInt = $$color_set_ref[0];
  my $setFg = $$color_set_ref[1];
  my $setBg = $$color_set_ref[2];

  &debug_msg("Called: colorize with colors @$color_set_ref\n") if (defined($opt_Debug) && $opt_Debug == 5);

  if (! $opt_Nocolor) {
    printf ("%s%d;%d;%dm", $escape, $intensity{$setInt}, $foreground{$setFg}, $background{$setBg});
  }
}

sub reset_ansi() {
  &debug_msg("Called: reset_ansi\n") if $opt_Debug;
  print $escape . $reset;
}

sub debug_msg() {
  my $message = $_[0];
  print "\nd: $opt_Debug -- $message\n";
}

__END__


=head1 NAME

  snifob - sniffer output beautifier

=head1 SYNOPSIS

  snifob [OPTION]

=head1 DESCRIPTION

  snifob colorizes snort and tcpdump output to make the
  output easier to read.  It will also de-hexify
  tcpdump -x output.

  --help	show help output
  --nocolor	do not colorize output
  --nodecode	do not de-hexify tcpdump -x output
  --run		run "$sniffer" with options "$sniffer_options"
		and markup stdout (doesn't dump packet payload)
  --drun	run "$sniffer_dump" with options "$sniffer_options_dump"
		and markup stdout (dumps packet payload)
  --debug       outputs debugging info, can range from 1-5 where 5 is 
                very verbose

  The user can change the execution of snifob by setting the
  following variables inside the section marked:
  ## begin user configurable

  $sniffer	         - sets the sniffer that the --run option uses
  $sniffer_options       - sets the options for the sniffer used
                           by --run
  
  # the following should include an option or options to dump
  #   the packet payload
  $sniffer_dump	         - sets the sniffer that the --drun option uses
  $sniffer_options _dump - sets the options for the sniffer used
                           by --drun

  @default_color_set  - the default color set that is used when snifob
			isn't highlighting output

                        There are other color sets available for
                        configuration in this section as well.

  the syntax for a color set is:
  @default_color_set = (<intensity>, <foreground>, <background>);

  the supported intensities are:
    bright
    dark

  the supported background and foreground colors are:
    black
    red
    green
    yellow
    blue
    magenta
    cyan
    white

  To use snort with snifob, you need to patch snort with the
  diff found at:

  http://www.nosneros.net/hso/programs/snifob/download/snort-1.7.line.buffered.patch

  After applying the patch snort 1.7, run ./configure with
  what ever options you deem appropriate and then make &&
  make install. You might need to delete config.cache and
  config.status before running ./configure.

=head1 AUTHOR

  snifob was written by Holt Sorenson <hso@nosneros.net>

=head1 BUGS/ISSUES/Reasons snifob needs a therapist

  
  -C is not supported when using snort
   This probably won't change any time soon because I have been
   unable to figure out a way to match the lines which are basically
   arbitrary text that can be up to 64 characters long, and include
   spaces (If spaces weren't included I could conceivably do
   something like [^ ] && (ch > "!"  && ch <= "~").

  -V is not supported with snoop because I have not finished it

  tcpdump (and possibly others) display . instead of a space, so
   if you seen a bright green (default printable ascii color) you
   should check to see if the . corresponds to 2e which is
   actually a period. I might add a switch to allow users to toggle
   this behavior.

=head1 SEE ALSO

  snort(8), tcpdump(1), perl(1p)

  http://www.snort.org
  http://www.tcpdump.org

  A word about Snort -- I don't mean to imply that Snort
  is just a sniffer, per say.  It has the ability to display
  information about packets near real time from the
  command line. However, Snort is a [free] "lightweight
  network intrusion detection system, capable of performing 
  real-time  traffic analysis and packet logging on IP
  networks.  It can perform protocol analysis, content
  searching/matching and can be used to detect a variety
  of attacks and probes, such as buffer overflows, stealth
  port scans, CGI attacks, SMB probes, OS fingerprinting
  attempts, and much more."  If you're interested in using
  Snort, go to the URL above, it comes with many
  recommendations.

  If you're developing in perl, I highly recommend using the
  Devel-ptkdb module authored by Andrew E. Page <aep@world.std.com>
  You can acquire it at:

  http://www.cpan.org/modules/by-category/03_Development_Support/Devel/

=head1 ACKNOWLEDGEMENTS

  gnash for the idea, amholder for the query "why are
  you using tcpdump?  snort has better payload display.",
  dR. drewenstein for harrassing me about the multiple
  possibilities of tcpdump output that I wasn't accounting for,
  and especially to family for putting up with the hands that
  are seemingly welded to computers.

=head1 COPYRIGHT

  Copyright 2000, Holt Sorenson <hso@nosneros.net>, All Rights Reserved.

  This program is licensed under the provisions of
  the Artistic license as enumerated at:

  http://www.nosneros.net/hso/programs/licenses/artistic-license.html

  There are a couple additional quid pro quos:
  -By downloading or otherwise acquiring snifob you agree to the
   terms of the license and these quid pro quos.
  -Any legal or arbitration issues are settled in the current
   physical locale of the author.
  -The author retains the right to change the license snifob is
   made available under.  If the author does change the license,
   the change will apply to that version, and versions later
   released under the same license.  The author commits to keeping
   the license liberal and available to use for netizens.

  This is not a requirement, but if snifob is helpful and you use
  it often, I would like to here from you. If you use it outside
  of your personal life, I'd like to know where you use it.  I ask
  this out of curiousity, not to track usage for compensation, or
  some other strange reason.

  The most current version of snifob can be found at:

  http://www.nosneros.net/hso/programs/snifob/
