CURL as a proxy, handle the HTTPS / CONNECT method

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

Requests to HTTP URLs work fine, but I'm stumbling over how to handle HTTPS requests and, more specifically, SSLv3 acknowledgment after the client sends a CONNECT proxy request.

Closest to all, I came to what looks like an answer:

  • CURLOPT_HTTPPROXYTUNNEL libcurl option for tunneling data between client and target server
  • stream_socket_enable_crypto () to possibly do things with encrypted data

I'm really not sure, so it’s with great pleasure a pointer to how to deal with this.

Here is an example 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("'\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([az]+://)?[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); ?> 
+6
source share
1 answer

If I understand correctly, you are writing an HTTP proxy server in PHP. The CURLOPT_HTTPPROXYTUNNEL option CURLOPT_HTTPPROXYTUNNEL 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 does not matter.

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

Using cURL, as in your example, will create multiple connections. To handle multiple connections, I used pcntl_fork , which expands a new process for each CONNECT request.

Here is a working example:

 <?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([az]+://)?[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
source

Source: https://habr.com/ru/post/946568/


All Articles