1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
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 "quiche/quic/tools/quic_client_base.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "quiche/quic/core/crypto/quic_random.h"
11 #include "quiche/quic/core/http/spdy_utils.h"
12 #include "quiche/quic/core/quic_packet_writer.h"
13 #include "quiche/quic/core/quic_path_validator.h"
14 #include "quiche/quic/core/quic_server_id.h"
15 #include "quiche/quic/core/quic_utils.h"
16 #include "quiche/quic/platform/api/quic_flags.h"
17 #include "quiche/quic/platform/api/quic_logging.h"
18
19 namespace quic {
20
21 namespace {
22
23 // Implements the basic feature of a result delegate for path validation for
24 // connection migration. If the validation succeeds, migrate to the alternative
25 // path. Otherwise, stay on the current path.
26 class QuicClientSocketMigrationValidationResultDelegate
27 : public QuicPathValidator::ResultDelegate {
28 public:
QuicClientSocketMigrationValidationResultDelegate(QuicClientBase * client)29 explicit QuicClientSocketMigrationValidationResultDelegate(
30 QuicClientBase* client)
31 : QuicPathValidator::ResultDelegate(), client_(client) {}
32
33 virtual ~QuicClientSocketMigrationValidationResultDelegate() = default;
34
35 // QuicPathValidator::ResultDelegate
36 // Overridden to start migration and takes the ownership of the writer in the
37 // context.
OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,QuicTime)38 void OnPathValidationSuccess(
39 std::unique_ptr<QuicPathValidationContext> context,
40 QuicTime /*start_time*/) override {
41 QUIC_DLOG(INFO) << "Successfully validated path from " << *context
42 << ". Migrate to it now.";
43 auto migration_context = std::unique_ptr<PathMigrationContext>(
44 static_cast<PathMigrationContext*>(context.release()));
45 client_->session()->MigratePath(
46 migration_context->self_address(), migration_context->peer_address(),
47 migration_context->WriterToUse(), /*owns_writer=*/false);
48 QUICHE_DCHECK(migration_context->WriterToUse() != nullptr);
49 // Hand the ownership of the alternative writer to the client.
50 client_->set_writer(migration_context->ReleaseWriter());
51 }
52
OnPathValidationFailure(std::unique_ptr<QuicPathValidationContext> context)53 void OnPathValidationFailure(
54 std::unique_ptr<QuicPathValidationContext> context) override {
55 QUIC_LOG(WARNING) << "Fail to validate path " << *context
56 << ", stop migrating.";
57 client_->session()->connection()->OnPathValidationFailureAtClient(
58 /*is_multi_port=*/false, *context);
59 }
60
61 protected:
client()62 QuicClientBase* client() { return client_; }
63
64 private:
65 QuicClientBase* client_;
66 };
67
68 class ServerPreferredAddressResultDelegateWithWriter
69 : public QuicClientSocketMigrationValidationResultDelegate {
70 public:
ServerPreferredAddressResultDelegateWithWriter(QuicClientBase * client)71 ServerPreferredAddressResultDelegateWithWriter(QuicClientBase* client)
72 : QuicClientSocketMigrationValidationResultDelegate(client) {}
73
74 // Overridden to transfer the ownership of the new writer.
OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,QuicTime)75 void OnPathValidationSuccess(
76 std::unique_ptr<QuicPathValidationContext> context,
77 QuicTime /*start_time*/) override {
78 client()->session()->connection()->OnServerPreferredAddressValidated(
79 *context, false);
80 auto migration_context = std::unique_ptr<PathMigrationContext>(
81 static_cast<PathMigrationContext*>(context.release()));
82 client()->set_writer(migration_context->ReleaseWriter());
83 }
84 };
85
86 } // namespace
87
88 QuicClientBase::NetworkHelper::~NetworkHelper() = default;
89
QuicClientBase(const QuicServerId & server_id,const ParsedQuicVersionVector & supported_versions,const QuicConfig & config,QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<NetworkHelper> network_helper,std::unique_ptr<ProofVerifier> proof_verifier,std::unique_ptr<SessionCache> session_cache)90 QuicClientBase::QuicClientBase(
91 const QuicServerId& server_id,
92 const ParsedQuicVersionVector& supported_versions, const QuicConfig& config,
93 QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
94 std::unique_ptr<NetworkHelper> network_helper,
95 std::unique_ptr<ProofVerifier> proof_verifier,
96 std::unique_ptr<SessionCache> session_cache)
97 : server_id_(server_id),
98 initialized_(false),
99 local_port_(0),
100 config_(config),
101 crypto_config_(std::move(proof_verifier), std::move(session_cache)),
102 helper_(helper),
103 alarm_factory_(alarm_factory),
104 supported_versions_(supported_versions),
105 initial_max_packet_length_(0),
106 num_sent_client_hellos_(0),
107 connection_error_(QUIC_NO_ERROR),
108 connected_or_attempting_connect_(false),
109 network_helper_(std::move(network_helper)),
110 connection_debug_visitor_(nullptr),
111 server_connection_id_length_(kQuicDefaultConnectionIdLength),
112 client_connection_id_length_(0) {}
113
114 QuicClientBase::~QuicClientBase() = default;
115
Initialize()116 bool QuicClientBase::Initialize() {
117 num_sent_client_hellos_ = 0;
118 connection_error_ = QUIC_NO_ERROR;
119 connected_or_attempting_connect_ = false;
120
121 // If an initial flow control window has not explicitly been set, then use the
122 // same values that Chrome uses.
123 const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
124 const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
125 if (config()->GetInitialStreamFlowControlWindowToSend() ==
126 kDefaultFlowControlSendWindow) {
127 config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
128 }
129 if (config()->GetInitialSessionFlowControlWindowToSend() ==
130 kDefaultFlowControlSendWindow) {
131 config()->SetInitialSessionFlowControlWindowToSend(
132 kSessionMaxRecvWindowSize);
133 }
134
135 if (!network_helper_->CreateUDPSocketAndBind(server_address_,
136 bind_to_address_, local_port_)) {
137 return false;
138 }
139
140 initialized_ = true;
141 return true;
142 }
143
Connect()144 bool QuicClientBase::Connect() {
145 // Attempt multiple connects until the maximum number of client hellos have
146 // been sent.
147 int num_attempts = 0;
148 while (!connected() &&
149 num_attempts <= QuicCryptoClientStream::kMaxClientHellos) {
150 StartConnect();
151 while (EncryptionBeingEstablished()) {
152 WaitForEvents();
153 }
154 ParsedQuicVersion version = UnsupportedQuicVersion();
155 if (session() != nullptr && !CanReconnectWithDifferentVersion(&version)) {
156 // We've successfully created a session but we're not connected, and we
157 // cannot reconnect with a different version. Give up trying.
158 break;
159 }
160 num_attempts++;
161 }
162 if (session() == nullptr) {
163 QUIC_BUG(quic_bug_10906_1) << "Missing session after Connect";
164 return false;
165 }
166 return session()->connection()->connected();
167 }
168
StartConnect()169 void QuicClientBase::StartConnect() {
170 QUICHE_DCHECK(initialized_);
171 QUICHE_DCHECK(!connected());
172 QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
173 ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
174 const bool can_reconnect_with_different_version =
175 CanReconnectWithDifferentVersion(&mutual_version);
176 if (connected_or_attempting_connect()) {
177 // Clear queued up data if client can not try to connect with a different
178 // version.
179 if (!can_reconnect_with_different_version) {
180 ClearDataToResend();
181 }
182 // Before we destroy the last session and create a new one, gather its stats
183 // and update the stats for the overall connection.
184 UpdateStats();
185 }
186
187 const quic::ParsedQuicVersionVector client_supported_versions =
188 can_reconnect_with_different_version
189 ? ParsedQuicVersionVector{mutual_version}
190 : supported_versions();
191
192 session_ = CreateQuicClientSession(
193 client_supported_versions,
194 new QuicConnection(GetNextConnectionId(), QuicSocketAddress(),
195 server_address(), helper(), alarm_factory(), writer,
196 /* owns_writer= */ false, Perspective::IS_CLIENT,
197 client_supported_versions, connection_id_generator_));
198 if (can_reconnect_with_different_version) {
199 session()->set_client_original_supported_versions(supported_versions());
200 }
201 if (connection_debug_visitor_ != nullptr) {
202 session()->connection()->set_debug_visitor(connection_debug_visitor_);
203 }
204 session()->connection()->set_client_connection_id(GetClientConnectionId());
205 if (initial_max_packet_length_ != 0) {
206 session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
207 }
208 // Reset |writer()| after |session()| so that the old writer outlives the old
209 // session.
210 set_writer(writer);
211 InitializeSession();
212 if (can_reconnect_with_different_version) {
213 // This is a reconnect using server supported |mutual_version|.
214 session()->connection()->SetVersionNegotiated();
215 }
216 set_connected_or_attempting_connect(true);
217 }
218
InitializeSession()219 void QuicClientBase::InitializeSession() { session()->Initialize(); }
220
Disconnect()221 void QuicClientBase::Disconnect() {
222 QUICHE_DCHECK(initialized_);
223
224 initialized_ = false;
225 if (connected()) {
226 session()->connection()->CloseConnection(
227 QUIC_PEER_GOING_AWAY, "Client disconnecting",
228 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
229 }
230
231 ClearDataToResend();
232
233 network_helper_->CleanUpAllUDPSockets();
234 }
235
proof_verifier() const236 ProofVerifier* QuicClientBase::proof_verifier() const {
237 return crypto_config_.proof_verifier();
238 }
239
EncryptionBeingEstablished()240 bool QuicClientBase::EncryptionBeingEstablished() {
241 return !session_->IsEncryptionEstablished() &&
242 session_->connection()->connected();
243 }
244
WaitForEvents()245 bool QuicClientBase::WaitForEvents() {
246 if (!connected()) {
247 QUIC_BUG(quic_bug_10906_2)
248 << "Cannot call WaitForEvents on non-connected client";
249 return false;
250 }
251
252 network_helper_->RunEventLoop();
253
254 return WaitForEventsPostprocessing();
255 }
256
WaitForEventsPostprocessing()257 bool QuicClientBase::WaitForEventsPostprocessing() {
258 QUICHE_DCHECK(session() != nullptr);
259 ParsedQuicVersion version = UnsupportedQuicVersion();
260 if (!connected() && CanReconnectWithDifferentVersion(&version)) {
261 QUIC_DLOG(INFO) << "Can reconnect with version: " << version
262 << ", attempting to reconnect.";
263
264 Connect();
265 }
266
267 return HasActiveRequests();
268 }
269
MigrateSocket(const QuicIpAddress & new_host)270 bool QuicClientBase::MigrateSocket(const QuicIpAddress& new_host) {
271 return MigrateSocketWithSpecifiedPort(new_host, local_port_);
272 }
273
MigrateSocketWithSpecifiedPort(const QuicIpAddress & new_host,int port)274 bool QuicClientBase::MigrateSocketWithSpecifiedPort(
275 const QuicIpAddress& new_host, int port) {
276 if (!connected()) {
277 QUICHE_DVLOG(1)
278 << "MigrateSocketWithSpecifiedPort failed as connection has closed";
279 return false;
280 }
281
282 network_helper_->CleanUpAllUDPSockets();
283 std::unique_ptr<QuicPacketWriter> writer =
284 CreateWriterForNewNetwork(new_host, port);
285 if (writer == nullptr) {
286 QUICHE_DVLOG(1)
287 << "MigrateSocketWithSpecifiedPort failed from writer creation";
288 return false;
289 }
290 if (!session()->MigratePath(network_helper_->GetLatestClientAddress(),
291 session()->connection()->peer_address(),
292 writer.get(), false)) {
293 QUICHE_DVLOG(1)
294 << "MigrateSocketWithSpecifiedPort failed from session()->MigratePath";
295 return false;
296 }
297 set_writer(writer.release());
298 return true;
299 }
300
ValidateAndMigrateSocket(const QuicIpAddress & new_host)301 bool QuicClientBase::ValidateAndMigrateSocket(const QuicIpAddress& new_host) {
302 QUICHE_DCHECK(VersionHasIetfQuicFrames(
303 session_->connection()->version().transport_version));
304 if (!connected()) {
305 return false;
306 }
307
308 std::unique_ptr<QuicPacketWriter> writer =
309 CreateWriterForNewNetwork(new_host, local_port_);
310 if (writer == nullptr) {
311 return false;
312 }
313 // Asynchronously start migration.
314 session_->ValidatePath(
315 std::make_unique<PathMigrationContext>(
316 std::move(writer), network_helper_->GetLatestClientAddress(),
317 session_->peer_address()),
318 std::make_unique<QuicClientSocketMigrationValidationResultDelegate>(this),
319 PathValidationReason::kConnectionMigration);
320 return true;
321 }
322
CreateWriterForNewNetwork(const QuicIpAddress & new_host,int port)323 std::unique_ptr<QuicPacketWriter> QuicClientBase::CreateWriterForNewNetwork(
324 const QuicIpAddress& new_host, int port) {
325 set_bind_to_address(new_host);
326 set_local_port(port);
327 if (!network_helper_->CreateUDPSocketAndBind(server_address_,
328 bind_to_address_, port)) {
329 return nullptr;
330 }
331
332 QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
333 QUIC_LOG_IF(WARNING, writer == writer_.get())
334 << "The new writer is wrapped in the same wrapper as the old one, thus "
335 "appearing to have the same address as the old one.";
336 return std::unique_ptr<QuicPacketWriter>(writer);
337 }
338
ChangeEphemeralPort()339 bool QuicClientBase::ChangeEphemeralPort() {
340 auto current_host = network_helper_->GetLatestClientAddress().host();
341 return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
342 }
343
session()344 QuicSession* QuicClientBase::session() { return session_.get(); }
345
session() const346 const QuicSession* QuicClientBase::session() const { return session_.get(); }
347
network_helper()348 QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
349 return network_helper_.get();
350 }
351
network_helper() const352 const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
353 return network_helper_.get();
354 }
355
WaitForStreamToClose(QuicStreamId id)356 void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
357 if (!connected()) {
358 QUIC_BUG(quic_bug_10906_3)
359 << "Cannot WaitForStreamToClose on non-connected client";
360 return;
361 }
362
363 while (connected() && !session_->IsClosedStream(id)) {
364 WaitForEvents();
365 }
366 }
367
WaitForOneRttKeysAvailable()368 bool QuicClientBase::WaitForOneRttKeysAvailable() {
369 if (!connected()) {
370 QUIC_BUG(quic_bug_10906_4)
371 << "Cannot WaitForOneRttKeysAvailable on non-connected client";
372 return false;
373 }
374
375 while (connected() && !session_->OneRttKeysAvailable()) {
376 WaitForEvents();
377 }
378
379 // If the handshake fails due to a timeout, the connection will be closed.
380 QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
381 return connected();
382 }
383
WaitForHandshakeConfirmed()384 bool QuicClientBase::WaitForHandshakeConfirmed() {
385 if (!session_->connection()->version().UsesTls()) {
386 return WaitForOneRttKeysAvailable();
387 }
388 // Otherwise, wait for receipt of HANDSHAKE_DONE frame.
389 while (connected() && session_->GetHandshakeState() < HANDSHAKE_CONFIRMED) {
390 WaitForEvents();
391 }
392
393 // If the handshake fails due to a timeout, the connection will be closed.
394 QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
395 return connected();
396 }
397
connected() const398 bool QuicClientBase::connected() const {
399 return session_.get() && session_->connection() &&
400 session_->connection()->connected();
401 }
402
goaway_received() const403 bool QuicClientBase::goaway_received() const {
404 return session_ != nullptr && session_->transport_goaway_received();
405 }
406
GetNumSentClientHellos()407 int QuicClientBase::GetNumSentClientHellos() {
408 // If we are not actively attempting to connect, the session object
409 // corresponds to the previous connection and should not be used.
410 const int current_session_hellos = !connected_or_attempting_connect_
411 ? 0
412 : GetNumSentClientHellosFromSession();
413 return num_sent_client_hellos_ + current_session_hellos;
414 }
415
UpdateStats()416 void QuicClientBase::UpdateStats() {
417 num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
418 }
419
GetNumReceivedServerConfigUpdates()420 int QuicClientBase::GetNumReceivedServerConfigUpdates() {
421 // If we are not actively attempting to connect, the session object
422 // corresponds to the previous connection and should not be used.
423 return !connected_or_attempting_connect_
424 ? 0
425 : GetNumReceivedServerConfigUpdatesFromSession();
426 }
427
connection_error() const428 QuicErrorCode QuicClientBase::connection_error() const {
429 // Return the high-level error if there was one. Otherwise, return the
430 // connection error from the last session.
431 if (connection_error_ != QUIC_NO_ERROR) {
432 return connection_error_;
433 }
434 if (session_ == nullptr) {
435 return QUIC_NO_ERROR;
436 }
437 return session_->error();
438 }
439
GetNextConnectionId()440 QuicConnectionId QuicClientBase::GetNextConnectionId() {
441 if (server_connection_id_override_.has_value()) {
442 return *server_connection_id_override_;
443 }
444 return GenerateNewConnectionId();
445 }
446
GenerateNewConnectionId()447 QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
448 return QuicUtils::CreateRandomConnectionId(server_connection_id_length_);
449 }
450
GetClientConnectionId()451 QuicConnectionId QuicClientBase::GetClientConnectionId() {
452 return QuicUtils::CreateRandomConnectionId(client_connection_id_length_);
453 }
454
CanReconnectWithDifferentVersion(ParsedQuicVersion * version) const455 bool QuicClientBase::CanReconnectWithDifferentVersion(
456 ParsedQuicVersion* version) const {
457 if (session_ == nullptr || session_->connection() == nullptr ||
458 session_->error() != QUIC_INVALID_VERSION) {
459 return false;
460 }
461
462 const auto& server_supported_versions =
463 session_->connection()->server_supported_versions();
464 if (server_supported_versions.empty()) {
465 return false;
466 }
467
468 for (const auto& client_version : supported_versions_) {
469 if (std::find(server_supported_versions.begin(),
470 server_supported_versions.end(),
471 client_version) != server_supported_versions.end()) {
472 *version = client_version;
473 return true;
474 }
475 }
476 return false;
477 }
478
HasPendingPathValidation()479 bool QuicClientBase::HasPendingPathValidation() {
480 return session()->HasPendingPathValidation();
481 }
482
483 class ValidationResultDelegate : public QuicPathValidator::ResultDelegate {
484 public:
ValidationResultDelegate(QuicClientBase * client)485 ValidationResultDelegate(QuicClientBase* client)
486 : QuicPathValidator::ResultDelegate(), client_(client) {}
487
OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,QuicTime start_time)488 void OnPathValidationSuccess(
489 std::unique_ptr<QuicPathValidationContext> context,
490 QuicTime start_time) override {
491 QUIC_DLOG(INFO) << "Successfully validated path from " << *context
492 << ", validation started at " << start_time;
493 client_->AddValidatedPath(std::move(context));
494 }
OnPathValidationFailure(std::unique_ptr<QuicPathValidationContext> context)495 void OnPathValidationFailure(
496 std::unique_ptr<QuicPathValidationContext> context) override {
497 QUIC_LOG(WARNING) << "Fail to validate path " << *context
498 << ", stop migrating.";
499 client_->session()->connection()->OnPathValidationFailureAtClient(
500 /*is_multi_port=*/false, *context);
501 }
502
503 private:
504 QuicClientBase* client_;
505 };
506
ValidateNewNetwork(const QuicIpAddress & host)507 void QuicClientBase::ValidateNewNetwork(const QuicIpAddress& host) {
508 std::unique_ptr<QuicPacketWriter> writer =
509 CreateWriterForNewNetwork(host, local_port_);
510 auto result_delegate = std::make_unique<ValidationResultDelegate>(this);
511 if (writer == nullptr) {
512 result_delegate->OnPathValidationFailure(
513 std::make_unique<PathMigrationContext>(
514 nullptr, network_helper_->GetLatestClientAddress(),
515 session_->peer_address()));
516 return;
517 }
518 session()->ValidatePath(
519 std::make_unique<PathMigrationContext>(
520 std::move(writer), network_helper_->GetLatestClientAddress(),
521 session_->peer_address()),
522 std::move(result_delegate), PathValidationReason::kConnectionMigration);
523 }
524
OnServerPreferredAddressAvailable(const QuicSocketAddress & server_preferred_address)525 void QuicClientBase::OnServerPreferredAddressAvailable(
526 const QuicSocketAddress& server_preferred_address) {
527 const auto self_address = session_->self_address();
528 if (network_helper_ == nullptr ||
529 !network_helper_->CreateUDPSocketAndBind(server_preferred_address,
530 self_address.host(), 0)) {
531 return;
532 }
533 QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
534 if (writer == nullptr) {
535 return;
536 }
537 session()->ValidatePath(
538 std::make_unique<PathMigrationContext>(
539 std::unique_ptr<QuicPacketWriter>(writer),
540 network_helper_->GetLatestClientAddress(), server_preferred_address),
541 std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(this),
542 PathValidationReason::kServerPreferredAddressMigration);
543 }
544
545 } // namespace quic
546