$value) { if (substr($key, 0, 5) == "HTTP_") { $key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5))))); $result[$key] = $value; } else { $result[$key] = $value; } } return $result; } } define("PROXY_PREFIX", "http" . (isset($_SERVER['HTTPS']) ? "s" : "") . "://" . $_SERVER["SERVER_NAME"] . ($_SERVER["SERVER_PORT"] != 80 ? ":" . $_SERVER["SERVER_PORT"] : "") . $_SERVER["SCRIPT_NAME"] . "/"); //Makes an HTTP request via cURL, using request data that was passed directly to this script. function makeRequest($url) { //Tell cURL to make the request using the brower's user-agent if there is one, or a fallback user-agent otherwise. $user_agent = $_SERVER["HTTP_USER_AGENT"]; if (empty($user_agent)) { $user_agent = "Mozilla/5.0 (compatible; nrird.xyz/proxy)"; } $ch = curl_init(); curl_setopt($ch, CURLOPT_USERAGENT, $user_agent); //Proxy the browser's request headers. $browserRequestHeaders = getallheaders(); //(...but let cURL set some of these headers on its own.) //TODO: The unset()s below assume that browsers' request headers //will use casing (capitalizations) that appear within them. unset($browserRequestHeaders["Host"]); unset($browserRequestHeaders["Content-Length"]); //Throw away the browser's Accept-Encoding header if any; //let cURL make the request using gzip if possible. unset($browserRequestHeaders["Accept-Encoding"]); curl_setopt($ch, CURLOPT_ENCODING, ""); //Transform the associative array from getallheaders() into an //indexed array of header strings to be passed to cURL. $curlRequestHeaders = array(); foreach ($browserRequestHeaders as $name => $value) { $curlRequestHeaders[] = $name . ": " . $value; } curl_setopt($ch, CURLOPT_HTTPHEADER, $curlRequestHeaders); //Proxy any received GET/POST/PUT data. switch ($_SERVER["REQUEST_METHOD"]) { case "GET": $getData = array(); foreach ($_GET as $key => $value) { $getData[] = urlencode($key) . "=" . urlencode($value); } if (count($getData) > 0) { //Remove any GET data from the URL, and re-add what was read. //TODO: Is the code in this "GET" case necessary? //It reads, strips, then re-adds all GET data; this may be a no-op. $url = substr($url, 0, strrpos($url, "?")); $url .= "?" . implode("&", $getData); } break; case "POST": curl_setopt($ch, CURLOPT_POST, true); //For some reason, $HTTP_RAW_POST_DATA isn't working as documented at //http://php.net/manual/en/reserved.variables.httprawpostdata.php //but the php://input method works. This is likely to be flaky //across different server environments. //More info here: http://stackoverflow.com/questions/8899239/http-raw-post-data-not-being-populated-after-upgrade-to-php-5-3 curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents("php://input")); break; case "PUT": curl_setopt($ch, CURLOPT_PUT, true); curl_setopt($ch, CURLOPT_INFILE, fopen("php://input")); break; } //Other cURL options. curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt ($ch, CURLOPT_FAILONERROR, true); //Set the request URL. curl_setopt($ch, CURLOPT_URL, $url); //Make the request. $response = curl_exec($ch); $responseInfo = curl_getinfo($ch); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); curl_close($ch); //Setting CURLOPT_HEADER to true above forces the response headers and body //to be output together--separate them. $responseHeaders = substr($response, 0, $headerSize); $responseBody = substr($response, $headerSize); return array("headers" => $responseHeaders, "body" => $responseBody, "responseInfo" => $responseInfo); } //Converts relative URLs to absolute ones, given a base URL. //Modified version of code found at http://nashruddin.com/PHP_Script_for_Converting_Relative_to_Absolute_URL function rel2abs($rel, $base) { if (empty($rel)) $rel = "."; if (parse_url($rel, PHP_URL_SCHEME) != "" || strpos($rel, "//") === 0) return $rel; //Return if already an absolute URL if ($rel[0] == "#" || $rel[0] == "?") return $base.$rel; //Queries and anchors extract(parse_url($base)); //Parse base URL and convert to local variables: $scheme, $host, $path $path = isset($path) ? preg_replace('#/[^/]*$#', "", $path) : "/"; //Remove non-directory element from path if ($rel[0] == '/') $path = ""; //Destroy path if relative url points to root $port = isset($port) && $port != 80 ? ":" . $port : ""; $auth = ""; if (isset($user)) { $auth = $user; if (isset($pass)) { $auth .= ":" . $pass; } $auth .= "@"; } $abs = "$auth$host$path$port/$rel"; //Dirty absolute URL for ($n = 1; $n > 0; $abs = preg_replace(array("#(/\.?/)#", "#/(?!\.\.)[^/]+/\.\./#"), "/", $abs, -1, $n)) {} //Replace '//' or '/./' or '/foo/../' with '/' return $scheme . "://" . $abs; //Absolute URL is ready. } //Proxify contents of url() references in blocks of CSS text. function proxifyCSS($css, $baseURL) { return preg_replace_callback( '/url\((.*?)\)/i', function($matches) use ($baseURL) { $url = $matches[1]; //Remove any surrounding single or double quotes from the URL so it can be passed to rel2abs - the quotes are optional in CSS //Assume that if there is a leading quote then there should be a trailing quote, so just use trim() to remove them if (strpos($url, "'") === 0) { $url = trim($url, "'"); } if (strpos($url, "\"") === 0) { $url = trim($url, "\""); } if (stripos($url, "data:") === 0) return "url(" . $url . ")"; //The URL isn't an HTTP URL but is actual binary data. Don't proxify it. return "url(" . PROXY_PREFIX . rel2abs($url, $baseURL) . ")"; }, $css); } // Create log function recordLog($url) { $userip = $_SERVER['REMOTE_ADDR']; $rdate = date("d-m-Y", time()); $data = $rdate.','.$userip.','.$url.PHP_EOL; $logfile = 'logs/'.$userip.'_log.txt'; $fp = fopen($logfile, 'a'); fwrite($fp, $data); } $proxy_prefix = PROXY_PREFIX; $htmlcode = << Upload
Enter the full URL to proxify:
ENDHTML; $url = substr($_SERVER["REQUEST_URI"], strlen($_SERVER["SCRIPT_NAME"]) + 1); if (empty($url)) die($htmlcode); if (strpos($url, "//") === 0) $url = "http:" . $url; //Assume that any supplied URLs starting with // are HTTP URLs. if (!preg_match("@^.*://@", $url)) $url = "http://" . $url; //Assume that any supplied URLs without a scheme are HTTP URLs. // recordLog($url); $response = makeRequest($url); $rawResponseHeaders = $response["headers"]; $responseBody = $response["body"]; $responseInfo = $response["responseInfo"]; //cURL can make multiple requests internally (while following 302 redirects), and reports //headers for every request it makes. Only proxy the last set of received response headers, //corresponding to the final request made by cURL for any given call to makeRequest(). $responseHeaderBlocks = array_filter(explode("\r\n\r\n", $rawResponseHeaders)); $lastHeaderBlock = end($responseHeaderBlocks); $headerLines = explode("\r\n", $lastHeaderBlock); foreach ($headerLines as $header) { if (stripos($header, "Content-Length") === false && stripos($header, "Transfer-Encoding") === false) { header($header); } } $contentType = ""; if (isset($responseInfo["content_type"])) $contentType = $responseInfo["content_type"]; //This is presumably a web page, so attempt to proxify the DOM. if (stripos($contentType, "text/html") !== false) { //Attempt to normalize character encoding. $responseBody = mb_convert_encoding($responseBody, "HTML-ENTITIES", mb_detect_encoding($responseBody)); //Parse the DOM. $doc = new DomDocument(); @$doc->loadHTML($responseBody); $xpath = new DOMXPath($doc); //Rewrite forms so that their actions point back to the proxy. foreach($xpath->query('//form') as $form) { $method = $form->getAttribute("method"); $action = $form->getAttribute("action"); //If the form doesn't have an action, the action is the page itself. //Otherwise, change an existing action to an absolute version. $action = empty($action) ? $url : rel2abs($action, $url); //Rewrite the form action to point back at the proxy. $form->setAttribute("action", PROXY_PREFIX . $action); } //Profixy