2022-10-05 17:20:02 +00:00
|
|
|
<?php
|
2024-02-19 04:53:46 +00:00
|
|
|
// Caterpillar - The simple and parasitic web proxy with spam filter
|
2024-02-26 04:49:14 +00:00
|
|
|
// Namhyeon Go (Catswords Research) <abuse@catswords.net>
|
2024-02-19 04:46:43 +00:00
|
|
|
// https://github.com/gnh1201/caterpillar
|
2022-10-07 04:55:07 +00:00
|
|
|
// Created at: 2022-10-06
|
2024-02-25 17:51:08 +00:00
|
|
|
// Updated at: 2024-02-26
|
2022-10-05 17:20:02 +00:00
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
define("PHP_HTTPPROXY_VERSION", "0.2.0-dev");
|
2022-11-25 12:37:34 +00:00
|
|
|
|
2024-02-20 02:46:26 +00:00
|
|
|
if (strpos($_SERVER['HTTP_USER_AGENT'], "php-httpproxy/") !== 0) {
|
|
|
|
exit('<!DOCTYPE html><html><head><title>It works!</title><meta charset="utf-8"></head><body><h1>It works!</h1><p><a href="https://github.com/gnh1201/caterpillar">Download the client</a></p><hr><p>php-httpproxy/' . PHP_HTTPPROXY_VERSION . ' (Server; PHP ' . phpversion() . '; abuse@catswords.net)</p></body></html>');
|
2022-10-07 04:48:58 +00:00
|
|
|
}
|
|
|
|
|
2022-10-05 17:25:44 +00:00
|
|
|
ini_set("default_socket_timeout", 1); // must be. because of `feof()` works
|
2022-10-06 03:40:40 +00:00
|
|
|
ini_set("max_execution_time", 0);
|
2022-10-05 17:20:02 +00:00
|
|
|
|
2024-02-26 04:55:22 +00:00
|
|
|
function jsonrpc2_encode($params) {
|
|
|
|
$data = array(
|
|
|
|
"jsonrpc" => "2.0",
|
|
|
|
"params" => $params
|
|
|
|
);
|
|
|
|
return json_encode($data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function jsonrpc2_result_encode($result) {
|
|
|
|
$data = array(
|
|
|
|
"jsonrpc" => "2.0",
|
|
|
|
"result" => $result
|
|
|
|
);
|
|
|
|
return json_encode($data);
|
|
|
|
}
|
|
|
|
|
2024-02-26 04:49:14 +00:00
|
|
|
function jsonrpc2_error_encode($error) {
|
|
|
|
$data = array(
|
|
|
|
"jsonrpc" => "2.0",
|
|
|
|
"error" => $error
|
|
|
|
);
|
|
|
|
return json_encode($data);
|
|
|
|
}
|
|
|
|
|
2024-02-26 04:55:22 +00:00
|
|
|
|
2022-10-06 12:03:05 +00:00
|
|
|
function parse_headers($str) { // Parses HTTP headers into an array
|
2022-10-06 12:48:49 +00:00
|
|
|
// https://stackoverflow.com/questions/16934409/curl-as-proxy-deal-with-https-connect-method
|
|
|
|
// https://stackoverflow.com/questions/12433958/how-to-parse-response-headers-in-php
|
2022-10-06 12:03:05 +00:00
|
|
|
$headers = array();
|
2022-10-05 17:20:02 +00:00
|
|
|
|
2022-10-06 12:03:05 +00:00
|
|
|
$lines = preg_split("'\r?\n'", $str);
|
2022-10-06 02:05:59 +00:00
|
|
|
|
2022-10-06 12:03:05 +00:00
|
|
|
$first_line = array_shift($lines);
|
|
|
|
$headers['@method'] = explode(' ', $first_line);
|
2022-10-06 02:39:40 +00:00
|
|
|
|
2022-10-06 12:03:05 +00:00
|
|
|
foreach ($lines as $line) {
|
|
|
|
if (!preg_match('/^([^:]+):(.*)$/', $line, $out)) continue;
|
|
|
|
$headers[$out[1]] = trim($out[2]);
|
|
|
|
}
|
2022-10-05 17:20:02 +00:00
|
|
|
|
2022-10-06 12:03:05 +00:00
|
|
|
return $headers;
|
|
|
|
}
|
2022-10-05 17:20:02 +00:00
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
// stateless mode
|
|
|
|
function relay_request($params) {
|
2024-02-26 04:49:14 +00:00
|
|
|
$buffer_size = $params['buffer_size'];
|
2024-02-26 05:12:57 +00:00
|
|
|
$request_data = base64_decode($params['request_data']);
|
|
|
|
$request_header = parse_headers($request_data);
|
|
|
|
$request_length = intval($params['request_length']);
|
2024-02-26 04:49:14 +00:00
|
|
|
$client_address = $params['client_address'];
|
|
|
|
$client_port = intval($params['client_port']);
|
|
|
|
$client_encoding = $params['client_encoding'];
|
|
|
|
$remote_address = $params['remote_address'];
|
|
|
|
$remote_port = intval($params['remote_port']);
|
|
|
|
$scheme = $params['scheme'];
|
|
|
|
$url = $params['url'];
|
|
|
|
$datetime = $params['datetime']; // format: %Y-%m-%d %H:%M:%S.%f
|
|
|
|
|
|
|
|
if (in_array($scheme, array("https", "ssl", "tls"))) {
|
|
|
|
$remote_address = "tls://" . $remote_address;
|
2024-02-25 17:51:08 +00:00
|
|
|
}
|
2024-02-26 04:49:14 +00:00
|
|
|
|
2024-02-26 05:12:57 +00:00
|
|
|
switch ($request_header['@method'][0]) {
|
2024-02-25 17:51:08 +00:00
|
|
|
case "CONNECT":
|
2024-02-26 05:12:57 +00:00
|
|
|
echo sprintf("%s 200 Connection Established\r\n\r\n", $request_header['@method'][2]);
|
2024-02-25 17:51:08 +00:00
|
|
|
break;
|
2024-02-26 04:49:14 +00:00
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
default:
|
2024-02-26 04:49:14 +00:00
|
|
|
$fp = fsockopen($remote_address, $remote_port, $error_code, $error_message, 1);
|
2024-02-25 17:51:08 +00:00
|
|
|
if (!$fp) {
|
2024-02-26 04:49:14 +00:00
|
|
|
$error = array(
|
|
|
|
"status" => 400,
|
|
|
|
"code" => $error_code,
|
|
|
|
"message" => $error_message
|
|
|
|
);
|
|
|
|
echo "HTTP/1.1 400 Bad Request\r\n\r\n" . jsonrpc2_error_encode($error);
|
2024-02-25 17:51:08 +00:00
|
|
|
} else {
|
2024-02-26 05:12:57 +00:00
|
|
|
fwrite($fp, $request_data);
|
2024-02-26 04:49:14 +00:00
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
$buf = null;
|
|
|
|
while (!feof($fp) && $buf !== false) {
|
|
|
|
$buf = fgets($fp, $buffer_size);
|
|
|
|
echo $buf;
|
|
|
|
}
|
2024-02-26 04:49:14 +00:00
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
fclose($fp);
|
|
|
|
}
|
|
|
|
}
|
2022-11-25 10:32:17 +00:00
|
|
|
}
|
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
// stateful mode
|
|
|
|
function relay_connect($params) {
|
2024-02-26 04:49:14 +00:00
|
|
|
$buffer_size = $params['buffer_size'];
|
|
|
|
$client_address = $params['client_address'];
|
|
|
|
$client_port = intval($params['client_port']);
|
|
|
|
$client_encoding = $params['client_encoding'];
|
|
|
|
$remote_address = $params['remote_address'];
|
|
|
|
$remote_port = intval($params['remote_port']);
|
|
|
|
$scheme = $params['scheme'];
|
|
|
|
$url = $params['url'];
|
|
|
|
$datetime = $params['datetime']; // format: %Y-%m-%d %H:%M:%S.%f
|
|
|
|
|
|
|
|
$fp = fsockopen($client_address, $client_port, $error_code, $error_message, 1);
|
|
|
|
if (!$fp) {
|
|
|
|
$error = array(
|
|
|
|
"status" => 400,
|
|
|
|
"code" => $error_code,
|
|
|
|
"message" => $error_message
|
|
|
|
);
|
|
|
|
echo "HTTP/1.1 400 Bad Request\r\n\r\n" . jsonrpc2_error_encode($error);
|
|
|
|
} else {
|
2024-02-26 04:55:22 +00:00
|
|
|
fwrite($fp, jsonrpc2_result_encode(array("success" => true)));
|
|
|
|
|
|
|
|
$buf = null;
|
|
|
|
while (!feof($fp) && $buf !== false) {
|
|
|
|
$buf = fgets($fp, $buffer_size);
|
|
|
|
// todo
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose($fp);
|
2024-02-26 04:49:14 +00:00
|
|
|
}
|
2024-02-25 17:51:08 +00:00
|
|
|
}
|
2022-10-06 12:03:05 +00:00
|
|
|
|
2024-02-25 17:51:08 +00:00
|
|
|
// parse context
|
|
|
|
$context = json_decode(file_get_contents('php://input'), true);
|
|
|
|
|
|
|
|
// check is it jsonrpc
|
|
|
|
if ($context['jsonrpc'] == "2.0") {
|
|
|
|
$method = $context['method'];
|
|
|
|
switch ($method) {
|
|
|
|
case "relay_request":
|
|
|
|
relay_request($context['params']); // stateless mode
|
|
|
|
break;
|
|
|
|
case "relay_connect":
|
|
|
|
relay_connect($context['params']); // stateful mode
|
|
|
|
break;
|
|
|
|
}
|
2024-02-17 09:54:31 +00:00
|
|
|
}
|