1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
#!/usr/bin/env perl
# This Socks Proxy Server allows 4 and 5 version on the same port
# May process many clients in parallel
use strict;
use lib '../lib';
use Coro::PatchSet 0.04;
BEGIN {
# make IO::Select Coro aware
package IO::Select;
use Coro::Select;
use IO::Select;
}
use IO::Socket::Socks qw(:constants :DEFAULT);
use Coro;
use Coro::Socket;
# make our server Coro aware
unshift @IO::Socket::Socks::ISA, 'Coro::Socket';
my $server = IO::Socket::Socks->new(SocksVersion => [4,5], ProxyAddr => 'localhost', ProxyPort => 1080, Listen => 10)
or die $SOCKS_ERROR;
warn "Server started at ", $server->sockhost, ":", $server->sockport;
$server->blocking(0); # accept() shouldn't block main thread
my $server_selector = IO::Select->new($server);
while (1) {
$server_selector->can_read();
my $client = $server->accept() # just accept
or next; # without socks handshake
async_pool {
$client->ready() # and make handshake in separate thread
or return;
my ($cmd, $host, $port) = @{$client->command};
if ($cmd == CMD_CONNECT) {
my $sock = Coro::Socket->new(
PeerAddr => $host,
PeerPort => $port,
Timeout => 10
);
if ($sock) {
$client->command_reply(
$client->version == 4 ? REQUEST_GRANTED : REPLY_SUCCESS,
$sock->sockhost,
$sock->sockport
);
my $selector = IO::Select->new($client, $sock);
my $buf;
SELECT:
while (1) {
my @ready = $selector->can_read();
for my $s (@ready) {
last SELECT unless $s->sysread($buf, 1024);
if ($s == $client) {
$sock->syswrite($buf);
}
else {
$client->syswrite($buf);
}
}
}
$sock->close();
}
else {
$client->command_reply(
$client->version == 4 ? REQUEST_FAILED : REPLY_HOST_UNREACHABLE,
$host,
$port
);
}
}
else {
$client->command_reply(
$client->version == 4 ? REQUEST_FAILED : REPLY_CMD_NOT_SUPPORTED,
$host,
$port
);
}
$client->close();
};
}
$server->close();
|