From 6ca8ab6c0dd4e48679eca15ba55ea37155598e27 Mon Sep 17 00:00:00 2001 From: Josef Schugt Date: Thu, 21 Jul 2016 11:00:42 +0100 Subject: [PATCH] Errors are reported to the client instead of ignoring them. --- proxy.php | 172 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 33 deletions(-) diff --git a/proxy.php b/proxy.php index 438134c..03292ce 100644 --- a/proxy.php +++ b/proxy.php @@ -45,13 +45,82 @@ /* * * STOP EDITING HERE UNLESS YOU KNOW WHAT YOU ARE DOING * * */ +/* How curl can fail */ +$curl_errno_text = array( + 1 => "CURLE_UNSUPPORTED_PROTOCOL", + 2 => "CURLE_FAILED_INIT", + 3 => "CURLE_URL_MALFORMAT", + 4 => "CURLE_URL_MALFORMAT_USER", + 5 => "CURLE_COULDNT_RESOLVE_PROXY", + 6 => "CURLE_COULDNT_RESOLVE_HOST", + 7 => "CURLE_COULDNT_CONNECT", + 8 => "CURLE_FTP_WEIRD_SERVER_REPLY", + 9 => "CURLE_FTP_ACCESS_DENIED", + 10 => "CURLE_FTP_USER_PASSWORD_INCORRECT", + 11 => "CURLE_FTP_WEIRD_PASS_REPLY", + 12 => "CURLE_FTP_WEIRD_USER_REPLY", + 13 => "CURLE_FTP_WEIRD_PASV_REPLY", + 14 => "CURLE_FTP_WEIRD_227_FORMAT", + 15 => "CURLE_FTP_CANT_GET_HOST", + 16 => "CURLE_FTP_CANT_RECONNECT", + 17 => "CURLE_FTP_COULDNT_SET_BINARY", + 18 => "CURLE_FTP_PARTIAL_FILE or CURLE_PARTIAL_FILE", + 19 => "CURLE_FTP_COULDNT_RETR_FILE", + 20 => "CURLE_FTP_WRITE_ERROR", + 21 => "CURLE_FTP_QUOTE_ERROR", + 22 => "CURLE_HTTP_NOT_FOUND or CURLE_HTTP_RETURNED_ERROR", + 23 => "CURLE_WRITE_ERROR", + 24 => "CURLE_MALFORMAT_USER", + 25 => "CURLE_FTP_COULDNT_STOR_FILE", + 26 => "CURLE_READ_ERROR", + 27 => "CURLE_OUT_OF_MEMORY", + 28 => "CURLE_OPERATION_TIMEDOUT or CURLE_OPERATION_TIMEOUTED", + 29 => "CURLE_FTP_COULDNT_SET_ASCII", + 30 => "CURLE_FTP_PORT_FAILED", + 31 => "CURLE_FTP_COULDNT_USE_REST", + 32 => "CURLE_FTP_COULDNT_GET_SIZE", + 33 => "CURLE_HTTP_RANGE_ERROR", + 34 => "CURLE_HTTP_POST_ERROR", + 35 => "CURLE_SSL_CONNECT_ERROR", + 36 => "CURLE_BAD_DOWNLOAD_RESUME or CURLE_FTP_BAD_DOWNLOAD_RESUME", + 37 => "CURLE_FILE_COULDNT_READ_FILE", + 38 => "CURLE_LDAP_CANNOT_BIND", + 39 => "CURLE_LDAP_SEARCH_FAILED", + 40 => "CURLE_LIBRARY_NOT_FOUND", + 41 => "CURLE_FUNCTION_NOT_FOUND", + 42 => "CURLE_ABORTED_BY_CALLBACK", + 43 => "CURLE_BAD_FUNCTION_ARGUMENT", + 44 => "CURLE_BAD_CALLING_ORDER", + 45 => "CURLE_HTTP_PORT_FAILED", + 46 => "CURLE_BAD_PASSWORD_ENTERED", + 47 => "CURLE_TOO_MANY_REDIRECTS", + 48 => "CURLE_UNKNOWN_TELNET_OPTION", + 49 => "CURLE_TELNET_OPTION_SYNTAX", + 50 => "CURLE_OBSOLETE", + 51 => "CURLE_SSL_PEER_CERTIFICATE", + 52 => "CURLE_GOT_NOTHING", + 53 => "CURLE_SSL_ENGINE_NOTFOUND", + 54 => "CURLE_SSL_ENGINE_SETFAILED", + 55 => "CURLE_SEND_ERROR", + 56 => "CURLE_RECV_ERROR", + 57 => "CURLE_SHARE_IN_USE", + 58 => "CURLE_SSL_CERTPROBLEM", + 59 => "CURLE_SSL_CIPHER", + 60 => "CURLE_SSL_CACERT", + 61 => "CURLE_BAD_CONTENT_ENCODING", + 62 => "CURLE_LDAP_INVALID_URL", + 63 => "CURLE_FILESIZE_EXCEEDED", + 64 => "CURLE_FTP_SSL_FAILED", + 79 => "CURLE_SSH" +); + // identify request headers $request_headers = array( ); foreach ($_SERVER as $key => $value) { if (strpos($key, 'HTTP_') === 0 || strpos($key, 'CONTENT_') === 0) { $headername = str_replace('_', ' ', str_replace('HTTP_', '', $key)); $headername = str_replace(' ', '-', ucwords(strtolower($headername))); - if (!in_array($headername, array( 'Host', 'X-Proxy-Url' ))) { + if (!in_array($headername, array('Host', 'X-Proxy-Url'))) { $request_headers[] = "$headername: $value"; } } @@ -95,63 +164,100 @@ } // ignore requests for proxy :) -if (preg_match('!' . $_SERVER['SCRIPT_NAME'] . '!', $request_url) || empty($request_url) || count($p_request_url) == 1) { +if (preg_match('!' . $_SERVER['SCRIPT_NAME'] . '!', $request_url) || + empty($request_url) || count($p_request_url) == 1) { csajax_debug_message('Invalid request - make sure that csurl variable is not empty'); exit; } +$accessGranted = true; + // check against valid requests if (CSAJAX_FILTERS) { $parsed = $p_request_url; if (CSAJAX_FILTER_DOMAIN) { if (!in_array($parsed['host'], $valid_requests)) { - csajax_debug_message('Invalid domain - ' . $parsed['host'] . ' does not included in valid requests'); - exit; + $response_headers = "HTTP/1.1 403 Forbidden" . "\r\n" . "Status: 403 Forbidden"; + $response_content = "

