1
#include "source/common/quic/envoy_quic_client_session.h"
2

            
3
#include <openssl/ssl.h>
4

            
5
#include <memory>
6

            
7
#include "envoy/network/transport_socket.h"
8

            
9
#include "source/common/event/dispatcher_impl.h"
10
#include "source/common/quic/envoy_quic_proof_verifier.h"
11
#include "source/common/quic/envoy_quic_utils.h"
12
#include "source/common/quic/quic_filter_manager_connection_impl.h"
13
#include "source/common/quic/quic_network_connectivity_observer_impl.h"
14

            
15
namespace Envoy {
16
namespace Quic {
17

            
18
// This class is only used to provide extra context during certificate validation. As such it does
19
// not implement the various interfaces which are irrelevant to certificate validation.
20
class CertValidationContext : public Network::TransportSocketCallbacks {
21
public:
22
  CertValidationContext(EnvoyQuicClientSession& session, Network::IoHandle& io_handle)
23
3096
      : session_(session), io_handle_(io_handle) {}
24

            
25
  // Network::TransportSocketCallbacks
26
4
  Network::IoHandle& ioHandle() final { return io_handle_; }
27
  const Network::IoHandle& ioHandle() const override { return io_handle_; }
28
2944
  Network::Connection& connection() override { return session_; }
29
  // Below methods shouldn't be called during cert verification.
30
  void raiseEvent(Network::ConnectionEvent /*event*/) override { PANIC("unexpectedly reached"); }
31
  bool shouldDrainReadBuffer() override { PANIC("unexpectedly reached"); }
32
  void setTransportSocketIsReadable() override { PANIC("unexpectedly reached"); }
33
  void flushWriteBuffer() override { PANIC("unexpectedly reached"); }
34

            
35
private:
36
  EnvoyQuicClientSession& session_;
37
  Network::IoHandle& io_handle_;
38
};
39

            
40
using CertValidationContextPtr = std::unique_ptr<CertValidationContext>;
41

            
42
// An implementation of the verify context interface.
43
class EnvoyQuicProofVerifyContextImpl : public EnvoyQuicProofVerifyContext {
44
public:
45
  EnvoyQuicProofVerifyContextImpl(
46
      Event::Dispatcher& dispatcher, const bool is_server,
47
      const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
48
      QuicSslConnectionInfo& ssl_info, CertValidationContextPtr validation_context)
49
3096
      : dispatcher_(dispatcher), is_server_(is_server),
50
3096
        transport_socket_options_(transport_socket_options), ssl_info_(ssl_info),
51
3096
        validation_context_(std::move(validation_context)) {}
52

            
53
  // EnvoyQuicProofVerifyContext
54
5898
  bool isServer() const override { return is_server_; }
55
2951
  Event::Dispatcher& dispatcher() const override { return dispatcher_; }
56
2951
  const Network::TransportSocketOptionsConstSharedPtr& transportSocketOptions() const override {
57
2951
    return transport_socket_options_;
58
2951
  }
59

            
60
  Extensions::TransportSockets::Tls::CertValidator::ExtraValidationContext
61
2955
  extraValidationContext() const override {
62
2955
    ASSERT(ssl_info_.ssl());
63
2955
    return {validation_context_.get()};
64
2955
  }
65

            
66
private:
67
  Event::Dispatcher& dispatcher_;
68
  const bool is_server_;
69
  const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options_;
70
  QuicSslConnectionInfo& ssl_info_;
71
  CertValidationContextPtr validation_context_;
72
};
73

            
74
EnvoyQuicClientSession::EnvoyQuicClientSession(
75
    const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions,
76
    std::unique_ptr<EnvoyQuicClientConnection> connection,
77
    quic::QuicForceBlockablePacketWriter* absl_nullable writer,
78
    EnvoyQuicClientConnection::EnvoyQuicMigrationHelper* absl_nullable migration_helper,
79
    const quic::QuicConnectionMigrationConfig& migration_config,
80
    const quic::QuicServerId& server_id,
81
    std::shared_ptr<quic::QuicCryptoClientConfig> crypto_config, Event::Dispatcher& dispatcher,
82
    uint32_t send_buffer_limit, EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory,
83
    QuicStatNames& quic_stat_names, OptRef<Http::HttpServerPropertiesCache> rtt_cache,
84
    Stats::Scope& scope,
85
    const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
86
    OptRef<Network::UpstreamTransportSocketFactory> transport_socket_factory)
87
3096
    : QuicFilterManagerConnectionImpl(
88
3096
          *connection, connection->connection_id(), dispatcher, send_buffer_limit,
89
3096
          std::make_shared<QuicSslConnectionInfo>(*this),
90
3096
          std::make_unique<StreamInfo::StreamInfoImpl>(
91
3096
              dispatcher.timeSource(),
92
3096
              connection->connectionSocket()->connectionInfoProviderSharedPtr(),
93
3096
              StreamInfo::FilterState::LifeSpan::Connection),
94
3096
          quic_stat_names, scope),
95
3096
      quic::QuicSpdyClientSession(config, supported_versions, connection.release(),
96
3096
                                  /*visitor=*/nullptr, writer, migration_helper, migration_config,
97
3096
                                  server_id, crypto_config.get()),
98
3096
      crypto_config_(crypto_config), crypto_stream_factory_(crypto_stream_factory),
99
3096
      rtt_cache_(rtt_cache), transport_socket_options_(transport_socket_options),
100
3096
      transport_socket_factory_(makeOptRefFromPtr(
101
3096
          dynamic_cast<QuicTransportSocketFactoryBase*>(transport_socket_factory.ptr()))),
102
3096
      session_handles_migration_(migration_helper != nullptr) {
103
3096
  ENVOY_BUG(migration_helper == nullptr || writer != nullptr,
104
3096
            "writer must be set if migration helper is set");
105

            
106
3096
  streamInfo().setUpstreamInfo(std::make_shared<StreamInfo::UpstreamInfoImpl>());
107
3096
  if (transport_socket_options_ != nullptr &&
108
3096
      !transport_socket_options_->applicationProtocolListOverride().empty()) {
109
    configured_alpns_ = transport_socket_options_->applicationProtocolListOverride();
110
3096
  } else if (transport_socket_factory_.has_value() &&
111
3096
             !transport_socket_factory_->supportedAlpnProtocols().empty()) {
112
1858
    configured_alpns_ =
113
1858
        std::vector<std::string>(transport_socket_factory_->supportedAlpnProtocols().begin(),
114
1858
                                 transport_socket_factory_->supportedAlpnProtocols().end());
115
1858
  }
116
3096
#ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
117
3096
  http_datagram_support_ = quic::HttpDatagramSupport::kRfc;
118
3096
#endif
119
3096
}
120

            
121
3096
EnvoyQuicClientSession::~EnvoyQuicClientSession() {
122
3096
  ASSERT(!connection()->connected());
123
3096
  network_connection_ = nullptr;
124
3096
  if (registry_.has_value()) {
125
7
    registry_->unregisterObserver(*network_connectivity_observer_);
126
7
  }
127
3096
}
128

            
129
4
absl::string_view EnvoyQuicClientSession::requestedServerName() const { return server_id().host(); }
130

            
131
2965
void EnvoyQuicClientSession::connect() {
132
2965
  streamInfo().upstreamInfo()->upstreamTiming().onUpstreamConnectStart(dispatcher_.timeSource());
133
2965
  dynamic_cast<EnvoyQuicClientConnection*>(network_connection_)
134
2965
      ->setUpConnectionSocket(
135
2965
          *static_cast<EnvoyQuicClientConnection*>(connection())->connectionSocket(), *this);
136
  // Start version negotiation and crypto handshake during which the connection may fail if server
137
  // doesn't support the one and only supported version.
138
2965
  CryptoConnect();
139
2965
}
140

            
141
void EnvoyQuicClientSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
142
3096
                                                quic::ConnectionCloseSource source) {
143
  // Latch latest srtt.
144
3096
  if (OneRttKeysAvailable() && rtt_cache_) {
145
21
    const quic::QuicConnectionStats& stats = connection()->GetStats();
146
21
    if (stats.srtt_us > 0) {
147
21
      Http::HttpServerPropertiesCache::Origin origin("https", server_id().host(),
148
21
                                                     server_id().port());
149
21
      rtt_cache_->setSrtt(origin, std::chrono::microseconds(stats.srtt_us));
150
21
    }
151
21
  }
152
3096
  quic::QuicSpdyClientSession::OnConnectionClosed(frame, source);
153
3096
  quic_stat_names_.chargeQuicConnectionCloseStats(stats_scope_, frame.quic_error_code, source,
154
3096
                                                  true);
155
3096
  onConnectionCloseEvent(frame, source, version());
156
3096
}
157

            
158
3096
void EnvoyQuicClientSession::Initialize() {
159
3096
  quic::QuicSpdyClientSession::Initialize();
160
3096
  initialized_ = true;
161
3096
  network_connection_->setEnvoyConnection(*this, *this);
162
3096
}
163

            
164
523246
void EnvoyQuicClientSession::OnCanWrite() {
165
523246
  uint64_t old_bytes_to_send = bytesToSend();
166
523246
  quic::QuicSpdyClientSession::OnCanWrite();
167
523246
  const bool has_sent_any_data = bytesToSend() != old_bytes_to_send;
168
523246
  maybeUpdateDelayCloseTimer(has_sent_any_data);
169
523246
}
170

            
171
1587
void EnvoyQuicClientSession::OnHttp3GoAway(uint64_t stream_id) {
172
1587
  ENVOY_CONN_LOG(debug, "HTTP/3 GOAWAY received", *this);
173
1587
  quic::QuicSpdyClientSession::OnHttp3GoAway(stream_id);
174
1587
  if (http_connection_callbacks_ != nullptr) {
175
    // HTTP/3 GOAWAY doesn't have an error code field.
176
1587
    http_connection_callbacks_->onGoAway(Http::GoAwayErrorCode::NoError);
177
1587
  }
178
1587
}
179

            
180
504
void EnvoyQuicClientSession::OnRstStream(const quic::QuicRstStreamFrame& frame) {
181
504
  QuicSpdyClientSession::OnRstStream(frame);
182
504
  incrementSentQuicResetStreamErrorStats(frame.error(),
183
504
                                         /*from_self*/ false, /*is_upstream*/ true);
184
504
}
185

            
186
8976
void EnvoyQuicClientSession::OnCanCreateNewOutgoingStream(bool unidirectional) {
187
8976
  if (!http_connection_callbacks_ || unidirectional) {
188
3057
    return;
189
3057
  }
190
5919
  uint32_t streams_available = streamsAvailable();
191
5919
  http_connection_callbacks_->onMaxStreamsChanged(streams_available);
192
5919
}
193

            
194
4028
std::unique_ptr<quic::QuicSpdyClientStream> EnvoyQuicClientSession::CreateClientStream() {
195
4028
  ASSERT(codec_stats_.has_value() && http3_options_.has_value());
196
4028
  return std::make_unique<EnvoyQuicClientStream>(GetNextOutgoingBidirectionalStreamId(), this,
197
4028
                                                 quic::BIDIRECTIONAL, codec_stats_.value(),
198
4028
                                                 http3_options_.value());
199
4028
}
200

            
201
4
quic::QuicSpdyStream* EnvoyQuicClientSession::CreateIncomingStream(quic::QuicStreamId /*id*/) {
202
  // Envoy doesn't support server initiated stream.
203
4
  return nullptr;
204
4
}
205

            
206
quic::QuicSpdyStream*
207
EnvoyQuicClientSession::CreateIncomingStream(quic::PendingStream* /*pending*/) {
208
  // Envoy doesn't support server push.
209
  IS_ENVOY_BUG("unexpectes server push call");
210
  return nullptr;
211
}
212

            
213
554493
bool EnvoyQuicClientSession::hasDataToWrite() { return HasDataToWrite(); }
214

            
215
21662
const quic::QuicConnection* EnvoyQuicClientSession::quicConnection() const {
216
21662
  return initialized_ ? connection() : nullptr;
217
21662
}
218

            
219
4558
quic::QuicConnection* EnvoyQuicClientSession::quicConnection() {
220
4558
  return initialized_ ? connection() : nullptr;
221
4558
}
222

            
223
5919
uint64_t EnvoyQuicClientSession::streamsAvailable() {
224
5919
  const quic::UberQuicStreamIdManager& manager = ietf_streamid_manager();
225
5919
  ASSERT(manager.max_outgoing_bidirectional_streams() >=
226
5919
         manager.outgoing_bidirectional_stream_count());
227
5919
  uint32_t streams_available =
228
5919
      manager.max_outgoing_bidirectional_streams() - manager.outgoing_bidirectional_stream_count();
229
5919
  return streams_available;
230
5919
}
231

            
232
2877
void EnvoyQuicClientSession::OnTlsHandshakeComplete() {
233
2877
  quic::QuicSpdyClientSession::OnTlsHandshakeComplete();
234

            
235
  // Fake this to make sure we set the connection pool stream limit correctly
236
  // before use. This may result in OnCanCreateNewOutgoingStream with zero
237
  // available streams.
238
2877
  OnCanCreateNewOutgoingStream(false);
239
2877
  streamInfo().upstreamInfo()->upstreamTiming().onUpstreamConnectComplete(dispatcher_.timeSource());
240
2877
  streamInfo().upstreamInfo()->upstreamTiming().onUpstreamHandshakeComplete(
241
2877
      dispatcher_.timeSource());
242

            
243
2877
  raiseConnectionEvent(Network::ConnectionEvent::Connected);
244
2877
}
245

            
246
3096
std::unique_ptr<quic::QuicCryptoClientStreamBase> EnvoyQuicClientSession::CreateQuicCryptoStream() {
247
3096
  return crypto_stream_factory_.createEnvoyQuicCryptoClientStream(
248
3096
      server_id(), this,
249
3096
      std::make_unique<EnvoyQuicProofVerifyContextImpl>(
250
3096
          dispatcher_, /*is_server=*/false, transport_socket_options_, *quic_ssl_info_,
251
3096
          std::make_unique<CertValidationContext>(
252
3096
              *this, network_connection_->connectionSocket()->ioHandle())),
253
3096
      crypto_config(), this);
254
3096
}
255

            
256
void EnvoyQuicClientSession::setHttp3Options(
257
3062
    const envoy::config::core::v3::Http3ProtocolOptions& http3_options) {
258
3062
  QuicFilterManagerConnectionImpl::setHttp3Options(http3_options);
259
3062
  if (http3_options_->disable_qpack()) {
260
5
    DisableHuffmanEncoding();
261
5
    DisableCookieCrumbling();
262
5
    set_qpack_maximum_dynamic_table_capacity(0);
263
5
  }
264
3062
  if (!http3_options_->has_quic_protocol_options()) {
265
2785
    return;
266
2785
  }
267
277
  static_cast<EnvoyQuicClientConnection*>(connection())
268
277
      ->setNumPtosForPortMigration(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
269
277
          http3_options.quic_protocol_options(), num_timeouts_to_trigger_port_migration, 0));
270

            
271
277
  if (http3_options_->quic_protocol_options().has_connection_keepalive()) {
272
2
    const uint64_t initial_interval = PROTOBUF_GET_MS_OR_DEFAULT(
273
2
        http3_options_->quic_protocol_options().connection_keepalive(), initial_interval, 0);
274
2
    const uint64_t max_interval =
275
2
        PROTOBUF_GET_MS_OR_DEFAULT(http3_options_->quic_protocol_options().connection_keepalive(),
276
2
                                   max_interval, quic::kPingTimeoutSecs);
277
    // If the keepalive max_interval is configured to zero, disable the probe completely.
278
2
    if (max_interval == 0u) {
279
1
      disable_keepalive_ = true;
280
1
      return;
281
1
    }
282
1
    connection()->set_keep_alive_ping_timeout(
283
1
        quic::QuicTime::Delta::FromMilliseconds(max_interval));
284
1
    if (max_interval > initial_interval && initial_interval > 0u) {
285
1
      connection()->set_initial_retransmittable_on_wire_timeout(
286
1
          quic::QuicTime::Delta::FromMilliseconds(initial_interval));
287
1
    }
288
1
  }
289
277
}
290

            
291
726473
bool EnvoyQuicClientSession::ShouldKeepConnectionAlive() const {
292
  // Do not probe at all if keepalive is disabled via config.
293
726473
  return !disable_keepalive_ && quic::QuicSpdyClientSession::ShouldKeepConnectionAlive();
294
726473
}
295

            
296
void EnvoyQuicClientSession::OnProofVerifyDetailsAvailable(
297
2936
    const quic::ProofVerifyDetails& verify_details) {
298
2936
  if (static_cast<const CertVerifyResult&>(verify_details).isValid()) {
299
2936
    quic_ssl_info_->onCertValidated();
300
2936
  }
301
2936
}
302

            
303
void EnvoyQuicClientSession::OnNewEncryptionKeyAvailable(
304
6920
    quic::EncryptionLevel level, std::unique_ptr<quic::QuicEncrypter> encrypter) {
305
6920
  quic::QuicSpdyClientSession::OnNewEncryptionKeyAvailable(level, std::move(encrypter));
306
6920
  if (level == quic::ENCRYPTION_ZERO_RTT) {
307
1142
    ENVOY_CONN_LOG(trace, "able to send early data", *this);
308
1142
    raiseConnectionEvent(Network::ConnectionEvent::ConnectedZeroRtt);
309
1142
  }
310
6920
}
311
void EnvoyQuicClientSession::OnServerPreferredAddressAvailable(
312
8
    const quic::QuicSocketAddress& server_preferred_address) {
313
8
  quic::QuicSpdyClientSession::OnServerPreferredAddressAvailable(server_preferred_address);
314
8
  if (!session_handles_migration_) {
315
4
    static_cast<EnvoyQuicClientConnection*>(connection())
316
4
        ->probeAndMigrateToServerPreferredAddress(server_preferred_address);
317
4
  }
318
8
}
319

            
320
5843
std::vector<std::string> EnvoyQuicClientSession::GetAlpnsToOffer() const {
321
5843
  return configured_alpns_.empty() ? quic::QuicSpdyClientSession::GetAlpnsToOffer()
322
5843
                                   : configured_alpns_;
323
5843
}
324

            
325
4165
void EnvoyQuicClientSession::OnConfigNegotiated() {
326
4165
  received_custom_transport_parameters_ = config()->received_custom_transport_parameters();
327
4165
  if (config()->HasReceivedIPv6AlternateServerAddress()) {
328
    received_ipv6_alternate_server_address_ = config()->ReceivedIPv6AlternateServerAddress();
329
  }
330
4165
  if (config()->HasReceivedIPv4AlternateServerAddress()) {
331
8
    received_ipv4_alternate_server_address_ = config()->ReceivedIPv4AlternateServerAddress();
332
8
  }
333
4165
  quic::QuicSpdyClientSession::OnConfigNegotiated();
334
4165
}
335

            
336
7
void EnvoyQuicClientSession::registerNetworkObserver(EnvoyQuicNetworkObserverRegistry& registry) {
337
7
  if (network_connectivity_observer_ == nullptr) {
338
7
    network_connectivity_observer_ = std::make_unique<QuicNetworkConnectivityObserverImpl>(*this);
339
7
  }
340
7
  registry.registerObserver(*network_connectivity_observer_);
341
7
  registry_ = makeOptRef(registry);
342
7
}
343

            
344
4
void EnvoyQuicClientSession::StartDraining() {
345
4
  ENVOY_CONN_LOG(
346
4
      trace, "Failed to migrate to the default network {}. Drain the connection on network {}.",
347
4
      *this, migration_manager().default_network(), migration_manager().current_network());
348
4
  quic::QuicSpdyClientSession::StartDraining();
349
  // Treat draining as receiving a GOAWAY.
350
4
  if (http_connection_callbacks_ != nullptr) {
351
    // HTTP/3 GOAWAY doesn't have an error code field.
352
4
    http_connection_callbacks_->onGoAway(Http::GoAwayErrorCode::NoError);
353
4
  }
354
4
}
355

            
356
} // namespace Quic
357
} // namespace Envoy