Line data Source code
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 :
13 : #include "quic_filter_manager_connection_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 0 : : session_(session), io_handle_(io_handle) {}
24 :
25 : // Network::TransportSocketCallbacks
26 0 : Network::IoHandle& ioHandle() final { return io_handle_; }
27 0 : const Network::IoHandle& ioHandle() const override { return io_handle_; }
28 0 : Network::Connection& connection() override { return session_; }
29 : // Below methods shouldn't be called during cert verification.
30 0 : void raiseEvent(Network::ConnectionEvent /*event*/) override { PANIC("unexpectedly reached"); }
31 0 : bool shouldDrainReadBuffer() override { PANIC("unexpectedly reached"); }
32 0 : void setTransportSocketIsReadable() override { PANIC("unexpectedly reached"); }
33 0 : 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 : : dispatcher_(dispatcher), is_server_(is_server),
50 : transport_socket_options_(transport_socket_options), ssl_info_(ssl_info),
51 0 : validation_context_(std::move(validation_context)) {}
52 :
53 : // EnvoyQuicProofVerifyContext
54 0 : bool isServer() const override { return is_server_; }
55 0 : Event::Dispatcher& dispatcher() const override { return dispatcher_; }
56 0 : const Network::TransportSocketOptionsConstSharedPtr& transportSocketOptions() const override {
57 0 : return transport_socket_options_;
58 0 : }
59 :
60 : Extensions::TransportSockets::Tls::CertValidator::ExtraValidationContext
61 0 : extraValidationContext() const override {
62 0 : ASSERT(ssl_info_.ssl());
63 0 : return {validation_context_.get()};
64 0 : }
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, const quic::QuicServerId& server_id,
77 : std::shared_ptr<quic::QuicCryptoClientConfig> crypto_config, Event::Dispatcher& dispatcher,
78 : uint32_t send_buffer_limit, EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory,
79 : QuicStatNames& quic_stat_names, OptRef<Http::HttpServerPropertiesCache> rtt_cache,
80 : Stats::Scope& scope,
81 : const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
82 : OptRef<Network::UpstreamTransportSocketFactory> transport_socket_factory)
83 : : QuicFilterManagerConnectionImpl(
84 : *connection, connection->connection_id(), dispatcher, send_buffer_limit,
85 : std::make_shared<QuicSslConnectionInfo>(*this),
86 : std::make_unique<StreamInfo::StreamInfoImpl>(
87 : dispatcher.timeSource(),
88 : connection->connectionSocket()->connectionInfoProviderSharedPtr())),
89 : quic::QuicSpdyClientSession(config, supported_versions, connection.release(), server_id,
90 : crypto_config.get()),
91 : crypto_config_(crypto_config), crypto_stream_factory_(crypto_stream_factory),
92 : quic_stat_names_(quic_stat_names), rtt_cache_(rtt_cache), scope_(scope),
93 : transport_socket_options_(transport_socket_options),
94 : transport_socket_factory_(makeOptRefFromPtr(
95 0 : dynamic_cast<QuicClientTransportSocketFactory*>(transport_socket_factory.ptr()))) {
96 0 : streamInfo().setUpstreamInfo(std::make_shared<StreamInfo::UpstreamInfoImpl>());
97 0 : if (transport_socket_options_ != nullptr &&
98 0 : !transport_socket_options_->applicationProtocolListOverride().empty()) {
99 0 : configured_alpns_ = transport_socket_options_->applicationProtocolListOverride();
100 0 : } else if (transport_socket_factory_.has_value() &&
101 0 : !transport_socket_factory_->supportedAlpnProtocols().empty()) {
102 0 : configured_alpns_ =
103 0 : std::vector<std::string>(transport_socket_factory_->supportedAlpnProtocols().begin(),
104 0 : transport_socket_factory_->supportedAlpnProtocols().end());
105 0 : }
106 0 : }
107 :
108 0 : EnvoyQuicClientSession::~EnvoyQuicClientSession() {
109 0 : ASSERT(!connection()->connected());
110 0 : network_connection_ = nullptr;
111 0 : }
112 :
113 0 : absl::string_view EnvoyQuicClientSession::requestedServerName() const { return server_id().host(); }
114 :
115 0 : void EnvoyQuicClientSession::connect() {
116 0 : streamInfo().upstreamInfo()->upstreamTiming().onUpstreamConnectStart(dispatcher_.timeSource());
117 0 : dynamic_cast<EnvoyQuicClientConnection*>(network_connection_)
118 0 : ->setUpConnectionSocket(
119 0 : *static_cast<EnvoyQuicClientConnection*>(connection())->connectionSocket(), *this);
120 : // Start version negotiation and crypto handshake during which the connection may fail if server
121 : // doesn't support the one and only supported version.
122 0 : CryptoConnect();
123 0 : }
124 :
125 : void EnvoyQuicClientSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
126 0 : quic::ConnectionCloseSource source) {
127 : // Latch latest srtt.
128 0 : if (OneRttKeysAvailable() && rtt_cache_) {
129 0 : const quic::QuicConnectionStats& stats = connection()->GetStats();
130 0 : if (stats.srtt_us > 0) {
131 0 : Http::HttpServerPropertiesCache::Origin origin("https", server_id().host(),
132 0 : server_id().port());
133 0 : rtt_cache_->setSrtt(origin, std::chrono::microseconds(stats.srtt_us));
134 0 : }
135 0 : }
136 0 : quic::QuicSpdyClientSession::OnConnectionClosed(frame, source);
137 0 : quic_stat_names_.chargeQuicConnectionCloseStats(scope_, frame.quic_error_code, source, true);
138 0 : onConnectionCloseEvent(frame, source, version());
139 0 : }
140 :
141 0 : void EnvoyQuicClientSession::Initialize() {
142 0 : quic::QuicSpdyClientSession::Initialize();
143 0 : initialized_ = true;
144 0 : network_connection_->setEnvoyConnection(*this, *this);
145 0 : }
146 :
147 0 : void EnvoyQuicClientSession::OnCanWrite() {
148 0 : uint64_t old_bytes_to_send = bytesToSend();
149 0 : quic::QuicSpdyClientSession::OnCanWrite();
150 0 : const bool has_sent_any_data = bytesToSend() != old_bytes_to_send;
151 0 : maybeUpdateDelayCloseTimer(has_sent_any_data);
152 0 : }
153 :
154 0 : void EnvoyQuicClientSession::OnHttp3GoAway(uint64_t stream_id) {
155 0 : ENVOY_CONN_LOG(debug, "HTTP/3 GOAWAY received", *this);
156 0 : quic::QuicSpdyClientSession::OnHttp3GoAway(stream_id);
157 0 : if (http_connection_callbacks_ != nullptr) {
158 : // HTTP/3 GOAWAY doesn't have an error code field.
159 0 : http_connection_callbacks_->onGoAway(Http::GoAwayErrorCode::NoError);
160 0 : }
161 0 : }
162 :
163 : void EnvoyQuicClientSession::MaybeSendRstStreamFrame(quic::QuicStreamId id,
164 : quic::QuicResetStreamError error,
165 0 : quic::QuicStreamOffset bytes_written) {
166 0 : QuicSpdyClientSession::MaybeSendRstStreamFrame(id, error, bytes_written);
167 0 : quic_stat_names_.chargeQuicResetStreamErrorStats(scope_, error, /*from_self*/ true,
168 0 : /*is_upstream*/ true);
169 0 : }
170 :
171 0 : void EnvoyQuicClientSession::OnRstStream(const quic::QuicRstStreamFrame& frame) {
172 0 : QuicSpdyClientSession::OnRstStream(frame);
173 0 : quic_stat_names_.chargeQuicResetStreamErrorStats(scope_, frame.error(),
174 0 : /*from_self*/ false, /*is_upstream*/ true);
175 0 : }
176 :
177 0 : void EnvoyQuicClientSession::OnCanCreateNewOutgoingStream(bool unidirectional) {
178 0 : if (!http_connection_callbacks_ || unidirectional) {
179 0 : return;
180 0 : }
181 0 : uint32_t streams_available = streamsAvailable();
182 0 : http_connection_callbacks_->onMaxStreamsChanged(streams_available);
183 0 : }
184 :
185 0 : std::unique_ptr<quic::QuicSpdyClientStream> EnvoyQuicClientSession::CreateClientStream() {
186 0 : ASSERT(codec_stats_.has_value() && http3_options_.has_value());
187 0 : return std::make_unique<EnvoyQuicClientStream>(GetNextOutgoingBidirectionalStreamId(), this,
188 0 : quic::BIDIRECTIONAL, codec_stats_.value(),
189 0 : http3_options_.value());
190 0 : }
191 :
192 0 : quic::QuicSpdyStream* EnvoyQuicClientSession::CreateIncomingStream(quic::QuicStreamId /*id*/) {
193 : // Envoy doesn't support server initiated stream.
194 0 : return nullptr;
195 0 : }
196 :
197 : quic::QuicSpdyStream*
198 0 : EnvoyQuicClientSession::CreateIncomingStream(quic::PendingStream* /*pending*/) {
199 : // Envoy doesn't support server push.
200 0 : IS_ENVOY_BUG("unexpectes server push call");
201 0 : return nullptr;
202 0 : }
203 :
204 0 : bool EnvoyQuicClientSession::hasDataToWrite() { return HasDataToWrite(); }
205 :
206 0 : const quic::QuicConnection* EnvoyQuicClientSession::quicConnection() const {
207 0 : return initialized_ ? connection() : nullptr;
208 0 : }
209 :
210 0 : quic::QuicConnection* EnvoyQuicClientSession::quicConnection() {
211 0 : return initialized_ ? connection() : nullptr;
212 0 : }
213 :
214 0 : uint64_t EnvoyQuicClientSession::streamsAvailable() {
215 0 : const quic::UberQuicStreamIdManager& manager = ietf_streamid_manager();
216 0 : ASSERT(manager.max_outgoing_bidirectional_streams() >=
217 0 : manager.outgoing_bidirectional_stream_count());
218 0 : uint32_t streams_available =
219 0 : manager.max_outgoing_bidirectional_streams() - manager.outgoing_bidirectional_stream_count();
220 0 : return streams_available;
221 0 : }
222 :
223 0 : void EnvoyQuicClientSession::OnTlsHandshakeComplete() {
224 0 : quic::QuicSpdyClientSession::OnTlsHandshakeComplete();
225 :
226 : // Fake this to make sure we set the connection pool stream limit correctly
227 : // before use. This may result in OnCanCreateNewOutgoingStream with zero
228 : // available streams.
229 0 : OnCanCreateNewOutgoingStream(false);
230 0 : streamInfo().upstreamInfo()->upstreamTiming().onUpstreamConnectComplete(dispatcher_.timeSource());
231 0 : streamInfo().upstreamInfo()->upstreamTiming().onUpstreamHandshakeComplete(
232 0 : dispatcher_.timeSource());
233 :
234 0 : raiseConnectionEvent(Network::ConnectionEvent::Connected);
235 0 : }
236 :
237 0 : std::unique_ptr<quic::QuicCryptoClientStreamBase> EnvoyQuicClientSession::CreateQuicCryptoStream() {
238 0 : return crypto_stream_factory_.createEnvoyQuicCryptoClientStream(
239 0 : server_id(), this,
240 0 : std::make_unique<EnvoyQuicProofVerifyContextImpl>(
241 0 : dispatcher_, /*is_server=*/false, transport_socket_options_, *quic_ssl_info_,
242 0 : std::make_unique<CertValidationContext>(
243 0 : *this, network_connection_->connectionSocket()->ioHandle())),
244 0 : crypto_config(), this, /*has_application_state = */ version().UsesHttp3());
245 0 : }
246 :
247 : void EnvoyQuicClientSession::setHttp3Options(
248 0 : const envoy::config::core::v3::Http3ProtocolOptions& http3_options) {
249 0 : QuicFilterManagerConnectionImpl::setHttp3Options(http3_options);
250 0 : if (!http3_options_->has_quic_protocol_options()) {
251 0 : return;
252 0 : }
253 0 : static_cast<EnvoyQuicClientConnection*>(connection())
254 0 : ->setNumPtosForPortMigration(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
255 0 : http3_options.quic_protocol_options(), num_timeouts_to_trigger_port_migration, 4));
256 :
257 0 : if (http3_options_->quic_protocol_options().has_connection_keepalive()) {
258 0 : const uint64_t initial_interval = PROTOBUF_GET_MS_OR_DEFAULT(
259 0 : http3_options_->quic_protocol_options().connection_keepalive(), initial_interval, 0);
260 0 : const uint64_t max_interval =
261 0 : PROTOBUF_GET_MS_OR_DEFAULT(http3_options_->quic_protocol_options().connection_keepalive(),
262 0 : max_interval, quic::kPingTimeoutSecs);
263 : // If the keepalive max_interval is configured to zero, disable the probe completely.
264 0 : if (max_interval == 0u) {
265 0 : disable_keepalive_ = true;
266 0 : return;
267 0 : }
268 0 : connection()->set_keep_alive_ping_timeout(
269 0 : quic::QuicTime::Delta::FromMilliseconds(max_interval));
270 0 : if (max_interval > initial_interval && initial_interval > 0u) {
271 0 : connection()->set_initial_retransmittable_on_wire_timeout(
272 0 : quic::QuicTime::Delta::FromMilliseconds(initial_interval));
273 0 : }
274 0 : }
275 0 : }
276 :
277 0 : bool EnvoyQuicClientSession::ShouldKeepConnectionAlive() const {
278 : // Do not probe at all if keepalive is disabled via config.
279 0 : return !disable_keepalive_ && quic::QuicSpdyClientSession::ShouldKeepConnectionAlive();
280 0 : }
281 :
282 : void EnvoyQuicClientSession::OnProofVerifyDetailsAvailable(
283 0 : const quic::ProofVerifyDetails& verify_details) {
284 0 : if (static_cast<const CertVerifyResult&>(verify_details).isValid()) {
285 0 : quic_ssl_info_->onCertValidated();
286 0 : }
287 0 : }
288 :
289 : void EnvoyQuicClientSession::OnNewEncryptionKeyAvailable(
290 0 : quic::EncryptionLevel level, std::unique_ptr<quic::QuicEncrypter> encrypter) {
291 0 : quic::QuicSpdyClientSession::OnNewEncryptionKeyAvailable(level, std::move(encrypter));
292 0 : if (level == quic::ENCRYPTION_ZERO_RTT) {
293 0 : ENVOY_CONN_LOG(trace, "able to send early data", *this);
294 0 : raiseConnectionEvent(Network::ConnectionEvent::ConnectedZeroRtt);
295 0 : }
296 0 : }
297 : void EnvoyQuicClientSession::OnServerPreferredAddressAvailable(
298 0 : const quic::QuicSocketAddress& server_preferred_address) {
299 0 : static_cast<EnvoyQuicClientConnection*>(connection())
300 0 : ->probeAndMigrateToServerPreferredAddress(server_preferred_address);
301 0 : }
302 :
303 0 : std::vector<std::string> EnvoyQuicClientSession::GetAlpnsToOffer() const {
304 0 : return configured_alpns_.empty() ? quic::QuicSpdyClientSession::GetAlpnsToOffer()
305 0 : : configured_alpns_;
306 0 : }
307 :
308 : } // namespace Quic
309 : } // namespace Envoy
|