duandeng2265 2013-06-05 07:54
浏览 207
已采纳

cURL作为代理,处理HTTPS / CONNECT方法

This script listens on an IP/port and intends to act as a HTTP(S) proxy.

Requests to HTTP URLs work fine, but I'm stumbling on how to deal with HTTPS requests and more specifically, an SSLv3 handshake after the client sends a CONNECT request to the proxy.

The closest I've came to what looks like an answer is:

  • CURLOPT_HTTPPROXYTUNNEL libcurl option to tunnel data between client and target server
  • stream_socket_enable_crypto() to possibly "do stuff" with the encrypted data

I'm really not sure, so a pointer as to how to deal with this would be greatly appreciated.

Here is a sample request: http://pastebin.com/xkWhGyjW

<?php

class proxy {

    static $server;
    static $client;

    static function headers($str) { // Parses HTTP headers into an array
        $tmp = preg_split("'?
'",$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([^
]+)'ims",$input,$request);
    $headers = proxy::headers($input);

        echo $input,"

";
            if(preg_match("'^CONNECT '",$input)) { // HTTPS
                // Tell the client we can deal with this
                socket_write(proxy::$client,"HTTP/1.1 200 Connection Established

");
                // 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);


?>
  • 写回答

1条回答 默认 最新

  • douza6300 2013-06-11 09:09
    关注

    If I understand correctly, you're writing a HTTP proxy server in PHP. The CURLOPT_HTTPPROXYTUNNEL option is used when you want to connect to a proxy server using the PHP cURL library and use CONNECT instead of GET. In this case it's not relevant.

    When your proxy server (PROXY) receives the CONNECT request, it should connect to the specified host (ENDPOINT) using socket_create and socket_connect. Once the connection is established, let the client (CLIENT) know by sending HTTP/1.1 200 Connection Established. After that, you'll want to copy all data that the ENDPOINT sends to PROXY to the CLIENT and all data that the CLIENT sends to PROXY to the ENDPOINT.

    Using cURL like in your example will create multiple connections. To handle multiple connections, I've used pcntl_fork, which forks a new process on every CONNECT request.

    Here's a working example:

    <?php
    
    class proxy {
    
        static $server;
        static $client;
    
        static function headers($str) { // Parses HTTP headers into an array
            $tmp = preg_split("'?
    '",$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([^
    ]+)'ims",$input,$request);
        $headers = proxy::headers($input);
    
            echo $input,"
    
    ";
                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
    
    ");
    
                    // 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)
    ";
                            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)
    ";
                            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);
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 有赏,i卡绘世画不出
  • ¥15 如何用stata画出文献中常见的安慰剂检验图
  • ¥15 c语言链表结构体数据插入
  • ¥40 使用MATLAB解答线性代数问题
  • ¥15 COCOS的问题COCOS的问题
  • ¥15 FPGA-SRIO初始化失败
  • ¥15 MapReduce实现倒排索引失败
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码