PHP利用sockets实现http的POST请求,用来执行shell命令

<?php
// 允许的 CORS 配置
$allowedOrigins = ["*"];
$allowedMethods = "POST, OPTIONS";
$allowedHeaders = "Content-Type";

// 创建 TCP Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    die("Socket creation failed: " . socket_strerror(socket_last_error()) . "\n");
}

// 绑定 IP 和端口
if (!socket_bind($socket, '0.0.0.0', 9502)) {
    die("Socket bind failed: " . socket_strerror(socket_last_error()) . "\n");
}

// 开始监听
if (!socket_listen($socket)) {
    die("Socket listen failed: " . socket_strerror(socket_last_error()) . "\n");
}

echo "HTTP server running on http://0.0.0.0:9502\n";

// 注册信号处理器,防止僵尸进程
pcntl_signal(SIGCHLD, function($signo) {
    while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
});

while (true) {
    // 接受客户端连接
    $client = socket_accept($socket);
    if ($client === false) {
        echo "Socket accept failed: " . socket_strerror(socket_last_error()) . "\n";
        continue;
    }

    // 创建子进程处理请求
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("Fork failed");
    } elseif ($pid) {
        // 父进程:关闭客户端连接并继续监听
        socket_close($client);
        continue;
    } else {
        // 子进程:处理请求
        handleRequest($client);
        exit(0); // 处理完成后退出子进程
    }
}

// 关闭 Socket(通常不会执行到这里)
socket_close($socket);

/**
 * 处理客户端请求
 */
function handleRequest($client) {
    global $allowedOrigins, $allowedMethods, $allowedHeaders;

    // 读取 HTTP 请求(最多读取 8KB)
    $request = socket_read($client, 8192);
    if ($request === false) {
        echo "Socket read failed: " . socket_strerror(socket_last_error()) . "\n";
        return;
    }

    // 解析 HTTP 请求方法
    $method = strtoupper(explode(' ', $request)[0]);

    // 处理 OPTIONS 预检请求
    if ($method === 'OPTIONS') {
        $response = "HTTP/1.1 204 No Content\r\n";
        $response .= "Access-Control-Allow-Origin: " . implode(", ", $allowedOrigins) . "\r\n";
        $response .= "Access-Control-Allow-Methods: $allowedMethods\r\n";
        $response .= "Access-Control-Allow-Headers: $allowedHeaders\r\n";
        $response .= "Connection: close\r\n\r\n";
        socket_write($client, $response);
        socket_close($client);
        return;
    }

    // 处理 POST 请求
    if ($method === 'POST') {
        // 解析 POST Body
        $body = '';
        $lines = explode("\r\n", $request);
        $bodyStart = false;
        foreach ($lines as $line) {
            if ($line === '') {
                $bodyStart = true;
                continue;
            }
            if ($bodyStart) {
                $body .= $line . "\n";
            }
        }

        // 解析 JSON 数据
        $data = json_decode(trim($body), true);
        $cmd = sprintf("/root/selfTest/update_test.sh %s", $data['cmd']) ?? 'ls /root';

        // 使用 proc_open 执行命令(非阻塞读取输出)
        $descriptors = [
            0 => ["pipe", "r"], // 标准输入
            1 => ["pipe", "w"], // 标准输出
            2 => ["pipe", "w"], // 标准错误
        ];

        $process = proc_open($cmd, $descriptors, $pipes);
        if (!is_resource($process)) {
            $response = buildResponse(500, "Failed to execute command.");
            socket_write($client, $response);
            socket_close($client);
            return;
        }

        // 设置管道为非阻塞模式
        stream_set_blocking($pipes[1], false);
        stream_set_blocking($pipes[2], false);

        // 实时读取输出
        $output = '';
        $stderr = '';
        do {
            $read = [$pipes[1], $pipes[2]];
            $write = null;
            $except = null;
            if (stream_select($read, $write, $except, 0) > 0) {
                foreach ($read as $stream) {
                    if ($stream === $pipes[1]) {
                        $output .= fread($stream, 8192);
                    } elseif ($stream === $pipes[2]) {
                        $stderr .= fread($stream, 8192);
                    }
                }
            }
        } while (proc_get_status($process)['running']);

        // 关闭管道和进程
        fclose($pipes[0]);
        fclose($pipes[1]);
        fclose($pipes[2]);
        $exitCode = proc_close($process);

        // 构建响应
        $response = "HTTP/1.1 200 OK\r\n";
        $response .= "Access-Control-Allow-Origin: *\r\n";
        $response .= "Content-Type: application/json\r\n";
        $response .= "Connection: close\r\n\r\n";
        $response .= json_encode([
            'command' => $cmd,
            'exit_code' => $exitCode,
            'output' => $output,
            'error' => $stderr,
        ]);

        socket_write($client, $response);
        socket_close($client);
    } else {
        // 其他请求方法返回错误
        $response = buildResponse(405, "Only POST requests are supported.");
        socket_write($client, $response);
        socket_close($client);
    }
}

/**
 * 构建带 CORS 头的错误响应
 */
function buildResponse($statusCode, $message) {
    $statusText = [
        200 => 'OK',
        204 => 'No Content',
        400 => 'Bad Request',
        405 => 'Method Not Allowed',
        500 => 'Internal Server Error',
    ];
    $response = "HTTP/1.1 $statusCode {$statusText[$statusCode]}\r\n";
    $response .= "Access-Control-Allow-Origin: *\r\n";
    $response .= "Content-Type: text/plain\r\n";
    $response .= "Connection: close\r\n\r\n";
    $response .= $message;
    return $response;
}

 

posted @ 2025-04-03 15:42  你说夕阳很美  阅读(2)  评论(0)    收藏  举报