• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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