File: multi-client-multi-ver-coro-based-server.pl

package info (click to toggle)
libio-socket-socks-perl 0.65-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 288 kB
  • ctags: 71
  • sloc: perl: 2,894; makefile: 14
file content (96 lines) | stat: -rwxr-xr-x 2,062 bytes parent folder | download
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();