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("'\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);
?>
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 useCONNECT
instead ofGET
. In this case it's not relevant.When your proxy server (PROXY) receives the
CONNECT
request, it should connect to the specified host (ENDPOINT) usingsocket_create
andsocket_connect
. Once the connection is established, let the client (CLIENT) know by sendingHTTP/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 everyCONNECT
request.Here's a working example: