• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/socket/connect_job_factory.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/check.h"
11 #include "base/containers/flat_set.h"
12 #include "base/memory/scoped_refptr.h"
13 #include "net/base/host_port_pair.h"
14 #include "net/base/network_anonymization_key.h"
15 #include "net/base/privacy_mode.h"
16 #include "net/base/proxy_chain.h"
17 #include "net/base/proxy_server.h"
18 #include "net/base/request_priority.h"
19 #include "net/dns/public/secure_dns_policy.h"
20 #include "net/http/http_proxy_connect_job.h"
21 #include "net/socket/connect_job.h"
22 #include "net/socket/next_proto.h"
23 #include "net/socket/socket_tag.h"
24 #include "net/socket/socks_connect_job.h"
25 #include "net/socket/ssl_connect_job.h"
26 #include "net/socket/transport_connect_job.h"
27 #include "net/ssl/ssl_config.h"
28 #include "net/traffic_annotation/network_traffic_annotation.h"
29 #include "third_party/abseil-cpp/absl/types/optional.h"
30 #include "third_party/abseil-cpp/absl/types/variant.h"
31 #include "url/gurl.h"
32 #include "url/scheme_host_port.h"
33 
34 namespace net {
35 
36 namespace {
37 
38 template <typename T>
CreateFactoryIfNull(std::unique_ptr<T> in)39 std::unique_ptr<T> CreateFactoryIfNull(std::unique_ptr<T> in) {
40   if (in)
41     return in;
42   return std::make_unique<T>();
43 }
44 
UsingSsl(const ConnectJobFactory::Endpoint & endpoint)45 bool UsingSsl(const ConnectJobFactory::Endpoint& endpoint) {
46   if (absl::holds_alternative<url::SchemeHostPort>(endpoint)) {
47     return GURL::SchemeIsCryptographic(
48         base::ToLowerASCII(absl::get<url::SchemeHostPort>(endpoint).scheme()));
49   }
50 
51   DCHECK(
52       absl::holds_alternative<ConnectJobFactory::SchemelessEndpoint>(endpoint));
53   return absl::get<ConnectJobFactory::SchemelessEndpoint>(endpoint).using_ssl;
54 }
55 
ToHostPortPair(const ConnectJobFactory::Endpoint & endpoint)56 HostPortPair ToHostPortPair(const ConnectJobFactory::Endpoint& endpoint) {
57   if (absl::holds_alternative<url::SchemeHostPort>(endpoint)) {
58     return HostPortPair::FromSchemeHostPort(
59         absl::get<url::SchemeHostPort>(endpoint));
60   }
61 
62   DCHECK(
63       absl::holds_alternative<ConnectJobFactory::SchemelessEndpoint>(endpoint));
64   return absl::get<ConnectJobFactory::SchemelessEndpoint>(endpoint)
65       .host_port_pair;
66 }
67 
ToTransportEndpoint(const ConnectJobFactory::Endpoint & endpoint)68 TransportSocketParams::Endpoint ToTransportEndpoint(
69     const ConnectJobFactory::Endpoint& endpoint) {
70   if (absl::holds_alternative<url::SchemeHostPort>(endpoint))
71     return absl::get<url::SchemeHostPort>(endpoint);
72 
73   DCHECK(
74       absl::holds_alternative<ConnectJobFactory::SchemelessEndpoint>(endpoint));
75   return absl::get<ConnectJobFactory::SchemelessEndpoint>(endpoint)
76       .host_port_pair;
77 }
78 
SupportedProtocolsFromSSLConfig(const SSLConfig & config)79 base::flat_set<std::string> SupportedProtocolsFromSSLConfig(
80     const SSLConfig& config) {
81   // We convert because `SSLConfig` uses `NextProto` for ALPN protocols while
82   // `TransportConnectJob` and DNS logic needs `std::string`. See
83   // https://crbug.com/1286835.
84   return base::MakeFlatSet<std::string>(config.alpn_protos, /*comp=*/{},
85                                         NextProtoToString);
86 }
87 
MaybeForceHttp11(const ProxyServer & proxy_server,const CommonConnectJobParams * common_connect_job_params,const NetworkAnonymizationKey & network_anonymization_key,SSLConfig * proxy_server_ssl_config)88 void MaybeForceHttp11(const ProxyServer& proxy_server,
89                       const CommonConnectJobParams* common_connect_job_params,
90                       const NetworkAnonymizationKey& network_anonymization_key,
91                       SSLConfig* proxy_server_ssl_config) {
92   HttpServerProperties* http_server_properties =
93       common_connect_job_params->http_server_properties;
94   if (http_server_properties) {
95     if (proxy_server.is_https()) {
96       http_server_properties->MaybeForceHTTP11(
97           url::SchemeHostPort(url::kHttpsScheme,
98                               proxy_server.host_port_pair().host(),
99                               proxy_server.host_port_pair().port()),
100           network_anonymization_key, proxy_server_ssl_config);
101     }
102   }
103 }
104 
105 }  // namespace
106 
ConnectJobFactory(std::unique_ptr<HttpProxyConnectJob::Factory> http_proxy_connect_job_factory,std::unique_ptr<SOCKSConnectJob::Factory> socks_connect_job_factory,std::unique_ptr<SSLConnectJob::Factory> ssl_connect_job_factory,std::unique_ptr<TransportConnectJob::Factory> transport_connect_job_factory)107 ConnectJobFactory::ConnectJobFactory(
108     std::unique_ptr<HttpProxyConnectJob::Factory>
109         http_proxy_connect_job_factory,
110     std::unique_ptr<SOCKSConnectJob::Factory> socks_connect_job_factory,
111     std::unique_ptr<SSLConnectJob::Factory> ssl_connect_job_factory,
112     std::unique_ptr<TransportConnectJob::Factory> transport_connect_job_factory)
113     : http_proxy_connect_job_factory_(
114           CreateFactoryIfNull(std::move(http_proxy_connect_job_factory))),
115       socks_connect_job_factory_(
116           CreateFactoryIfNull(std::move(socks_connect_job_factory))),
117       ssl_connect_job_factory_(
118           CreateFactoryIfNull(std::move(ssl_connect_job_factory))),
119       transport_connect_job_factory_(
120           CreateFactoryIfNull(std::move(transport_connect_job_factory))) {}
121 
122 ConnectJobFactory::~ConnectJobFactory() = default;
123 
CreateConnectJob(url::SchemeHostPort endpoint,const ProxyChain & proxy_chain,const absl::optional<NetworkTrafficAnnotationTag> & proxy_annotation_tag,const SSLConfig * ssl_config_for_origin,const SSLConfig * base_ssl_config_for_proxies,bool force_tunnel,PrivacyMode privacy_mode,const OnHostResolutionCallback & resolution_callback,RequestPriority request_priority,SocketTag socket_tag,const NetworkAnonymizationKey & network_anonymization_key,SecureDnsPolicy secure_dns_policy,const CommonConnectJobParams * common_connect_job_params,ConnectJob::Delegate * delegate) const124 std::unique_ptr<ConnectJob> ConnectJobFactory::CreateConnectJob(
125     url::SchemeHostPort endpoint,
126     const ProxyChain& proxy_chain,
127     const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
128     const SSLConfig* ssl_config_for_origin,
129     const SSLConfig* base_ssl_config_for_proxies,
130     bool force_tunnel,
131     PrivacyMode privacy_mode,
132     const OnHostResolutionCallback& resolution_callback,
133     RequestPriority request_priority,
134     SocketTag socket_tag,
135     const NetworkAnonymizationKey& network_anonymization_key,
136     SecureDnsPolicy secure_dns_policy,
137     const CommonConnectJobParams* common_connect_job_params,
138     ConnectJob::Delegate* delegate) const {
139   return CreateConnectJob(
140       Endpoint(std::move(endpoint)), proxy_chain, proxy_annotation_tag,
141       ssl_config_for_origin, base_ssl_config_for_proxies, force_tunnel,
142       privacy_mode, resolution_callback, request_priority, socket_tag,
143       network_anonymization_key, secure_dns_policy, common_connect_job_params,
144       delegate);
145 }
146 
CreateConnectJob(bool using_ssl,HostPortPair endpoint,const ProxyChain & proxy_chain,const absl::optional<NetworkTrafficAnnotationTag> & proxy_annotation_tag,const SSLConfig * ssl_config_for_origin,const SSLConfig * base_ssl_config_for_proxies,bool force_tunnel,PrivacyMode privacy_mode,const OnHostResolutionCallback & resolution_callback,RequestPriority request_priority,SocketTag socket_tag,const NetworkAnonymizationKey & network_anonymization_key,SecureDnsPolicy secure_dns_policy,const CommonConnectJobParams * common_connect_job_params,ConnectJob::Delegate * delegate) const147 std::unique_ptr<ConnectJob> ConnectJobFactory::CreateConnectJob(
148     bool using_ssl,
149     HostPortPair endpoint,
150     const ProxyChain& proxy_chain,
151     const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
152     const SSLConfig* ssl_config_for_origin,
153     const SSLConfig* base_ssl_config_for_proxies,
154     bool force_tunnel,
155     PrivacyMode privacy_mode,
156     const OnHostResolutionCallback& resolution_callback,
157     RequestPriority request_priority,
158     SocketTag socket_tag,
159     const NetworkAnonymizationKey& network_anonymization_key,
160     SecureDnsPolicy secure_dns_policy,
161     const CommonConnectJobParams* common_connect_job_params,
162     ConnectJob::Delegate* delegate) const {
163   SchemelessEndpoint schemeless_endpoint{using_ssl, std::move(endpoint)};
164   return CreateConnectJob(
165       std::move(schemeless_endpoint), proxy_chain, proxy_annotation_tag,
166       ssl_config_for_origin, base_ssl_config_for_proxies, force_tunnel,
167       privacy_mode, resolution_callback, request_priority, socket_tag,
168       network_anonymization_key, secure_dns_policy, common_connect_job_params,
169       delegate);
170 }
171 
CreateConnectJob(Endpoint endpoint,const ProxyChain & proxy_chain,const absl::optional<NetworkTrafficAnnotationTag> & proxy_annotation_tag,const SSLConfig * ssl_config_for_origin,const SSLConfig * base_ssl_config_for_proxies,bool force_tunnel,PrivacyMode privacy_mode,const OnHostResolutionCallback & resolution_callback,RequestPriority request_priority,SocketTag socket_tag,const NetworkAnonymizationKey & network_anonymization_key,SecureDnsPolicy secure_dns_policy,const CommonConnectJobParams * common_connect_job_params,ConnectJob::Delegate * delegate) const172 std::unique_ptr<ConnectJob> ConnectJobFactory::CreateConnectJob(
173     Endpoint endpoint,
174     const ProxyChain& proxy_chain,
175     const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
176     const SSLConfig* ssl_config_for_origin,
177     const SSLConfig* base_ssl_config_for_proxies,
178     bool force_tunnel,
179     PrivacyMode privacy_mode,
180     const OnHostResolutionCallback& resolution_callback,
181     RequestPriority request_priority,
182     SocketTag socket_tag,
183     const NetworkAnonymizationKey& network_anonymization_key,
184     SecureDnsPolicy secure_dns_policy,
185     const CommonConnectJobParams* common_connect_job_params,
186     ConnectJob::Delegate* delegate) const {
187   scoped_refptr<HttpProxySocketParams> http_proxy_params;
188   scoped_refptr<SOCKSSocketParams> socks_params;
189   base::flat_set<std::string> no_alpn_protocols;
190 
191   DCHECK(proxy_chain.IsValid());
192   if (!proxy_chain.is_direct()) {
193     // The first iteration of this loop is taken for all types of proxies and
194     // creates a TransportSocketParams and other socket params based on the
195     // proxy type. For nested proxies, we then create additional SSLSocketParam
196     // and HttpProxySocketParam objects for the remaining hops. This is done by
197     // working backwards through the proxy chain and creating socket params
198     // such that connect jobs will be created recursively with dependencies in
199     // the correct order (in other words, the inner-most connect job will
200     // establish a connection to the first proxy, and then that connection
201     // will get used to establish a connection to the second proxy).
202     for (size_t proxy_index = 0; proxy_index < proxy_chain.length();
203          ++proxy_index) {
204       const ProxyServer& proxy_server = proxy_chain.GetProxyServer(proxy_index);
205 
206       SSLConfig proxy_server_ssl_config;
207       if (proxy_server.is_secure_http_like()) {
208         DCHECK(base_ssl_config_for_proxies);
209         proxy_server_ssl_config = *base_ssl_config_for_proxies;
210         // Disable cert verification network fetches for secure proxies, since
211         // those network requests are probably going to need to go through the
212         // proxy chain too.
213         //
214         // Any proxy-specific SSL behavior here should also be configured for
215         // QUIC proxies.
216         //
217         proxy_server_ssl_config.disable_cert_verification_network_fetches =
218             true;
219         MaybeForceHttp11(proxy_server, common_connect_job_params,
220                          network_anonymization_key, &proxy_server_ssl_config);
221       }
222 
223       scoped_refptr<TransportSocketParams> proxy_tcp_params;
224       if (proxy_index == 0) {
225         // In the first iteration create the only TransportSocketParams object,
226         // corresponding to the transport socket we want to create to the first
227         // proxy.
228         // TODO(crbug.com/1206799): For an http-like proxy, should this pass a
229         // `SchemeHostPort`, so proxies can participate in ECH? Note doing so
230         // with `SCHEME_HTTP` requires handling the HTTPS record upgrade.
231         proxy_tcp_params = base::MakeRefCounted<TransportSocketParams>(
232             proxy_server.host_port_pair(), proxy_dns_network_anonymization_key_,
233             secure_dns_policy, resolution_callback,
234             proxy_server.is_secure_http_like()
235                 ? SupportedProtocolsFromSSLConfig(proxy_server_ssl_config)
236                 : no_alpn_protocols);
237       } else {
238         // TODO(https://crbug.com/1491092): For now we will assume that proxy
239         // chains with multiple proxies must all use HTTPS.
240         CHECK(http_proxy_params);
241         CHECK(http_proxy_params->ssl_params());
242         CHECK(
243             proxy_chain.GetProxyServer(proxy_index - 1).is_secure_http_like());
244       }
245 
246       if (proxy_server.is_http_like()) {
247         scoped_refptr<SSLSocketParams> ssl_params;
248         if (proxy_server.is_secure_http_like()) {
249           // Set `ssl_params`, and unset `proxy_tcp_params`.
250           ssl_params = base::MakeRefCounted<SSLSocketParams>(
251               std::move(proxy_tcp_params), /*socks_proxy_params=*/nullptr,
252               std::move(http_proxy_params), proxy_server.host_port_pair(),
253               proxy_server_ssl_config, PRIVACY_MODE_DISABLED,
254               network_anonymization_key);
255           proxy_tcp_params = nullptr;
256         }
257 
258         // The endpoint parameter for this HttpProxySocketParams, which is what
259         // we will CONNECT to, should correspond to either `endpoint` (for
260         // one-hop proxies) or the proxy server at index 1 (for n-hop proxies).
261         HostPortPair connect_host_port_pair;
262         bool should_tunnel;
263         if (proxy_index + 1 == proxy_chain.length()) {
264           connect_host_port_pair = ToHostPortPair(endpoint);
265           should_tunnel = force_tunnel || UsingSsl(endpoint);
266         } else {
267           const auto& next_proxy_server =
268               proxy_chain.GetProxyServer(proxy_index + 1);
269           connect_host_port_pair = next_proxy_server.host_port_pair();
270           // TODO(https://crbug.com/1491092): For now we will assume that proxy
271           // chains with multiple proxies must all use HTTPS.
272           CHECK(next_proxy_server.is_secure_http_like());
273           should_tunnel = true;
274         }
275 
276         // TODO(crbug.com/1206799): Pass `endpoint` directly (preserving
277         // scheme when available)?
278         http_proxy_params = base::MakeRefCounted<HttpProxySocketParams>(
279             std::move(proxy_tcp_params), std::move(ssl_params),
280             connect_host_port_pair, proxy_chain, proxy_index, should_tunnel,
281             *proxy_annotation_tag, network_anonymization_key,
282             secure_dns_policy);
283       } else {
284         DCHECK(proxy_server.is_socks());
285         DCHECK_EQ(1u, proxy_chain.length());
286         // TODO(crbug.com/1206799): Pass `endpoint` directly (preserving scheme
287         // when available)?
288         socks_params = base::MakeRefCounted<SOCKSSocketParams>(
289             std::move(proxy_tcp_params),
290             proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5,
291             ToHostPortPair(endpoint), network_anonymization_key,
292             *proxy_annotation_tag);
293       }
294     }
295   }
296 
297   // Deal with SSL - which layers on top of any given proxy.
298   if (UsingSsl(endpoint)) {
299     DCHECK(ssl_config_for_origin);
300     scoped_refptr<TransportSocketParams> ssl_tcp_params;
301     if (proxy_chain.is_direct()) {
302       ssl_tcp_params = base::MakeRefCounted<TransportSocketParams>(
303           ToTransportEndpoint(endpoint), network_anonymization_key,
304           secure_dns_policy, resolution_callback,
305           SupportedProtocolsFromSSLConfig(*ssl_config_for_origin));
306     }
307     // TODO(crbug.com/1206799): Pass `endpoint` directly (preserving scheme
308     // when available)?
309     auto ssl_params = base::MakeRefCounted<SSLSocketParams>(
310         std::move(ssl_tcp_params), std::move(socks_params),
311         std::move(http_proxy_params), ToHostPortPair(endpoint),
312         *ssl_config_for_origin, privacy_mode, network_anonymization_key);
313     return ssl_connect_job_factory_->Create(
314         request_priority, socket_tag, common_connect_job_params,
315         std::move(ssl_params), delegate, /*net_log=*/nullptr);
316   }
317 
318   // Only SSL/TLS-based endpoints have ALPN protocols.
319   if (proxy_chain.is_direct()) {
320     auto tcp_params = base::MakeRefCounted<TransportSocketParams>(
321         ToTransportEndpoint(endpoint), network_anonymization_key,
322         secure_dns_policy, resolution_callback, no_alpn_protocols);
323     return transport_connect_job_factory_->Create(
324         request_priority, socket_tag, common_connect_job_params, tcp_params,
325         delegate, /*net_log=*/nullptr);
326   }
327 
328   const ProxyServer& first_proxy_server =
329       proxy_chain.GetProxyServer(/*chain_index=*/0);
330   if (first_proxy_server.is_http_like()) {
331     return http_proxy_connect_job_factory_->Create(
332         request_priority, socket_tag, common_connect_job_params,
333         std::move(http_proxy_params), delegate, /*net_log=*/nullptr);
334   }
335 
336   DCHECK(first_proxy_server.is_socks());
337   return socks_connect_job_factory_->Create(
338       request_priority, socket_tag, common_connect_job_params,
339       std::move(socks_params), delegate, /*net_log=*/nullptr);
340 }
341 
342 }  // namespace net
343