• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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/client_socket_pool.h"
6 
7 #include <memory>
8 #include <string_view>
9 #include <utility>
10 #include <vector>
11 
12 #include "base/check_op.h"
13 #include "base/feature_list.h"
14 #include "base/functional/bind.h"
15 #include "base/strings/strcat.h"
16 #include "net/base/features.h"
17 #include "net/base/host_port_pair.h"
18 #include "net/base/proxy_chain.h"
19 #include "net/base/session_usage.h"
20 #include "net/dns/public/secure_dns_policy.h"
21 #include "net/http/http_proxy_connect_job.h"
22 #include "net/log/net_log_event_type.h"
23 #include "net/log/net_log_with_source.h"
24 #include "net/socket/connect_job.h"
25 #include "net/socket/connect_job_factory.h"
26 #include "net/socket/socks_connect_job.h"
27 #include "net/socket/ssl_connect_job.h"
28 #include "net/socket/stream_socket.h"
29 #include "net/spdy/spdy_session.h"
30 #include "net/spdy/spdy_session_pool.h"
31 #include "net/ssl/ssl_config.h"
32 #include "url/gurl.h"
33 #include "url/scheme_host_port.h"
34 #include "url/url_constants.h"
35 
36 namespace net {
37 
38 namespace {
39 
40 // The maximum duration, in seconds, to keep used idle persistent sockets alive.
41 int64_t g_used_idle_socket_timeout_s = 300;  // 5 minutes
42 
43 // Invoked by the transport socket pool after host resolution is complete
44 // to allow the connection to be aborted, if a matching SPDY session can
45 // be found. Returns OnHostResolutionCallbackResult::kMayBeDeletedAsync if such
46 // a session is found, as it will post a task that may delete the calling
47 // ConnectJob. Also returns kMayBeDeletedAsync if there may already be such
48 // a task posted.
OnHostResolution(SpdySessionPool * spdy_session_pool,const SpdySessionKey & spdy_session_key,bool is_for_websockets,const HostPortPair & host_port_pair,const std::vector<HostResolverEndpointResult> & endpoint_results,const std::set<std::string> & aliases)49 OnHostResolutionCallbackResult OnHostResolution(
50     SpdySessionPool* spdy_session_pool,
51     const SpdySessionKey& spdy_session_key,
52     bool is_for_websockets,
53     const HostPortPair& host_port_pair,
54     const std::vector<HostResolverEndpointResult>& endpoint_results,
55     const std::set<std::string>& aliases) {
56   DCHECK(host_port_pair == spdy_session_key.host_port_pair());
57 
58   // It is OK to dereference spdy_session_pool, because the
59   // ClientSocketPoolManager will be destroyed in the same callback that
60   // destroys the SpdySessionPool.
61   return spdy_session_pool->OnHostResolutionComplete(
62       spdy_session_key, is_for_websockets, endpoint_results, aliases);
63 }
64 
65 }  // namespace
66 
SocketParams(const std::vector<SSLConfig::CertAndStatus> & allowed_bad_certs)67 ClientSocketPool::SocketParams::SocketParams(
68     const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs)
69     : allowed_bad_certs_(allowed_bad_certs) {}
70 
71 ClientSocketPool::SocketParams::~SocketParams() = default;
72 
73 scoped_refptr<ClientSocketPool::SocketParams>
CreateForHttpForTesting()74 ClientSocketPool::SocketParams::CreateForHttpForTesting() {
75   return base::MakeRefCounted<SocketParams>(
76       /*allowed_bad_certs=*/std::vector<SSLConfig::CertAndStatus>());
77 }
78 
79 // static
GetPrivacyModeGroupIdPrefix(PrivacyMode privacy_mode)80 std::string_view ClientSocketPool::GroupId::GetPrivacyModeGroupIdPrefix(
81     PrivacyMode privacy_mode) {
82   switch (privacy_mode) {
83     case PrivacyMode::PRIVACY_MODE_DISABLED:
84       return "";
85     case PrivacyMode::PRIVACY_MODE_ENABLED:
86       return "pm/";
87     case PrivacyMode::PRIVACY_MODE_ENABLED_WITHOUT_CLIENT_CERTS:
88       return "pmwocc/";
89     case PrivacyMode::PRIVACY_MODE_ENABLED_PARTITIONED_STATE_ALLOWED:
90       return "pmpsa/";
91   }
92 }
93 
94 // static
GetSecureDnsPolicyGroupIdPrefix(SecureDnsPolicy secure_dns_policy)95 std::string_view ClientSocketPool::GroupId::GetSecureDnsPolicyGroupIdPrefix(
96     SecureDnsPolicy secure_dns_policy) {
97   switch (secure_dns_policy) {
98     case SecureDnsPolicy::kAllow:
99       return "";
100     case SecureDnsPolicy::kDisable:
101       return "dsd/";
102     case SecureDnsPolicy::kBootstrap:
103       return "dns_bootstrap/";
104   }
105 }
106 
GroupId()107 ClientSocketPool::GroupId::GroupId()
108     : privacy_mode_(PrivacyMode::PRIVACY_MODE_DISABLED) {}
109 
GroupId(url::SchemeHostPort destination,PrivacyMode privacy_mode,NetworkAnonymizationKey network_anonymization_key,SecureDnsPolicy secure_dns_policy,bool disable_cert_network_fetches)110 ClientSocketPool::GroupId::GroupId(
111     url::SchemeHostPort destination,
112     PrivacyMode privacy_mode,
113     NetworkAnonymizationKey network_anonymization_key,
114     SecureDnsPolicy secure_dns_policy,
115     bool disable_cert_network_fetches)
116     : destination_(std::move(destination)),
117       privacy_mode_(privacy_mode),
118       network_anonymization_key_(
119           NetworkAnonymizationKey::IsPartitioningEnabled()
120               ? std::move(network_anonymization_key)
121               : NetworkAnonymizationKey()),
122       secure_dns_policy_(secure_dns_policy),
123       disable_cert_network_fetches_(disable_cert_network_fetches) {
124   DCHECK(destination_.IsValid());
125 
126   // ClientSocketPool only expected to be used for HTTP/HTTPS/WS/WSS cases, and
127   // "ws"/"wss" schemes should be converted to "http"/"https" equivalent first.
128   DCHECK(destination_.scheme() == url::kHttpScheme ||
129          destination_.scheme() == url::kHttpsScheme);
130 }
131 
132 ClientSocketPool::GroupId::GroupId(const GroupId& group_id) = default;
133 
134 ClientSocketPool::GroupId::~GroupId() = default;
135 
136 ClientSocketPool::GroupId& ClientSocketPool::GroupId::operator=(
137     const GroupId& group_id) = default;
138 
139 ClientSocketPool::GroupId& ClientSocketPool::GroupId::operator=(
140     GroupId&& group_id) = default;
141 
ToString() const142 std::string ClientSocketPool::GroupId::ToString() const {
143   return base::StrCat(
144       {disable_cert_network_fetches_ ? "disable_cert_network_fetches/" : "",
145        GetSecureDnsPolicyGroupIdPrefix(secure_dns_policy_),
146        GetPrivacyModeGroupIdPrefix(privacy_mode_), destination_.Serialize(),
147        NetworkAnonymizationKey::IsPartitioningEnabled()
148            ? base::StrCat(
149                  {" <", network_anonymization_key_.ToDebugString(), ">"})
150            : ""});
151 }
152 
153 ClientSocketPool::~ClientSocketPool() = default;
154 
155 // static
used_idle_socket_timeout()156 base::TimeDelta ClientSocketPool::used_idle_socket_timeout() {
157   return base::Seconds(g_used_idle_socket_timeout_s);
158 }
159 
160 // static
set_used_idle_socket_timeout(base::TimeDelta timeout)161 void ClientSocketPool::set_used_idle_socket_timeout(base::TimeDelta timeout) {
162   DCHECK_GT(timeout.InSeconds(), 0);
163   g_used_idle_socket_timeout_s = timeout.InSeconds();
164 }
165 
ClientSocketPool(bool is_for_websockets,const CommonConnectJobParams * common_connect_job_params,std::unique_ptr<ConnectJobFactory> connect_job_factory)166 ClientSocketPool::ClientSocketPool(
167     bool is_for_websockets,
168     const CommonConnectJobParams* common_connect_job_params,
169     std::unique_ptr<ConnectJobFactory> connect_job_factory)
170     : is_for_websockets_(is_for_websockets),
171       common_connect_job_params_(common_connect_job_params),
172       connect_job_factory_(std::move(connect_job_factory)) {}
173 
NetLogTcpClientSocketPoolRequestedSocket(const NetLogWithSource & net_log,const GroupId & group_id)174 void ClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket(
175     const NetLogWithSource& net_log,
176     const GroupId& group_id) {
177   // TODO(eroman): Split out the host and port parameters.
178   net_log.AddEvent(NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
179                    [&] { return NetLogGroupIdParams(group_id); });
180 }
181 
NetLogGroupIdParams(const GroupId & group_id)182 base::Value::Dict ClientSocketPool::NetLogGroupIdParams(
183     const GroupId& group_id) {
184   return base::Value::Dict().Set("group_id", group_id.ToString());
185 }
186 
CreateConnectJob(GroupId group_id,scoped_refptr<SocketParams> socket_params,const ProxyChain & proxy_chain,const std::optional<NetworkTrafficAnnotationTag> & proxy_annotation_tag,RequestPriority request_priority,SocketTag socket_tag,ConnectJob::Delegate * delegate)187 std::unique_ptr<ConnectJob> ClientSocketPool::CreateConnectJob(
188     GroupId group_id,
189     scoped_refptr<SocketParams> socket_params,
190     const ProxyChain& proxy_chain,
191     const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
192     RequestPriority request_priority,
193     SocketTag socket_tag,
194     ConnectJob::Delegate* delegate) {
195   bool using_ssl = GURL::SchemeIsCryptographic(group_id.destination().scheme());
196 
197   // If applicable, set up a callback to handle checking for H2 IP pooling
198   // opportunities. We don't perform H2 IP pooling to or through proxy servers,
199   // so ignore those cases.
200   OnHostResolutionCallback resolution_callback;
201   if (using_ssl && proxy_chain.is_direct()) {
202     resolution_callback = base::BindRepeating(
203         &OnHostResolution, common_connect_job_params_->spdy_session_pool,
204         // TODO(crbug.com/40181080): Pass along as SchemeHostPort.
205         SpdySessionKey(HostPortPair::FromSchemeHostPort(group_id.destination()),
206                        group_id.privacy_mode(), proxy_chain,
207                        SessionUsage::kDestination, socket_tag,
208                        group_id.network_anonymization_key(),
209                        group_id.secure_dns_policy(),
210                        group_id.disable_cert_network_fetches()),
211         is_for_websockets_);
212   }
213 
214   // Force a CONNECT tunnel for websockets. If this is false, the connect job
215   // may still use a tunnel for other reasons.
216   bool force_tunnel = is_for_websockets_;
217 
218   // Only offer HTTP/1.1 for WebSockets. Although RFC 8441 defines WebSockets
219   // over HTTP/2, a single WSS/HTTPS origin may support HTTP over HTTP/2
220   // without supporting WebSockets over HTTP/2. Offering HTTP/2 for a fresh
221   // connection would break such origins.
222   //
223   // However, still offer HTTP/1.1 rather than skipping ALPN entirely. While
224   // this will not change the application protocol (HTTP/1.1 is default), it
225   // provides hardening against cross-protocol attacks and allows for the False
226   // Start (RFC 7918) optimization.
227   ConnectJobFactory::AlpnMode alpn_mode =
228       is_for_websockets_ ? ConnectJobFactory::AlpnMode::kHttp11Only
229                          : ConnectJobFactory::AlpnMode::kHttpAll;
230 
231   return connect_job_factory_->CreateConnectJob(
232       group_id.destination(), proxy_chain, proxy_annotation_tag,
233       socket_params->allowed_bad_certs(), alpn_mode, force_tunnel,
234       group_id.privacy_mode(), resolution_callback, request_priority,
235       socket_tag, group_id.network_anonymization_key(),
236       group_id.secure_dns_policy(), group_id.disable_cert_network_fetches(),
237       common_connect_job_params_, delegate);
238 }
239 
240 }  // namespace net
241