├── readme.txt └── tcp-proxy.pl /readme.txt: -------------------------------------------------------------------------------- 1 | This is a simple TCP proxy written in Perl that uses IO::Socket::INET and 2 | IO::Select abstractions to create an evented (asynchronous, on select() call 3 | based) server. 4 | 5 | It was written by Peteris Krumins (peter@catonmat.net). 6 | His blog is at http://www.catonmat.net -- good coders code, great reuse. 7 | 8 | Currently it's written only for demonstrative purposes for my article "Turn 9 | any Linux computer into SOCKS5 proxy in one command," which can be read here: 10 | 11 | http://www.catonmat.net/blog/linux-socks5-proxy 12 | 13 | ------------------------------------------------------------------------------ 14 | 15 | The ports are currently hard coded in the source code. The proxy tunnels all 16 | connections from 0.0.0.0:1080 to localhost:55555. 17 | 18 | The idea is that if you start a ssh socks5 proxy via 19 | 20 | ssh -N -D 55555 localhost 21 | 22 | Then no one can access this proxy, except people on localhost, which is of no 23 | good. 24 | 25 | This Perl program opens up this ssh socks5 proxy for everyone. 26 | 27 | Please see the article for more details: 28 | 29 | http://www.catonmat.net/blog/linux-socks5-proxy 30 | 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | Happy proxying! 35 | 36 | Sincerely, 37 | Peteris Krumins 38 | http://www.catonmat.net 39 | 40 | -------------------------------------------------------------------------------- /tcp-proxy.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Peteris Krumins (peter@catonmat.net) 4 | # http://www.catonmat.net -- good coders code, great reuse 5 | # 6 | # A simple TCP proxy that implements IP-based access control 7 | # Currently the ports are hard-coded, and it proxies 8 | # 0.0.0.0:1080 to localhost:55555. 9 | # 10 | # Written for the article "Turn any Linux computer into SOCKS5 11 | # proxy in one command," which can be read here: 12 | # 13 | # http://www.catonmat.net/blog/linux-socks5-proxy 14 | # 15 | 16 | use warnings; 17 | use strict; 18 | 19 | use IO::Socket; 20 | use IO::Select; 21 | 22 | my @allowed_ips = ('1.2.3.4', '5.6.7.8', '127.0.0.1', '192.168.1.2'); 23 | my $ioset = IO::Select->new; 24 | my %socket_map; 25 | 26 | my $debug = 1; 27 | 28 | sub new_conn { 29 | my ($host, $port) = @_; 30 | return IO::Socket::INET->new( 31 | PeerAddr => $host, 32 | PeerPort => $port 33 | ) || die "Unable to connect to $host:$port: $!"; 34 | } 35 | 36 | sub new_server { 37 | my ($host, $port) = @_; 38 | my $server = IO::Socket::INET->new( 39 | LocalAddr => $host, 40 | LocalPort => $port, 41 | ReuseAddr => 1, 42 | Listen => 100 43 | ) || die "Unable to listen on $host:$port: $!"; 44 | } 45 | 46 | sub new_connection { 47 | my $server = shift; 48 | my $client = $server->accept; 49 | my $client_ip = client_ip($client); 50 | 51 | unless (client_allowed($client)) { 52 | print "Connection from $client_ip denied.\n" if $debug; 53 | $client->close; 54 | return; 55 | } 56 | print "Connection from $client_ip accepted.\n" if $debug; 57 | 58 | my $remote = new_conn('localhost', 55555); 59 | $ioset->add($client); 60 | $ioset->add($remote); 61 | 62 | $socket_map{$client} = $remote; 63 | $socket_map{$remote} = $client; 64 | } 65 | 66 | sub close_connection { 67 | my $client = shift; 68 | my $client_ip = client_ip($client); 69 | my $remote = $socket_map{$client}; 70 | 71 | $ioset->remove($client); 72 | $ioset->remove($remote); 73 | 74 | delete $socket_map{$client}; 75 | delete $socket_map{$remote}; 76 | 77 | $client->close; 78 | $remote->close; 79 | 80 | print "Connection from $client_ip closed.\n" if $debug; 81 | } 82 | 83 | sub client_ip { 84 | my $client = shift; 85 | return inet_ntoa($client->sockaddr); 86 | } 87 | 88 | sub client_allowed { 89 | my $client = shift; 90 | my $client_ip = client_ip($client); 91 | return grep { $_ eq $client_ip } @allowed_ips; 92 | } 93 | 94 | print "Starting a server on 0.0.0.0:1080\n"; 95 | my $server = new_server('0.0.0.0', 1080); 96 | $ioset->add($server); 97 | 98 | while (1) { 99 | for my $socket ($ioset->can_read) { 100 | if ($socket == $server) { 101 | new_connection($server); 102 | } 103 | else { 104 | next unless exists $socket_map{$socket}; 105 | my $remote = $socket_map{$socket}; 106 | my $buffer; 107 | my $read = $socket->sysread($buffer, 4096); 108 | if ($read) { 109 | $remote->syswrite($buffer); 110 | } 111 | else { 112 | close_connection($socket); 113 | } 114 | } 115 | } 116 | } 117 | 118 | --------------------------------------------------------------------------------