mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-11 06:36:11 +03:00
228 lines
7.8 KiB
C++
228 lines
7.8 KiB
C++
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#include "net/proxy_resolution/proxy_resolver_winhttp.h"
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <winhttp.h>
|
||
|
|
||
|
#include "base/macros.h"
|
||
|
#include "base/strings/string_util.h"
|
||
|
#include "base/strings/utf_string_conversions.h"
|
||
|
#include "net/base/net_errors.h"
|
||
|
#include "net/proxy_resolution/proxy_info.h"
|
||
|
#include "net/proxy_resolution/proxy_resolver.h"
|
||
|
#include "url/gurl.h"
|
||
|
|
||
|
using base::TimeDelta;
|
||
|
using base::TimeTicks;
|
||
|
|
||
|
namespace net {
|
||
|
namespace {
|
||
|
|
||
|
static void FreeInfo(WINHTTP_PROXY_INFO* info) {
|
||
|
if (info->lpszProxy)
|
||
|
GlobalFree(info->lpszProxy);
|
||
|
if (info->lpszProxyBypass)
|
||
|
GlobalFree(info->lpszProxyBypass);
|
||
|
}
|
||
|
|
||
|
static Error WinHttpErrorToNetError(DWORD win_http_error) {
|
||
|
switch (win_http_error) {
|
||
|
case ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR:
|
||
|
case ERROR_WINHTTP_INTERNAL_ERROR:
|
||
|
case ERROR_WINHTTP_INCORRECT_HANDLE_TYPE:
|
||
|
return ERR_FAILED;
|
||
|
case ERROR_WINHTTP_LOGIN_FAILURE:
|
||
|
return ERR_PROXY_AUTH_UNSUPPORTED;
|
||
|
case ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT:
|
||
|
return ERR_PAC_SCRIPT_FAILED;
|
||
|
case ERROR_WINHTTP_INVALID_URL:
|
||
|
case ERROR_WINHTTP_OPERATION_CANCELLED:
|
||
|
case ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT:
|
||
|
case ERROR_WINHTTP_UNRECOGNIZED_SCHEME:
|
||
|
return ERR_PAC_STATUS_NOT_OK;
|
||
|
case ERROR_NOT_ENOUGH_MEMORY:
|
||
|
return ERR_INSUFFICIENT_RESOURCES;
|
||
|
default:
|
||
|
return ERR_FAILED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class ProxyResolverWinHttp : public ProxyResolver {
|
||
|
public:
|
||
|
ProxyResolverWinHttp(const scoped_refptr<PacFileData>& script_data);
|
||
|
~ProxyResolverWinHttp() override;
|
||
|
|
||
|
// ProxyResolver implementation:
|
||
|
int GetProxyForURL(const GURL& url,
|
||
|
ProxyInfo* results,
|
||
|
CompletionOnceCallback /*callback*/,
|
||
|
std::unique_ptr<Request>* /*request*/,
|
||
|
const NetLogWithSource& /*net_log*/) override;
|
||
|
|
||
|
private:
|
||
|
bool OpenWinHttpSession();
|
||
|
void CloseWinHttpSession();
|
||
|
|
||
|
// Proxy configuration is cached on the session handle.
|
||
|
HINTERNET session_handle_;
|
||
|
|
||
|
const GURL pac_url_;
|
||
|
|
||
|
DISALLOW_COPY_AND_ASSIGN(ProxyResolverWinHttp);
|
||
|
};
|
||
|
|
||
|
ProxyResolverWinHttp::ProxyResolverWinHttp(
|
||
|
const scoped_refptr<PacFileData>& script_data)
|
||
|
: session_handle_(NULL),
|
||
|
pac_url_(script_data->type() == PacFileData::TYPE_AUTO_DETECT
|
||
|
? GURL("http://wpad/wpad.dat")
|
||
|
: script_data->url()) {}
|
||
|
|
||
|
ProxyResolverWinHttp::~ProxyResolverWinHttp() {
|
||
|
CloseWinHttpSession();
|
||
|
}
|
||
|
|
||
|
int ProxyResolverWinHttp::GetProxyForURL(const GURL& query_url,
|
||
|
ProxyInfo* results,
|
||
|
CompletionOnceCallback /*callback*/,
|
||
|
std::unique_ptr<Request>* /*request*/,
|
||
|
const NetLogWithSource& /*net_log*/) {
|
||
|
// If we don't have a WinHTTP session, then create a new one.
|
||
|
if (!session_handle_ && !OpenWinHttpSession())
|
||
|
return ERR_FAILED;
|
||
|
|
||
|
// Windows' system resolver does not support WebSocket URLs in proxy.pac. This
|
||
|
// was tested in version 10.0.16299, and is also implied by the description of
|
||
|
// the ERROR_WINHTTP_UNRECOGNIZED_SCHEME error code in the Microsoft
|
||
|
// documentation at
|
||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/winhttp/nf-winhttp-winhttpgetproxyforurl.
|
||
|
// See https://crbug.com/862121.
|
||
|
GURL mutable_query_url = query_url;
|
||
|
if (query_url.SchemeIsWSOrWSS()) {
|
||
|
GURL::Replacements replacements;
|
||
|
replacements.SetSchemeStr(query_url.SchemeIsCryptographic() ? "https"
|
||
|
: "http");
|
||
|
mutable_query_url = query_url.ReplaceComponents(replacements);
|
||
|
}
|
||
|
|
||
|
// If we have been given an empty PAC url, then use auto-detection.
|
||
|
//
|
||
|
// NOTE: We just use DNS-based auto-detection here like Firefox. We do this
|
||
|
// to avoid WinHTTP's auto-detection code, which while more featureful (it
|
||
|
// supports DHCP based auto-detection) also appears to have issues.
|
||
|
//
|
||
|
WINHTTP_AUTOPROXY_OPTIONS options = {0};
|
||
|
options.fAutoLogonIfChallenged = FALSE;
|
||
|
options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
|
||
|
base::string16 pac_url16 = base::ASCIIToUTF16(pac_url_.spec());
|
||
|
options.lpszAutoConfigUrl = pac_url16.c_str();
|
||
|
|
||
|
WINHTTP_PROXY_INFO info = {0};
|
||
|
DCHECK(session_handle_);
|
||
|
|
||
|
// Per http://msdn.microsoft.com/en-us/library/aa383153(VS.85).aspx, it is
|
||
|
// necessary to first try resolving with fAutoLogonIfChallenged set to false.
|
||
|
// Otherwise, we fail over to trying it with a value of true. This way we
|
||
|
// get good performance in the case where WinHTTP uses an out-of-process
|
||
|
// resolver. This is important for Vista and Win2k3.
|
||
|
BOOL ok = WinHttpGetProxyForUrl(
|
||
|
session_handle_, base::ASCIIToUTF16(mutable_query_url.spec()).c_str(),
|
||
|
&options, &info);
|
||
|
if (!ok) {
|
||
|
if (ERROR_WINHTTP_LOGIN_FAILURE == GetLastError()) {
|
||
|
options.fAutoLogonIfChallenged = TRUE;
|
||
|
ok = WinHttpGetProxyForUrl(
|
||
|
session_handle_, base::ASCIIToUTF16(mutable_query_url.spec()).c_str(),
|
||
|
&options, &info);
|
||
|
}
|
||
|
if (!ok) {
|
||
|
DWORD error = GetLastError();
|
||
|
// If we got here because of RPC timeout during out of process PAC
|
||
|
// resolution, no further requests on this session are going to work.
|
||
|
if (ERROR_WINHTTP_TIMEOUT == error ||
|
||
|
ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR == error) {
|
||
|
CloseWinHttpSession();
|
||
|
}
|
||
|
return WinHttpErrorToNetError(error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int rv = OK;
|
||
|
|
||
|
switch (info.dwAccessType) {
|
||
|
case WINHTTP_ACCESS_TYPE_NO_PROXY:
|
||
|
results->UseDirect();
|
||
|
break;
|
||
|
case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
|
||
|
// According to MSDN:
|
||
|
//
|
||
|
// The proxy server list contains one or more of the following strings
|
||
|
// separated by semicolons or whitespace.
|
||
|
//
|
||
|
// ([<scheme>=][<scheme>"://"]<server>[":"<port>])
|
||
|
//
|
||
|
// Based on this description, ProxyInfo::UseNamedProxy() isn't
|
||
|
// going to handle all the variations (in particular <scheme>=).
|
||
|
//
|
||
|
// However in practice, it seems that WinHTTP is simply returning
|
||
|
// things like "foopy1:80;foopy2:80". It strips out the non-HTTP
|
||
|
// proxy types, and stops the list when PAC encounters a "DIRECT".
|
||
|
// So UseNamedProxy() should work OK.
|
||
|
results->UseNamedProxy(base::UTF16ToASCII(info.lpszProxy));
|
||
|
break;
|
||
|
default:
|
||
|
NOTREACHED();
|
||
|
rv = ERR_FAILED;
|
||
|
}
|
||
|
|
||
|
FreeInfo(&info);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
bool ProxyResolverWinHttp::OpenWinHttpSession() {
|
||
|
DCHECK(!session_handle_);
|
||
|
session_handle_ = WinHttpOpen(NULL,
|
||
|
WINHTTP_ACCESS_TYPE_NO_PROXY,
|
||
|
WINHTTP_NO_PROXY_NAME,
|
||
|
WINHTTP_NO_PROXY_BYPASS,
|
||
|
0);
|
||
|
if (!session_handle_)
|
||
|
return false;
|
||
|
|
||
|
// Since this session handle will never be used for WinHTTP connections,
|
||
|
// these timeouts don't really mean much individually. However, WinHTTP's
|
||
|
// out of process PAC resolution will use a combined (sum of all timeouts)
|
||
|
// value to wait for an RPC reply.
|
||
|
BOOL rv = WinHttpSetTimeouts(session_handle_, 10000, 10000, 5000, 5000);
|
||
|
DCHECK(rv);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void ProxyResolverWinHttp::CloseWinHttpSession() {
|
||
|
if (session_handle_) {
|
||
|
WinHttpCloseHandle(session_handle_);
|
||
|
session_handle_ = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
ProxyResolverFactoryWinHttp::ProxyResolverFactoryWinHttp()
|
||
|
: ProxyResolverFactory(false /*expects_pac_bytes*/) {
|
||
|
}
|
||
|
|
||
|
int ProxyResolverFactoryWinHttp::CreateProxyResolver(
|
||
|
const scoped_refptr<PacFileData>& pac_script,
|
||
|
std::unique_ptr<ProxyResolver>* resolver,
|
||
|
CompletionOnceCallback callback,
|
||
|
std::unique_ptr<Request>* request) {
|
||
|
resolver->reset(new ProxyResolverWinHttp(pac_script));
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
} // namespace net
|