diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/net/proxy_resolution | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz |
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/proxy_resolution')
23 files changed, 2565 insertions, 428 deletions
diff --git a/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc b/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc index 68fc89b5455..7a7ca7518ee 100644 --- a/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc +++ b/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc @@ -25,7 +25,6 @@ #include "net/base/proxy_delegate.h" #include "net/base/url_util.h" #include "net/log/net_log.h" -#include "net/log/net_log_capture_mode.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_util.h" #include "net/log/net_log_with_source.h" @@ -44,7 +43,7 @@ #elif defined(OS_IOS) #include "net/proxy_resolution/proxy_config_service_ios.h" #include "net/proxy_resolution/proxy_resolver_mac.h" -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) #include "net/proxy_resolution/proxy_config_service_mac.h" #include "net/proxy_resolution/proxy_resolver_mac.h" #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) @@ -60,7 +59,7 @@ namespace net { namespace { -#if defined(OS_WIN) || defined(OS_IOS) || defined(OS_MACOSX) || \ +#if defined(OS_WIN) || defined(OS_APPLE) || \ (defined(OS_LINUX) && !defined(OS_CHROMEOS)) constexpr net::NetworkTrafficAnnotationTag kSystemProxyConfigTrafficAnnotation = net::DefineNetworkTrafficAnnotation("proxy_config_system", R"( @@ -257,7 +256,7 @@ class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory { std::unique_ptr<ProxyResolverFactory> CreateProxyResolverFactory() override { #if defined(OS_WIN) return std::make_unique<ProxyResolverFactoryWinHttp>(); -#elif defined(OS_MACOSX) +#elif defined(OS_APPLE) return std::make_unique<ProxyResolverFactoryMac>(); #else NOTREACHED(); @@ -266,7 +265,7 @@ class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory { } static bool IsSupported() { -#if defined(OS_WIN) || defined(OS_MACOSX) +#if defined(OS_WIN) || defined(OS_APPLE) return true; #else return false; @@ -1348,39 +1347,38 @@ void ConfiguredProxyResolutionService::ForceReloadProxyConfig() { ApplyProxyConfigIfAvailable(); } -std::unique_ptr<base::DictionaryValue> -ConfiguredProxyResolutionService::GetProxyNetLogValues(int info_sources) { - std::unique_ptr<base::DictionaryValue> net_info_dict( - new base::DictionaryValue()); +base::Value ConfiguredProxyResolutionService::GetProxyNetLogValues( + int info_sources) { + base::Value net_info_dict(base::Value::Type::DICTIONARY); if (info_sources & NET_INFO_PROXY_SETTINGS) { - std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); + base::Value dict(base::Value::Type::DICTIONARY); if (fetched_config_) - dict->SetKey("original", fetched_config_->value().ToValue()); + dict.SetKey("original", fetched_config_->value().ToValue()); if (config_) - dict->SetKey("effective", config_->value().ToValue()); + dict.SetKey("effective", config_->value().ToValue()); - net_info_dict->Set(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS), - std::move(dict)); + net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS), + std::move(dict)); } if (info_sources & NET_INFO_BAD_PROXIES) { - auto list = std::make_unique<base::ListValue>(); + base::Value list(base::Value::Type::LIST); - for (auto& it : proxy_retry_info_) { + for (const auto& it : proxy_retry_info_) { const std::string& proxy_uri = it.first; const ProxyRetryInfo& retry_info = it.second; - auto dict = std::make_unique<base::DictionaryValue>(); - dict->SetString("proxy_uri", proxy_uri); - dict->SetString("bad_until", - NetLog::TickCountToString(retry_info.bad_until)); + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetStringKey("proxy_uri", proxy_uri); + dict.SetStringKey("bad_until", + NetLog::TickCountToString(retry_info.bad_until)); - list->Append(std::move(dict)); + list.Append(std::move(dict)); } - net_info_dict->Set(NetInfoSourceToString(NET_INFO_BAD_PROXIES), - std::move(list)); + net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_BAD_PROXIES), + std::move(list)); } return net_info_dict; @@ -1402,7 +1400,7 @@ ConfiguredProxyResolutionService::CreateSystemProxyConfigService( #elif defined(OS_IOS) return std::make_unique<ProxyConfigServiceIOS>( kSystemProxyConfigTrafficAnnotation); -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) return std::make_unique<ProxyConfigServiceMac>( main_task_runner, kSystemProxyConfigTrafficAnnotation); #elif defined(OS_CHROMEOS) diff --git a/chromium/net/proxy_resolution/configured_proxy_resolution_service.h b/chromium/net/proxy_resolution/configured_proxy_resolution_service.h index 768ca0f7530..d2ea119490b 100644 --- a/chromium/net/proxy_resolution/configured_proxy_resolution_service.h +++ b/chromium/net/proxy_resolution/configured_proxy_resolution_service.h @@ -171,8 +171,7 @@ class NET_EXPORT ConfiguredProxyResolutionService void ForceReloadProxyConfig(); // ProxyResolutionService - std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues( - int info_sources) override; + base::Value GetProxyNetLogValues(int info_sources) override; // ProxyResolutionService bool CastToConfiguredProxyResolutionService( @@ -363,9 +362,6 @@ class NET_EXPORT ConfiguredProxyResolutionService base::Optional<ProxyConfigWithAnnotation> fetched_config_; base::Optional<ProxyConfigWithAnnotation> config_; - // The time when the proxy configuration was last read from the system. - base::TimeTicks config_last_update_time_; - // Map of the known bad proxies and the information about the retry time. ProxyRetryInfoMap proxy_retry_info_; diff --git a/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc b/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc index f31ae9a7b22..4a388eb742f 100644 --- a/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc +++ b/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc @@ -83,7 +83,7 @@ void ConvertResponseToUTF16(const std::string& charset, // Guess the charset by looking at the BOM. base::StringPiece bytes_str(bytes); for (const auto& bom : kBomMappings) { - if (bytes_str.starts_with(bom.prefix)) { + if (base::StartsWith(bytes_str, bom.prefix)) { return ConvertResponseToUTF16( bom.charset, // Strip the BOM in the converted response. diff --git a/chromium/net/proxy_resolution/proxy_bypass_rules.cc b/chromium/net/proxy_resolution/proxy_bypass_rules.cc index 8c54fa92e38..1540016bed3 100644 --- a/chromium/net/proxy_resolution/proxy_bypass_rules.cc +++ b/chromium/net/proxy_resolution/proxy_bypass_rules.cc @@ -34,8 +34,8 @@ const char kBypassSimpleHostnames[] = "<local>"; bool IsLinkLocalIP(const GURL& url) { // Quick fail if definitely not link-local, to avoid doing unnecessary work in // common case. - if (!(url.host_piece().starts_with("169.254.") || - url.host_piece().starts_with("["))) { + if (!(base::StartsWith(url.host_piece(), "169.254.") || + base::StartsWith(url.host_piece(), "["))) { return false; } @@ -53,7 +53,7 @@ bool IsLinkLocalIP(const GURL& url) { // addresses. However for proxy resolving such URLs should bypass the use // of a PAC script, since the destination is local. bool IsIPv4MappedLoopback(const GURL& url) { - if (!url.host_piece().starts_with("[::ffff")) + if (!base::StartsWith(url.host_piece(), "[::ffff")) return false; IPAddress ip_address; diff --git a/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc b/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc index 2d422c7ca58..eeee2ded55d 100644 --- a/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc +++ b/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc @@ -433,7 +433,7 @@ class ProxyConfigServiceLinuxTest : public PlatformTest, void TearDown() override { // Delete the temporary KDE home directory. - base::DeleteFileRecursively(user_home_); + base::DeletePathRecursively(user_home_); PlatformTest::TearDown(); } diff --git a/chromium/net/proxy_resolution/proxy_info.cc b/chromium/net/proxy_resolution/proxy_info.cc index 0172e0edf98..2396fefa698 100644 --- a/chromium/net/proxy_resolution/proxy_info.cc +++ b/chromium/net/proxy_resolution/proxy_info.cc @@ -19,7 +19,6 @@ void ProxyInfo::Use(const ProxyInfo& other) { proxy_resolve_end_time_ = other.proxy_resolve_end_time_; proxy_list_ = other.proxy_list_; proxy_retry_info_ = other.proxy_retry_info_; - traffic_annotation_ = other.traffic_annotation_; did_bypass_proxy_ = other.did_bypass_proxy_; } @@ -57,10 +56,6 @@ void ProxyInfo::OverrideProxyList(const ProxyList& proxy_list) { proxy_list_ = proxy_list; } -void ProxyInfo::SetAlternativeProxy(const ProxyServer& proxy_server) { - alternative_proxy_ = proxy_server; -} - std::string ProxyInfo::ToPacString() const { return proxy_list_.ToPacString(); } @@ -82,9 +77,7 @@ void ProxyInfo::Reset() { proxy_resolve_start_time_ = base::TimeTicks(); proxy_resolve_end_time_ = base::TimeTicks(); proxy_list_.Clear(); - alternative_proxy_ = net::ProxyServer(); proxy_retry_info_.clear(); - traffic_annotation_.reset(); did_bypass_proxy_ = false; } diff --git a/chromium/net/proxy_resolution/proxy_info.h b/chromium/net/proxy_resolution/proxy_info.h index 76558417d35..69cae35f612 100644 --- a/chromium/net/proxy_resolution/proxy_info.h +++ b/chromium/net/proxy_resolution/proxy_info.h @@ -67,10 +67,6 @@ class NET_EXPORT ProxyInfo { // proxy configuration. void OverrideProxyList(const ProxyList& proxy_list); - // Sets the alternative service to try when connecting to the first valid - // proxy server, but does not otherwise reset the proxy configuration. - void SetAlternativeProxy(const ProxyServer& proxy_server); - // Returns true if this proxy info specifies a direct connection. bool is_direct() const { // We don't implicitly fallback to DIRECT unless it was added to the list. @@ -90,6 +86,21 @@ class NET_EXPORT ProxyInfo { return proxy_server().is_https(); } + // Returns true if the first proxy server is an HTTP compatible proxy. + bool is_http_like() const { + if (is_empty()) + return false; + return proxy_server().is_http_like(); + } + + // Returns true if the first proxy server is an HTTP compatible proxy over a + // secure connection. + bool is_secure_http_like() const { + if (is_empty()) + return false; + return proxy_server().is_secure_http_like(); + } + // Returns true if the first valid proxy server is an http proxy. bool is_http() const { if (is_empty()) @@ -146,9 +157,6 @@ class NET_EXPORT ProxyInfo { // Deletes any entry which doesn't have one of the specified proxy schemes. void RemoveProxiesWithoutScheme(int scheme_bit_field); - // Returns the alternative proxy, which may be invalid. - const ProxyServer& alternative_proxy() const { return alternative_proxy_; } - void set_proxy_resolve_start_time( const base::TimeTicks& proxy_resolve_start_time) { proxy_resolve_start_time_ = proxy_resolve_start_time; @@ -188,10 +196,6 @@ class NET_EXPORT ProxyInfo { // try. If proxy_list_ is empty, then there is nothing left to fall back to. ProxyList proxy_list_; - // An alternative to proxy_server() (in the sense of HTTP Alternative - // Services). - ProxyServer alternative_proxy_; - // List of proxies that have been tried already. ProxyRetryInfoMap proxy_retry_info_; diff --git a/chromium/net/proxy_resolution/proxy_resolution_service.h b/chromium/net/proxy_resolution/proxy_resolution_service.h index 4a49fc8caa7..93673f76126 100644 --- a/chromium/net/proxy_resolution/proxy_resolution_service.h +++ b/chromium/net/proxy_resolution/proxy_resolution_service.h @@ -18,10 +18,6 @@ #include "net/proxy_resolution/proxy_info.h" #include "url/gurl.h" -namespace base { -class DictionaryValue; -} // namespace base - namespace net { class ConfiguredProxyResolutionService; @@ -99,8 +95,7 @@ class NET_EXPORT ProxyResolutionService { // Returns proxy related debug information to be included in the NetLog. The // data should be appropriate for any capture mode. |info_sources| is a bit // field of NET_INFO_SOURCE. - virtual std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues( - int info_sources) = 0; + virtual base::Value GetProxyNetLogValues(int info_sources) = 0; // Returns true if |this| is an instance of ConfiguredProxyResolutionService // and assigns |this| to the out parameter. Otherwise returns false and sets diff --git a/chromium/net/proxy_resolution/proxy_server_unittest.cc b/chromium/net/proxy_resolution/proxy_server_unittest.cc deleted file mode 100644 index e0b9c91c660..00000000000 --- a/chromium/net/proxy_resolution/proxy_server_unittest.cc +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright (c) 2010 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/base/proxy_server.h" -#include "base/stl_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -namespace { - -// Test the creation of ProxyServer using ProxyServer::FromURI, which parses -// inputs of the form [<scheme>"://"]<host>[":"<port>]. Verify that each part -// was labelled correctly, and the accessors all give the right data. -TEST(ProxyServerTest, FromURI) { - const struct { - const char* const input_uri; - const char* const expected_uri; - ProxyServer::Scheme expected_scheme; - const char* const expected_host; - int expected_port; - const char* const expected_pac_string; - } tests[] = { - // HTTP proxy URIs: - {"foopy:10", // No scheme. - "foopy:10", - ProxyServer::SCHEME_HTTP, - "foopy", - 10, - "PROXY foopy:10"}, - {"http://foopy", // No port. - "foopy:80", - ProxyServer::SCHEME_HTTP, - "foopy", - 80, - "PROXY foopy:80"}, - {"http://foopy:10", - "foopy:10", - ProxyServer::SCHEME_HTTP, - "foopy", - 10, - "PROXY foopy:10"}, - - // IPv6 HTTP proxy URIs: - {"[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10", // No scheme. - "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10", - ProxyServer::SCHEME_HTTP, - "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", - 10, - "PROXY [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10"}, - {"http://[3ffe:2a00:100:7031::1]", // No port. - "[3ffe:2a00:100:7031::1]:80", - ProxyServer::SCHEME_HTTP, - "3ffe:2a00:100:7031::1", - 80, - "PROXY [3ffe:2a00:100:7031::1]:80"}, - {"http://[::192.9.5.5]", - "[::192.9.5.5]:80", - ProxyServer::SCHEME_HTTP, - "::192.9.5.5", - 80, - "PROXY [::192.9.5.5]:80"}, - {"http://[::FFFF:129.144.52.38]:80", - "[::FFFF:129.144.52.38]:80", - ProxyServer::SCHEME_HTTP, - "::FFFF:129.144.52.38", - 80, - "PROXY [::FFFF:129.144.52.38]:80"}, - - // SOCKS4 proxy URIs: - {"socks4://foopy", // No port. - "socks4://foopy:1080", - ProxyServer::SCHEME_SOCKS4, - "foopy", - 1080, - "SOCKS foopy:1080"}, - {"socks4://foopy:10", - "socks4://foopy:10", - ProxyServer::SCHEME_SOCKS4, - "foopy", - 10, - "SOCKS foopy:10"}, - - // SOCKS5 proxy URIs - {"socks5://foopy", // No port. - "socks5://foopy:1080", - ProxyServer::SCHEME_SOCKS5, - "foopy", - 1080, - "SOCKS5 foopy:1080"}, - {"socks5://foopy:10", - "socks5://foopy:10", - ProxyServer::SCHEME_SOCKS5, - "foopy", - 10, - "SOCKS5 foopy:10"}, - - // SOCKS proxy URIs (should default to SOCKS5) - {"socks://foopy", // No port. - "socks5://foopy:1080", - ProxyServer::SCHEME_SOCKS5, - "foopy", - 1080, - "SOCKS5 foopy:1080"}, - {"socks://foopy:10", - "socks5://foopy:10", - ProxyServer::SCHEME_SOCKS5, - "foopy", - 10, - "SOCKS5 foopy:10"}, - - // HTTPS proxy URIs: - {"https://foopy", // No port - "https://foopy:443", - ProxyServer::SCHEME_HTTPS, - "foopy", - 443, - "HTTPS foopy:443"}, - {"https://foopy:10", // Non-standard port - "https://foopy:10", - ProxyServer::SCHEME_HTTPS, - "foopy", - 10, - "HTTPS foopy:10"}, - {"https://1.2.3.4:10", // IP Address - "https://1.2.3.4:10", - ProxyServer::SCHEME_HTTPS, - "1.2.3.4", - 10, - "HTTPS 1.2.3.4:10"}, - }; - - for (size_t i = 0; i < base::size(tests); ++i) { - ProxyServer uri = - ProxyServer::FromURI(tests[i].input_uri, ProxyServer::SCHEME_HTTP); - EXPECT_TRUE(uri.is_valid()); - EXPECT_FALSE(uri.is_direct()); - EXPECT_EQ(tests[i].expected_uri, uri.ToURI()); - EXPECT_EQ(tests[i].expected_scheme, uri.scheme()); - EXPECT_EQ(tests[i].expected_host, uri.host_port_pair().host()); - EXPECT_EQ(tests[i].expected_port, uri.host_port_pair().port()); - EXPECT_EQ(tests[i].expected_pac_string, uri.ToPacString()); - } -} - -TEST(ProxyServerTest, DefaultConstructor) { - ProxyServer proxy_server; - EXPECT_FALSE(proxy_server.is_valid()); -} - -// Test parsing of the special URI form "direct://". Analagous to the "DIRECT" -// entry in a PAC result. -TEST(ProxyServerTest, Direct) { - ProxyServer uri = ProxyServer::FromURI("direct://", ProxyServer::SCHEME_HTTP); - EXPECT_TRUE(uri.is_valid()); - EXPECT_TRUE(uri.is_direct()); - EXPECT_EQ("direct://", uri.ToURI()); - EXPECT_EQ("DIRECT", uri.ToPacString()); -} - -// Test parsing some invalid inputs. -TEST(ProxyServerTest, Invalid) { - const char* const tests[] = { - "", - " ", - "dddf:", // not a valid port - "dddd:d", // not a valid port - "http://", // not a valid host/port. - "direct://xyz", // direct is not allowed a host/port. - "http:/", // ambiguous, but will fail because of bad port. - "http:", // ambiguous, but will fail because of bad port. - }; - - for (size_t i = 0; i < base::size(tests); ++i) { - ProxyServer uri = ProxyServer::FromURI(tests[i], ProxyServer::SCHEME_HTTP); - EXPECT_FALSE(uri.is_valid()); - EXPECT_FALSE(uri.is_direct()); - EXPECT_FALSE(uri.is_http()); - EXPECT_FALSE(uri.is_socks()); - } -} - -// Test that LWS (SP | HT) is disregarded from the ends. -TEST(ProxyServerTest, Whitespace) { - const char* const tests[] = { - " foopy:80", - "foopy:80 \t", - " \tfoopy:80 ", - }; - - for (size_t i = 0; i < base::size(tests); ++i) { - ProxyServer uri = ProxyServer::FromURI(tests[i], ProxyServer::SCHEME_HTTP); - EXPECT_EQ("foopy:80", uri.ToURI()); - } -} - -// Test parsing a ProxyServer from a PAC representation. -TEST(ProxyServerTest, FromPACString) { - const struct { - const char* const input_pac; - const char* const expected_uri; - } tests[] = { - { - "PROXY foopy:10", - "foopy:10", - }, - { - " PROXY foopy:10 ", - "foopy:10", - }, - { - "pRoXy foopy:10", - "foopy:10", - }, - { - "PROXY foopy", // No port. - "foopy:80", - }, - { - "socks foopy", - "socks4://foopy:1080", - }, - { - "socks4 foopy", - "socks4://foopy:1080", - }, - { - "socks5 foopy", - "socks5://foopy:1080", - }, - { - "socks5 foopy:11", - "socks5://foopy:11", - }, - { - " direct ", - "direct://", - }, - { - "https foopy", - "https://foopy:443", - }, - { - "https foopy:10", - "https://foopy:10", - }, - }; - - for (size_t i = 0; i < base::size(tests); ++i) { - ProxyServer uri = ProxyServer::FromPacString(tests[i].input_pac); - EXPECT_TRUE(uri.is_valid()); - EXPECT_EQ(tests[i].expected_uri, uri.ToURI()); - } -} - -// Test parsing a ProxyServer from an invalid PAC representation. -TEST(ProxyServerTest, FromPACStringInvalid) { - const char* const tests[] = { - "PROXY", // missing host/port. - "HTTPS", // missing host/port. - "SOCKS", // missing host/port. - "DIRECT foopy:10", // direct cannot have host/port. - }; - - for (size_t i = 0; i < base::size(tests); ++i) { - ProxyServer uri = ProxyServer::FromPacString(tests[i]); - EXPECT_FALSE(uri.is_valid()); - } -} - -TEST(ProxyServerTest, ComparatorAndEquality) { - const struct { - // Inputs. - ProxyServer server1; - ProxyServer server2; - - // Expectation. - // -1 means server1 is less than server2 - // 0 means server1 equals server2 - // 1 means server1 is greater than server2 - int expected_comparison; - } kTests[] = { - {// Equal. - ProxyServer::FromURI("foo:11", ProxyServer::SCHEME_HTTP), - ProxyServer::FromURI("http://foo:11", ProxyServer::SCHEME_HTTP), 0}, - {// Port is different. - ProxyServer::FromURI("foo:333", ProxyServer::SCHEME_HTTP), - ProxyServer::FromURI("foo:444", ProxyServer::SCHEME_HTTP), -1}, - {// Host is different. - ProxyServer::FromURI("foo:33", ProxyServer::SCHEME_HTTP), - ProxyServer::FromURI("bar:33", ProxyServer::SCHEME_HTTP), 1}, - {// Scheme is different. - ProxyServer::FromURI("socks4://foo:33", ProxyServer::SCHEME_HTTP), - ProxyServer::FromURI("http://foo:33", ProxyServer::SCHEME_HTTP), 1}, - {// Trusted is different. - ProxyServer(ProxyServer::SCHEME_HTTPS, HostPortPair("foo", 33), - false /* is_trusted_proxy */), - ProxyServer(ProxyServer::SCHEME_HTTPS, HostPortPair("foo", 33), - true /* is_trusted_proxy */), - -1}, - }; - - for (const auto& test : kTests) { - EXPECT_TRUE(test.server1.is_valid()); - EXPECT_TRUE(test.server2.is_valid()); - - switch (test.expected_comparison) { - case -1: - EXPECT_TRUE(test.server1 < test.server2); - EXPECT_FALSE(test.server2 < test.server1); - EXPECT_FALSE(test.server2 == test.server1); - EXPECT_FALSE(test.server1 == test.server2); - break; - case 0: - EXPECT_FALSE(test.server1 < test.server2); - EXPECT_FALSE(test.server2 < test.server1); - EXPECT_TRUE(test.server2 == test.server1); - EXPECT_TRUE(test.server1 == test.server2); - break; - case 1: - EXPECT_FALSE(test.server1 < test.server2); - EXPECT_TRUE(test.server2 < test.server1); - EXPECT_FALSE(test.server2 == test.server1); - EXPECT_FALSE(test.server1 == test.server2); - break; - default: - FAIL() << "Invalid expectation. Can be only -1, 0, 1"; - } - } -} - -} // namespace - -} // namespace net diff --git a/chromium/net/proxy_resolution/win/proxy_config_service_win.cc b/chromium/net/proxy_resolution/win/proxy_config_service_win.cc index 04dd08d5f57..5c7b72a216d 100644 --- a/chromium/net/proxy_resolution/win/proxy_config_service_win.cc +++ b/chromium/net/proxy_resolution/win/proxy_config_service_win.cc @@ -95,21 +95,19 @@ void ProxyConfigServiceWin::StartWatchingRegistryForChanges() { AddKeyToWatchList( HKEY_CURRENT_USER, - STRING16_LITERAL("Software\\Microsoft\\Windows\\CurrentVersion\\") - STRING16_LITERAL("Internet Settings")); + L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); AddKeyToWatchList( HKEY_LOCAL_MACHINE, - STRING16_LITERAL("Software\\Microsoft\\Windows\\CurrentVersion\\") - STRING16_LITERAL("Internet Settings")); + L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); AddKeyToWatchList(HKEY_LOCAL_MACHINE, - STRING16_LITERAL("SOFTWARE\\Policies\\Microsoft\\Windows\\") - STRING16_LITERAL("CurrentVersion\\Internet Settings")); + L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\" + L"Internet Settings"); } bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey, - const base::char16* subkey) { + const wchar_t* subkey) { std::unique_ptr<base::win::RegKey> key = std::make_unique<base::win::RegKey>(); if (key->Create(rootkey, subkey, KEY_NOTIFY) != ERROR_SUCCESS) diff --git a/chromium/net/proxy_resolution/win/proxy_config_service_win.h b/chromium/net/proxy_resolution/win/proxy_config_service_win.h index 937c2028c93..de544576add 100644 --- a/chromium/net/proxy_resolution/win/proxy_config_service_win.h +++ b/chromium/net/proxy_resolution/win/proxy_config_service_win.h @@ -70,7 +70,7 @@ class NET_EXPORT_PRIVATE ProxyConfigServiceWin // Creates a new key and appends it to |keys_to_watch_|. If the key fails to // be created, it is not appended to the list and we return false. - bool AddKeyToWatchList(HKEY rootkey, const base::char16* subkey); + bool AddKeyToWatchList(HKEY rootkey, const wchar_t* subkey); // This is called whenever one of the registry keys we are watching change. void OnObjectSignaled(base::win::RegKey* key); diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc index 707c427ba8f..742f1534346 100644 --- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc @@ -4,15 +4,167 @@ #include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h" +#include <utility> + +#include "net/base/net_errors.h" +#include "net/proxy_resolution/proxy_info.h" +#include "net/proxy_resolution/proxy_list.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolver.h" +#include "net/traffic_annotation/network_traffic_annotation.h" + namespace net { -WindowsSystemProxyResolutionRequest::WindowsSystemProxyResolutionRequest() = - default; -WindowsSystemProxyResolutionRequest::~WindowsSystemProxyResolutionRequest() = - default; +namespace { + +constexpr net::NetworkTrafficAnnotationTag kWindowsResolverTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("proxy_config_windows_resolver", R"( + semantics { + sender: "Proxy Config for Windows System Resolver" + description: + "Establishing a connection through a proxy server using system proxy " + "settings and Windows system proxy resolution code." + trigger: + "Whenever a network request is made when the system proxy settings " + "are used, the Windows system proxy resolver is enabled, and the " + "result indicates usage of a proxy server." + data: + "Proxy configuration." + destination: OTHER + destination_other: + "The proxy server specified in the configuration." + } + policy { + cookies_allowed: NO + setting: + "User cannot override system proxy settings, but can change them " + "through 'Advanced/System/Open proxy settings'." + policy_exception_justification: + "Using either of 'ProxyMode', 'ProxyServer', or 'ProxyPacUrl' " + "policies can set Chrome to use a specific proxy settings and avoid " + "system proxy." + })"); + +} // namespace + +WindowsSystemProxyResolutionRequest::WindowsSystemProxyResolutionRequest( + WindowsSystemProxyResolutionService* service, + const GURL& url, + const std::string& method, + ProxyInfo* results, + CompletionOnceCallback user_callback, + const NetLogWithSource& net_log, + scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver) + : windows_system_proxy_resolver_(windows_system_proxy_resolver), + service_(service), + user_callback_(std::move(user_callback)), + results_(results), + url_(url), + method_(method), + net_log_(net_log), + creation_time_(base::TimeTicks::Now()) { + DCHECK(!user_callback_.is_null()); + DCHECK(windows_system_proxy_resolver_); +} + +WindowsSystemProxyResolutionRequest::~WindowsSystemProxyResolutionRequest() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (service_) { + service_->RemovePendingRequest(this); + net_log_.AddEvent(NetLogEventType::CANCELLED); + + if (IsStarted()) + CancelResolveJob(); + + net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE); + } +} LoadState WindowsSystemProxyResolutionRequest::GetLoadState() const { - return LOAD_STATE_IDLE; + // TODO(https://crbug.com/1032820): Consider adding a LoadState for "We're + // waiting on system APIs to do their thing". + return LOAD_STATE_RESOLVING_PROXY_FOR_URL; +} + +int WindowsSystemProxyResolutionRequest::Start() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!was_completed()); + DCHECK(!IsStarted()); + + // Kicks off an asynchronous call that'll eventually call back into + // AsynchronousProxyResolutionComplete() with a result. + if (!windows_system_proxy_resolver_->GetProxyForUrl(this, url_.spec())) + return ERR_FAILED; + + // Asynchronous proxy resolution has begun. + return ERR_IO_PENDING; +} + +void WindowsSystemProxyResolutionRequest::CancelResolveJob() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(IsStarted()); + // The request may already be running in the resolver. + // TODO(https://crbug.com/1032820): Cancel callback instead of just ignoring + // it. + windows_system_proxy_resolver_->RemovePendingCallbackTarget(this); + DCHECK(!IsStarted()); +} + +bool WindowsSystemProxyResolutionRequest::IsStarted() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return windows_system_proxy_resolver_->HasPendingCallbackTarget(this); +} + +int WindowsSystemProxyResolutionRequest::UpdateResultsOnProxyResolutionComplete( + const ProxyList& proxy_list, + int net_error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!was_completed()); + + results_->UseProxyList(proxy_list); + + // Make sure IsStarted() returns false while DidFinishResolvingProxy() runs. + windows_system_proxy_resolver_->RemovePendingCallbackTarget(this); + + // Note that DidFinishResolvingProxy might modify |results_|. + const int updated_result = service_->DidFinishResolvingProxy( + url_, method_, results_, net_error, net_log_); + + // Make a note in the results which configuration was in use at the + // time of the resolve. + results_->set_proxy_resolve_start_time(creation_time_); + results_->set_proxy_resolve_end_time(base::TimeTicks::Now()); + results_->set_traffic_annotation( + MutableNetworkTrafficAnnotationTag(kWindowsResolverTrafficAnnotation)); + + return updated_result; +} + +int WindowsSystemProxyResolutionRequest::SynchronousProxyResolutionComplete( + int net_error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + ProxyList proxy_list; + const int updated_result = + UpdateResultsOnProxyResolutionComplete(proxy_list, net_error); + service_ = nullptr; + return updated_result; +} + +void WindowsSystemProxyResolutionRequest::AsynchronousProxyResolutionComplete( + const ProxyList& proxy_list, + int net_error, + int windows_error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(https://crbug.com/1032820): Log Windows error |windows_error|. + + net_error = UpdateResultsOnProxyResolutionComplete(proxy_list, net_error); + + CompletionOnceCallback callback = std::move(user_callback_); + + service_->RemovePendingRequest(this); + service_ = nullptr; + user_callback_.Reset(); + std::move(callback).Run(net_error); } } // namespace net diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h index 575dcffde76..e5af2b644de 100644 --- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h @@ -5,17 +5,40 @@ #ifndef NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLUTION_REQUEST_H_ #define NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLUTION_REQUEST_H_ +#include <memory> +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" +#include "base/time/time.h" +#include "net/base/completion_once_callback.h" +#include "net/base/net_export.h" +#include "net/base/network_isolation_key.h" +#include "net/log/net_log_with_source.h" #include "net/proxy_resolution/proxy_resolution_request.h" +#include "url/gurl.h" namespace net { +class ProxyInfo; +class ProxyList; +class WindowsSystemProxyResolutionService; +class WindowsSystemProxyResolver; + // This is the concrete implementation of ProxyResolutionRequest used by // WindowsSystemProxyResolutionService. Manages a single asynchronous proxy // resolution request. -class WindowsSystemProxyResolutionRequest final +class NET_EXPORT WindowsSystemProxyResolutionRequest : public ProxyResolutionRequest { public: - WindowsSystemProxyResolutionRequest(); + WindowsSystemProxyResolutionRequest( + WindowsSystemProxyResolutionService* service, + const GURL& url, + const std::string& method, + ProxyInfo* results, + const CompletionOnceCallback user_callback, + const NetLogWithSource& net_log, + scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver); WindowsSystemProxyResolutionRequest( const WindowsSystemProxyResolutionRequest&) = delete; @@ -24,7 +47,62 @@ class WindowsSystemProxyResolutionRequest final ~WindowsSystemProxyResolutionRequest() override; + // ProxyResolutionRequest LoadState GetLoadState() const override; + + // Starts the resolve proxy request. + int Start(); + + // Cancels the callback from the resolver for a previously started proxy + // resolution. + void CancelResolveJob(); + + bool IsStarted(); + + // Returns true if the request has been completed. + bool was_completed() const { return user_callback_.is_null(); } + + // Helper to call after ProxyResolver completion (both synchronous and + // asynchronous). Fixes up the result that is to be returned to user. + int UpdateResultsOnProxyResolutionComplete(const ProxyList& proxy_list, + int net_error); + + // Helper to call if the request completes synchronously, since in that case + // the request will not be added to |pending_requests_| (in + // WindowsSystemProxyResolutionService). + int SynchronousProxyResolutionComplete(int net_error); + + // Callback for when the WinHttp request has completed. This is the main way + // that proxy resolutions will complete. The |proxy_list| is the list of + // proxies returned by WinHttp translated into Chromium-friendly terms. The + // |net_error| describes the status of the proxy resolution request. If + // WinHttp fails for some reason, |windows_error| contains the specific error + // returned by WinHttp. + virtual void AsynchronousProxyResolutionComplete(const ProxyList& proxy_list, + int net_error, + int windows_error); + + protected: + // The resolver will do the work of talking to system APIs and translating the + // results into something Chromium understands. + scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver_; + + private: + // Note that Request holds a bare pointer to the + // WindowsSystemProxyResolutionService. Outstanding requests are cancelled + // during ~WindowsSystemProxyResolutionService, so this is guaranteed to be + // valid throughout the lifetime of this object. + WindowsSystemProxyResolutionService* service_; + CompletionOnceCallback user_callback_; + ProxyInfo* results_; + const GURL url_; + const std::string method_; + NetLogWithSource net_log_; + // Time when the request was created. Stored here rather than in |results_| + // because the time in |results_| will be cleared. + base::TimeTicks creation_time_; + + SEQUENCE_CHECKER(sequence_checker_); }; } // namespace net diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc index 16fc5643558..1dcf0964681 100644 --- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc @@ -4,15 +4,68 @@ #include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h" +#include <utility> + +#include "base/logging.h" +#include "base/memory/ptr_util.h" #include "base/values.h" +#include "base/win/windows_version.h" #include "net/base/net_errors.h" +#include "net/log/net_log.h" +#include "net/log/net_log_event_type.h" +#include "net/log/net_log_with_source.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolver.h" +#include "net/proxy_resolution/win/winhttp_proxy_resolver_functions.h" namespace net { -WindowsSystemProxyResolutionService::WindowsSystemProxyResolutionService() = - default; -WindowsSystemProxyResolutionService::~WindowsSystemProxyResolutionService() = - default; +// static +bool WindowsSystemProxyResolutionService::IsSupported() { + if (base::win::GetVersion() < base::win::Version::WIN8) { + LOG(WARNING) << "WindowsSystemProxyResolutionService is only supported for " + "Windows 8 and later."; + return false; + } + + if (!WinHttpProxyResolverFunctions::GetInstance() + .are_all_functions_loaded()) { + LOG(ERROR) << "Failed to load functions necessary for " + "WindowsSystemProxyResolutionService!"; + return false; + } + + return true; +} + +// static +std::unique_ptr<WindowsSystemProxyResolutionService> +WindowsSystemProxyResolutionService::Create(NetLog* net_log) { + if (!IsSupported()) + return nullptr; + + return base::WrapUnique(new WindowsSystemProxyResolutionService(net_log)); +} + +WindowsSystemProxyResolutionService::WindowsSystemProxyResolutionService( + NetLog* net_log) + : create_proxy_resolver_function_for_testing_(nullptr), net_log_(net_log) {} + +WindowsSystemProxyResolutionService::~WindowsSystemProxyResolutionService() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Cancel any in-progress requests. + // This cancels the internal requests, but leaves the responsibility of + // canceling the high-level Request (by deleting it) to the client. + // Since |pending_requests_| might be modified in one of the requests' + // callbacks (if it deletes another request), iterating through the set in a + // for-loop will not work. + while (!pending_requests_.empty()) { + WindowsSystemProxyResolutionRequest* req = *pending_requests_.begin(); + ProxyList empty_list; + req->AsynchronousProxyResolutionComplete(empty_list, ERR_ABORTED, 0); + pending_requests_.erase(req); + } +} int WindowsSystemProxyResolutionService::ResolveProxy( const GURL& url, @@ -22,36 +75,74 @@ int WindowsSystemProxyResolutionService::ResolveProxy( CompletionOnceCallback callback, std::unique_ptr<ProxyResolutionRequest>* request, const NetLogWithSource& net_log) { - return ERR_NOT_IMPLEMENTED; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!callback.is_null()); + DCHECK(request); + + net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE); + + // TODO(https://crbug.com/1032820): Use a more detailed error. + if (!CreateWindowsSystemProxyResolverIfNeeded()) + return DidFinishResolvingProxy(url, method, results, ERR_FAILED, net_log); + + auto req = std::make_unique<WindowsSystemProxyResolutionRequest>( + this, url, method, results, std::move(callback), net_log, + windows_system_proxy_resolver_); + + const int net_error = req->Start(); + if (net_error != ERR_IO_PENDING) + return req->SynchronousProxyResolutionComplete(net_error); + + DCHECK(!ContainsPendingRequest(req.get())); + pending_requests_.insert(req.get()); + + // Completion will be notified through |callback|, unless the caller cancels + // the request using |request|. + *request = std::move(req); + return net_error; } void WindowsSystemProxyResolutionService::ReportSuccess( - const ProxyInfo& proxy_info) {} + const ProxyInfo& proxy_info) { + // TODO(https://crbug.com/1032820): Update proxy retry info with new proxy + // resolution data. +} void WindowsSystemProxyResolutionService::SetProxyDelegate( - ProxyDelegate* delegate) {} + ProxyDelegate* delegate) { + // TODO(https://crbug.com/1032820): Implement proxy delegates. +} -void WindowsSystemProxyResolutionService::OnShutdown() {} +void WindowsSystemProxyResolutionService::OnShutdown() { + // TODO(https://crbug.com/1032820): Add cleanup here as necessary. If cleanup + // is unnecessary, update the interface to not require an implementation for + // this so OnShutdown() can be removed. +} bool WindowsSystemProxyResolutionService::MarkProxiesAsBadUntil( const ProxyInfo& results, base::TimeDelta retry_delay, const std::vector<ProxyServer>& additional_bad_proxies, const NetLogWithSource& net_log) { + // TODO(https://crbug.com/1032820): Implement bad proxy cache. We should be + // able to share logic with the ConfiguredProxyResolutionService to accomplish + // this. return false; } -void WindowsSystemProxyResolutionService::ClearBadProxiesCache() {} +void WindowsSystemProxyResolutionService::ClearBadProxiesCache() { + proxy_retry_info_.clear(); +} const ProxyRetryInfoMap& WindowsSystemProxyResolutionService::proxy_retry_info() const { return proxy_retry_info_; } -std::unique_ptr<base::DictionaryValue> -WindowsSystemProxyResolutionService::GetProxyNetLogValues(int info_sources) { - std::unique_ptr<base::DictionaryValue> net_info_dict( - new base::DictionaryValue()); +base::Value WindowsSystemProxyResolutionService::GetProxyNetLogValues( + int info_sources) { + // TODO (https://crbug.com/1032820): Implement net logs. + base::Value net_info_dict(base::Value::Type::DICTIONARY); return net_info_dict; } @@ -63,4 +154,66 @@ bool WindowsSystemProxyResolutionService:: return false; } +void WindowsSystemProxyResolutionService:: + SetCreateWindowsSystemProxyResolverFunctionForTesting( + CreateWindowsSystemProxyResolverFunctionForTesting function) { + create_proxy_resolver_function_for_testing_ = function; +} + +void WindowsSystemProxyResolutionService:: + SetWindowsSystemProxyResolverForTesting( + scoped_refptr<WindowsSystemProxyResolver> + windows_system_proxy_resolver) { + windows_system_proxy_resolver_ = windows_system_proxy_resolver; +} + +bool WindowsSystemProxyResolutionService::ContainsPendingRequest( + WindowsSystemProxyResolutionRequest* req) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return pending_requests_.count(req) == 1; +} + +void WindowsSystemProxyResolutionService::RemovePendingRequest( + WindowsSystemProxyResolutionRequest* req) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(ContainsPendingRequest(req)); + pending_requests_.erase(req); +} + +bool WindowsSystemProxyResolutionService:: + CreateWindowsSystemProxyResolverIfNeeded() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (windows_system_proxy_resolver_) + return true; + + if (create_proxy_resolver_function_for_testing_) { + windows_system_proxy_resolver_ = + create_proxy_resolver_function_for_testing_(); + } else { + windows_system_proxy_resolver_ = + WindowsSystemProxyResolver::CreateWindowsSystemProxyResolver(); + } + + return !!windows_system_proxy_resolver_; +} + +int WindowsSystemProxyResolutionService::DidFinishResolvingProxy( + const GURL& url, + const std::string& method, + ProxyInfo* result, + int result_code, + const NetLogWithSource& net_log) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // TODO(https://crbug.com/1032820): Implement net logs. + // TODO(https://crbug.com/1032820): Implement proxy delegate. + // TODO(https://crbug.com/1032820): Implement proxy retry info. + + if (result_code != OK) + result->UseDirect(); + + net_log.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE); + return OK; +} + } // namespace net diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h index f5b820f3f03..1e82a11d0c6 100644 --- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h @@ -7,12 +7,24 @@ #include "net/proxy_resolution/proxy_resolution_service.h" +#include <memory> +#include <set> #include <string> +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" #include "net/base/net_export.h" namespace net { +class NetLog; +class WindowsSystemProxyResolutionRequest; +class WindowsSystemProxyResolver; + +using CreateWindowsSystemProxyResolverFunctionForTesting = + scoped_refptr<WindowsSystemProxyResolver> (*)(); + // This class decides which proxy server(s) to use for a particular URL request. // It does NOT support passing in fetched proxy configurations. Instead, it // relies entirely on WinHttp APIs to determine the proxy that should be used @@ -20,7 +32,14 @@ namespace net { class NET_EXPORT WindowsSystemProxyResolutionService : public ProxyResolutionService { public: - WindowsSystemProxyResolutionService(); + // The WinHttp functions used in the resolver via the WinHttpAPIWrapper are + // only supported on Windows 8 and above. + static bool IsSupported() WARN_UNUSED_RESULT; + + // Creates a WindowsSystemProxyResolutionService or returns nullptr if the + // runtime dependencies are not satisfied. + static std::unique_ptr<WindowsSystemProxyResolutionService> Create( + NetLog* net_log); WindowsSystemProxyResolutionService( const WindowsSystemProxyResolutionService&) = delete; @@ -47,15 +66,64 @@ class NET_EXPORT WindowsSystemProxyResolutionService const NetLogWithSource& net_log) override; void ClearBadProxiesCache() override; const ProxyRetryInfoMap& proxy_retry_info() const override; - std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues( - int info_sources) override; + base::Value GetProxyNetLogValues(int info_sources) override; bool CastToConfiguredProxyResolutionService( ConfiguredProxyResolutionService** configured_proxy_resolution_service) override WARN_UNUSED_RESULT; + // Used in tests to provide a fake |windows_system_proxy_resolver_|. + void SetCreateWindowsSystemProxyResolverFunctionForTesting( + CreateWindowsSystemProxyResolverFunctionForTesting function); + void SetWindowsSystemProxyResolverForTesting( + scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver); + private: + friend class WindowsSystemProxyResolutionRequest; + friend class WindowsSystemProxyResolutionServiceTest; + + explicit WindowsSystemProxyResolutionService(NetLog* net_log); + + typedef std::set<WindowsSystemProxyResolutionRequest*> PendingRequests; + + bool ContainsPendingRequest(WindowsSystemProxyResolutionRequest* req) + WARN_UNUSED_RESULT; + void RemovePendingRequest(WindowsSystemProxyResolutionRequest* req); + + // Lazily creates |windows_system_proxy_resolver_|. + bool CreateWindowsSystemProxyResolverIfNeeded() WARN_UNUSED_RESULT; + + size_t PendingRequestSizeForTesting() const { + return pending_requests_.size(); + } + + // Called when proxy resolution has completed (either synchronously or + // asynchronously). Handles logging the result, and cleaning out + // bad entries from the results list. + int DidFinishResolvingProxy(const GURL& url, + const std::string& method, + ProxyInfo* result, + int result_code, + const NetLogWithSource& net_log); + + CreateWindowsSystemProxyResolverFunctionForTesting + create_proxy_resolver_function_for_testing_; + // Map of the known bad proxies and the information about the retry time. ProxyRetryInfoMap proxy_retry_info_; + + // Set of pending/in-progress requests. + PendingRequests pending_requests_; + + // This is the log for any generated events. + NetLog* net_log_; + + // This object encapsulates all WinHttp logic in Chromium-friendly terms. It + // manages the lifetime of the WinHttp session (which is + // per-resolution-service). This will get handed off to individual resolution + // requests so that they can query/cancel proxy resolution as needed. + scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver_; + + SEQUENCE_CHECKER(sequence_checker_); }; } // namespace net diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc index 94d3c7f8102..7e312c2eba3 100644 --- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc @@ -3,18 +3,356 @@ // found in the LICENSE file. #include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h" + +#include <limits> +#include <memory> +#include <string> +#include <unordered_map> + +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/run_loop.h" +#include "base/sequence_checker.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "net/base/network_isolation_key.h" +#include "net/base/test_completion_callback.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" +#include "net/proxy_resolution/proxy_config.h" +#include "net/proxy_resolution/proxy_info.h" +#include "net/proxy_resolution/proxy_list.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolver.h" +#include "net/proxy_resolution/win/winhttp_api_wrapper.h" +#include "net/test/gtest_util.h" +#include "net/test/test_with_task_environment.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +using net::test::IsError; +using net::test::IsOk; namespace net { -TEST(WindowsSystemProxyResolutionServiceTest, - CastToConfiguredProxyResolutionService) { - WindowsSystemProxyResolutionService service; +namespace { + +const GURL kResourceUrl("https://example.test:8080/"); + +class MockWindowsSystemProxyResolver : public WindowsSystemProxyResolver { + public: + MockWindowsSystemProxyResolver() : WindowsSystemProxyResolver(nullptr) {} + + void set_get_proxy_for_url_success(bool get_proxy_for_url_success) { + get_proxy_for_url_success_ = get_proxy_for_url_success; + } + bool GetProxyForUrl(WindowsSystemProxyResolutionRequest* callback_target, + const std::string& url) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!get_proxy_for_url_success_) + return false; + + const int request_handle = proxy_resolver_identifier_++; + pending_callback_target_map_[callback_target] = request_handle; + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&MockWindowsSystemProxyResolver::DoQueryComplete, + base::Unretained(this), callback_target, + request_handle)); + + return get_proxy_for_url_success_; + } + + void RemovePendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + pending_callback_target_map_.erase(callback_target); + } + + bool HasPendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target) const override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return (pending_callback_target_map_.find(callback_target) != + pending_callback_target_map_.end()); + } + + void add_server_to_proxy_list(const ProxyServer& proxy_server) { + proxy_list_.AddProxyServer(proxy_server); + } + + void set_net_error(int net_error) { net_error_ = net_error; } + + void set_windows_error(int windows_error) { windows_error_ = windows_error; } + + private: + ~MockWindowsSystemProxyResolver() override { + if (!pending_callback_target_map_.empty()) + ADD_FAILURE() + << "The WindowsSystemProxyResolutionRequests must account for all " + "pending requests in the WindowsSystemProxyResolver."; + } + + void DoQueryComplete(WindowsSystemProxyResolutionRequest* callback_target, + int request_handle) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (HasPendingCallbackTarget(callback_target) && + pending_callback_target_map_[callback_target] == request_handle) + callback_target->AsynchronousProxyResolutionComplete( + proxy_list_, net_error_, windows_error_); + } + + bool get_proxy_for_url_success_ = true; + ProxyList proxy_list_; + int net_error_ = OK; + // TODO(https://crbug.com/1032820): Add tests for the |windows_error_| + // code when it is used. + int windows_error_ = 0; + + int proxy_resolver_identifier_ = 1; + std::unordered_map<WindowsSystemProxyResolutionRequest*, int> + pending_callback_target_map_; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +scoped_refptr<WindowsSystemProxyResolver> +CreateWindowsSystemProxyResolverFails() { + return nullptr; +} + +} // namespace + +// These tests verify the behavior of the WindowsSystemProxyResolutionService in +// isolation by mocking out the WindowsSystemProxyResolver. +class WindowsSystemProxyResolutionServiceTest : public TestWithTaskEnvironment { + public: + void SetUp() override { + testing::Test::SetUp(); + + if (!WindowsSystemProxyResolutionService::IsSupported()) { + GTEST_SKIP() + << "Windows System Proxy Resolution is only supported on Windows 8+."; + } + + proxy_resolver_ = base::MakeRefCounted<MockWindowsSystemProxyResolver>(); + proxy_resolution_service_ = + WindowsSystemProxyResolutionService::Create(/*net_log=*/nullptr); + proxy_resolution_service_->SetWindowsSystemProxyResolverForTesting( + proxy_resolver_); + } + + WindowsSystemProxyResolutionService* service() { + return proxy_resolution_service_.get(); + } + + scoped_refptr<MockWindowsSystemProxyResolver> resolver() { + return proxy_resolver_; + } + + size_t PendingRequestSizeForTesting() { + return proxy_resolution_service_->PendingRequestSizeForTesting(); + } + + void ResetProxyResolutionService() { proxy_resolution_service_.reset(); } + + void DoResolveProxyCompletedSynchronouslyTest() { + // Make sure there would be a proxy result on success. + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS foopy:8443"); + resolver()->add_server_to_proxy_list(proxy_server); + + ProxyInfo info; + TestCompletionCallback callback; + NetLogWithSource log; + std::unique_ptr<ProxyResolutionRequest> request; + const int result = service()->ResolveProxy( + kResourceUrl, std::string(), NetworkIsolationKey(), &info, + callback.callback(), &request, log); + + EXPECT_THAT(result, IsOk()); + EXPECT_TRUE(info.is_direct()); + EXPECT_FALSE(callback.have_result()); + EXPECT_EQ(PendingRequestSizeForTesting(), 0u); + EXPECT_EQ(request, nullptr); + } + + void DoResolveProxyTest(const ProxyList& expected_proxy_list) { + ProxyInfo info; + TestCompletionCallback callback; + NetLogWithSource log; + std::unique_ptr<ProxyResolutionRequest> request; + int result = service()->ResolveProxy(kResourceUrl, std::string(), + NetworkIsolationKey(), &info, + callback.callback(), &request, log); + + ASSERT_THAT(result, IsError(ERR_IO_PENDING)); + ASSERT_EQ(PendingRequestSizeForTesting(), 1u); + ASSERT_NE(request, nullptr); + + // Wait for result to come back. + EXPECT_THAT(callback.GetResult(result), IsOk()); + + EXPECT_TRUE(expected_proxy_list.Equals(info.proxy_list())); + EXPECT_EQ(PendingRequestSizeForTesting(), 0u); + EXPECT_NE(request, nullptr); + } + + scoped_refptr<WindowsSystemProxyResolver> + CreateMockWindowsSystemProxyResolver() { + return proxy_resolver_; + } + + private: + std::unique_ptr<WindowsSystemProxyResolutionService> + proxy_resolution_service_; + scoped_refptr<MockWindowsSystemProxyResolver> proxy_resolver_; +}; + +TEST_F(WindowsSystemProxyResolutionServiceTest, + ResolveProxyFailedToCreateResolver) { + service()->SetWindowsSystemProxyResolverForTesting(nullptr); + service()->SetCreateWindowsSystemProxyResolverFunctionForTesting( + &CreateWindowsSystemProxyResolverFails); + DoResolveProxyCompletedSynchronouslyTest(); +} + +TEST_F(WindowsSystemProxyResolutionServiceTest, + ResolveProxyCompletedSynchronously) { + resolver()->set_get_proxy_for_url_success(false); + DoResolveProxyCompletedSynchronouslyTest(); +} + +TEST_F(WindowsSystemProxyResolutionServiceTest, + ResolveProxyFailedAsynchronously) { + resolver()->set_net_error(ERR_FAILED); + + // Make sure there would be a proxy result on success. + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS foopy:8443"); + resolver()->add_server_to_proxy_list(proxy_server); + + ProxyInfo info; + TestCompletionCallback callback; + NetLogWithSource log; + std::unique_ptr<ProxyResolutionRequest> request; + int result = service()->ResolveProxy(kResourceUrl, std::string(), + NetworkIsolationKey(), &info, + callback.callback(), &request, log); + + ASSERT_THAT(result, IsError(ERR_IO_PENDING)); + ASSERT_EQ(PendingRequestSizeForTesting(), 1u); + ASSERT_NE(request, nullptr); + + // Wait for result to come back. + EXPECT_THAT(callback.GetResult(result), IsOk()); + + EXPECT_TRUE(info.is_direct()); + EXPECT_EQ(PendingRequestSizeForTesting(), 0u); + EXPECT_NE(request, nullptr); +} + +TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyEmptyResults) { + ProxyList expected_proxy_list; + DoResolveProxyTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyWithResults) { + ProxyList expected_proxy_list; + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS foopy:8443"); + resolver()->add_server_to_proxy_list(proxy_server); + expected_proxy_list.AddProxyServer(proxy_server); + + DoResolveProxyTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolutionServiceTest, + MultipleProxyResolutionRequests) { + ProxyList expected_proxy_list; + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS foopy:8443"); + resolver()->add_server_to_proxy_list(proxy_server); + expected_proxy_list.AddProxyServer(proxy_server); + NetLogWithSource log; + + ProxyInfo first_proxy_info; + TestCompletionCallback first_callback; + std::unique_ptr<ProxyResolutionRequest> first_request; + int result = service()->ResolveProxy( + kResourceUrl, std::string(), NetworkIsolationKey(), &first_proxy_info, + first_callback.callback(), &first_request, log); + ASSERT_THAT(result, IsError(ERR_IO_PENDING)); + ASSERT_EQ(PendingRequestSizeForTesting(), 1u); + ASSERT_NE(first_request, nullptr); + + ProxyInfo second_proxy_info; + TestCompletionCallback second_callback; + std::unique_ptr<ProxyResolutionRequest> second_request; + result = service()->ResolveProxy( + kResourceUrl, std::string(), NetworkIsolationKey(), &second_proxy_info, + second_callback.callback(), &second_request, log); + ASSERT_THAT(result, IsError(ERR_IO_PENDING)); + ASSERT_EQ(PendingRequestSizeForTesting(), 2u); + ASSERT_NE(second_request, nullptr); + + // Wait for results to come back. + EXPECT_THAT(first_callback.GetResult(result), IsOk()); + EXPECT_THAT(second_callback.GetResult(result), IsOk()); + + EXPECT_TRUE(expected_proxy_list.Equals(first_proxy_info.proxy_list())); + EXPECT_NE(first_request, nullptr); + EXPECT_TRUE(expected_proxy_list.Equals(second_proxy_info.proxy_list())); + EXPECT_NE(second_request, nullptr); + + EXPECT_EQ(PendingRequestSizeForTesting(), 0u); +} + +TEST_F(WindowsSystemProxyResolutionServiceTest, + ProxyResolutionServiceDestructionWithInFlightRequests) { + ProxyList expected_proxy_list; + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS foopy:8443"); + resolver()->add_server_to_proxy_list(proxy_server); + expected_proxy_list.AddProxyServer(proxy_server); + NetLogWithSource log; + + ProxyInfo first_proxy_info; + TestCompletionCallback first_callback; + std::unique_ptr<ProxyResolutionRequest> first_request; + int result = service()->ResolveProxy( + kResourceUrl, std::string(), NetworkIsolationKey(), &first_proxy_info, + first_callback.callback(), &first_request, log); + ASSERT_THAT(result, IsError(ERR_IO_PENDING)); + ASSERT_EQ(PendingRequestSizeForTesting(), 1u); + ASSERT_NE(first_request, nullptr); + + ProxyInfo second_proxy_info; + TestCompletionCallback second_callback; + std::unique_ptr<ProxyResolutionRequest> second_request; + result = service()->ResolveProxy( + kResourceUrl, std::string(), NetworkIsolationKey(), &second_proxy_info, + second_callback.callback(), &second_request, log); + ASSERT_THAT(result, IsError(ERR_IO_PENDING)); + ASSERT_EQ(PendingRequestSizeForTesting(), 2u); + ASSERT_NE(second_request, nullptr); + + // There are now 2 in-flight proxy resolution requests. Deleting the proxy + // resolution service should call the callbacks immediately and do any + // appropriate error handling. + ResetProxyResolutionService(); + EXPECT_TRUE(first_callback.have_result()); + EXPECT_TRUE(second_callback.have_result()); + + EXPECT_TRUE(first_proxy_info.is_direct()); + EXPECT_TRUE(second_proxy_info.is_direct()); +} +TEST_F(WindowsSystemProxyResolutionServiceTest, + CastToConfiguredProxyResolutionService) { auto configured_service = ConfiguredProxyResolutionService::CreateDirect(); ConfiguredProxyResolutionService* casted_service = configured_service.get(); - EXPECT_FALSE(service.CastToConfiguredProxyResolutionService(&casted_service)); + EXPECT_FALSE( + service()->CastToConfiguredProxyResolutionService(&casted_service)); EXPECT_EQ(nullptr, casted_service); } diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc new file mode 100644 index 00000000000..8117b62b99a --- /dev/null +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc @@ -0,0 +1,368 @@ +// Copyright 2020 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/win/windows_system_proxy_resolver.h" + +#include <cwchar> +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "net/base/host_port_pair.h" +#include "net/base/net_errors.h" +#include "net/base/proxy_server.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h" +#include "net/proxy_resolution/win/winhttp_api_wrapper.h" +#include "url/url_canon.h" + +namespace net { + +namespace { + +bool GetProxyServerFromWinHttpResultEntry( + const WINHTTP_PROXY_RESULT_ENTRY& result_entry, + ProxyServer* out_proxy_server) { + // TODO(https://crbug.com/1032820): Include net logs for proxy bypass + if (!result_entry.fProxy) { + *out_proxy_server = ProxyServer::Direct(); + return true; + } + + ProxyServer::Scheme scheme = ProxyServer::Scheme::SCHEME_INVALID; + switch (result_entry.ProxyScheme) { + case (INTERNET_SCHEME_HTTP): + scheme = ProxyServer::Scheme::SCHEME_HTTP; + break; + case (INTERNET_SCHEME_HTTPS): + scheme = ProxyServer::Scheme::SCHEME_HTTPS; + break; + case (INTERNET_SCHEME_SOCKS): + scheme = ProxyServer::Scheme::SCHEME_SOCKS4; + break; + default: + LOG(WARNING) << "Of the possible proxy schemes returned by WinHttp, " + "Chrome supports HTTP(S) and SOCKS4. The ProxyScheme " + "that triggered this message is: " + << result_entry.ProxyScheme; + break; + } + + if (scheme == ProxyServer::Scheme::SCHEME_INVALID) + return false; + + // Chrome expects a specific port from WinHttp. The WinHttp documentation on + // MSDN makes it unclear whether or not a specific port is guaranteed. + if (result_entry.ProxyPort == INTERNET_DEFAULT_PORT) { + LOG(WARNING) << "WinHttpGetProxyForUrlEx() returned a proxy with " + "INTERNET_PORT_DEFAULT!"; + return false; + } + + // Since there is a proxy in the result (i.e. |fProxy| is TRUE), the + // |pwszProxy| is guaranteed to be non-null and non-empty. + DCHECK(!!result_entry.pwszProxy); + DCHECK(wcslen(result_entry.pwszProxy) > 0); + + base::string16 host_wide(result_entry.pwszProxy, + wcslen(result_entry.pwszProxy)); + if (!base::IsStringASCII(host_wide)) { + const int kInitialBufferSize = 256; + url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output; + if (!url::IDNToASCII(host_wide.data(), host_wide.length(), + &punycode_output)) + return false; + + host_wide.assign(punycode_output.data(), punycode_output.length()); + } + + // At this point the string in |host_wide| is ASCII. + std::string host; + if (!base::UTF16ToUTF8(host_wide.data(), host_wide.length(), &host)) + return false; + + HostPortPair host_and_port(host, result_entry.ProxyPort); + *out_proxy_server = ProxyServer(scheme, host_and_port); + return true; +} + +} // namespace + +// static +scoped_refptr<WindowsSystemProxyResolver> +WindowsSystemProxyResolver::CreateWindowsSystemProxyResolver() { + scoped_refptr<WindowsSystemProxyResolver> resolver = base::WrapRefCounted( + new WindowsSystemProxyResolver(std::make_unique<WinHttpAPIWrapper>())); + if (resolver->Initialize()) { + return resolver; + } + return nullptr; +} + +WindowsSystemProxyResolver::WindowsSystemProxyResolver( + std::unique_ptr<WinHttpAPIWrapper> winhttp_api_wrapper) + : winhttp_api_wrapper_(std::move(winhttp_api_wrapper)), + sequenced_task_runner_(base::SequencedTaskRunnerHandle::Get()) {} +WindowsSystemProxyResolver::~WindowsSystemProxyResolver() = default; + +bool WindowsSystemProxyResolver::Initialize() { + if (!winhttp_api_wrapper_->CallWinHttpOpen()) + 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. + if (!winhttp_api_wrapper_->CallWinHttpSetTimeouts(10000, 10000, 5000, 5000)) + return false; + + // This sets the entry point for every callback in the WinHttp session created + // above. + if (!winhttp_api_wrapper_->CallWinHttpSetStatusCallback( + &WindowsSystemProxyResolver::WinHttpStatusCallback)) + return false; + + return true; +} + +bool WindowsSystemProxyResolver::GetProxyForUrl( + WindowsSystemProxyResolutionRequest* callback_target, + const std::string& url) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Fetch the current system proxy settings. These are per current network + // interface and per current user. + ScopedIEConfig scoped_ie_config; + if (!winhttp_api_wrapper_->CallWinHttpGetIEProxyConfigForCurrentUser( + scoped_ie_config.config())) + return false; + + // This will create a handle specifically for WinHttpGetProxyForUrlEx(). + HINTERNET resolver_handle = nullptr; + if (!winhttp_api_wrapper_->CallWinHttpCreateProxyResolver(&resolver_handle)) + return false; + + // WinHttp will do all necessary proxy resolution fallback for this request. + // If automatic settings aren't configured or fail, it'll use any manually + // configured proxies on the machine. The WINHTTP_AUTOPROXY_ALLOW_STATIC flag + // tells the APIs to pick up manually configured proxies. + // + // Separately, Windows allows different proxy settings for different network + // interfaces. The WINHTTP_AUTOPROXY_OPTIONS flag tells WinHttp to + // differentiate between these settings and to get the proxy that's most + // specific to the current interface. + WINHTTP_AUTOPROXY_OPTIONS autoproxy_options = {0}; + autoproxy_options.dwFlags = + WINHTTP_AUTOPROXY_ALLOW_STATIC | WINHTTP_AUTOPROXY_ALLOW_CM; + + // The fAutoLogonIfChallenged option has been deprecated and should always be + // set to FALSE throughout Windows 10. Even in earlier versions of the OS, + // this feature did not work particularly well. + // https://support.microsoft.com/en-us/help/3161949/ms16-077-description-of-the-security-update-for-wpad-june-14-2016 + autoproxy_options.fAutoLogonIfChallenged = FALSE; + + // Sets a specific PAC URL if there was one in the IE configs. + if (scoped_ie_config.config()->lpszAutoConfigUrl) { + autoproxy_options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + autoproxy_options.lpszAutoConfigUrl = + scoped_ie_config.config()->lpszAutoConfigUrl; + } + + // Similarly, allow WPAD if it was enabled in the IE configs. + if (!!scoped_ie_config.config()->fAutoDetect) { + autoproxy_options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT; + + // Enable WPAD using both DNS and DHCP, since that is what idiomatic Windows + // applications do. + autoproxy_options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DNS_A; + autoproxy_options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP; + } + + // Now that everything is set-up, ask WinHTTP to get the actual proxy list. + const DWORD_PTR context = reinterpret_cast<DWORD_PTR>(this); + if (!winhttp_api_wrapper_->CallWinHttpGetProxyForUrlEx( + resolver_handle, url, &autoproxy_options, context)) { + winhttp_api_wrapper_->CallWinHttpCloseHandle(resolver_handle); + return false; + } + + // Saves of the object which will receive the callback once the operation + // completes. + AddPendingCallbackTarget(callback_target, resolver_handle); + + // On a successful call to WinHttpGetProxyForUrlEx(), the callback set by + // CallWinHttpSetStatusCallback() is guaranteed to be called exactly once. + // That may happen at any time on any thread. In order to make sure this + // object does not destruct before that callback occurs, it must AddRef() + // itself. This reference will be Release()'d in the callback. + base::RefCountedThreadSafe<WindowsSystemProxyResolver>::AddRef(); + + return true; +} + +void WindowsSystemProxyResolver::AddPendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target, + HINTERNET resolver_handle) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + pending_callback_target_map_[callback_target] = resolver_handle; +} + +void WindowsSystemProxyResolver::RemovePendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + pending_callback_target_map_.erase(callback_target); +} + +bool WindowsSystemProxyResolver::HasPendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return (pending_callback_target_map_.find(callback_target) != + pending_callback_target_map_.end()); +} + +WindowsSystemProxyResolutionRequest* +WindowsSystemProxyResolver::LookupCallbackTargetFromResolverHandle( + HINTERNET resolver_handle) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + WindowsSystemProxyResolutionRequest* pending_callback_target = nullptr; + for (auto target : pending_callback_target_map_) { + if (target.second == resolver_handle) { + pending_callback_target = target.first; + break; + } + } + return pending_callback_target; +} + +// static +void __stdcall WindowsSystemProxyResolver::WinHttpStatusCallback( + HINTERNET resolver_handle, + DWORD_PTR context, + DWORD status, + void* info, + DWORD info_len) { + DCHECK(resolver_handle); + DCHECK(context); + WindowsSystemProxyResolver* windows_system_proxy_resolver = + reinterpret_cast<WindowsSystemProxyResolver*>(context); + + // Make a copy of any error information in |info| so it can be accessed from + // the subsequently posted task. The |info| pointer's lifetime is managed by + // WinHTTP and hence is not valid once this frame returns. + int windows_error = S_OK; + if (info && status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) { + WINHTTP_ASYNC_RESULT* result = static_cast<WINHTTP_ASYNC_RESULT*>(info); + windows_error = result->dwError; + } + + // It is possible for PostTask() to fail (ex: during shutdown). In that case, + // the WindowsSystemProxyResolver in |context| will leak. This is expected to + // be either unusual or to occur during shutdown, where a leak doesn't matter. + // Since calling the |context| on the wrong thread may be problematic, it will + // be allowed to leak here if PostTask() fails. + windows_system_proxy_resolver->sequenced_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&WindowsSystemProxyResolver::DoWinHttpStatusCallback, + windows_system_proxy_resolver, resolver_handle, status, + windows_error)); +} + +void WindowsSystemProxyResolver::DoWinHttpStatusCallback( + HINTERNET resolver_handle, + DWORD status, + int windows_error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // The |resolver_handle| should correspond to a HINTERNET resolver_handle in + // |pending_callback_target_map_| unless the associated attempt to get a proxy + // for an URL has been cancelled. + WindowsSystemProxyResolutionRequest* pending_callback_target = + LookupCallbackTargetFromResolverHandle(resolver_handle); + + // There is no work to do if this request has been cancelled. + if (pending_callback_target) { + switch (status) { + case WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE: + GetProxyResultForCallbackTarget(pending_callback_target, + resolver_handle); + break; + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + HandleErrorForCallbackTarget(pending_callback_target, windows_error); + break; + default: + LOG(WARNING) << "DoWinHttpStatusCallback() expects only callbacks for " + "WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE and " + "WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, not: " + << status; + HandleErrorForCallbackTarget(pending_callback_target, E_UNEXPECTED); + break; + } + + // No matter what happened above, the |pending_callback_target| should no + // longer be in the |pending_callback_target_map_|. Either the callback was + // handled or it was cancelled. This pointer will be explicitly cleared to + // make it obvious that it can no longer be used safely. + DCHECK(!HasPendingCallbackTarget(pending_callback_target)); + pending_callback_target = nullptr; + } + + // The HINTERNET |resolver_handle| for this attempt at getting a proxy is no + // longer needed. + winhttp_api_wrapper_->CallWinHttpCloseHandle(resolver_handle); + + // The current WindowsSystemProxyResolver object may now be Release()'d on the + // correct sequence after all work is done, thus balancing out the AddRef() + // from WinHttpGetProxyForUrlEx(). + base::RefCountedThreadSafe<WindowsSystemProxyResolver>::Release(); +} + +void WindowsSystemProxyResolver::GetProxyResultForCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target, + HINTERNET resolver_handle) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(HasPendingCallbackTarget(callback_target)); + + WINHTTP_PROXY_RESULT proxy_result = {0}; + if (!winhttp_api_wrapper_->CallWinHttpGetProxyResult(resolver_handle, + &proxy_result)) { + // TODO(https://crbug.com/1032820): Use a more detailed net error. + callback_target->AsynchronousProxyResolutionComplete(ProxyList(), + ERR_FAILED, 0); + return; + } + + // Translate the results for ProxyInfo. + ProxyList proxy_list; + for (DWORD i = 0u; i < proxy_result.cEntries; ++i) { + ProxyServer proxy_server; + if (GetProxyServerFromWinHttpResultEntry(proxy_result.pEntries[i], + &proxy_server)) + proxy_list.AddProxyServer(proxy_server); + } + + // The |proxy_result| must be freed. + winhttp_api_wrapper_->CallWinHttpFreeProxyResult(&proxy_result); + + // The consumer of this proxy resolution may not understand an empty proxy + // list. Thus, this case is considered an error. + int net_error = proxy_list.IsEmpty() ? ERR_FAILED : OK; + callback_target->AsynchronousProxyResolutionComplete(proxy_list, net_error, + 0); +} + +void WindowsSystemProxyResolver::HandleErrorForCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target, + int windows_error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(HasPendingCallbackTarget(callback_target)); + + // TODO(https://crbug.com/1032820): Use a more detailed net error. + callback_target->AsynchronousProxyResolutionComplete(ProxyList(), ERR_FAILED, + windows_error); +} + +} // namespace net diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h new file mode 100644 index 00000000000..3596ec8f935 --- /dev/null +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h @@ -0,0 +1,131 @@ +// Copyright 2020 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. + +#ifndef NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLVER_H_ +#define NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLVER_H_ + +#include <windows.h> +#include <winhttp.h> + +#include <memory> +#include <string> +#include <unordered_map> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" +#include "net/base/net_export.h" +#include "net/proxy_resolution/proxy_list.h" + +namespace net { + +class WindowsSystemProxyResolutionRequest; +class WinHttpAPIWrapper; + +// This provides a layer of abstraction between calling code Windows-specific +// code. It is shared between the WindowsSystemProxyResolutionService and +// inflight WinHttp callbacks. Internally, it takes care of all interaction with +// WinHttp. The only time this object is ever access outside of its sequence is +// during the WinHttp callback. For the sake of that callback, this must be +// RefcountedThreadSafe. +class NET_EXPORT WindowsSystemProxyResolver + : public base::RefCountedThreadSafe<WindowsSystemProxyResolver> { + public: + static scoped_refptr<WindowsSystemProxyResolver> + CreateWindowsSystemProxyResolver(); + + WindowsSystemProxyResolver(const WindowsSystemProxyResolver&) = delete; + WindowsSystemProxyResolver& operator=(const WindowsSystemProxyResolver&) = + delete; + + // This will first fetch the current system proxy settings by calling into + // WinHttpGetIEProxyConfigForCurrentUser() and then resolve the proxy using + // those settings as an input into WinHttpGetProxyForUrlEx(). + virtual bool GetProxyForUrl( + WindowsSystemProxyResolutionRequest* callback_target, + const std::string& url) WARN_UNUSED_RESULT; + + // After calling GetProxyForUrl(), a |callback_target| is saved internally for + // when proxy resolution is complete. When a + // WindowsSystemProxyResolutionRequest wants to avoid receiving a callback, + // it must remove itself from the list of pending callback targets. + virtual void RemovePendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target); + virtual bool HasPendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target) const + WARN_UNUSED_RESULT; + + protected: + explicit WindowsSystemProxyResolver( + std::unique_ptr<WinHttpAPIWrapper> winhttp_api_wrapper); + virtual ~WindowsSystemProxyResolver(); + + private: + friend class base::RefCountedThreadSafe<WindowsSystemProxyResolver>; + friend class WindowsSystemProxyResolverTest; + + // Sets up the WinHttp session that will be used throughout the lifetime of + // this object. + bool Initialize(); + + // These will interact with |pending_callback_target_map_|. + void AddPendingCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target, + HINTERNET handle); + WindowsSystemProxyResolutionRequest* LookupCallbackTargetFromResolverHandle( + HINTERNET resolver_handle) const; + + // This is the callback provided to WinHttp. Once a call to resolve a proxy + // succeeds or errors out, it'll call into here with |context| being a pointer + // to a WindowsSystemProxyResolver that has been kept alive. This callback can + // hit in any thread and will immediately post a task to the right sequence. + static void __stdcall WinHttpStatusCallback(HINTERNET resolver_handle, + DWORD_PTR context, + DWORD status, + void* info, + DWORD info_len); + + // Called from WinHttpStatusCallback on the right sequence. This will make + // decisions about what to do from the results of the proxy resolution call. + // Note that the WindowsSystemProxyResolutionRequest that asked for this proxy + // may have decided they no longer need an answer (ex: the request has gone + // away), so this function has to deal with that situation too. + void DoWinHttpStatusCallback(HINTERNET resolver_handle, + DWORD status, + int windows_error); + + // On a successful call to WinHttpGetProxyForUrlEx(), this translates WinHttp + // results into Chromium-friendly structures before notifying the right + // WindowsSystemProxyResolutionRequest. + void GetProxyResultForCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target, + HINTERNET resolver_handle); + + // On a failed call to WinHttpGetProxyForUrlEx(), this will notify the right + // WindowsSystemProxyResolutionRequest of the error. + void HandleErrorForCallbackTarget( + WindowsSystemProxyResolutionRequest* callback_target, + int windows_error); + + // This is a thin wrapper over WinHttp APIs that may be overridden for + // testing. + std::unique_ptr<WinHttpAPIWrapper> winhttp_api_wrapper_; + + // This is the mapping of WindowsSystemProxyResolutionRequest objects that + // called GetProxyForUrl() to the handle that's being used for their proxy + // resolution call. Upon receiving a callback from WinHttp (which includes an + // HINTERNET handle), a reverse lookup here will get the right + // WindowsSystemProxyResolutionRequest to use. + std::unordered_map<WindowsSystemProxyResolutionRequest*, HINTERNET> + pending_callback_target_map_; + + SEQUENCE_CHECKER(sequence_checker_); + scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; +}; + +} // namespace net + +#endif // NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLVER_H_ diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc new file mode 100644 index 00000000000..9f48f4b88ab --- /dev/null +++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc @@ -0,0 +1,864 @@ +// Copyright 2020 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/win/windows_system_proxy_resolver.h" + +#include <windows.h> +#include <winhttp.h> + +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/run_loop.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "net/base/net_errors.h" +#include "net/base/proxy_server.h" +#include "net/base/test_completion_callback.h" +#include "net/proxy_resolution/proxy_config.h" +#include "net/proxy_resolution/proxy_list.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h" +#include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h" +#include "net/proxy_resolution/win/winhttp_api_wrapper.h" +#include "net/test/test_with_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace net { + +namespace { + +constexpr char kUrl[] = "https://example.test:8080/"; + +void CopySettingToIEProxyConfigString(const base::string16& setting, + LPWSTR* ie_proxy_config_string) { + *ie_proxy_config_string = static_cast<LPWSTR>( + GlobalAlloc(GPTR, sizeof(base::char16) * (setting.length() + 1))); + memcpy(*ie_proxy_config_string, setting.data(), + sizeof(wchar_t) * setting.length()); +} + +class MockProxyResolutionRequest final + : public WindowsSystemProxyResolutionRequest { + public: + MockProxyResolutionRequest( + CompletionOnceCallback user_callback, + const NetLogWithSource& net_log, + scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver) + : WindowsSystemProxyResolutionRequest(/*service=*/nullptr, + GURL(), + /*method=*/std::string(), + /*results=*/nullptr, + std::move(user_callback), + net_log, + windows_system_proxy_resolver) {} + ~MockProxyResolutionRequest() override = default; + + LoadState GetLoadState() const override { + return LOAD_STATE_RESOLVING_PROXY_FOR_URL; + } + + void AsynchronousProxyResolutionComplete(const ProxyList& proxy_list, + int net_error, + int windows_error) override { + run_loop_.Quit(); + EXPECT_TRUE(windows_system_proxy_resolver_->HasPendingCallbackTarget(this)); + windows_system_proxy_resolver_->RemovePendingCallbackTarget(this); + EXPECT_FALSE( + windows_system_proxy_resolver_->HasPendingCallbackTarget(this)); + + proxy_list_ = proxy_list; + net_error_ = net_error; + windows_error_ = windows_error; + } + + void WaitForProxyResolutionComplete() { run_loop_.Run(); } + + const ProxyList& proxy_list() const { return proxy_list_; } + + int net_error() const { return net_error_; } + + int windows_error() const { return windows_error_; } + + private: + base::RunLoop run_loop_; + ProxyList proxy_list_; + int net_error_ = 0; + int windows_error_ = 0; +}; + +// This limit is arbitrary and exists only to make memory management in this +// test easier. +constexpr unsigned int kMaxProxyEntryLimit = 10u; + +// This class will internally validate behavior that MUST be present in the code +// in order to successfully use WinHttp APIs. +class MockWinHttpAPIWrapper : public WinHttpAPIWrapper { + public: + MockWinHttpAPIWrapper() {} + ~MockWinHttpAPIWrapper() override { + if (did_call_get_proxy_result_) + EXPECT_TRUE(did_call_free_proxy_result_); + EXPECT_TRUE(opened_proxy_resolvers_.empty()); + ResetWinHttpResults(); + } + + void set_call_winhttp_open_success(bool open_success) { + open_success_ = open_success; + } + bool CallWinHttpOpen() override { + did_call_open_ = true; + return open_success_; + } + + void set_call_winhttp_set_timeouts_success(bool set_timeouts_success) { + set_timeouts_success_ = set_timeouts_success; + } + bool CallWinHttpSetTimeouts(int resolve_timeout, + int connect_timeout, + int send_timeout, + int receive_timeout) override { + EXPECT_TRUE(did_call_open_); + did_call_set_timeouts_ = true; + return set_timeouts_success_; + } + + void set_call_winhttp_set_status_callback_success( + bool set_status_callback_success) { + set_status_callback_success_ = set_status_callback_success; + } + bool CallWinHttpSetStatusCallback( + WINHTTP_STATUS_CALLBACK internet_callback) override { + EXPECT_TRUE(did_call_open_); + EXPECT_NE(internet_callback, nullptr); + EXPECT_EQ(callback_, nullptr); + callback_ = internet_callback; + did_call_set_status_callback_ = true; + return set_status_callback_success_; + } + + void set_call_winhttp_get_ie_proxy_config_success( + bool get_ie_proxy_config_success) { + get_ie_proxy_config_success_ = get_ie_proxy_config_success; + } + void set_ie_proxy_config(bool is_autoproxy_enabled, + const base::string16& pac_url, + const base::string16& proxy, + const base::string16& proxy_bypass) { + is_autoproxy_enabled_ = is_autoproxy_enabled; + pac_url_ = pac_url; + proxy_ = proxy; + proxy_bypass_ = proxy_bypass; + } + bool CallWinHttpGetIEProxyConfigForCurrentUser( + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_proxy_config) override { + did_call_get_ie_proxy_config_ = true; + ie_proxy_config->fAutoDetect = is_autoproxy_enabled_ ? TRUE : FALSE; + if (!pac_url_.empty()) { + CopySettingToIEProxyConfigString(pac_url_, + &ie_proxy_config->lpszAutoConfigUrl); + } + if (!proxy_.empty()) { + CopySettingToIEProxyConfigString(proxy_, &ie_proxy_config->lpszProxy); + } + if (!proxy_bypass_.empty()) { + CopySettingToIEProxyConfigString(proxy_bypass_, + &ie_proxy_config->lpszProxyBypass); + } + return get_ie_proxy_config_success_; + } + + void set_call_winhttp_create_proxy_resolver_success( + bool create_proxy_resolver_success) { + create_proxy_resolver_success_ = create_proxy_resolver_success; + } + bool CallWinHttpCreateProxyResolver(HINTERNET* out_resolver_handle) override { + EXPECT_TRUE(did_call_set_status_callback_); + EXPECT_NE(out_resolver_handle, nullptr); + if (!out_resolver_handle) + return false; + + did_call_create_proxy_resolver_ = true; + if (!create_proxy_resolver_success_) + return false; + + // The caller will be using this handle as an identifier later, so make this + // unique. + *out_resolver_handle = + reinterpret_cast<HINTERNET>(proxy_resolver_identifier_++); + EXPECT_EQ(opened_proxy_resolvers_.count(*out_resolver_handle), 0u); + opened_proxy_resolvers_.emplace(*out_resolver_handle); + + return true; + } + + void set_call_winhttp_get_proxy_for_url_success( + bool get_proxy_for_url_success) { + get_proxy_for_url_success_ = get_proxy_for_url_success; + } + bool CallWinHttpGetProxyForUrlEx(HINTERNET resolver_handle, + const std::string& url, + WINHTTP_AUTOPROXY_OPTIONS* autoproxy_options, + DWORD_PTR context) override { + // This API must be called only after the session has been correctly set up. + EXPECT_TRUE(did_call_open_); + EXPECT_TRUE(did_call_set_timeouts_); + EXPECT_TRUE(did_call_set_status_callback_); + EXPECT_NE(callback_, nullptr); + EXPECT_TRUE(did_call_get_ie_proxy_config_); + EXPECT_TRUE(did_call_create_proxy_resolver_); + EXPECT_TRUE(!did_call_get_proxy_result_); + EXPECT_TRUE(!did_call_free_proxy_result_); + + // This API must always receive valid inputs. + EXPECT_TRUE(!url.empty()); + EXPECT_TRUE(autoproxy_options); + EXPECT_FALSE(autoproxy_options->fAutoLogonIfChallenged); + EXPECT_TRUE(autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_ALLOW_STATIC); + EXPECT_TRUE(autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_ALLOW_CM); + if (autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL) { + EXPECT_TRUE(autoproxy_options->lpszAutoConfigUrl); + } else { + EXPECT_TRUE(!autoproxy_options->lpszAutoConfigUrl); + } + if (autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) { + EXPECT_TRUE(autoproxy_options->dwAutoDetectFlags & + WINHTTP_AUTO_DETECT_TYPE_DNS_A); + EXPECT_TRUE(autoproxy_options->dwAutoDetectFlags & + WINHTTP_AUTO_DETECT_TYPE_DHCP); + } else { + EXPECT_TRUE(!autoproxy_options->dwAutoDetectFlags); + } + + EXPECT_NE(resolver_handle, nullptr); + EXPECT_EQ(opened_proxy_resolvers_.count(resolver_handle), 1u); + EXPECT_NE(context, 0u); + if (!resolver_handle || !context || !callback_) + return false; + + did_call_get_proxy_for_url_ = true; + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&MockWinHttpAPIWrapper::RunCallback, + base::Unretained(this), resolver_handle, context)); + return get_proxy_for_url_success_; + } + + void set_call_winhttp_get_proxy_result_success( + bool get_proxy_result_success) { + get_proxy_result_success_ = get_proxy_result_success; + } + void SetCallbackStatusAndInfo(DWORD callback_status, DWORD info_error) { + callback_status_ = callback_status; + callback_info_ = std::make_unique<WINHTTP_ASYNC_RESULT>(); + callback_info_->dwError = info_error; + } + void AddToProxyResults(const ProxyServer& proxy_server, + bool bypass = false, + bool skip_port = false) { + EXPECT_LT(proxy_result_.cEntries, kMaxProxyEntryLimit - 1); + + // Assign memory as needed. + if (proxy_result_.cEntries == 0) { + proxy_result_.pEntries = + new WINHTTP_PROXY_RESULT_ENTRY[kMaxProxyEntryLimit]; + std::memset(proxy_result_.pEntries, 0, + kMaxProxyEntryLimit * sizeof(WINHTTP_PROXY_RESULT_ENTRY)); + + // The memory of the strings above will be backed by a vector of strings. + proxy_list_.reserve(kMaxProxyEntryLimit); + } + + if (bypass) { + proxy_result_.pEntries[proxy_result_.cEntries].fBypass = TRUE; + } else if (!proxy_server.is_direct()) { + // Now translate the ProxyServer into a WINHTTP_PROXY_RESULT_ENTRY and + // assign. + proxy_result_.pEntries[proxy_result_.cEntries].fProxy = TRUE; + + switch (proxy_server.scheme()) { + case ProxyServer::Scheme::SCHEME_HTTP: + proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme = + INTERNET_SCHEME_HTTP; + break; + case ProxyServer::Scheme::SCHEME_HTTPS: + proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme = + INTERNET_SCHEME_HTTPS; + break; + case ProxyServer::Scheme::SCHEME_SOCKS4: + proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme = + INTERNET_SCHEME_SOCKS; + break; + default: + ADD_FAILURE() + << "Of the possible proxy schemes returned by WinHttp, Chrome " + "supports HTTP(S) and SOCKS4. The ProxyServer::Scheme that " + "triggered this message is: " + << proxy_server.scheme(); + break; + } + + base::string16 proxy_host(proxy_server.host_port_pair().host().begin(), + proxy_server.host_port_pair().host().end()); + proxy_list_.push_back(proxy_host); + + wchar_t* proxy_host_raw = const_cast<wchar_t*>(proxy_list_.back().data()); + proxy_result_.pEntries[proxy_result_.cEntries].pwszProxy = proxy_host_raw; + + if (skip_port) + proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort = + INTERNET_DEFAULT_PORT; + else + proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort = + proxy_server.host_port_pair().port(); + } + + proxy_result_.cEntries++; + } + bool CallWinHttpGetProxyResult(HINTERNET resolver_handle, + WINHTTP_PROXY_RESULT* proxy_result) override { + EXPECT_TRUE(did_call_get_proxy_for_url_); + EXPECT_NE(resolver_handle, nullptr); + EXPECT_EQ(opened_proxy_resolvers_.count(resolver_handle), 1u); + if (!get_proxy_result_success_) + return false; + + EXPECT_NE(proxy_result, nullptr); + proxy_result->cEntries = proxy_result_.cEntries; + proxy_result->pEntries = proxy_result_.pEntries; + + did_call_get_proxy_result_ = true; + return get_proxy_result_success_; + } + + void CallWinHttpFreeProxyResult(WINHTTP_PROXY_RESULT* proxy_result) override { + EXPECT_TRUE(did_call_get_proxy_result_); + EXPECT_NE(proxy_result, nullptr); + did_call_free_proxy_result_ = true; + } + + void CallWinHttpCloseHandle(HINTERNET internet_handle) override { + EXPECT_EQ(opened_proxy_resolvers_.count(internet_handle), 1u); + opened_proxy_resolvers_.erase(internet_handle); + } + + void ResetWinHttpResults() { + if (proxy_result_.pEntries) { + delete[] proxy_result_.pEntries; + proxy_result_.pEntries = nullptr; + proxy_result_ = {0}; + } + proxy_list_.clear(); + } + + private: + void RunCallback(HINTERNET resolver_handle, DWORD_PTR context) { + EXPECT_NE(callback_, nullptr); + EXPECT_NE(resolver_handle, nullptr); + EXPECT_EQ(opened_proxy_resolvers_.count(resolver_handle), 1u); + EXPECT_NE(context, 0u); + callback_(resolver_handle, context, callback_status_, callback_info_.get(), + sizeof(callback_info_.get())); + + // As soon as the callback resolves, WinHttp may choose to delete the memory + // contained by |callback_info_|. This is simulated here. + callback_info_.reset(); + } + + // Data configurable by tests to simulate errors and results from WinHttp. + bool open_success_ = true; + bool set_timeouts_success_ = true; + bool set_status_callback_success_ = true; + bool get_ie_proxy_config_success_ = true; + bool create_proxy_resolver_success_ = true; + bool get_proxy_for_url_success_ = true; + bool get_proxy_result_success_ = true; + DWORD callback_status_ = WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE; + std::unique_ptr<WINHTTP_ASYNC_RESULT> callback_info_; + bool is_autoproxy_enabled_ = false; + base::string16 pac_url_; + base::string16 proxy_; + base::string16 proxy_bypass_; + WINHTTP_PROXY_RESULT proxy_result_ = {0}; + std::vector<base::string16> proxy_list_; + + // Data used internally in the mock to function and validate its own behavior. + bool did_call_open_ = false; + bool did_call_set_timeouts_ = false; + bool did_call_set_status_callback_ = false; + bool did_call_get_ie_proxy_config_ = false; + int proxy_resolver_identifier_ = 1; + std::set<HINTERNET> opened_proxy_resolvers_; + bool did_call_create_proxy_resolver_ = false; + bool did_call_get_proxy_for_url_ = false; + bool did_call_get_proxy_result_ = false; + bool did_call_free_proxy_result_ = false; + WINHTTP_STATUS_CALLBACK callback_ = nullptr; +}; + +} // namespace + +// These tests verify the behavior of the WindowsSystemProxyResolver in +// isolation by mocking out the WinHttpAPIWrapper it uses and the +// WindowsSystemProxyResolutionRequest it normally reports back to. +class WindowsSystemProxyResolverTest : public TestWithTaskEnvironment { + public: + void SetUp() override { + testing::Test::SetUp(); + + if (!WindowsSystemProxyResolutionService::IsSupported()) { + GTEST_SKIP() + << "Windows System Proxy Resolution is only supported on Windows 8+."; + } + + winhttp_api_wrapper_ = new MockWinHttpAPIWrapper(); + // In general, the WindowsSystemProxyResolver should be created via + // CreateWindowsSystemProxyResolver(), so the constructor is protected. Thus + // base::MakeRefCounted cannot be used here. + proxy_resolver_ = base::WrapRefCounted( + new WindowsSystemProxyResolver(base::WrapUnique(winhttp_api_wrapper_))); + } + + void TearDown() override { + EXPECT_TRUE(!proxy_resolver_ || proxy_resolver_->HasOneRef()) + << "This test has a memory leak!"; + ResetProxyResolutionService(); + + testing::Test::TearDown(); + } + + MockWinHttpAPIWrapper* winhttp_api_wrapper() { return winhttp_api_wrapper_; } + + scoped_refptr<WindowsSystemProxyResolver> proxy_resolver() { + return proxy_resolver_; + } + + bool InitializeResolver() { return proxy_resolver_->Initialize(); } + + void AddNoPortProxyToResults() { + const ProxyServer proxy_result = + ProxyServer::FromPacString("PROXY foopy:8080"); + winhttp_api_wrapper_->AddToProxyResults(proxy_result, /*bypass=*/false, + /*skip_port=*/true); + } + + void AddDirectProxyToResults(ProxyList* out_proxy_list) { + winhttp_api_wrapper_->AddToProxyResults(ProxyServer::Direct()); + out_proxy_list->AddProxyServer(ProxyServer::Direct()); + } + + void AddBypassedProxyToResults(ProxyList* out_proxy_list) { + winhttp_api_wrapper_->AddToProxyResults(ProxyServer::Direct(), + /*bypass=*/true); + out_proxy_list->AddProxyServer(ProxyServer::Direct()); + } + + void AddHTTPProxyToResults(ProxyList* out_proxy_list) { + const ProxyServer proxy_result = + ProxyServer::FromPacString("PROXY foopy:8080"); + winhttp_api_wrapper_->AddToProxyResults(proxy_result); + out_proxy_list->AddProxyServer(proxy_result); + } + + void AddHTTPSProxyToResults(ProxyList* out_proxy_list) { + const ProxyServer proxy_result = + ProxyServer::FromPacString("HTTPS foopy:8443"); + winhttp_api_wrapper_->AddToProxyResults(proxy_result); + out_proxy_list->AddProxyServer(proxy_result); + } + + void AddSOCKSProxyToResults(ProxyList* out_proxy_list) { + const ProxyServer proxy_result = + ProxyServer::FromPacString("SOCKS4 foopy:8080"); + winhttp_api_wrapper_->AddToProxyResults(proxy_result); + out_proxy_list->AddProxyServer(proxy_result); + } + + void AddIDNProxyToResults(ProxyList* out_proxy_list) { + const ProxyServer proxy_result = + ProxyServer::FromPacString("HTTPS föopy:8080"); + winhttp_api_wrapper_->AddToProxyResults(proxy_result); + + const ProxyServer expected_proxy_result = + ProxyServer::FromPacString("HTTPS xn--fopy-5jr83a:8080"); + out_proxy_list->AddProxyServer(expected_proxy_result); + } + + void PerformGetProxyForUrlAndValidateResult(const ProxyList& proxy_list, + int net_error, + int windows_error) { + ASSERT_TRUE(InitializeResolver()); + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + ASSERT_TRUE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); + + proxy_resolution_request.WaitForProxyResolutionComplete(); + + EXPECT_TRUE(proxy_list.Equals(proxy_resolution_request.proxy_list())); + EXPECT_EQ(proxy_resolution_request.net_error(), net_error); + EXPECT_EQ(proxy_resolution_request.windows_error(), windows_error); + } + + void DoFailedGetProxyForUrlTest(int net_error, int windows_error) { + PerformGetProxyForUrlAndValidateResult(ProxyList(), net_error, + windows_error); + } + + void DoProxyConfigTest(const ProxyConfig& proxy_config) { + ProxyList proxy_list; + AddHTTPSProxyToResults(&proxy_list); + + base::string16 pac_url; + if (proxy_config.has_pac_url()) + pac_url = base::UTF8ToUTF16(proxy_config.pac_url().spec()); + + base::string16 proxy; + if (!proxy_config.proxy_rules().single_proxies.IsEmpty()) + proxy = base::UTF8ToUTF16( + proxy_config.proxy_rules().single_proxies.ToPacString()); + + base::string16 proxy_bypass; + if (!proxy_config.proxy_rules().bypass_rules.ToString().empty()) + proxy_bypass = + base::UTF8ToUTF16(proxy_config.proxy_rules().bypass_rules.ToString()); + + winhttp_api_wrapper_->set_ie_proxy_config(proxy_config.auto_detect(), + pac_url, proxy, proxy_bypass); + + PerformGetProxyForUrlAndValidateResult(proxy_list, OK, 0); + } + + void DoGetProxyForUrlTest(const ProxyList& proxy_list) { + PerformGetProxyForUrlAndValidateResult(proxy_list, OK, 0); + } + + void ResetProxyResolutionService() { + winhttp_api_wrapper_ = nullptr; + proxy_resolver_.reset(); + } + + private: + MockWinHttpAPIWrapper* winhttp_api_wrapper_ = nullptr; + scoped_refptr<WindowsSystemProxyResolver> proxy_resolver_; +}; + +TEST_F(WindowsSystemProxyResolverTest, InitializeFailOnOpen) { + winhttp_api_wrapper()->set_call_winhttp_open_success(false); + EXPECT_FALSE(InitializeResolver()); +} + +TEST_F(WindowsSystemProxyResolverTest, InitializeFailOnSetTimeouts) { + winhttp_api_wrapper()->set_call_winhttp_set_timeouts_success(false); + EXPECT_FALSE(InitializeResolver()); +} + +TEST_F(WindowsSystemProxyResolverTest, InitializeFailOnSetStatusCallback) { + winhttp_api_wrapper()->set_call_winhttp_set_status_callback_success(false); + EXPECT_FALSE(InitializeResolver()); +} + +TEST_F(WindowsSystemProxyResolverTest, InitializeSucceedsIfWinHttpAPIsWork) { + EXPECT_TRUE(InitializeResolver()); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnGetIEProxySettings) { + winhttp_api_wrapper()->set_call_winhttp_get_ie_proxy_config_success(false); + ASSERT_TRUE(InitializeResolver()); + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + EXPECT_FALSE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + EXPECT_FALSE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); +} + +TEST_F(WindowsSystemProxyResolverTest, + GetProxyForUrlFailOnCreateProxyResolver) { + winhttp_api_wrapper()->set_call_winhttp_create_proxy_resolver_success(false); + ASSERT_TRUE(InitializeResolver()); + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + EXPECT_FALSE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + EXPECT_FALSE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); +} + +TEST_F(WindowsSystemProxyResolverTest, + GetProxyForUrlFailOnWinHttpGetProxyForUrlEx) { + winhttp_api_wrapper()->set_call_winhttp_get_proxy_for_url_success(false); + ASSERT_TRUE(InitializeResolver()); + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + EXPECT_FALSE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + EXPECT_FALSE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnFailedCallback) { + winhttp_api_wrapper()->SetCallbackStatusAndInfo( + WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, API_RECEIVE_RESPONSE); + DoFailedGetProxyForUrlTest(ERR_FAILED, API_RECEIVE_RESPONSE); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnGetProxyResult) { + winhttp_api_wrapper()->set_call_winhttp_get_proxy_result_success(false); + DoFailedGetProxyForUrlTest(ERR_FAILED, 0); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnDefaultPort) { + AddNoPortProxyToResults(); + DoFailedGetProxyForUrlTest(ERR_FAILED, 0); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnNoResults) { + DoFailedGetProxyForUrlTest(ERR_FAILED, 0); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlCancellation) { + ASSERT_TRUE(InitializeResolver()); + + // This extra scope is needed so that the MockProxyResolutionRequest destructs + // before the end of the test. This should help catch any use-after-free issue + // in the code. + { + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + ASSERT_TRUE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); + + proxy_resolver()->RemovePendingCallbackTarget(&proxy_resolution_request); + EXPECT_FALSE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); + } + + // There must never be a callback and the resolver must not be leaked. + base::RunLoop().RunUntilIdle(); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlCancelAndRestart) { + ProxyList expected_proxy_list; + AddHTTPSProxyToResults(&expected_proxy_list); + + ASSERT_TRUE(InitializeResolver()); + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + ASSERT_TRUE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); + + // Abandon the proxy resolution for this request. + proxy_resolver()->RemovePendingCallbackTarget(&proxy_resolution_request); + EXPECT_FALSE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); + + // Start a new proxy resolution for the request. + ASSERT_TRUE( + proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl)); + ASSERT_TRUE( + proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request)); + + // The received callback must be for the second GetProxyForUrl(). + proxy_resolution_request.WaitForProxyResolutionComplete(); + EXPECT_TRUE( + expected_proxy_list.Equals(proxy_resolution_request.proxy_list())); + EXPECT_EQ(proxy_resolution_request.net_error(), OK); + EXPECT_EQ(proxy_resolution_request.windows_error(), 0); + + // There must never be a callback for the first request and the resolver must + // not be leaked. + base::RunLoop().RunUntilIdle(); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigDirect) { + DoProxyConfigTest(ProxyConfig::CreateDirect()); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigAutoDetect) { + DoProxyConfigTest(ProxyConfig::CreateAutoDetect()); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigPacUrl) { + const GURL pac_url("http://pac-site.test/path/to/pac-url.pac"); + DoProxyConfigTest(ProxyConfig::CreateFromCustomPacURL(pac_url)); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigSingleProxy) { + ProxyConfig config; + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS ignored:33"); + config.proxy_rules().single_proxies.AddProxyServer(proxy_server); + DoProxyConfigTest(config); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigBypass) { + ProxyConfig config; + config.proxy_rules().bypass_rules.AddRuleFromString("example.test"); + DoProxyConfigTest(config); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigMultipleSettings) { + ProxyConfig config; + config.set_auto_detect(true); + + const GURL pac_url("http://pac-site.test/path/to/pac-url.pac"); + config.set_pac_url(pac_url); + + const ProxyServer proxy_server = + ProxyServer::FromPacString("HTTPS ignored:33"); + config.proxy_rules().single_proxies.AddProxyServer(proxy_server); + + DoProxyConfigTest(config); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlDirect) { + ProxyList expected_proxy_list; + AddDirectProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlBypass) { + ProxyList expected_proxy_list; + AddBypassedProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlHTTP) { + ProxyList expected_proxy_list; + AddHTTPProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlHTTPS) { + ProxyList expected_proxy_list; + AddHTTPSProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlSOCKS) { + ProxyList expected_proxy_list; + AddSOCKSProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlIDNProxy) { + ProxyList expected_proxy_list; + AddIDNProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlMultipleResults) { + ProxyList expected_proxy_list; + AddHTTPSProxyToResults(&expected_proxy_list); + AddDirectProxyToResults(&expected_proxy_list); + DoGetProxyForUrlTest(expected_proxy_list); +} + +TEST_F(WindowsSystemProxyResolverTest, MultipleCallsToGetProxyForUrl) { + ProxyList expected_proxy_list; + AddHTTPSProxyToResults(&expected_proxy_list); + AddDirectProxyToResults(&expected_proxy_list); + ASSERT_TRUE(InitializeResolver()); + + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest first_proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE( + proxy_resolver()->GetProxyForUrl(&first_proxy_resolution_request, kUrl)); + ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget( + &first_proxy_resolution_request)); + + MockProxyResolutionRequest second_proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE( + proxy_resolver()->GetProxyForUrl(&second_proxy_resolution_request, kUrl)); + ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget( + &second_proxy_resolution_request)); + + first_proxy_resolution_request.WaitForProxyResolutionComplete(); + second_proxy_resolution_request.WaitForProxyResolutionComplete(); + + EXPECT_TRUE( + expected_proxy_list.Equals(first_proxy_resolution_request.proxy_list())); + EXPECT_EQ(first_proxy_resolution_request.net_error(), OK); + EXPECT_EQ(first_proxy_resolution_request.windows_error(), 0); + + EXPECT_TRUE( + expected_proxy_list.Equals(second_proxy_resolution_request.proxy_list())); + EXPECT_EQ(second_proxy_resolution_request.net_error(), OK); + EXPECT_EQ(second_proxy_resolution_request.windows_error(), 0); +} + +TEST_F(WindowsSystemProxyResolverTest, + MultipleCallsToGetProxyForUrlWithOneCancellation) { + ProxyList expected_proxy_list; + AddHTTPSProxyToResults(&expected_proxy_list); + AddDirectProxyToResults(&expected_proxy_list); + ASSERT_TRUE(InitializeResolver()); + + // This extra scope is needed so that the MockProxyResolutionRequests destruct + // before the end of the test. This should help catch any use-after-free issue + // in the code. + { + TestCompletionCallback unused_callback; + NetLogWithSource unused_log; + MockProxyResolutionRequest first_proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE(proxy_resolver()->GetProxyForUrl( + &first_proxy_resolution_request, kUrl)); + ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget( + &first_proxy_resolution_request)); + + MockProxyResolutionRequest second_proxy_resolution_request( + unused_callback.callback(), unused_log, proxy_resolver()); + ASSERT_TRUE(proxy_resolver()->GetProxyForUrl( + &second_proxy_resolution_request, kUrl)); + ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget( + &second_proxy_resolution_request)); + + proxy_resolver()->RemovePendingCallbackTarget( + &first_proxy_resolution_request); + EXPECT_FALSE(proxy_resolver()->HasPendingCallbackTarget( + &first_proxy_resolution_request)); + second_proxy_resolution_request.WaitForProxyResolutionComplete(); + + EXPECT_TRUE(expected_proxy_list.Equals( + second_proxy_resolution_request.proxy_list())); + EXPECT_EQ(second_proxy_resolution_request.net_error(), OK); + EXPECT_EQ(second_proxy_resolution_request.windows_error(), 0); + } + + // There must never be a callback for the first request and the resolver must + // not be leaked. + base::RunLoop().RunUntilIdle(); +} + +} // namespace net diff --git a/chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc new file mode 100644 index 00000000000..fe4a0328683 --- /dev/null +++ b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc @@ -0,0 +1,118 @@ +// Copyright 2020 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/win/winhttp_api_wrapper.h" + +#include <utility> + +#include "base/check_op.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "net/proxy_resolution/win/winhttp_proxy_resolver_functions.h" + +namespace net { + +// TODO(https://crbug.com/1032820): Capture telemetry for WinHttp APIs if +// interesting. + +ScopedIEConfig::ScopedIEConfig() = default; +ScopedIEConfig::~ScopedIEConfig() { + if (ie_config.lpszAutoConfigUrl) + GlobalFree(ie_config.lpszAutoConfigUrl); + if (ie_config.lpszProxy) + GlobalFree(ie_config.lpszProxy); + if (ie_config.lpszProxyBypass) + GlobalFree(ie_config.lpszProxyBypass); +} + +WinHttpAPIWrapper::WinHttpAPIWrapper() = default; +WinHttpAPIWrapper::~WinHttpAPIWrapper() { + if (session_handle_) + ignore_result(CallWinHttpSetStatusCallback(nullptr)); + CloseSessionHandle(); +} + +bool WinHttpAPIWrapper::CallWinHttpOpen() { + DCHECK_EQ(nullptr, session_handle_); + session_handle_ = + WinHttpOpen(nullptr, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); + return (session_handle_ != nullptr); +} + +bool WinHttpAPIWrapper::CallWinHttpSetTimeouts(int resolve_timeout, + int connect_timeout, + int send_timeout, + int receive_timeout) { + DCHECK_NE(nullptr, session_handle_); + return (!!WinHttpSetTimeouts(session_handle_, resolve_timeout, + connect_timeout, send_timeout, receive_timeout)); +} + +bool WinHttpAPIWrapper::CallWinHttpSetStatusCallback( + WINHTTP_STATUS_CALLBACK internet_callback) { + DCHECK_NE(nullptr, session_handle_); + const WINHTTP_STATUS_CALLBACK winhttp_status_callback = + WinHttpSetStatusCallback( + session_handle_, internet_callback, + WINHTTP_CALLBACK_FLAG_REQUEST_ERROR | + WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE, + 0); + return (winhttp_status_callback != WINHTTP_INVALID_STATUS_CALLBACK); +} + +bool WinHttpAPIWrapper::CallWinHttpGetIEProxyConfigForCurrentUser( + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_proxy_config) { + return !!WinHttpGetIEProxyConfigForCurrentUser(ie_proxy_config); +} + +bool WinHttpAPIWrapper::CallWinHttpCreateProxyResolver( + HINTERNET* out_resolver_handle) { + DCHECK_NE(nullptr, session_handle_); + const DWORD result = + WinHttpProxyResolverFunctions::GetInstance().create_proxy_resolver( + session_handle_, out_resolver_handle); + return (result == ERROR_SUCCESS); +} + +bool WinHttpAPIWrapper::CallWinHttpGetProxyForUrlEx( + HINTERNET resolver_handle, + const std::string& url, + WINHTTP_AUTOPROXY_OPTIONS* autoproxy_options, + DWORD_PTR context) { + const base::string16 wide_url(url.begin(), url.end()); + // TODO(https://crbug.com/1032820): Upgrade to WinHttpGetProxyForUrlEx2() + // if there is a clear reason to do so. + const DWORD result = + WinHttpProxyResolverFunctions::GetInstance().get_proxy_for_url_ex( + resolver_handle, wide_url.data(), autoproxy_options, context); + return (result == ERROR_IO_PENDING); +} + +bool WinHttpAPIWrapper::CallWinHttpGetProxyResult( + HINTERNET resolver_handle, + WINHTTP_PROXY_RESULT* proxy_result) { + const DWORD result = + WinHttpProxyResolverFunctions::GetInstance().get_proxy_result( + resolver_handle, proxy_result); + return (result == ERROR_SUCCESS); +} + +VOID WinHttpAPIWrapper::CallWinHttpFreeProxyResult( + WINHTTP_PROXY_RESULT* proxy_result) { + WinHttpProxyResolverFunctions::GetInstance().free_proxy_result(proxy_result); +} + +void WinHttpAPIWrapper::CallWinHttpCloseHandle(HINTERNET internet_handle) { + WinHttpCloseHandle(internet_handle); +} + +void WinHttpAPIWrapper::CloseSessionHandle() { + if (session_handle_) { + CallWinHttpCloseHandle(session_handle_); + session_handle_ = nullptr; + } +} + +} // namespace net diff --git a/chromium/net/proxy_resolution/win/winhttp_api_wrapper.h b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.h new file mode 100644 index 00000000000..f9b641d9453 --- /dev/null +++ b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.h @@ -0,0 +1,130 @@ +// Copyright 2020 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. + +#ifndef NET_PROXY_RESOLUTION_WIN_WINHTTP_API_WRAPPER_H_ +#define NET_PROXY_RESOLUTION_WIN_WINHTTP_API_WRAPPER_H_ + +#include <windows.h> +#include <winhttp.h> + +#include <memory> +#include <string> + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" + +namespace net { + +// This is a utility class that encapsulates the memory management necessary for +// WINHTTP_CURRENT_USER_IE_PROXY_CONFIG in RAII style. +class ScopedIEConfig final { + public: + ScopedIEConfig(); + + ScopedIEConfig(const ScopedIEConfig&) = delete; + ScopedIEConfig& operator=(const ScopedIEConfig&) = delete; + + ~ScopedIEConfig(); + + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* config() { return &ie_config; } + + private: + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0}; +}; + +// This provides a layer of abstraction between calling code and WinHTTP APIs, +// allowing them to be mocked out for testing. This object is not thread safe +// and it's expected that the caller will handle using it on the same thread or +// sequence. In general, documentation for these APIs can be found here: +// https://docs.microsoft.com/en-us/windows/win32/api/winhttp/ +class NET_EXPORT WinHttpAPIWrapper { + public: + WinHttpAPIWrapper(); + + WinHttpAPIWrapper(const WinHttpAPIWrapper&) = delete; + WinHttpAPIWrapper& operator=(const WinHttpAPIWrapper&) = delete; + + virtual ~WinHttpAPIWrapper(); + + // Creates our WinHttp session handle |session_handle_|. The lifetime of that + // session handle is determined by the lifetime of this object. It'll get + // closed when this object destructs. + virtual bool CallWinHttpOpen() WARN_UNUSED_RESULT; + + // Controls the timeout for WinHttpGetProxyForUrlEx(). + virtual bool CallWinHttpSetTimeouts(int resolve_timeout, + int connect_timeout, + int send_timeout, + int receive_timeout) WARN_UNUSED_RESULT; + + // Sets the callback WinHttp will call into with the result of any + // asynchronous call. + virtual bool CallWinHttpSetStatusCallback( + WINHTTP_STATUS_CALLBACK internet_callback) WARN_UNUSED_RESULT; + + // Fetches the proxy configs for the current active network connection and + // current Windows user. The |ie_proxy_config| says whether or not + // AutoProxy (WPAD) is enabled and if there's a PAC URL configured for this + // connection/user. + virtual bool CallWinHttpGetIEProxyConfigForCurrentUser( + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_proxy_config) WARN_UNUSED_RESULT; + + // Creates a handle |resolver_handle| that should be used for the call to + // WinHttpGetProxyForUrlEx(). + virtual bool CallWinHttpCreateProxyResolver(HINTERNET* out_resolver_handle) + WARN_UNUSED_RESULT; + + // Using the specific |resolver_handle| handle from + // CallWinHttpCreateProxyResolver(), resolve a proxy for a specific |url| with + // the aid of some |autoproxy_options|. When WinHttpGetProxyForUrlEx() + // finishes its work or hits an error, it'll call into the callback set by + // CallWinHttpSetStatusCallback() above exactly once and supply the provided + // |context|. + // + // WinHttpGetProxyForUrlEx() will go async to do all necessary logic. As long + // as it receives good inputs (valid handle, valid combination of flags, + // non-null PAC URL if needed), this API will almost always return + // ERROR_IO_PENDING. It'll only fail for reasons like running out of memory. + // When it returns ERROR_IO_PENDING, CallWinHttpGetProxyForUrlEx() will return + // true. + // + // WinHttpGetProxyForUrlEx() will do proxy fallback internally and return to + // a proxy result. It will first check WPAD (if enabled). If that fails, it'll + // attempt to download and run any provided PAC script. If the PAC script was + // not provided or if it fails, it'll use the right per-interface static + // proxy. If all else fails or isn't configured, it'll simply return DIRECT. + // WinHttpGetProxyForUrlEx() supports commonly used enterprise proxy features + // such as DirectAccess/NRPT. + virtual bool CallWinHttpGetProxyForUrlEx( + HINTERNET resolver_handle, + const std::string& url, + WINHTTP_AUTOPROXY_OPTIONS* autoproxy_options, + DWORD_PTR context) WARN_UNUSED_RESULT; + + // As long as CallWinHttpGetProxyForUrlEx() doesn't hit any errors, there will + // be a proxy result to examine. This function retrieves that proxy resolution + // result |proxy_result| using the resolver's handle |resolver_handle|. The + // result must be freed with CallWinHttpFreeProxyResult(). + virtual bool CallWinHttpGetProxyResult(HINTERNET resolver_handle, + WINHTTP_PROXY_RESULT* proxy_result) + WARN_UNUSED_RESULT; + + // Frees the |proxy_result| retrieved by CallWinHttpGetProxyResult(). + virtual void CallWinHttpFreeProxyResult(WINHTTP_PROXY_RESULT* proxy_result); + + // Every opened HINTERNET handle must be closed. This closes handle + // |internet_handle|. After being closed, WinHttp calls cannot be made using + // that handle. + virtual void CallWinHttpCloseHandle(HINTERNET internet_handle); + + private: + // Closes |session_handle_|. + void CloseSessionHandle(); + + HINTERNET session_handle_ = nullptr; +}; + +} // namespace net + +#endif // NET_PROXY_RESOLUTION_WIN_WINHTTP_API_WRAPPER_H_ diff --git a/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc new file mode 100644 index 00000000000..9fb8b4613f2 --- /dev/null +++ b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc @@ -0,0 +1,41 @@ +// Copyright 2020 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/win/winhttp_proxy_resolver_functions.h" + +namespace net { + +WinHttpProxyResolverFunctions::WinHttpProxyResolverFunctions() { + HMODULE winhttp_module = + LoadLibraryEx(L"winhttp.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (winhttp_module) { + create_proxy_resolver = reinterpret_cast<WinHttpCreateProxyResolverFunc>( + ::GetProcAddress(winhttp_module, "WinHttpCreateProxyResolver")); + get_proxy_for_url_ex = reinterpret_cast<WinHttpGetProxyForUrlExFunc>( + ::GetProcAddress(winhttp_module, "WinHttpGetProxyForUrlEx")); + get_proxy_result = reinterpret_cast<WinHttpGetProxyResultFunc>( + ::GetProcAddress(winhttp_module, "WinHttpGetProxyResult")); + free_proxy_result = reinterpret_cast<WinHttpFreeProxyResultFunc>( + ::GetProcAddress(winhttp_module, "WinHttpFreeProxyResult")); + } +} + +// Never called due to base::NoDestructor. +WinHttpProxyResolverFunctions::~WinHttpProxyResolverFunctions() = default; + +bool WinHttpProxyResolverFunctions::are_all_functions_loaded() const { + return create_proxy_resolver && get_proxy_for_url_ex && get_proxy_result && + free_proxy_result; +} + +// static +const WinHttpProxyResolverFunctions& +WinHttpProxyResolverFunctions::GetInstance() { + // This is a singleton for performance reasons. This avoids having to load + // proxy resolver functions multiple times. + static base::NoDestructor<WinHttpProxyResolverFunctions> instance; + return *instance; +} + +} // namespace net diff --git a/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h new file mode 100644 index 00000000000..51b512a0038 --- /dev/null +++ b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h @@ -0,0 +1,47 @@ +// Copyright 2020 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. + +#ifndef NET_PROXY_RESOLUTION_WIN_WINHTTP_PROXY_RESOLVER_FUNCTIONS_H_ +#define NET_PROXY_RESOLUTION_WIN_WINHTTP_PROXY_RESOLVER_FUNCTIONS_H_ + +#include <windows.h> +#include <winhttp.h> + +#include "base/no_destructor.h" + +namespace net { + +// Not all WinHttp APIs we'll be using exist in all versions of Windows. +// Several only exist in Windows 8+. Thus, each function entry point must be +// loaded dynamically. +struct WinHttpProxyResolverFunctions { + public: + WinHttpProxyResolverFunctions(const WinHttpProxyResolverFunctions&) = delete; + WinHttpProxyResolverFunctions& operator=( + const WinHttpProxyResolverFunctions&) = delete; + + bool are_all_functions_loaded() const; + + static const WinHttpProxyResolverFunctions& GetInstance(); + + using WinHttpCreateProxyResolverFunc = decltype(WinHttpCreateProxyResolver)*; + using WinHttpGetProxyForUrlExFunc = decltype(WinHttpGetProxyForUrlEx)*; + using WinHttpGetProxyResultFunc = decltype(WinHttpGetProxyResult)*; + using WinHttpFreeProxyResultFunc = decltype(WinHttpFreeProxyResult)*; + + WinHttpCreateProxyResolverFunc create_proxy_resolver = nullptr; + WinHttpGetProxyForUrlExFunc get_proxy_for_url_ex = nullptr; + WinHttpGetProxyResultFunc get_proxy_result = nullptr; + WinHttpFreeProxyResultFunc free_proxy_result = nullptr; + + private: + friend class base::NoDestructor<WinHttpProxyResolverFunctions>; + + WinHttpProxyResolverFunctions(); + ~WinHttpProxyResolverFunctions(); +}; + +} // namespace net + +#endif // NET_PROXY_RESOLUTION_WIN_WINHTTP_PROXY_RESOLVER_FUNCTIONS_H_ |