rebuild
This commit is contained in:
parent
dc1a3390b5
commit
1572a141ff
15
config.php
15
config.php
@ -1,5 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$AUTH_ENABLED = 0;
|
$config = [
|
||||||
$USERNAME = 'changeme';
|
"auth" => [
|
||||||
$PASSWORD = '1234';
|
METHOD_NO_AUTH => true,
|
||||||
|
METHOD_USER_PASS => function ($request) {
|
||||||
|
return $request['user'] == 'user' && $request['pass'] == 'pass';
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"log_level" => LOG_DEBUG,
|
||||||
|
"tcp_port" => 1080,
|
||||||
|
"udp_port" => 0,
|
||||||
|
"wanIP" => '192.168.1.1',
|
||||||
|
];
|
||||||
|
591
start.php
591
start.php
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file is part of https://github.com/walkor/php-socks5.
|
* This file is part of https://github.com/walkor/php-socks5.
|
||||||
*
|
*
|
||||||
@ -11,14 +12,15 @@
|
|||||||
* @link http://www.workerman.net/
|
* @link http://www.workerman.net/
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
*/
|
*/
|
||||||
|
ini_set("memory_limit", "512M");
|
||||||
|
|
||||||
use \Workerman\Worker;
|
use \Workerman\Worker;
|
||||||
use \Workerman\WebServer;
|
use \Workerman\Timer;
|
||||||
use \Workerman\Connection\TcpConnection;
|
|
||||||
use \Workerman\Connection\AsyncTcpConnection;
|
use \Workerman\Connection\AsyncTcpConnection;
|
||||||
|
use \Workerman\Connection\AsyncUdpConnection;
|
||||||
|
|
||||||
// 自动加载类
|
// 自动加载类
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
require_once __DIR__ . '/config.php';
|
|
||||||
|
|
||||||
define('STAGE_INIT', 0);
|
define('STAGE_INIT', 0);
|
||||||
define('STAGE_AUTH', 1);
|
define('STAGE_AUTH', 1);
|
||||||
@ -33,6 +35,16 @@ define('CMD_CONNECT', 1);
|
|||||||
define('CMD_BIND', 2);
|
define('CMD_BIND', 2);
|
||||||
define('CMD_UDP_ASSOCIATE', 3);
|
define('CMD_UDP_ASSOCIATE', 3);
|
||||||
|
|
||||||
|
define('ERR_GENERAL', 1);
|
||||||
|
define('ERR_NOT_ALLOW', 2);
|
||||||
|
define('ERR_NETWORK', 3);
|
||||||
|
define('ERR_HOST', 4);
|
||||||
|
define('ERR_REFUSE', 5);
|
||||||
|
define('ERR_TTL_EXPIRED', 6);
|
||||||
|
define('ERR_UNKNOW_COMMAND', 7);
|
||||||
|
define('ERR_UNKNOW_ADDR_TYPE', 8);
|
||||||
|
define('ERR_UNKNOW', 9);
|
||||||
|
|
||||||
define('ADDRTYPE_IPV4', 1);
|
define('ADDRTYPE_IPV4', 1);
|
||||||
define('ADDRTYPE_IPV6', 4);
|
define('ADDRTYPE_IPV6', 4);
|
||||||
define('ADDRTYPE_HOST', 3);
|
define('ADDRTYPE_HOST', 3);
|
||||||
@ -41,132 +53,515 @@ define('METHOD_NO_AUTH', 0);
|
|||||||
define('METHOD_GSSAPI', 1);
|
define('METHOD_GSSAPI', 1);
|
||||||
define('METHOD_USER_PASS', 2);
|
define('METHOD_USER_PASS', 2);
|
||||||
|
|
||||||
$worker = new Worker('tcp://0.0.0.0:1080');
|
require_once __DIR__ . '/config.php';
|
||||||
$worker->onConnect = function($connection)
|
|
||||||
{
|
if (count($config['auth']) == 0) {
|
||||||
|
$config['auth'] = [METHOD_NO_AUTH => true];
|
||||||
|
}
|
||||||
|
|
||||||
|
$worker = new Worker('tcp://0.0.0.0:' . $config['tcp_port']);
|
||||||
|
$worker->onConnect = function ($connection) {
|
||||||
$connection->stage = STAGE_INIT;
|
$connection->stage = STAGE_INIT;
|
||||||
|
$connection->auth_type = NULL;
|
||||||
};
|
};
|
||||||
$worker->onMessage = function($connection, $buffer)
|
$worker->onMessage = function ($connection, $buffer) {
|
||||||
{
|
global $config;
|
||||||
global $AUTH_ENABLED, $USERNAME, $PASSWORD;
|
logger(LOG_DEBUG, "recv:" . bin2hex($buffer));
|
||||||
switch($connection->stage)
|
switch ($connection->stage) {
|
||||||
{
|
// 初始化环节
|
||||||
case STAGE_INIT:
|
case STAGE_INIT:
|
||||||
if ($AUTH_ENABLED)
|
$request = [];
|
||||||
{
|
// 当前偏移量
|
||||||
$methodslen = ord($buffer[1]);
|
$offset = 0;
|
||||||
$methods = array();
|
|
||||||
for ($i = 0; $i < strlen($buffer)-3; $i++)
|
// 检测buffer长度
|
||||||
{
|
if (strlen($buffer) < 2) {
|
||||||
array_push($methods, ord($buffer[$i+3]));
|
logger(LOG_ERR, "init socks5 failed. buffer too short.");
|
||||||
}
|
|
||||||
if (in_array(METHOD_USER_PASS, $methods))
|
|
||||||
{
|
|
||||||
$connection->send("\x05\x02");
|
|
||||||
$connection->stage = STAGE_AUTH;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
echo "client does not support user/pass auth\n";
|
|
||||||
$connection->send("\x05\xff");
|
$connection->send("\x05\xff");
|
||||||
$connection->stage = STAGE_DESTROYED;
|
$connection->stage = STAGE_DESTROYED;
|
||||||
$connection->close();
|
$connection->close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$connection->send("\x05\x00");
|
|
||||||
$connection->stage = STAGE_ADDR;
|
// Socks5 版本
|
||||||
return;
|
$request['ver'] = ord($buffer[$offset]);
|
||||||
case STAGE_AUTH:
|
$offset += 1;
|
||||||
$userlen = ord($buffer[1]);
|
|
||||||
$user = substr($buffer, 2, $userlen);
|
// 认证方法数量
|
||||||
$passlen = ord($buffer[2 + $userlen]);
|
$request['method_count'] = ord($buffer[$offset]);
|
||||||
$pass = substr($buffer, 3 + $userlen, $passlen);
|
$offset += 1;
|
||||||
if ($user == $USERNAME && $pass == $PASSWORD)
|
|
||||||
{
|
if (strlen($buffer) < 2 + $request['method_count']) {
|
||||||
$connection->send("\x05\x00");
|
logger(LOG_ERR, "init authentic failed. buffer too short.");
|
||||||
$connection->stage = STAGE_ADDR;
|
$connection->send("\x05\xff");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
echo "auth failed\n";
|
|
||||||
$connection->send("\x05\x01");
|
// 客户端支持的认证方法
|
||||||
$connection->stage = STAGE_DESTROYED;
|
$request['methods'] = [];
|
||||||
$connection->close();
|
for ($i = 1; $i <= $request['method_count']; $i++) {
|
||||||
|
$request['methods'][] = ord($buffer[$offset]);
|
||||||
|
$offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($config['auth'] as $k => $v) {
|
||||||
|
if (in_array($k, $request['methods'])) {
|
||||||
|
|
||||||
|
logger(LOG_INFO, "auth client via method $k");
|
||||||
|
logger(LOG_DEBUG, "send:" . bin2hex("\x05" . chr($k)));
|
||||||
|
|
||||||
|
$connection->send("\x05" . chr($k));
|
||||||
|
if ($k == 0) {
|
||||||
|
$connection->stage = STAGE_ADDR;
|
||||||
|
} else {
|
||||||
|
$connection->stage = STAGE_AUTH;
|
||||||
|
}
|
||||||
|
$connection->auth_type = $k;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($connection->stage != STAGE_AUTH) {
|
||||||
|
logger(LOG_ERR, "client has no matched auth methods");
|
||||||
|
logger(LOG_DEBUG, "send:" . bin2hex("\x05\xff"));
|
||||||
|
$connection->send("\x05\xff");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
// 认证环节
|
||||||
|
case STAGE_AUTH:
|
||||||
|
|
||||||
|
$request = [];
|
||||||
|
// 当前偏移量
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
if (strlen($buffer) < 5) {
|
||||||
|
logger(LOG_ERR, "auth failed. buffer too short.");
|
||||||
|
$connection->send("\x01\x01");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// var_dump($connection->auth_type);
|
||||||
|
switch ($connection->auth_type) {
|
||||||
|
case METHOD_USER_PASS:
|
||||||
|
// 子协议 协商 版本
|
||||||
|
$request['sub_ver'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
// 用户名
|
||||||
|
$request['user_len'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
if (strlen($buffer) < 2 + $request['user_len'] + 2) {
|
||||||
|
logger(LOG_ERR, "auth username failed. buffer too short.");
|
||||||
|
$connection->send("\x01\x01");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request['user'] = substr($buffer, $offset, $request['user_len']);
|
||||||
|
$offset += $request['user_len'];
|
||||||
|
|
||||||
|
// 密码
|
||||||
|
$request['pass_len'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
|
||||||
|
if (strlen($buffer) < 2 + $request['user_len'] + 1 + $request['pass_len']) {
|
||||||
|
logger(LOG_ERR, "auth password failed. buffer too short.");
|
||||||
|
$connection->send("\x01\x01");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request['pass'] = substr($buffer, $offset, $request['pass_len']);
|
||||||
|
$offset += $request['pass_len'];
|
||||||
|
|
||||||
|
if ($config["auth"][METHOD_USER_PASS]($request)) {
|
||||||
|
logger(LOG_INFO, "auth ok");
|
||||||
|
$connection->send("\x01\x00");
|
||||||
|
$connection->stage = STAGE_ADDR;
|
||||||
|
} else {
|
||||||
|
logger(LOG_INFO, "auth failed");
|
||||||
|
$connection->send("\x01\x01");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger(LOG_ERR, "unsupport auth type");
|
||||||
|
$connection->send("\x01\x01");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case STAGE_ADDR:
|
case STAGE_ADDR:
|
||||||
$cmd = ord($buffer[1]);
|
$request = [];
|
||||||
if($cmd != CMD_CONNECT)
|
// 当前偏移量
|
||||||
{
|
$offset = 0;
|
||||||
echo "bad cmd $cmd\n";
|
|
||||||
$connection->close();
|
if (strlen($buffer) < 4) {
|
||||||
|
logger(LOG_ERR, "connect init failed. buffer too short.");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_GENERAL;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$header_data = parse_socket5_header($buffer);
|
|
||||||
if(!$header_data)
|
// Socks 版本
|
||||||
{
|
$request['ver'] = ord($buffer[$offset]);
|
||||||
$connection->close();
|
$offset += 1;
|
||||||
|
|
||||||
|
// 命令
|
||||||
|
$request['command'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
// RSV
|
||||||
|
$request['rsv'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
// AddressType
|
||||||
|
$request['addr_type'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
// DestAddr
|
||||||
|
switch ($request['addr_type']) {
|
||||||
|
case ADDRTYPE_IPV4:
|
||||||
|
|
||||||
|
if (strlen($buffer) < 4 + 4) {
|
||||||
|
logger(LOG_ERR, "connect init failed.[ADDRTYPE_IPV4] buffer too short.");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_GENERAL;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp = substr($buffer, $offset, 4);
|
||||||
|
$ip = 0;
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
// var_dump(ord($tmp[$i]));
|
||||||
|
$ip += ord($tmp[$i]) * pow(256, 3 - $i);
|
||||||
|
}
|
||||||
|
$request['dest_addr'] = long2ip($ip);
|
||||||
|
$offset += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ADDRTYPE_HOST:
|
||||||
|
$request['host_len'] = ord($buffer[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
if (strlen($buffer) < 4 + 1 + $request['host_len']) {
|
||||||
|
logger(LOG_ERR, "connect init failed.[ADDRTYPE_HOST] buffer too short.");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_GENERAL;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request['dest_addr'] = substr($buffer, $offset, $request['host_len']);
|
||||||
|
$offset += $request['host_len'];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ADDRTYPE_IPV6:
|
||||||
|
default:
|
||||||
|
logger(LOG_ERR, "unsupport ipv6. [ADDRTYPE_IPV6].");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_UNKNOW_ADDR_TYPE;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestPort
|
||||||
|
|
||||||
|
if (strlen($buffer) < $offset + 2) {
|
||||||
|
logger(LOG_ERR, "connect init failed.[port] buffer too short.");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_GENERAL;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$connection->stage = STAGE_CONNECTING;
|
$portData = unpack("n", substr($buffer, $offset, 2));
|
||||||
$remote_connection = new AsyncTcpConnection('tcp://'.$header_data[1].':'.$header_data[2]);
|
$request['dest_port'] = $portData[1];
|
||||||
$remote_connection->onConnect = function($remote_connection)use($connection)
|
$offset += 2;
|
||||||
{
|
|
||||||
$connection->state = STAGE_STREAM;
|
// var_dump($request);
|
||||||
$connection->send("\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10");
|
switch ($request['command']) {
|
||||||
$connection->pipe($remote_connection);
|
case CMD_CONNECT:
|
||||||
$remote_connection->pipe($connection);
|
logger(LOG_DEBUG, 'tcp://' . $request['dest_addr'] . ':' . $request['dest_port']);
|
||||||
};
|
if ($request['addr_type'] == ADDRTYPE_HOST) {
|
||||||
$remote_connection->connect();
|
if (!filter_var($request['dest_addr'], FILTER_VALIDATE_IP)) {
|
||||||
|
logger(LOG_DEBUG, 'resolve DNS ' . $request['dest_addr']);
|
||||||
|
$connection->stage = STAGE_DNS;
|
||||||
|
$addr = dns_get_record($request['dest_addr'], DNS_A);
|
||||||
|
$addr = $addr ? array_pop($addr) : null;
|
||||||
|
logger(LOG_DEBUG, 'DNS resolved ' . $request['dest_addr'] . ' => ' . $addr['ip']);
|
||||||
|
} else {
|
||||||
|
$addr['ip'] = $request['dest_addr'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$addr['ip'] = $request['dest_addr'];
|
||||||
|
}
|
||||||
|
if ($addr) {
|
||||||
|
$connection->stage = STAGE_CONNECTING;
|
||||||
|
$remote_connection = new AsyncTcpConnection('tcp://' . $addr['ip'] . ':' . $request['dest_port']);
|
||||||
|
$remote_connection->onConnect = function ($remote_connection) use ($connection, $request) {
|
||||||
|
$connection->state = STAGE_STREAM;
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = 0;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = $request['addr_type'];
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 18512;
|
||||||
|
|
||||||
|
$connection->send(packResponse($response));
|
||||||
|
$connection->pipe($remote_connection);
|
||||||
|
$remote_connection->pipe($connection);
|
||||||
|
logger(LOG_DEBUG, 'tcp://' . $request['dest_addr'] . ':' . $request['dest_port'] . ' [OK]');
|
||||||
|
};
|
||||||
|
$remote_connection->connect();
|
||||||
|
} else {
|
||||||
|
logger(LOG_DEBUG, 'DNS resolve failed.');
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_HOST;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CMD_UDP_ASSOCIATE:
|
||||||
|
$connection->stage = STAGE_UDP_ASSOC;
|
||||||
|
var_dump("CMD_UDP_ASSOCIATE " . $config['udp_port']);
|
||||||
|
if ($config['udp_port'] == 0) {
|
||||||
|
$connection->udpWorker = new Worker('udp://0.0.0.0:0');
|
||||||
|
$connection->udpWorker->incId = 0;
|
||||||
|
$connection->udpWorker->onMessage = function ($udp_connection, $data) use ($connection) {
|
||||||
|
udpWorkerOnMessage($udp_connection, $data, $connection->udpWorker);
|
||||||
|
};
|
||||||
|
$connection->udpWorker->listen();
|
||||||
|
$listenInfo = stream_socket_get_name($connection->udpWorker->getMainSocket(), false);
|
||||||
|
list($bind_addr, $bind_port) = explode(":", $listenInfo);
|
||||||
|
} else {
|
||||||
|
$bind_port = $config['udp_port'];
|
||||||
|
}
|
||||||
|
$bind_addr = $config['wanIP'];
|
||||||
|
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = 0;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = $bind_addr;
|
||||||
|
$response['bind_port'] = $bind_port;
|
||||||
|
|
||||||
|
logger(LOG_DEBUG, 'send:' . bin2hex(packResponse($response)));
|
||||||
|
$connection->send(packResponse($response));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger(LOG_ERR, "connect init failed. unknow command.");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
|
||||||
|
$response = [];
|
||||||
|
$response['ver'] = 5;
|
||||||
|
$response['rep'] = ERR_UNKNOW_COMMAND;
|
||||||
|
$response['rsv'] = 0;
|
||||||
|
$response['addr_type'] = ADDRTYPE_IPV4;
|
||||||
|
$response['bind_addr'] = '0.0.0.0';
|
||||||
|
$response['bind_port'] = 0;
|
||||||
|
|
||||||
|
$connection->close(packResponse($response));
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
$worker->onClose = function ($connection) {
|
||||||
|
logger(LOG_INFO, "client closed.");
|
||||||
|
};
|
||||||
|
|
||||||
|
function udpWorkerOnMessage($udp_connection, $data, &$worker)
|
||||||
function parse_socket5_header($buffer)
|
|
||||||
{
|
{
|
||||||
$addr_type = ord($buffer[3]);
|
|
||||||
switch($addr_type)
|
logger(LOG_DEBUG, 'send:' . bin2hex($data));
|
||||||
{
|
$request = [];
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
$request['rsv'] = substr($data, $offset, 2);
|
||||||
|
$offset += 2;
|
||||||
|
|
||||||
|
$request['frag'] = ord($data[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
$request['addr_type'] = ord($data[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
|
switch ($request['addr_type']) {
|
||||||
case ADDRTYPE_IPV4:
|
case ADDRTYPE_IPV4:
|
||||||
if(strlen($buffer) < 10)
|
$tmp = substr($data, $offset, 4);
|
||||||
{
|
$ip = 0;
|
||||||
echo bin2hex($buffer)."\n";
|
for ($i = 0; $i < 4; $i++) {
|
||||||
echo "buffer too short\n";
|
$ip += ord($tmp[$i]) * pow(256, 3 - $i);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
$dest_addr = ord($buffer[4]).'.'.ord($buffer[5]).'.'.ord($buffer[6]).'.'.ord($buffer[7]);
|
$request['dest_addr'] = long2ip($ip);
|
||||||
$port_data = unpack('n', substr($buffer, -2));
|
$offset += 4;
|
||||||
$dest_port = $port_data[1];
|
|
||||||
$header_length = 10;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ADDRTYPE_HOST:
|
case ADDRTYPE_HOST:
|
||||||
$addrlen = ord($buffer[4]);
|
$request['host_len'] = ord($data[$offset]);
|
||||||
if(strlen($buffer) < $addrlen + 5)
|
$offset += 1;
|
||||||
{
|
|
||||||
echo $buffer."\n";
|
$request['dest_addr'] = substr($data, $offset, $request['host_len']);
|
||||||
echo bin2hex($buffer)."\n";
|
$offset += $request['host_len'];
|
||||||
echo "buffer too short\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$dest_addr = substr($buffer, 5, $addrlen);
|
|
||||||
$port_data = unpack('n', substr($buffer, -2));
|
|
||||||
$dest_port = $port_data[1];
|
|
||||||
$header_length = $addrlen + 7;
|
|
||||||
break;
|
break;
|
||||||
case ADDRTYPE_IPV6:
|
|
||||||
if(strlen($buffer) < 22)
|
case ADDRTYPE_IPV6:
|
||||||
{
|
if (strlen($data) < 22) {
|
||||||
echo "buffer too short\n";
|
echo "buffer too short\n";
|
||||||
return false;
|
$error = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
echo "todo ipv6\n";
|
echo "todo ipv6\n";
|
||||||
return false;
|
$error = true;
|
||||||
default:
|
default:
|
||||||
echo "unsupported addrtype $addr_type\n";
|
echo "unsupported addrtype {$request['addr_type']}\n";
|
||||||
return false;
|
$error = true;
|
||||||
}
|
}
|
||||||
return array($addr_type, $dest_addr, $dest_port, $header_length);
|
|
||||||
|
$portData = unpack("n", substr($data, $offset, 2));
|
||||||
|
$request['dest_port'] = $portData[1];
|
||||||
|
$offset += 2;
|
||||||
|
// var_dump($request['dest_addr']);
|
||||||
|
if ($request['addr_type'] == ADDRTYPE_HOST) {
|
||||||
|
logger(LOG_DEBUG, '解析DNS');
|
||||||
|
$addr = dns_get_record($request['dest_addr'], DNS_A);
|
||||||
|
$addr = $addr ? array_pop($addr) : null;
|
||||||
|
logger(LOG_DEBUG, 'DNS 解析完成' . $addr['ip']);
|
||||||
|
} else {
|
||||||
|
$addr['ip'] = $request['dest_addr'];
|
||||||
|
}
|
||||||
|
// var_dump($request);
|
||||||
|
|
||||||
|
// var_dump($udp_connection);
|
||||||
|
|
||||||
|
$remote_connection = new AsyncUdpConnection('udp://' . $addr['ip'] . ':' . $request['dest_port']);
|
||||||
|
$remote_connection->id = $worker->incId++;
|
||||||
|
$remote_connection->udp_connection = $udp_connection;
|
||||||
|
$remote_connection->onConnect = function ($remote_connection) use ($data, $offset) {
|
||||||
|
$remote_connection->send(substr($data, $offset));
|
||||||
|
};
|
||||||
|
$remote_connection->onMessage = function ($remote_connection, $recv) use ($data, $offset, $udp_connection, $worker) {
|
||||||
|
$udp_connection->close(substr($data, 0, $offset) . $recv);
|
||||||
|
$remote_connection->close();
|
||||||
|
unset($worker->udpConnections[$remote_connection->id]);
|
||||||
|
};
|
||||||
|
$remote_connection->deadTime = time() + 3;
|
||||||
|
$remote_connection->connect();
|
||||||
|
$worker->udpConnections[$remote_connection->id] = $remote_connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果不是在根目录启动,则运行runAll方法
|
$udpWorker = new Worker('udp://0.0.0.0:1080');
|
||||||
if(!defined('GLOBAL_START'))
|
$udpWorker->incId = 0;
|
||||||
|
$udpWorker->onWorkerStart = function ($worker) {
|
||||||
|
$worker->udpConnections = [];
|
||||||
|
Timer::add(1, function () use ($worker) {
|
||||||
|
foreach ($worker->udpConnections as $id => $remote_connection) {
|
||||||
|
if ($remote_connection->deadTime < time()) {
|
||||||
|
$remote_connection->close();
|
||||||
|
$remote_connection->udp_connection->close();
|
||||||
|
unset($worker->udpConnections[$id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$udpWorker->onMessage = 'udpWorkerOnMessage';
|
||||||
|
|
||||||
|
function packResponse($response)
|
||||||
{
|
{
|
||||||
|
$data = "";
|
||||||
|
$data .= chr($response['ver']);
|
||||||
|
$data .= chr($response['rep']);
|
||||||
|
$data .= chr($response['rsv']);
|
||||||
|
$data .= chr($response['addr_type']);
|
||||||
|
|
||||||
|
switch ($response['addr_type']) {
|
||||||
|
case ADDRTYPE_IPV4:
|
||||||
|
$tmp = explode('.', $response['bind_addr']);
|
||||||
|
foreach ($tmp as $block) {
|
||||||
|
$data .= chr($block);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ADDRTYPE_HOST:
|
||||||
|
$host_len = strlen($response['bind_addr']);
|
||||||
|
$data .= chr($host_len);
|
||||||
|
$data .= $response['bind_addr'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data .= pack("n", $response['bind_port']);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function logger($level, $str)
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
if ($config['log_level'] >= $level) {
|
||||||
|
echo $str . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果不是在根目录启动,则运行runAll方法
|
||||||
|
if (!defined('GLOBAL_START')) {
|
||||||
Worker::runAll();
|
Worker::runAll();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user