#include "net/proxy_resolution/win/proxy_resolver_winhttp.h"
#include <windows.h>
#include <winhttp.h>
#include <memory>
#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::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_HTTP_RESPONSE_CODE_FAILURE;
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(const ProxyResolverWinHttp&) = delete;
ProxyResolverWinHttp& operator=(const ProxyResolverWinHttp&) = delete;
~ProxyResolverWinHttp() override;
int GetProxyForURL(const GURL& url,
const NetworkAnonymizationKey& network_anymization_key,
ProxyInfo* results,
CompletionOnceCallback ,
std::unique_ptr<Request>* ,
const NetLogWithSource& ) override;
private:
bool OpenWinHttpSession();
void CloseWinHttpSession();
HINTERNET session_handle_ = nullptr;
const GURL pac_url_;
};
ProxyResolverWinHttp::ProxyResolverWinHttp(
const scoped_refptr<PacFileData>& script_data)
: 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,
const NetworkAnonymizationKey& network_anonymization_key,
ProxyInfo* results,
CompletionOnceCallback ,
std::unique_ptr<Request>* ,
const NetLogWithSource& ) {
if (!session_handle_ && !OpenWinHttpSession())
return ERR_FAILED;
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);
}
WINHTTP_AUTOPROXY_OPTIONS options = {0};
options.fAutoLogonIfChallenged = FALSE;
options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
std::u16string pac_url16 = base::ASCIIToUTF16(pac_url_.spec());
options.lpszAutoConfigUrl = base::as_wcstr(pac_url16);
WINHTTP_PROXY_INFO info = {0};
DCHECK(session_handle_);
BOOL ok = WinHttpGetProxyForUrl(
session_handle_,
base::as_wcstr(base::ASCIIToUTF16(mutable_query_url.spec())), &options,
&info);
if (!ok) {
if (ERROR_WINHTTP_LOGIN_FAILURE == GetLastError()) {
options.fAutoLogonIfChallenged = TRUE;
ok = WinHttpGetProxyForUrl(
session_handle_,
base::as_wcstr(base::ASCIIToUTF16(mutable_query_url.spec())),
&options, &info);
}
if (!ok) {
DWORD error = GetLastError();
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:
results->UseNamedProxy(base::WideToUTF8(info.lpszProxy));
break;
default:
NOTREACHED();
}
FreeInfo(&info);
return rv;
}
bool ProxyResolverWinHttp::OpenWinHttpSession() {
DCHECK(!session_handle_);
session_handle_ =
WinHttpOpen(nullptr, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!session_handle_)
return false;
BOOL rv = WinHttpSetTimeouts(session_handle_, 10000, 10000, 5000, 5000);
DCHECK(rv);
return true;
}
void ProxyResolverWinHttp::CloseWinHttpSession() {
if (session_handle_) {
WinHttpCloseHandle(session_handle_);
session_handle_ = nullptr;
}
}
}
ProxyResolverFactoryWinHttp::ProxyResolverFactoryWinHttp()
: ProxyResolverFactory(false ) {
}
int ProxyResolverFactoryWinHttp::CreateProxyResolver(
const scoped_refptr<PacFileData>& pac_script,
std::unique_ptr<ProxyResolver>* resolver,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request) {
*resolver = std::make_unique<ProxyResolverWinHttp>(pac_script);
return OK;
}
}