Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions include/vcpkg/base/system.proxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <vcpkg/base/optional.h>

#include <string>

/*
* This is a helper class. It reads IE Proxy settings.
* For coherence of vcpkg design "using HTTP(S)_PROXY not WPAD", it is a user-friendly design to manually
* read the IE Proxy settings on Windows, and set the environment variables HTTP(S)_PROXY to the same
* when starting an external program (like CMake, which file(DOWNLOAD) only accept proxy settings
* by HTTP(S)_PROXY variables).
*/

namespace vcpkg::System
{
struct IEProxySetting
{
std::wstring server;
std::wstring bypass;
};

vcpkg::Optional<IEProxySetting> get_windows_ie_proxy_server();
}
28 changes: 26 additions & 2 deletions src/tls12-download.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <Windows.h>
#include <process.h>
#include <winhttp.h>

/*
* This program must be as small as possible, because it is committed in binary form to the
* vcpkg github repo to enable downloading the main vcpkg program on Windows 7, where TLS 1.2 is
Expand Down Expand Up @@ -208,8 +209,6 @@ int __stdcall entry()
abort_api_failure(std_out, L"GetEnvironmentVariableW");
}

write_message(std_out, L"\r\n");

const HANDLE out_file = CreateFileW(out_file_path, FILE_WRITE_DATA, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (out_file == INVALID_HANDLE_VALUE)
{
Expand All @@ -223,6 +222,31 @@ int __stdcall entry()
abort_api_failure(std_out, L"WinHttpOpen");
}

// If HTTPS_PROXY not found, try use IE Proxy. This works on Windows 10 20H2, so there's no need to determine
// the OS version >= 8.1.
if (access_type == WINHTTP_ACCESS_TYPE_NO_PROXY)
{
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy;
if (WinHttpGetIEProxyConfigForCurrentUser(&ieProxy) && ieProxy.lpszProxy != NULL)
{
WINHTTP_PROXY_INFO proxy;
proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy.lpszProxy = ieProxy.lpszProxy;
proxy.lpszProxyBypass = ieProxy.lpszProxyBypass;
WinHttpSetOption(session, WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));

write_message(std_out, L" (using IE proxy: ");
write_message(std_out, proxy.lpszProxy);
write_message(std_out, L")");

GlobalFree(ieProxy.lpszProxy);
GlobalFree(ieProxy.lpszProxyBypass);
GlobalFree(ieProxy.lpszAutoConfigUrl);
}
}

write_message(std_out, L"\r\n");

unsigned long secure_protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
if (!WinHttpSetOption(session, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(DWORD)))
{
Expand Down
47 changes: 14 additions & 33 deletions src/vcpkg/base/downloads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@
#include <vcpkg/base/system.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/system.process.h>
#include <vcpkg/base/system.proxy.h>
#include <vcpkg/base/util.h>

#if defined(_WIN32)
#include <VersionHelpers.h>
#endif

namespace vcpkg::Downloads
{
#if defined(_WIN32)
Expand Down Expand Up @@ -92,12 +89,8 @@ namespace vcpkg::Downloads
{
static ExpectedS<WinHttpSession> make()
{
auto h = WinHttpOpen(L"vcpkg/1.0",
IsWindows8Point1OrGreater() ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
: WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
auto h = WinHttpOpen(
L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!h) return Strings::concat("WinHttpOpen() failed: ", GetLastError());
WinHttpSession ret;
ret.m_hSession.reset(h);
Expand All @@ -116,31 +109,19 @@ namespace vcpkg::Downloads

WinHttpSetOption(ret.m_hSession.get(), WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
}
// Win7 IE Proxy fallback
else if (IsWindows7OrGreater() && !IsWindows8Point1OrGreater())
// IE Proxy fallback, this works on Windows 10
else
{
// First check if any proxy has been found automatically
WINHTTP_PROXY_INFO proxyInfo;
DWORD proxyInfoSize = sizeof(WINHTTP_PROXY_INFO);
auto noProxyFound =
!WinHttpQueryOption(ret.m_hSession.get(), WINHTTP_OPTION_PROXY, &proxyInfo, &proxyInfoSize) ||
proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY;

// If no proxy was found automatically, use IE's proxy settings, if any
if (noProxyFound)
// We do not use WPAD anymore
// Directly read IE Proxy setting
auto ieProxy = System::get_windows_ie_proxy_server();
if (ieProxy.has_value())
{
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy;
if (WinHttpGetIEProxyConfigForCurrentUser(&ieProxy) && ieProxy.lpszProxy != nullptr)
{
WINHTTP_PROXY_INFO proxy;
proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy.lpszProxy = ieProxy.lpszProxy;
proxy.lpszProxyBypass = ieProxy.lpszProxyBypass;
WinHttpSetOption(ret.m_hSession.get(), WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
GlobalFree(ieProxy.lpszProxy);
GlobalFree(ieProxy.lpszProxyBypass);
GlobalFree(ieProxy.lpszAutoConfigUrl);
}
WINHTTP_PROXY_INFO proxy;
proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy.lpszProxy = ieProxy.get()->server.data();
proxy.lpszProxyBypass = ieProxy.get()->bypass.data();
WinHttpSetOption(ret.m_hSession.get(), WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
}
}

Expand Down
25 changes: 25 additions & 0 deletions src/vcpkg/base/system.proxy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <vcpkg/base/system.proxy.h>

vcpkg::Optional<vcpkg::System::IEProxySetting> vcpkg::System::get_windows_ie_proxy_server()
{
#if defined(_WIN32)
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy;
if (WinHttpGetIEProxyConfigForCurrentUser(&ieProxy) && ieProxy.lpszProxy != nullptr)
{
vcpkg::System::IEProxySetting ieProxySetting;

ieProxySetting.server = ieProxy.lpszProxy;

if (ieProxy.lpszProxyBypass != nullptr) ieProxySetting.bypass = ieProxy.lpszProxyBypass;

GlobalFree(ieProxy.lpszProxy);
GlobalFree(ieProxy.lpszProxyBypass);
GlobalFree(ieProxy.lpszAutoConfigUrl);

return ieProxySetting;
}
return vcpkg::Optional<vcpkg::System::IEProxySetting>();
#else
return vcpkg::Optional<vcpkg::System::IEProxySetting>();
#endif
}
64 changes: 64 additions & 0 deletions src/vcpkg/build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/system.process.h>
#include <vcpkg/base/system.proxy.h>
#include <vcpkg/base/util.h>

#include <vcpkg/binarycaching.h>
Expand Down Expand Up @@ -396,6 +397,69 @@ namespace vcpkg::Build
if (auto p_val = val.get()) env.emplace(var, *p_val);
}

/*
* On Windows 10 (>= 8.1) it is a user-friendly way to automatically set HTTP_PROXY and HTTPS_PROXY
* environment variables by reading proxy settings via WinHttpGetIEProxyConfigForCurrentUser, preventing
* users set and unset these variables manually (which is not a decent way). It is common in China or any
* other regions that needs an proxy software (v2ray, shadowsocks, etc.), which sets the IE Proxy Settings,
* but not setting environment variables. This will make vcpkg easier to use, specially when use vcpkg in
* Visual Studio, we even cannot set HTTP(S)_PROXY in CLI, if we want to open or close Proxy we need to
* restart VS.
*/
auto ieProxy = System::get_windows_ie_proxy_server();
if (ieProxy.has_value())
{
std::string server = Strings::to_utf8(ieProxy.get()->server);

// Separate settings in IE Proxy Settings, which is rare?
// Python implementation:
// https://github.com/python/cpython/blob/7215d1ae25525c92b026166f9d5cac85fb1defe1/Lib/urllib/request.py#L2655
if (Strings::contains(server, "="))
{
auto proxy_settings = Strings::split(server, ';');
for (auto& s : proxy_settings)
{
auto kvp = Strings::split(s, '=');
if (kvp.size() == 2)
{
auto protocol = kvp[0];
auto address = kvp[1];
if (!Strings::contains(address, "://"))
{
address = Strings::concat(protocol, "://", address);
}
protocol = Strings::concat(Strings::ascii_to_uppercase(protocol.c_str()), "_PROXY");
env.emplace(protocol, address);
System::print2("-- Setting ", protocol, " environment variables to ", address, "\n");
}
}
}
// Specified http:// prefix
else if (Strings::starts_with(server, "http://"))
{
System::print2("-- Setting HTTP_PROXY environment variables to ", server, "\n");
env.emplace("HTTP_PROXY", server);
}
// Specified https:// prefix
else if (Strings::starts_with(server, "https://"))
{
System::print2("-- Setting HTTPS_PROXY environment variables to ", server, "\n");
env.emplace("HTTPS_PROXY", server);
}
// Most common case: "ip:port" style, apply to HTTP and HTTPS proxies.
// An HTTP(S)_PROXY means https requests go through that, it can be:
// http:// prefixed: the request go through an HTTP proxy without end-to-end security.
// https:// prefixed: the request go through an HTTPS proxy with end-to-end security.
// Nothing prefixed: don't know the default behaviour, seems considering HTTP proxy as default.
// We simply set "ip:port" to HTTP(S)_PROXY variables because it works on most common cases.
else
{
System::print2("-- Automatically setting HTTP(S)_PROXY environment variables to ", server, "\n");

env.emplace("HTTP_PROXY", server.c_str());
env.emplace("HTTPS_PROXY", server.c_str());
}
}
return {env};
});

Expand Down