<?php
class
Client
{
public
$connected
= true;
public
$data
=
''
;
public
$remote
= null;
public
$status
= 0;
}
class
Server
{
public
$clients
= [];
public
function
start()
{
$server
=
new
swoole_server(
'0.0.0.0'
, 8388, SWOOLE_BASE, SWOOLE_SOCK_TCP);
$server
->set([
'max_conn'
=> 1000,
'daemonize'
=> 1,
'reactor_num'
=> 1,
'worker_num'
=> 1,
'dispatch_mode'
=> 2,
'buffer_output_size'
=> 128 * 1024 * 1024,
'open_cpu_affinity'
=> 1,
'open_tcp_nodelay'
=> 1,
'log_file'
=>
'socks5_server.log'
,
]);
$server
->on(
'connect'
, [
$this
,
'onConnect'
]);
$server
->on(
'receive'
, [
$this
,
'onReceive'
]);
$server
->on(
'close'
, [
$this
,
'onClose'
]);
$server
->start();
}
public
function
onConnect(
$server
,
$fd
,
$fromID
)
{
$this
->clients[
$fd
] =
new
Client();
}
public
function
onReceive(
$server
,
$fd
,
$fromID
,
$data
)
{
(
$this
->clients[
$fd
])->data .=
$data
;
$this
->parse(
$server
,
$fd
);
}
public
function
onClose(
$server
,
$fd
,
$fromID
)
{
$client
=
$this
->clients[
$fd
];
$client
->connected = false;
}
private
function
parse(
$server
,
$fd
)
{
$client
=
$this
->clients[
$fd
];
switch
(
$client
->status) {
case
0: {
if
(
strlen
(
$client
->data) >= 2) {
$request
= unpack(
'c*'
,
substr
(
$client
->data, 0, 2));
if
(
$request
[1] !== 0x05) {
echo
'协议不正确:'
.
$request
[1], PHP_EOL;
$server
->close(
$fd
);
break
;
}
$nmethods
=
$request
[2];
if
(
strlen
(
$client
->data) >= 2 +
$nmethods
) {
$client
->data =
substr
(
$client
->data, 2 +
$nmethods
);
$server
->send(
$fd
,
"\x05\x00"
);
$client
->status = 1;
}
}
}
case
1: {
if
(
strlen
(
$client
->data) < 5)
break
;
$request
= unpack(
'c*'
,
$client
->data);
$aType
=
$request
[4];
if
(
$aType
=== 0x03) {
$domainLen
=
$request
[5];
if
(
strlen
(
$client
->data) < 5 +
$domainLen
+ 2) {
break
;
}
$domain
=
substr
(
$client
->data, 5,
$domainLen
);
$port
= unpack(
'n'
,
substr
(
$client
->data, 5 +
$domainLen
, 2))[1];
$client
->data =
substr
(
$client
->data, 5 +
$domainLen
+ 2);
}
else
if
(
$aType
=== 0x01) {
$domain
= long2ip(unpack(
'N'
,
substr
(
$client
->data, 4, 4))[1]);
$port
= unpack(
'n'
,
substr
(
$client
->data, 8, 2))[1];
$client
->data =
substr
(
$client
->data, 10);
}
else
{
echo
'不支持的atype:'
.
$aType
, PHP_EOL;
$server
->close(
$fd
);
break
;
}
$remote
=
new
swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
$remote
->on(
'connect'
,
function
(
$cli
)
use
(
$client
,
$server
,
$fd
,
$remote
) {
$server
->send(
$fd
,
"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00"
);
$client
->status = 2;
$client
->remote =
$remote
;
});
$remote
->on(
"error"
,
function
(swoole_client
$cli
)
use
(
$server
,
$fd
) {
echo
'connect to remote error.'
, PHP_EOL;
$server
->close(
$fd
);
});
$remote
->on(
'receive'
,
function
(
$cli
,
$data
)
use
(
$server
,
$fd
,
$client
) {
if
(!
$client
->connected) {
echo
'connection has been closed.'
, PHP_EOL;
return
;
}
$server
->send(
$fd
,
$data
);
});
$remote
->on(
'close'
,
function
(
$cli
)
use
(
$server
,
$fd
,
$client
) {
$client
->remote = null;
});
if
(
$aType
=== 0x03) {
swoole_async_dns_lookup(
$domain
,
function
(
$host
,
$ip
)
use
(
$remote
,
$port
,
$server
,
$fd
) {
if
(
empty
(
$ip
) ||
empty
(
$host
)) {
echo
"host:{$host}, ip:{$ip}\n"
;
$server
->close(
$fd
);
return
;
}
$remote
->connect(
$ip
,
$port
);
});
}
else
{
$remote
->connect(
$domain
,
$port
);
}
}
case
2: {
if
(
strlen
(
$client
->data) === 0) {
break
;
}
if
(
$client
->remote === null) {
echo
'remote connection has been closed.'
, PHP_EOL;
break
;
}
$sendByteCount
=
$client
->remote->send(
$client
->data);
if
(
$sendByteCount
=== false ||
$sendByteCount
<
strlen
(
$client
->data)) {
echo
'data length:'
,
strlen
(
$client
->data),
' send byte count:'
,
$sendByteCount
, PHP_EOL;
echo
$client
->data, PHP_EOL;
$server
->close(
$fd
);
}
$client
->data =
''
;
}
}
}
}
(
new
Server())->start();