• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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/tls_stream_attempt.h"
6 
7 #include <memory>
8 #include <optional>
9 #include <string_view>
10 
11 #include "base/memory/scoped_refptr.h"
12 #include "base/values.h"
13 #include "net/base/completion_once_callback.h"
14 #include "net/base/host_port_pair.h"
15 #include "net/base/net_errors.h"
16 #include "net/socket/client_socket_factory.h"
17 #include "net/socket/tcp_stream_attempt.h"
18 #include "net/ssl/ssl_cert_request_info.h"
19 
20 namespace net {
21 
22 // static
StateToString(State state)23 std::string_view TlsStreamAttempt::StateToString(State state) {
24   switch (state) {
25     case State::kNone:
26       return "None";
27     case State::kTcpAttempt:
28       return "TcpAttempt";
29     case State::kTcpAttemptComplete:
30       return "TcpAttemptComplete";
31     case State::kTlsAttempt:
32       return "TlsAttempt";
33     case State::kTlsAttemptComplete:
34       return "TlsAttemptComplete";
35   }
36 }
37 
TlsStreamAttempt(const StreamAttemptParams * params,IPEndPoint ip_endpoint,HostPortPair host_port_pair,SSLConfigProvider * ssl_config_provider)38 TlsStreamAttempt::TlsStreamAttempt(const StreamAttemptParams* params,
39                                    IPEndPoint ip_endpoint,
40                                    HostPortPair host_port_pair,
41                                    SSLConfigProvider* ssl_config_provider)
42     : StreamAttempt(params,
43                     ip_endpoint,
44                     NetLogSourceType::TLS_STREAM_ATTEMPT,
45                     NetLogEventType::TLS_STREAM_ATTEMPT_ALIVE),
46       host_port_pair_(std::move(host_port_pair)),
47       ssl_config_provider_(ssl_config_provider) {}
48 
49 TlsStreamAttempt::~TlsStreamAttempt() = default;
50 
GetLoadState() const51 LoadState TlsStreamAttempt::GetLoadState() const {
52   switch (next_state_) {
53     case State::kNone:
54       return LOAD_STATE_IDLE;
55     case State::kTcpAttempt:
56     case State::kTcpAttemptComplete:
57       CHECK(nested_attempt_);
58       return nested_attempt_->GetLoadState();
59     case State::kTlsAttempt:
60     case State::kTlsAttemptComplete:
61       return LOAD_STATE_SSL_HANDSHAKE;
62   }
63 }
64 
GetInfoAsValue() const65 base::Value::Dict TlsStreamAttempt::GetInfoAsValue() const {
66   base::Value::Dict dict;
67   dict.Set("next_state", StateToString(next_state_));
68   dict.Set("tcp_handshake_completed", tcp_handshake_completed_);
69   dict.Set("tls_handshake_started", tls_handshake_started_);
70   dict.Set("has_ssl_config", ssl_config_.has_value());
71   if (nested_attempt_) {
72     dict.Set("nested_attempt", nested_attempt_->GetInfoAsValue());
73   }
74   return dict;
75 }
76 
GetCertRequestInfo()77 scoped_refptr<SSLCertRequestInfo> TlsStreamAttempt::GetCertRequestInfo() {
78   return ssl_cert_request_info_;
79 }
80 
SetTcpHandshakeCompletionCallback(CompletionOnceCallback callback)81 void TlsStreamAttempt::SetTcpHandshakeCompletionCallback(
82     CompletionOnceCallback callback) {
83   CHECK(!tls_handshake_started_);
84   CHECK(!tcp_handshake_completion_callback_);
85   if (next_state_ <= State::kTcpAttemptComplete) {
86     tcp_handshake_completion_callback_ = std::move(callback);
87   }
88 }
89 
StartInternal()90 int TlsStreamAttempt::StartInternal() {
91   CHECK_EQ(next_state_, State::kNone);
92   next_state_ = State::kTcpAttempt;
93   return DoLoop(OK);
94 }
95 
GetNetLogStartParams()96 base::Value::Dict TlsStreamAttempt::GetNetLogStartParams() {
97   base::Value::Dict dict;
98   dict.Set("host_port", host_port_pair_.ToString());
99   return dict;
100 }
101 
OnIOComplete(int rv)102 void TlsStreamAttempt::OnIOComplete(int rv) {
103   CHECK_NE(rv, ERR_IO_PENDING);
104   rv = DoLoop(rv);
105   if (rv != ERR_IO_PENDING) {
106     NotifyOfCompletion(rv);
107   }
108 }
109 
DoLoop(int rv)110 int TlsStreamAttempt::DoLoop(int rv) {
111   CHECK_NE(next_state_, State::kNone);
112 
113   do {
114     State state = next_state_;
115     next_state_ = State::kNone;
116     switch (state) {
117       case State::kNone:
118         NOTREACHED() << "Invalid state";
119       case State::kTcpAttempt:
120         rv = DoTcpAttempt();
121         break;
122       case State::kTcpAttemptComplete:
123         rv = DoTcpAttemptComplete(rv);
124         break;
125       case State::kTlsAttempt:
126         rv = DoTlsAttempt(rv);
127         break;
128       case State::kTlsAttemptComplete:
129         rv = DoTlsAttemptComplete(rv);
130         break;
131     }
132   } while (next_state_ != State::kNone && rv != ERR_IO_PENDING);
133 
134   return rv;
135 }
136 
DoTcpAttempt()137 int TlsStreamAttempt::DoTcpAttempt() {
138   next_state_ = State::kTcpAttemptComplete;
139   nested_attempt_ =
140       std::make_unique<TcpStreamAttempt>(&params(), ip_endpoint(), &net_log());
141   return nested_attempt_->Start(
142       base::BindOnce(&TlsStreamAttempt::OnIOComplete, base::Unretained(this)));
143 }
144 
DoTcpAttemptComplete(int rv)145 int TlsStreamAttempt::DoTcpAttemptComplete(int rv) {
146   const LoadTimingInfo::ConnectTiming& nested_timing =
147       nested_attempt_->connect_timing();
148   mutable_connect_timing().connect_start = nested_timing.connect_start;
149 
150   tcp_handshake_completed_ = true;
151   if (tcp_handshake_completion_callback_) {
152     std::move(tcp_handshake_completion_callback_).Run(rv);
153   }
154 
155   if (rv != OK) {
156     return rv;
157   }
158 
159   net_log().BeginEvent(NetLogEventType::TLS_STREAM_ATTEMPT_WAIT_FOR_SSL_CONFIG);
160 
161   next_state_ = State::kTlsAttempt;
162 
163   if (ssl_config_.has_value()) {
164     // We restarted for ECH retry and already have a SSLConfig with retry
165     // configs.
166     return OK;
167   }
168 
169   return ssl_config_provider_->WaitForSSLConfigReady(
170       base::BindOnce(&TlsStreamAttempt::OnIOComplete, base::Unretained(this)));
171 }
172 
DoTlsAttempt(int rv)173 int TlsStreamAttempt::DoTlsAttempt(int rv) {
174   CHECK_EQ(rv, OK);
175 
176   net_log().EndEvent(NetLogEventType::TLS_STREAM_ATTEMPT_WAIT_FOR_SSL_CONFIG);
177 
178   next_state_ = State::kTlsAttemptComplete;
179 
180   std::unique_ptr<StreamSocket> nested_socket =
181       nested_attempt_->ReleaseStreamSocket();
182   if (!ssl_config_) {
183     CHECK(ssl_config_provider_);
184     auto get_config_result = ssl_config_provider_->GetSSLConfig();
185     // Clear `ssl_config_provider_` to avoid dangling pointer.
186     // TODO(bashi): Try not to clear the pointer. It seems that
187     // `ssl_config_provider_` should always outlive `this`.
188     ssl_config_provider_ = nullptr;
189 
190     if (get_config_result.has_value()) {
191       ssl_config_ = *get_config_result;
192     } else {
193       CHECK_EQ(get_config_result.error(), GetSSLConfigError::kAbort);
194       return ERR_ABORTED;
195     }
196   }
197 
198   nested_attempt_.reset();
199 
200   tls_handshake_started_ = true;
201   mutable_connect_timing().ssl_start = base::TimeTicks::Now();
202   tls_handshake_timeout_timer_.Start(
203       FROM_HERE, kTlsHandshakeTimeout,
204       base::BindOnce(&TlsStreamAttempt::OnTlsHandshakeTimeout,
205                      base::Unretained(this)));
206 
207   ssl_socket_ = params().client_socket_factory->CreateSSLClientSocket(
208       params().ssl_client_context, std::move(nested_socket), host_port_pair_,
209       *ssl_config_);
210 
211   net_log().BeginEvent(NetLogEventType::TLS_STREAM_ATTEMPT_CONNECT);
212 
213   return ssl_socket_->Connect(
214       base::BindOnce(&TlsStreamAttempt::OnIOComplete, base::Unretained(this)));
215 }
216 
DoTlsAttemptComplete(int rv)217 int TlsStreamAttempt::DoTlsAttemptComplete(int rv) {
218   net_log().EndEventWithNetErrorCode(
219       NetLogEventType::TLS_STREAM_ATTEMPT_CONNECT, rv);
220 
221   mutable_connect_timing().ssl_end = base::TimeTicks::Now();
222   tls_handshake_timeout_timer_.Stop();
223 
224   const bool ech_enabled = params().ssl_client_context->config().ech_enabled;
225 
226   if (!ech_retry_configs_ && rv == ERR_ECH_NOT_NEGOTIATED && ech_enabled) {
227     CHECK(ssl_socket_);
228     // We used ECH, and the server could not decrypt the ClientHello. However,
229     // it was able to handshake with the public name and send authenticated
230     // retry configs. If this is not the first time around, retry the connection
231     // with the new ECHConfigList, or with ECH disabled (empty retry configs),
232     // as directed.
233     //
234     // See
235     // https://www.ietf.org/archive/id/draft-ietf-tls-esni-22.html#section-6.1.6
236     ech_retry_configs_ = ssl_socket_->GetECHRetryConfigs();
237     ssl_config_->ech_config_list = *ech_retry_configs_;
238 
239     // TODO(crbug.com/346835898): Add a NetLog to record ECH retry configs.
240 
241     // Reset states.
242     tcp_handshake_completed_ = false;
243     tls_handshake_started_ = false;
244     ssl_socket_.reset();
245     ssl_cert_request_info_.reset();
246 
247     next_state_ = State::kTcpAttempt;
248     return OK;
249   }
250 
251   const bool is_ech_capable =
252       ssl_config_ && !ssl_config_->ech_config_list.empty();
253   SSLClientSocket::RecordSSLConnectResult(ssl_socket_.get(), rv, is_ech_capable,
254                                           ech_enabled, ech_retry_configs_,
255                                           connect_timing());
256 
257   if (rv == OK || IsCertificateError(rv)) {
258     CHECK(ssl_socket_);
259     SetStreamSocket(std::move(ssl_socket_));
260   } else if (rv == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
261     CHECK(ssl_socket_);
262     ssl_cert_request_info_ = base::MakeRefCounted<SSLCertRequestInfo>();
263     ssl_socket_->GetSSLCertRequestInfo(ssl_cert_request_info_.get());
264   }
265 
266   return rv;
267 }
268 
OnTlsHandshakeTimeout()269 void TlsStreamAttempt::OnTlsHandshakeTimeout() {
270   // TODO(bashi): The error code should be ERR_CONNECTION_TIMED_OUT but use
271   // ERR_TIMED_OUT for consistency with ConnectJobs.
272   OnIOComplete(ERR_TIMED_OUT);
273 }
274 
275 }  // namespace net
276