8

此脚本侦听 IP/端口并打算充当 HTTP(S) 代理。

对 HTTP URL 的请求工作正常,但我在如何处理 HTTPS 请求,更具体地说是客户端向代理发送 CONNECT 请求后的 SSLv3 握手时遇到了困难。

我最接近的答案是:

  • CURLOPT_HTTPPROXYTUNNEL libcurl 选项用于在客户端和目标服务器之间传输数据
  • stream_socket_enable_crypto() 可能对加密数据进行“处理”

我真的不确定,因此将不胜感激有关如何处理此问题的指针。

这是一个示例请求: http: //pastebin.com/xkWhGyjW

<?php

class proxy {

    static $server;
    static $client;

    static function headers($str) { // Parses HTTP headers into an array
        $tmp = preg_split("'\r?\n'",$str);
        $output = array();
        $output[] = explode(' ',array_shift($tmp));
        $post = ($output[0][0] == 'POST' ? true : false);

            foreach($tmp as $i => $header) {
                if($post && !trim($header)) {
                    $output['POST'] = $tmp[$i+1];
                    break;
                }
                else {
                    $l = explode(':',$header,2);
                    $output[$l[0]] = $l[0].': '.ltrim($l[1]);
                }
            }
        return $output;
    }

    public function output($curl,$data) {
        socket_write(proxy::$client,$data);
        return strlen($data);
    }
}




$ip = "127.0.0.1";
$port = 50000;

proxy::$server = socket_create(AF_INET,SOCK_STREAM, SOL_TCP);
socket_set_option(proxy::$server,SOL_SOCKET,SO_REUSEADDR,1);
socket_bind(proxy::$server,$ip,50000);
socket_getsockname(proxy::$server,$ip,$port);
socket_listen(proxy::$server);

while(proxy::$client = socket_accept(proxy::$server)) {

    $input = socket_read(proxy::$client,4096);
    preg_match("'^([^\s]+)\s([^\s]+)\s([^\r\n]+)'ims",$input,$request);
    $headers = proxy::headers($input);

        echo $input,"\n\n";
            if(preg_match("'^CONNECT '",$input)) { // HTTPS
                // Tell the client we can deal with this
                socket_write(proxy::$client,"HTTP/1.1 200 Connection Established\r\n\r\n");
                // Client sends binary data here (SSLv3, TLS handshake, Client hello?)
                // socket_read(proxy::$client,4096);
                // ?
            }
            else { // HTTP

                        $input = preg_replace("'^([^\s]+)\s([a-z]+://)?[a-z0-9\.\-]+'","\\1 ",$input);
                        $curl = curl_init($request[2]);
                        curl_setopt($curl,CURLOPT_HEADER,1);
                        curl_setopt($curl,CURLOPT_HTTPHEADER,$headers);
                        curl_setopt($curl,CURLOPT_TIMEOUT,15);
                        curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
                        curl_setopt($curl,CURLOPT_NOPROGRESS,1);
                        curl_setopt($curl,CURLOPT_VERBOSE,1);
                        curl_setopt($curl,CURLOPT_AUTOREFERER,true);
                        curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);
                        curl_setopt($curl,CURLOPT_WRITEFUNCTION, array("proxy","output"));
                        curl_exec($curl);
                        curl_close($curl);
            }
    socket_close(proxy::$client);
}
socket_close(proxy::$server);


?>
4

1 回答 1

7

如果我理解正确,您正在用 PHP 编写 HTTP 代理服务器。当CURLOPT_HTTPPROXYTUNNEL您想使用 PHP cURL 库连接到代理服务器并使用CONNECT而不是GET. 在这种情况下,它不相关。

当您的代理服务器 (PROXY) 收到请求时,它应该使用和CONNECT连接到指定的主机 (ENDPOINT) 。建立连接后,通过发送通知客户端(CLIENT)。之后,您需要将 ENDPOINT 发送到 PROXY 的所有数据复制到 CLIENT,并将 CLIENT 发送到 PROXY 的所有数据复制到 ENDPOINT。socket_createsocket_connectHTTP/1.1 200 Connection Established

