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 "net/third_party/quiche/src/quic/tools/quic_client_base.h"
6 
7 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
8 #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
9 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
10 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
11 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
12 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
13 
14 namespace quic {
15 
16 QuicClientBase::NetworkHelper::~NetworkHelper() = default;
17 
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)18 QuicClientBase::QuicClientBase(
19     const QuicServerId& server_id,
20     const ParsedQuicVersionVector& supported_versions,
21     const QuicConfig& config,
22     QuicConnectionHelperInterface* helper,
23     QuicAlarmFactory* alarm_factory,
24     std::unique_ptr<NetworkHelper> network_helper,
25     std::unique_ptr<ProofVerifier> proof_verifier,
26     std::unique_ptr<SessionCache> session_cache)
27     : server_id_(server_id),
28       initialized_(false),
29       local_port_(0),
30       config_(config),
31       crypto_config_(std::move(proof_verifier), std::move(session_cache)),
32       helper_(helper),
33       alarm_factory_(alarm_factory),
34       supported_versions_(supported_versions),
35       initial_max_packet_length_(0),
36       num_sent_client_hellos_(0),
37       connection_error_(QUIC_NO_ERROR),
38       connected_or_attempting_connect_(false),
39       network_helper_(std::move(network_helper)),
40       connection_debug_visitor_(nullptr) {}
41 
42 QuicClientBase::~QuicClientBase() = default;
43 
Initialize()44 bool QuicClientBase::Initialize() {
45   num_sent_client_hellos_ = 0;
46   connection_error_ = QUIC_NO_ERROR;
47   connected_or_attempting_connect_ = false;
48 
49   // If an initial flow control window has not explicitly been set, then use the
50   // same values that Chrome uses.
51   const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024;  // 15 MB
52   const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024;    //  6 MB
53   if (config()->GetInitialStreamFlowControlWindowToSend() ==
54       kDefaultFlowControlSendWindow) {
55     config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
56   }
57   if (config()->GetInitialSessionFlowControlWindowToSend() ==
58       kDefaultFlowControlSendWindow) {
59     config()->SetInitialSessionFlowControlWindowToSend(
60         kSessionMaxRecvWindowSize);
61   }
62 
63   if (!network_helper_->CreateUDPSocketAndBind(server_address_,
64                                                bind_to_address_, local_port_)) {
65     return false;
66   }
67 
68   initialized_ = true;
69   return true;
70 }
71 
Connect()72 bool QuicClientBase::Connect() {
73   // Attempt multiple connects until the maximum number of client hellos have
74   // been sent.
75   int num_attempts = 0;
76   while (!connected() &&
77          num_attempts <= QuicCryptoClientStream::kMaxClientHellos) {
78     StartConnect();
79     while (EncryptionBeingEstablished()) {
80       WaitForEvents();
81     }
82     ParsedQuicVersion version = UnsupportedQuicVersion();
83     if (session() != nullptr && !CanReconnectWithDifferentVersion(&version)) {
84       // We've successfully created a session but we're not connected, and we
85       // cannot reconnect with a different version.  Give up trying.
86       break;
87     }
88     num_attempts++;
89   }
90   return session()->connection()->connected();
91 }
92 
StartConnect()93 void QuicClientBase::StartConnect() {
94   DCHECK(initialized_);
95   DCHECK(!connected());
96   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
97   ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
98   const bool can_reconnect_with_different_version =
99       CanReconnectWithDifferentVersion(&mutual_version);
100   if (connected_or_attempting_connect()) {
101     // Clear queued up data if client can not try to connect with a different
102     // version.
103     if (!can_reconnect_with_different_version) {
104       ClearDataToResend();
105     }
106     // Before we destroy the last session and create a new one, gather its stats
107     // and update the stats for the overall connection.
108     UpdateStats();
109   }
110 
111   session_ = CreateQuicClientSession(
112       supported_versions(),
113       new QuicConnection(GetNextConnectionId(), server_address(), helper(),
114                          alarm_factory(), writer,
115                          /* owns_writer= */ false, Perspective::IS_CLIENT,
116                          can_reconnect_with_different_version
117                              ? ParsedQuicVersionVector{mutual_version}
118                              : supported_versions()));
119   if (connection_debug_visitor_ != nullptr) {
120     session()->connection()->set_debug_visitor(connection_debug_visitor_);
121   }
122   session()->connection()->set_client_connection_id(GetClientConnectionId());
123   if (initial_max_packet_length_ != 0) {
124     session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
125   }
126   // Reset |writer()| after |session()| so that the old writer outlives the old
127   // session.
128   set_writer(writer);
129   InitializeSession();
130   if (can_reconnect_with_different_version) {
131     // This is a reconnect using server supported |mutual_version|.
132     session()->connection()->SetVersionNegotiated();
133   }
134   set_connected_or_attempting_connect(true);
135 }
136 
InitializeSession()137 void QuicClientBase::InitializeSession() {
138   session()->Initialize();
139 }
140 
Disconnect()141 void QuicClientBase::Disconnect() {
142   DCHECK(initialized_);
143 
144   initialized_ = false;
145   if (connected()) {
146     session()->connection()->CloseConnection(
147         QUIC_PEER_GOING_AWAY, "Client disconnecting",
148         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
149   }
150 
151   ClearDataToResend();
152 
153   network_helper_->CleanUpAllUDPSockets();
154 }
155 
proof_verifier() const156 ProofVerifier* QuicClientBase::proof_verifier() const {
157   return crypto_config_.proof_verifier();
158 }
159 
EncryptionBeingEstablished()160 bool QuicClientBase::EncryptionBeingEstablished() {
161   return !session_->IsEncryptionEstablished() &&
162          session_->connection()->connected();
163 }
164 
WaitForEvents()165 bool QuicClientBase::WaitForEvents() {
166   DCHECK(connected());
167 
168   network_helper_->RunEventLoop();
169 
170   DCHECK(session() != nullptr);
171   ParsedQuicVersion version = UnsupportedQuicVersion();
172   if (!connected() && CanReconnectWithDifferentVersion(&version)) {
173     QUIC_DLOG(INFO) << "Can reconnect with version: " << version
174                     << ", attempting to reconnect.";
175 
176     Connect();
177   }
178 
179   return HasActiveRequests();
180 }
181 
MigrateSocket(const QuicIpAddress & new_host)182 bool QuicClientBase::MigrateSocket(const QuicIpAddress& new_host) {
183   return MigrateSocketWithSpecifiedPort(new_host, local_port_);
184 }
185 
MigrateSocketWithSpecifiedPort(const QuicIpAddress & new_host,int port)186 bool QuicClientBase::MigrateSocketWithSpecifiedPort(
187     const QuicIpAddress& new_host,
188     int port) {
189   if (!connected()) {
190     return false;
191   }
192 
193   network_helper_->CleanUpAllUDPSockets();
194 
195   set_bind_to_address(new_host);
196   if (!network_helper_->CreateUDPSocketAndBind(server_address_,
197                                                bind_to_address_, port)) {
198     return false;
199   }
200 
201   session()->connection()->SetSelfAddress(
202       network_helper_->GetLatestClientAddress());
203 
204   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
205   set_writer(writer);
206   session()->connection()->SetQuicPacketWriter(writer, false);
207 
208   return true;
209 }
210 
ChangeEphemeralPort()211 bool QuicClientBase::ChangeEphemeralPort() {
212   auto current_host = network_helper_->GetLatestClientAddress().host();
213   return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
214 }
215 
session()216 QuicSession* QuicClientBase::session() {
217   return session_.get();
218 }
219 
network_helper()220 QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
221   return network_helper_.get();
222 }
223 
network_helper() const224 const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
225   return network_helper_.get();
226 }
227 
WaitForStreamToClose(QuicStreamId id)228 void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
229   DCHECK(connected());
230 
231   while (connected() && !session_->IsClosedStream(id)) {
232     WaitForEvents();
233   }
234 }
235 
WaitForCryptoHandshakeConfirmed()236 bool QuicClientBase::WaitForCryptoHandshakeConfirmed() {
237   DCHECK(connected());
238 
239   while (connected() && !session_->OneRttKeysAvailable()) {
240     WaitForEvents();
241   }
242 
243   // If the handshake fails due to a timeout, the connection will be closed.
244   QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
245   return connected();
246 }
247 
connected() const248 bool QuicClientBase::connected() const {
249   return session_.get() && session_->connection() &&
250          session_->connection()->connected();
251 }
252 
goaway_received() const253 bool QuicClientBase::goaway_received() const {
254   return session_ != nullptr && session_->goaway_received();
255 }
256 
GetNumSentClientHellos()257 int QuicClientBase::GetNumSentClientHellos() {
258   // If we are not actively attempting to connect, the session object
259   // corresponds to the previous connection and should not be used.
260   const int current_session_hellos = !connected_or_attempting_connect_
261                                          ? 0
262                                          : GetNumSentClientHellosFromSession();
263   return num_sent_client_hellos_ + current_session_hellos;
264 }
265 
UpdateStats()266 void QuicClientBase::UpdateStats() {
267   num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
268 }
269 
GetNumReceivedServerConfigUpdates()270 int QuicClientBase::GetNumReceivedServerConfigUpdates() {
271   // If we are not actively attempting to connect, the session object
272   // corresponds to the previous connection and should not be used.
273   return !connected_or_attempting_connect_
274              ? 0
275              : GetNumReceivedServerConfigUpdatesFromSession();
276 }
277 
connection_error() const278 QuicErrorCode QuicClientBase::connection_error() const {
279   // Return the high-level error if there was one.  Otherwise, return the
280   // connection error from the last session.
281   if (connection_error_ != QUIC_NO_ERROR) {
282     return connection_error_;
283   }
284   if (session_ == nullptr) {
285     return QUIC_NO_ERROR;
286   }
287   return session_->error();
288 }
289 
GetNextConnectionId()290 QuicConnectionId QuicClientBase::GetNextConnectionId() {
291   QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
292   return !server_designated_id.IsEmpty() ? server_designated_id
293                                          : GenerateNewConnectionId();
294 }
295 
GetNextServerDesignatedConnectionId()296 QuicConnectionId QuicClientBase::GetNextServerDesignatedConnectionId() {
297   QuicCryptoClientConfig::CachedState* cached =
298       crypto_config_.LookupOrCreate(server_id_);
299   // If the cached state indicates that we should use a server-designated
300   // connection ID, then return that connection ID.
301   CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
302                            << "unexpected nullptr.";
303   return cached->has_server_designated_connection_id()
304              ? cached->GetNextServerDesignatedConnectionId()
305              : EmptyQuicConnectionId();
306 }
307 
GenerateNewConnectionId()308 QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
309   return QuicUtils::CreateRandomConnectionId();
310 }
311 
GetClientConnectionId()312 QuicConnectionId QuicClientBase::GetClientConnectionId() {
313   return EmptyQuicConnectionId();
314 }
315 
CanReconnectWithDifferentVersion(ParsedQuicVersion * version) const316 bool QuicClientBase::CanReconnectWithDifferentVersion(
317     ParsedQuicVersion* version) const {
318   if (session_ == nullptr || session_->connection() == nullptr ||
319       session_->error() != QUIC_INVALID_VERSION ||
320       session_->connection()->server_supported_versions().empty()) {
321     return false;
322   }
323   for (const auto& client_version : supported_versions_) {
324     if (QuicContainsValue(session_->connection()->server_supported_versions(),
325                           client_version)) {
326       *version = client_version;
327       return true;
328     }
329   }
330   return false;
331 }
332 
333 }  // namespace quic
334