2013-06-05 41 views
6

Tập lệnh này nghe trên IP/cổng và dự định hoạt động như một proxy HTTP (S).cURL làm proxy, xử lý phương thức HTTPS/CONNECT

Yêu cầu đối với URL HTTP hoạt động tốt, nhưng tôi đang lo ngại về cách xử lý các yêu cầu HTTPS và cụ thể hơn, bắt tay SSLv3 sau khi máy khách gửi yêu cầu CONNECT đến proxy.

Gần nhất tôi đã đến những gì trông giống như một câu trả lời là: libcurl tùy chọn

  • CURLOPT_HTTPPROXYTUNNEL để dữ liệu đường hầm giữa máy khách và máy chủ mục tiêu
  • stream_socket_enable_crypto() để có thể "làm công cụ" với dữ liệu được mã hóa

Tôi thực sự không chắc chắn, vì vậy một con trỏ về cách xử lý vấn đề này sẽ được đánh giá cao.

Đây là một yêu cầu mẫu: 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); 


?> 
+0

Bạn có thể đăng trên Pastebin mã của bạn cho các yêu cầu HTTP URL? –

+0

Tất nhiên ... Tôi đang sử dụng dòng lệnh cURL để kiểm tra. http://pastebin.com/xkWhGyjW – innvo

+0

Bạn đã thử các trang web khác ngoài google.com (theo pastebin của bạn) chưa? (xem tại đây http://superuser.com/questions/404116/cannot-connect-to-website-ssl-handshaking-fails) Và bạn đã thử kết nối mà không có proxy, tức là 'curl -L -i -v' https://www.google.com/ "'? Đầu ra là gì nếu bạn thêm '--trace tracetxt.tmp' vào cuộc gọi curl, khi sử dụng proxy và khi kết nối trực tiếp? Có lẽ sự khác biệt của các dấu vết có thể giúp đỡ? – stef77

Trả lời

4

Nếu tôi hiểu đúng, bạn đang viết một máy chủ proxy HTTP trong PHP. Tùy chọn CURLOPT_HTTPPROXYTUNNEL được sử dụng khi bạn muốn kết nối với máy chủ proxy bằng thư viện PHP cURL và sử dụng CONNECT thay vì GET. Trong trường hợp này, nó không liên quan.

Khi máy chủ proxy của bạn (PROXY) nhận yêu cầu CONNECT, nó phải kết nối với máy chủ được chỉ định (ENDPOINT) sử dụng socket_createsocket_connect. Khi kết nối được thiết lập, hãy để khách hàng (CLIENT) biết bằng cách gửi HTTP/1.1 200 Connection Established. Sau đó, bạn sẽ muốn sao chép tất cả dữ liệu mà ENDPOINT gửi đến PROXY đến CLIENT và tất cả dữ liệu mà CLIENT gửi tới PROXY đến ENDPOINT.

Sử dụng cURL như trong ví dụ của bạn sẽ tạo nhiều kết nối. Để xử lý nhiều kết nối, tôi đã sử dụng pcntl_fork, cấp một quy trình mới cho mọi yêu cầu CONNECT.

Dưới đây là một ví dụ làm việc:

<?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); 
+0

cảm ơn, tôi sẽ thử sau ngày hôm nay. Tôi đã ngã ba nhưng chỉ cần loại bỏ nó để đăng bài ở đây, vì trọng tâm chính là xử lý các kết nối HTTPS hoạt động như một proxy/và phương thức CONNECT. Chỉ cần một lưu ý, forking như thế sẽ để lại quá trình zombie, bằng cách sử dụng một cái gì đó như pcntl_signal (SIGCHLD, SIG_IGN) là hữu ích ... và ổ cắm máy khách/máy chủ cần phải được đóng bởi cha mẹ cũng như đứa trẻ. – innvo

+0

có vẻ như nó đang làm công việc cho tôi, làm tốt lắm thưa ngài. – innvo

+0

Yup, bạn sẽ phải cải thiện mã cho an toàn. Đó là một ví dụ nhanh chóng và bẩn về chức năng mong muốn. –