onConnect = function($connection) { $connection->stage = STAGE_INIT; }; $worker->onMessage = function($connection, $buffer) { switch($connection->stage) { case STAGE_INIT: $connection->send("\x05\x00"); $connection->stage = STAGE_ADDR; return; case STAGE_ADDR: $cmd = ord($buffer[1]); if($cmd != CMD_CONNECT) { echo "bad cmd $cmd\n"; $connection->close(); return; } $header_data = parse_socket5_header($buffer); if(!$header_data) { $connection->close(); return; } //$connection->send("\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10"); //$connection->stage = STAGE_DNS; $connection->stage = STAGE_CONNECTING; $remote_connection = new AsyncTcpConnection('tcp://'.$header_data[1].':'.$header_data[2]); $remote_connection->onConnect = function($remote_connection)use($connection) { $connection->state = STAGE_STREAM; $connection->send("\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10"); }; $remote_connection->onMessage = function($remote_connection, $buffer)use($connection) { $connection->send($buffer); }; $remote_connection->onClose = function($remote_connection)use($connection) { $connection->close(); }; $remote_connection->onError = function($remote_connection, $code, $type)use($connection) { if($connection->stage == STAGE_CONNECTING) { $connection->send("\x05\x03\x00\x01\x00\x00\x00\x00\x10\x10"); } $connection->close(); }; $connection->onMessage = function($connection, $data)use($remote_connection) { $remote_connection->send($data); }; $connection->onClose = function($connection)use($remote_connection) { $remote_connection->close(); }; $connection->onError = function($connection)use($remote_connection) { echo "connection err\n"; $connection->close(); $remote_connection->close(); }; } }; function parse_socket5_header($buffer) { $addr_type = ord($buffer[3]); switch($addr_type) { case ADDRTYPE_IPV4: if(strlen($buffer) < 10) { echo bin2hex($buffer)."\n"; echo "buffer too short\n"; return false; } $dest_addr = ord($buffer[4]).'.'.ord($buffer[5]).'.'.ord($buffer[6]).'.'.ord($buffer[7]); $port_data = unpack('n', substr($buffer, -2)); $dest_port = $port_data[1]; $header_length = 10; break; case ADDRTYPE_HOST: $addrlen = ord($buffer[4]); if(strlen($buffer) < $addrlen + 5) { echo $buffer."\n"; echo bin2hex($buffer)."\n"; 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; case ADDRTYPE_IPV6: if(strlen($buffer) < 22) { echo "buffer too short\n"; return false; } echo "todo ipv6\n"; return false; default: echo "unsupported addrtype $addr_type\n"; return false; } return array($addr_type, $dest_addr, $dest_port, $header_length); } // 如果不是在根目录启动,则运行runAll方法 if(!defined('GLOBAL_START')) { Worker::runAll(); }