#!/usr/bin/perl # Filename: tcp_forward # Author: David Ljung Madison # See License: http://MarginalHacks.com/License # Description: Forwards a tcp connection, allows snooping of both directions # Version: 1.01 use strict; use IO::Socket; use Net::hostent; # for OO version of gethostbyaddr ################################################## # Setup the variables ################################################## my $PROGNAME = $0; $PROGNAME =~ s|.*/||; # This gets and receives the streams by lines. # Pro: Easier for filtering # Con: Can't do tty or character based stuff (like logins, etc..) # Con: "Lines" can get long for binary data - wastes memory my $SEND_LINE_BY_LINE = 0; my $RECV_LINE_BY_LINE = 0; # Try this to see the example filter # Then do: tcp_forward -f 2000 -t getdave.com:80 -send # And then point a browser to localhost:2000 # (Try it with and without the filter) my $EXAMPLE_FILTER = 0; $SEND_LINE_BY_LINE = 1 if ($EXAMPLE_FILTER); # Reap zombies $SIG{CHLD} = 'IGNORE'; ################################################## # Usage ################################################## sub usage { my $msg; foreach $msg (@_) { print "ERROR: $msg\n"; } print "\n"; print "Usage:\t$PROGNAME [-options]\n"; print "\tTCP port forwarder. Forwards all traffic on a TCP port.\n"; print "\t-f From port (listen here)\n"; print "\t-t To location (send to here)\n"; print "\t-recv Listen to all traffic we get back\n"; print "\t-send Listen to all traffic we send (forward)\n"; print "\n"; exit -1; } sub parse_args { my ($from,$to_host,$to_port,$recv,$send); while ($#ARGV>=0) { my $arg=shift(@ARGV); if ($arg =~ /^-h$/) { usage(); } if ($arg =~ /^-f(rom)?$/) { $from = shift(@ARGV); next; } if ($arg =~ /^-to?$/) { $to_port = shift(@ARGV); $to_host = "localhost"; ($to_host,$to_port) = ($`,$') if ($to_port =~ /:/); next; } if ($arg =~ /^-s(end)?$/) { $send = 1; next; } if ($arg =~ /^-r(ecv)?$/) { $recv = 1; next; } if ($arg =~ /^-/) { usage("Unknown option: $arg"); } usage("Unknown argument [$arg]"); } usage("No 'from' port defined") unless (defined $from); usage("No 'to' host defined") unless (defined $to_host); usage("No 'to' port defined") unless (defined $to_port); ($from,$to_host,$to_port,$recv,$send); } ################################################## # Outbound Connection ################################################## sub outbound { my ($host,$port) = @_; $host = $host || "localhost"; $port = $port || 23; my $handle = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $host, PeerPort => $port) or die "cannot connect to port [$port] at localhost"; $handle->autoflush(1); $handle; } ################################################## # Server side ################################################## sub server { my ($from,$to_host,$to_port,$recv,$send) = @_; my $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $from, Listen => SOMAXCONN, Reuse => 1); die "Can't setup server: $!\n" unless $server; print STDERR "Server started on port $from\n"; ######################### # Get an incoming connection ######################### my $client; while ($client = $server->accept()) { print STDERR "Incoming connection\n"; $client->autoflush(1); ######################### # Fork so we can get more connections ######################### my $pid = fork; die("[$PROGNAME] Couldn't fork: $!\n") if (!defined $pid); if ($pid) { close $client; next; } ######################### # Setup an outbound connection ######################### my $out_handle = outbound($to_host,$to_port); print STDERR "Forwarding to $to_host:$to_port\n"; my $pid = fork; die("[$PROGNAME] Couldn't fork: $!\n") if (!defined $pid); if ($pid) { ######################### # Receive data from the outbound/forwarded port ######################### my ($c,$line); if ($RECV_LINE_BY_LINE) { while($line = <$out_handle>) { print $client $line; $line =~ s/[\r\n]//g; print STDERR "R [$line]\n" if ($recv); } } else { while(sysread($out_handle,$c,1)) { if ($recv) { if ($c =~ /[\r\n]/) { print STDERR "R [$line]\n" if ($line); undef $line; } else { $line.=$c; } } syswrite($client,$c); } print STDERR "R [$line] \n" if ($line && $recv); } # Kill the child kill 9, $pid; } else { ######################### # Send data to the forward ######################### my ($c,$line); if ($SEND_LINE_BY_LINE) { while($line = <$client>) { $line =~ s|GET /(.+) HTTP/1.0|GET http://GetDave.com/$1 HTTP/1.1| if ($EXAMPLE_FILTER); # See top of script print $out_handle $line; $line =~ s/[\r\n]//g; print STDERR "S [$line]\n" if ($send); } } else { while(sysread($client,$c,1)) { if ($send) { if ($c =~ /[\r\n]/) { print STDERR "S [$line]\n" if ($line); undef $line; } else { $line.=$c; } } syswrite($out_handle,$c); } print STDERR "S [$line] \n" if ($line && $send); } } print STDERR "Closed connection\n"; close $client; close $out_handle; exit; } } ################################################## # Main code ################################################## sub main { server(parse_args()); } main();