在您的示例中使用 cURL 将创建多个连接。为了处理多个连接,我使用pcntl_fork了 ,它在每个CONNECT请求上分叉一个新进程。

这是一个工作示例:

<?php

class proxy {

    static $server;
    static $client;

    static function headers($str) { // Parses HTTP headers into an array
        $tmp = preg_split("'\r?\n'",$str);
        $output = array();
        $output[] = explode(' ',array_shift($tmp));
        $post = ($output[0][0] == 'POST' ? true : false);

            foreach($tmp as $i => $header) {
                if($post && !trim($header)) {
                    $output['POST'] = $tmp[$i+1];
                    break;
                }
                else {
                    $l = explode(':',$header,2);
                    $output[$l[0]] = $l[0].': '.ltrim($l[1]);
                }
            }
        return $output;
    }

    public function output($curl,$data) {
        socket_write(proxy::$client,$data);
        return strlen($data);
    }
}




$ip = "127.0.0.1";
$port = 50000;

proxy::$server = socket_create(AF_INET,SOCK_STREAM, SOL_TCP);
socket_set_option(proxy::$server,SOL_SOCKET,SO_REUSEADDR,1);
socket_bind(proxy::$server,$ip,50000);
socket_getsockname(proxy::$server,$ip,$port);
socket_listen(proxy::$server);

while(proxy::$client = socket_accept(proxy::$server)) {

    $input = socket_read(proxy::$client,4096);
    preg_match("'^([^\s]+)\s([^\s]+)\s([^\r\n]+)'ims",$input,$request);
    $headers = proxy::headers($input);

        echo $input,"\n\n";
            if(preg_match("'^CONNECT ([^ ]+):(\d+) '",$input,$match)) { // HTTPS
                // fork to allow multiple connections
                if(pcntl_fork())
                    continue;

                $connect_host = $match[1];
                $connect_port = $match[2];

                // connect to endpoint
                $connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
                if(!socket_connect($connection, gethostbyname($connect_host), $connect_port))
                    exit;

                // let the client know that we're connected
                socket_write(proxy::$client,"HTTP/1.1 200 Connection Established\r\n\r\n");

                // proxy data
                $all_sockets = array($connection, proxy::$client);
                $null = null;
                while(($sockets = $all_sockets)
                      && false !== socket_select($sockets, $null, $null, 10)
                ) {
                    // can we read from the client without blocking?
                    if(in_array(proxy::$client, $sockets)) {
                        $buf = null;
                        socket_recv(proxy::$client, $buf, 8192, MSG_DONTWAIT);
                        echo "CLIENT => ENDPOINT (" . strlen($buf) . " bytes)\n";
                        if($buf === null)
                            exit;
                        socket_send($connection, $buf, strlen($buf), 0);
                    }

                    // can we read from the endpoint without blocking?
                    if(in_array($connection, $sockets)) {
                        $buf = null;
                        socket_recv($connection, $buf, 8192, MSG_DONTWAIT);
                        echo "ENDPOINT => CLIENT (" . strlen($buf) . " bytes)\n";
                        if($buf === null)
                            exit;
                        socket_send(proxy::$client, $buf, strlen($buf), 0);
                    }
                }

                exit;
            }
            else { // HTTP

                        $input = preg_replace("'^([^\s]+)\s([a-z]+://)?[a-z0-9\.\-]+'","\\1 ",$input);
                        $curl = curl_init($request[2]);
                        curl_setopt($curl,CURLOPT_HEADER,1);
                        curl_setopt($curl,CURLOPT_HTTPHEADER,$headers);
                        curl_setopt($curl,CURLOPT_TIMEOUT,15);
                        curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
                        curl_setopt($curl,CURLOPT_NOPROGRESS,1);
                        curl_setopt($curl,CURLOPT_VERBOSE,1);
                        curl_setopt($curl,CURLOPT_AUTOREFERER,true);
                        curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);
                        curl_setopt($curl,CURLOPT_WRITEFUNCTION, array("proxy","output"));
                        curl_exec($curl);
                        curl_close($curl);
            }
    socket_close(proxy::$client);
}
socket_close(proxy::$server);
于 2013-06-11T09:09:19.680 回答