#!/usr/bin/perl # Vincent 'rastakid' van Scherpenseel (rastakid [at] syn-ack [dot] org) - 17 May 2005 # # World Hopper 0.3 (whopper.pl) # Simple but powerfull tool to connect to remote services through a chain of HTTP (CONNECT) proxy # servers, to gain high anonymity. Opens a listening socket at port 1337 (default) and then waits # for a client to connect. When a client connects World Hopper builds a chain of proxy servers. # Tested OK with: telnet, pop3 and irc. # # Usage: whopper.pl [options] [dest_host:dest_port] # Proxyfile should contain HTTP CONNECT proxies, each on a new line (host:port). # If you omit the destination, your connecting client needs to send the CONNECT method request. # # Whopper lives at: http://proximus.syn-ack.org/whopper/ # See http://proximus.syn-ack.org/whopper/changelog.txt for changes. # Load modules use strict; use IO::Socket; use Net::hostent; use Fcntl qw(:flock :seek); use POSIX qw(strftime); my (@proxies, $proxy_host, $proxy_port, $server, $client, $hostinfo, $server_port, $childpid, $proxy_connection, $line, $iterate, $last_proxy, $non_http_output, $logtraffic, $traffic_log, $clienthost, $destination); $SIG{CHLD} = 'IGNORE'; # Prevent childs from becoming zombies. # GLOBAL VARIABLES $server_port = 1337; # set server listening port globally (default is: 1337). $logtraffic = 0; # enable/disable logtraffic globally (disabled by default). $traffic_log = "traffic.log"; # set traffic logfile globally (default is: traffic.log). # Command line reading if (@ARGV[0] eq "" or @ARGV[0] eq "-H" or @ARGV[0] eq "-h" or @ARGV[0] eq "--help") { die "Usage: whopper.pl [options] [dest_host:dest_port] Options: -H, -h, --help\t\t\t\tThis help menu -P (--port) \t\t\tPort to listen for incoming connections -L (--logtraffic) \t\tLog traffic to \n"; } for ($iterate=1;$iterate; close(INF); @proxies = grep(!/^#/, @proxies); # filter out comments @proxies = grep(!/^$/, @proxies); # filter out empty lines print "[ " . (strftime "%c", localtime) . "\tPID: $$ ] Found " . scalar(@proxies) . " proxy servers in @ARGV[0].\n"; if ($logtraffic) { # Open traffic log for appending open(TRAFFIC_LOG,">>$traffic_log"); } # Open listening sockets for clients $server = IO::Socket::INET->new(LocalPort => $server_port, Type => SOCK_STREAM, Reuse => 1, Listen => SOMAXCONN ) or die "Fatal: listening on port $server_port failed with error: $@\n"; print "[ " . (strftime "%c", localtime) . "\tPID: $$ ] Listening for client connections on port $server_port.\n"; while ($client = $server->accept()) { # Incoming client connection $non_http_output = 0; # Set to default (no non-HTTP output yet) $client->autoflush(1); $hostinfo = gethostbyaddr($client->peeraddr); $clienthost = $hostinfo ? $hostinfo->name : $client->peerhost; printf "\n[ " . (strftime "%c", localtime) . "\tPID: $$ ] Client connection from $clienthost.\n"; # Connect to first proxy server chomp($proxies[0]); $last_proxy = $proxies[0]; ($proxy_host,$proxy_port) = split(/:/,$proxies[0]); $proxy_connection = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $proxy_host, PeerPort => $proxy_port) or die "Fatal: connecting to proxy server $proxy_host:$proxy_port failed with error: $!"; $proxy_connection->autoflush(1); print "[ " . (strftime "%c", localtime) . "\tPID: $$ ] Connected to $proxy_host:$proxy_port.\n"; if ($logtraffic) { # Write line to traffic log flock(TRAFFIC_LOG, LOCK_EX); seek(TRAFFIC_LOG,0,2); print TRAFFIC_LOG "\n\n[ Connection to: $proxy_host:$proxy_port ]\n"; flock(TRAFFIC_LOG, LOCK_UN); } # Chain proxy servers for ($iterate=1;$iterateCONNECT $proxies[$iterate] HTTP/1.0\r\n\r\n"; flock(TRAFFIC_LOG, LOCK_UN); } } # Check if we only need to proxy or if we have a destination too if ($destination ne "") { print $proxy_connection "CONNECT $destination HTTP/1.0\r\n\r\n"; if ($logtraffic) { # Write line to traffic log flock(TRAFFIC_LOG, LOCK_EX); seek(TRAFFIC_LOG,0,2); print TRAFFIC_LOG ">CONNECT $destination HTTP/1.0\r\n\r\n"; flock(TRAFFIC_LOG, LOCK_UN); } } die "Fatal: forking failed with error: $!" unless defined($childpid = fork()); # Data Input/Output if ($childpid) { $iterate = 1; while (defined ($line = <$proxy_connection>)) { if ($logtraffic) { # Write line to traffic log flock(TRAFFIC_LOG, LOCK_EX); seek(TRAFFIC_LOG,0,2); print TRAFFIC_LOG "<$line"; flock(TRAFFIC_LOG, LOCK_UN); } # Filter out HTTP code 200s and empty lines on CONNECT (for POP3 and such compatibility) if (($line =~ /200 Connection established/ or $line =~ /Proxy-agent:/ or $line =~ /Proxy-Agent:/ or $line eq "\r" or $line eq "\n" or $line eq "\r\n") and $non_http_output == 0) { if ($line =~ /200 Connection established/) { if ($iterate == scalar(@proxies)) { print "[ " . (strftime "%c", localtime) . "\tPID: $childpid ] Connected to destination.\n"; } else { print "[ " . (strftime "%c", localtime) . "\tPID: $childpid ] Connected to $proxies[$iterate].\n"; $iterate++; } } $line = ""; } else { $non_http_output = 1; } print $client $line; } kill("TERM", $childpid); print "[ " . (strftime "%c", localtime) . "\tPID: $childpid ] Disconnect from client: $clienthost.\n"; } else { while (defined ($line = <$client>)) { print $proxy_connection $line; if ($logtraffic) { # Write line to traffic log flock(TRAFFIC_LOG, LOCK_EX); seek(TRAFFIC_LOG,0,2); print TRAFFIC_LOG ">$line"; flock(TRAFFIC_LOG, LOCK_UN); } } } }