#!/usr/bin/perl -w

# Not really a packet sniffer, more a proxy that dumps what it
# proxies. Written in the days before I learned to write perl somewhat
# cleanly. This would probably explode if you put in 'use strict'.

# Waider 1996 or 1997
# 13/05/2003 Make sockets reusable and non-lingering
$host = $ARGV[0] || die "Usage: $0 host listenport connectport";
$listenport = $ARGV[1] || die "Usage: $0 host listenport connectport";
$connectport = $ARGV[2] || die "Usage: $0 host listenport connectport";

$|=1;
use Socket;

$sockaddr = 'S n a4 x8';

($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $port) = getservbyname($listenport, 'tcp')
  unless $listenport =~ /^\d+$/;

$this = pack($sockaddr, &AF_INET, $listenport, "\0\0\0\0");

socket(S, &PF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
bind(S, $this) || die "bind: $!";
setsockopt(S, SOL_SOCKET, SO_REUSEADDR, 1 );
setsockopt(S, SOL_SOCKET, SO_LINGER, 0 );
listen(S, 5) || die "listen: $!";

select(S); $| = 1; select(STDOUT);

for (;;) {
  print "Listening again\n";
  ($addr = accept(NS,S)) || die $!;
  setsockopt(NS, SOL_SOCKET, SO_REUSEADDR, 1 );
  setsockopt(NS, SOL_SOCKET, SO_LINGER, 0 );
  print "accept ok\n";

  (undef,$port,$inetaddr) = unpack($sockaddr,$addr);
  @inetaddr = unpack('C4',$inetaddr);
  print "Connection from ", join('.', @inetaddr ), ":$port\n";

  (undef, undef, $cproto) = getprotobyname( 'tcp' );
  $cthat = pack_sockaddr_in( $connectport, inet_aton( $host ));

  socket( CS, PF_INET, SOCK_STREAM, $cproto ) || die $!;
  warn $!, next unless ( connect( CS, $cthat ) && print "Connected.\n" );

  $hexcount=0;
  $line="";

  select( CS ); $| = 1; select( STDOUT );
  select( NS ); $| = 1; select( STDOUT );

 readloop:
  while (1) {
	$rin=$rout=$win=$wout=$ein=$eout='';
	vec( $rin, fileno( NS ), 1) = 1;
	vec( $win, fileno( NS ), 1) = 1;
	vec( $rin, fileno( CS ), 1) = 1;
	vec( $win, fileno( CS ), 1) = 1;
	$ein=$rin|$win;
	$nfound=select( $rout=$rin, $wout=$win, $eout=$ein, 0 );

	if ( $nfound ) {
	  if ( vec( $rout, fileno( NS ), 1 )) {
		$nbytes=sysread( NS, $buf, 1024 );
		if ( !defined( $nbytes )) {
		  print "\n" . scalar( localtime(time )) . " CLIENT sysread() returned undef.\n";
		  last readloop;
		}
		if ( $nbytes < 1 ) {
		  print "\n" . scalar( localtime(time )) . " CLIENT sysread() returned < 1.\n";
		  last readloop;
		}
		print STDOUT "\n" . scalar( localtime(time )) . " FROM CLIENT\n";
		$hexcount = 0;
		$hextot = 0;
		printf( STDOUT "%06X ", $hextot );
		for ($i=0;$i<$nbytes; $i++) {
		  $x=substr($buf, $i, 1 );
		  printf( STDOUT "%02X ", ord($x));
		  $line.=(( $x ge " " ) && ( ord( $x ) <= 127 ))?$x:".";
		  $hexcount++;
		  $hextot++;
		  if ($hexcount == 16 ) {
			print STDOUT "  $line";
			$line="";
			print STDOUT"\n";
			printf( STDOUT "%06X ", $hextot );
			$hexcount=0;
		  }
		}

		syswrite( CS, $buf, $nbytes );
	  }

	  if ( vec( $rout, fileno( CS ), 1 )) {
		$nbytes=sysread( CS, $buf, 1024 );
		if ( !defined( $nbytes )) {
		  print "\n" . scalar( localtime(time )) . " SERVER sysread() returned undef. $!\n";
		  last readloop;
		}
		if ( $nbytes < 1 ) {
		  print "\n" . scalar( localtime(time )) . " SERVER sysread() returned < 1.\n";
		  last readloop;
		}

		if ( $line ne "" ) {
		  $z = 16 - length( $line );
		  print " "x($z*3);
		  print "  $line";
		  $line = "";
		}

		print STDOUT "\n" . scalar( localtime(time )) . " FROM SERVER\n";
		$hexcount = 0;
		$hextot = 0;
		printf( STDOUT "%06X ", $hextot );
		for ($i=0;$i<$nbytes; $i++) {
		  $x=substr($buf, $i, 1 );
		  printf( STDOUT "%02X ", ord($x));
		  $line.=(( $x ge " " ) && ( ord( $x ) <= 127 ))?$x:".";
		  $hexcount++;
		  $hextot++;
		  if ($hexcount == 16 ) {
			print STDOUT "  $line";
			$line="";
			print STDOUT"\n";
			printf( STDOUT "%06X ", $hextot );
			$hexcount=0;
		  }
		}

		for ($i=0;$i<$nbytes;$i++) {
		  syswrite( NS, substr($buf, $i, 1), 1 );
		}
	  }
	}

	if ( vec( $eout, fileno( NS ), 1 ) || vec( $eout, fileno( CS ), 1)) {
	  #print "Error.\n" ;
	  #last readloop;
	}
  }

  print STDOUT"\n";

  # Close connections
  close(NS);
  close(CS);
}