403 Forbidden

" . + "

This proxy does not allow access to

" . + "" . + "

Reason

" . + "Whitelist does not contain hostname" . + ""; + ""; + $accessGranted = false; } } else { $check_url = isset($parsed['scheme']) ? $parsed['scheme'] . '://' : ''; - $check_url .= isset($parsed['user']) ? $parsed['user'] . ($parsed['pass'] ? ':' . $parsed['pass'] : '') . '@' : ''; + $check_url .= isset($parsed['user']) ? $parsed['user'] . + ($parsed['pass'] ? ':' . $parsed['pass'] : '') . '@' : ''; $check_url .= isset($parsed['host']) ? $parsed['host'] : ''; $check_url .= isset($parsed['port']) ? ':' . $parsed['port'] : ''; $check_url .= isset($parsed['path']) ? $parsed['path'] : ''; if (!in_array($check_url, $valid_requests)) { - csajax_debug_message('Invalid domain - ' . $request_url . ' does not included in valid requests'); - exit; + $response_headers = "HTTP/1.1 403 Forbidden" . "\r\n" . "Status: 403 Forbidden"; + $response_content = "

403 Forbidden

" . + "

This proxy does not allow access to

" . + "" . + "

Reason

" . + "Whitelist does not contain URL" . + ""; + $accessGranted = false; } } } +if ($accessGranted) { // append query string for GET requests -if ($request_method == 'GET' && count($request_params) > 0 && (!array_key_exists('query', $p_request_url) || empty($p_request_url['query']))) { - $request_url .= '?' . http_build_query($request_params); -} + if ($request_method == 'GET' && count($request_params) > 0 && + (!array_key_exists('query', $p_request_url) || empty($p_request_url['query']))) { + $request_url .= '?' . http_build_query($request_params); + } -// let the request begin -$ch = curl_init($request_url); -curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers); // (re-)send headers -curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return response -curl_setopt($ch, CURLOPT_HEADER, true); // enabled response headers -// add data for POST, PUT or DELETE requests -if ('POST' == $request_method) { - $post_data = is_array($request_params) ? http_build_query($request_params) : $request_params; - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); -} elseif ('PUT' == $request_method || 'DELETE' == $request_method) { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request_method); - curl_setopt($ch, CURLOPT_POSTFIELDS, $request_params); -} + // let the request begin + $ch = curl_init($request_url); + array_push($request_headers, 'Expect:'); // Don't use 100-Expect + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return response + curl_setopt($ch, CURLOPT_HEADER, true); // enabled response headers + // add data for POST, PUT or DELETE requests + if ('POST' == $request_method) { + $post_data = is_array($request_params) ? http_build_query($request_params) : $request_params; + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + $max = count($request_headers); + for ($i = 0; $i < $max; $i++) { + if (preg_match('/Content-Length:/', $request_headers[$i])) { + $request_headers[$i] = 'Content-Length: ' . strlen($post_data); // Set corrected Content-Length + } + } + } elseif ('PUT' == $request_method || 'DELETE' == $request_method) { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request_method); + curl_setopt($ch, CURLOPT_POSTFIELDS, $request_params); + } -// Set multiple options for curl according to configuration -if (is_array($curl_options) && 0 <= count($curl_options)) { - curl_setopt_array($ch, $curl_options); -} + // Set multiple options for curl according to configuration + if (is_array($curl_options) && 0 <= count($curl_options)) { + curl_setopt_array($ch, $curl_options); + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers); -// retrieve response (headers and content) -$response = curl_exec($ch); -curl_close($ch); + // retrieve response (headers and content) and errors + $response = curl_exec($ch); + $curl_errno = curl_errno($ch); + $curl_error = curl_error($ch); -// split response to header and content -list($response_headers, $response_content) = preg_split('/(\r\n){2}/', $response, 2); + curl_close($ch); + + if($curl_errno) { + // generate response content and headers if error are encountered + $response_content = "" . $curl_error . "\r\n" . $curl_errno_text[$curl_errno]; + $response_headers = "HTTP/1.1 503 Service Unavailable" . "\r\n" . "Status: 503 Service Unavailable"; + } else { + // split response to header and content + list($response_headers, $response_content) = preg_split('/(\r\n){2}/', $response, 2); + } +} // (re-)send the headers $response_headers = preg_split('/(\r\n){1}/', $response_headers);