Here's an update which adds some basic support for proxying images, downloadable files, and CSS. Note that it does *NOT* filter all possible remote file accesses. A few examples I can think of off the top of my head...
No filtering supported in:
- Any JavaScript.
- Any inline CSS.
- Weird CSS stuff like -moz-binding.
- HTML5 video/audio elements.
- Archaic embed objects. Yes, that means Flash garbage like fucking Farmville and fucking Youtube.
This *will* lead to privacy leaks, so don't expect this script to keep you anonymous on the internets.
Other missing features include:
- All form interaction is unsupported.
- Cookies are totally unsupported.
- Lots of HTTP header features (including the previously-mentioned cookies). Things like Accept-*, cache stuff, and probably a lot of other headers which should be proxied.
Additionally, two requests are made to the server for each file proxied. This is slightly inefficient, to say the least. But it's not *too* bad, since the first request is an HTTP HEAD request, which only fetches the HTTP headers for the file in question, and none of the actual file data.
For sanity reasons, the script is broken into three different files.
include.php:
Code:
<?php
function get_var_GET($varname) {
if (!isset($_GET[$varname])) return null;
$varname = $_GET[$varname];
if (!get_magic_quotes_gpc()) $varname = addslashes($varname);
return $varname;
}
?>
proxy.php:
Code:
<?php
function proxy_file($url, $recurse = 0) {
if (++$recurse > 5) return FALSE;
$uri = parse_url($url);
$uri['proto'] = (
(isset($uri['proto']) && ($uri['proto'] == 'https')) ?
'ssl://' :
''
);
$uri['port'] = isset($uri['port']) ? $uri['port'] : 80;
$uri['path'] = isset($uri['path']) ? $uri['path'] : '/';
$uri['query'] = isset($uri['query']) ? ('?' . $uri['query']) : '';
$path = $uri['path'] . $uri['query'];
$auth = (
(isset($uri['user']) || isset($uri['pass'])) ?
('Authentication: Basic ' . base64_encode(@$uri['user'] . ':' . @$uri['pass']) . "\r\n") :
''
);
$handle = @fsockopen($uri['proto'] . $uri['host'], $uri['port']);
if (!$handle) return FALSE;
fputs($handle, "HEAD {$path} HTTP/1.1\r\nHost: {$uri['host']}\r\n{$auth}Connection: close\r\n\r\n");
$headers = array();
while (!feof($handle)) {
$line = trim(fgets($handle, 1024));
if (empty($line)) break;
$headers[] = $line;
}
fclose($handle);
$result = null;
array_shift($headers);
foreach ($headers as $header) {
list($key, $value) = explode(':', $header, 2);
$value = trim($value);
switch (strtolower(trim($key))) {
case 'location': $result = proxy_read(resolve_path($url, $value), $recurse);
break;
case 'content-type': $result = $value;
break;
}
if (!empty($result)) break;
}
if (empty($result)) $result = 'text/plain; charset=UTF-8';
$ret = file($url);
array_unshift($ret, $result);
return $ret;
}
function resolve_path($url, $rel_path) {
if (parse_url($rel_path) !== FALSE) {
return $rel_path;
}
$uri = parse_url($url);
$uri['proto'] = (isset($uri['proto']) ? $uri['proto'] : 'http://');
$uri['port'] = (isset($uri['port']) ? (':' . $uri['port']) : '');
$auth = (
(isset($uri['user']) || isset($uri['pass'])) ?
(urlencode(@$uri['user']) . ':' . urlencode(@$uri['pass']) . '@') :
''
);
$rel_path = str_replace('\\', '/', $rel_path);
if ($rel_path{0} == '/') {
return $uri['proto'] . '://' . $auth . $uri['host'] . $uri['port'] . $rel_path;
}
return $uri['proto'] . '://' . $auth . $uri['host'] . $uri['port'] . @$uri['path'] . '/' . $rel_path;
}
?>
index.php:
Code:
<?php
ini_set("display_errors", '1');
require_once('include.php');
require_once('proxy.php');
$url = get_var_GET('url');
if ((substr($url, 0, 7) == 'http://') ||
(substr($url, 0, 8) == 'https://')) {
$uri = parse_url($url);
$path = $uri['path'];
$host = (isset($uri['proto']) ? $uri['proto'] : 'http') . '://' .
$uri['host'] .
(isset($uri['port']) ? (':' . $uri['port']) : '');
$base = substr($url, 0, strrpos($url, '/'));
$lines = proxy_file($url);
$ctype = array_shift($lines);
header('Content-Type: ' . $ctype);
list($type) = explode(';', $ctype);
$type = strtolower(trim($type));
foreach ($lines as $line) {
switch ($type) {
case 'text/css':
$rep = 'stripslashes("$1index.php?url=" . urlencode((substr("$2", 0, 1) == \'/\') ? "$host$2" : ((strstr("$2", ":") !== FALSE) ? "$2" : "$base/$2")) . "$3")';
$line = preg_replace('/(\s*url\s*\(\s*["\'])([^"\']*)(["\'\s]*\))/ie', $rep, $line);
break;
case 'application/xml':
case 'application/xhtml+xml':
case 'text/html':
case 'text/xml':
$rep = 'stripslashes("$1index.php?url=" . urlencode((substr("$2", 0, 1) == \'/\') ? "$host$2" : ((strstr("$2", ":") !== FALSE) ? "$2" : "$base/$2")) . "$3")';
$line = preg_replace('/(<a\s[^<>]*href\s*=\s*["\']?)([\w:\/.\?&;=%#+\-]+)(["\'\s>])/ie', $rep, $line);
$line = preg_replace('/(<form\s[^<>]*action\s*=\s*["\']?)([\w:\/.\?&;=%#+\-]+)(["\'\s>])/ie', $rep, $line);
$line = preg_replace('/(<img\s[^<>]*src\s*=\s*["\']?)([\w:\/.\?&;=%#+\-]+)(["\'\s>])/ie', $rep, $line);
$line = preg_replace('/(<link\s[^<>]*href\s*=\s*["\']?)([\w:\/.\?&;=%#+\-]+)(["\'\s>])/ie', $rep, $line);
$line = preg_replace('/(<script\s[^<>]*src\s*=\s*["\']?)([\w:\/.\?&;=%#+\-]+)(["\'\s>])/ie', $rep, $line);
break;
}
echo $line;
}
}
else {
header('Content-Type: text/html; charset=UTF-8');
echo <<<HEREDOC
<!DOCTYPE html>
<html>
<head>
<title>The überproxy</title>
</head>
<body>
<form method="get" action="index.php">
<input name="url" type="text" value="http://">
<input type="submit" value="Go">
</form>
</body>
</html>
HEREDOC;
}
?>