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:
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);
?>
To use a proxy with Curl, you must pass the required proxy address using the -x (or --proxy) command-line option and proxy credentials using the -U (or --proxy-user) command-line switch. Proxy credentials may also be passed in the proxy string and will be URL decoded by Curl.
Modern proxy servers can be used as gateways for requests that access both HTTP and HTTPS resources.
Proxies - Everything curl. A proxy is a machine or software that does something on behalf of you, the client. You can also see it as a middle man that sits between you and the server you want to work with, a middle man that you connect to instead of the actual remote 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 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("'\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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With