| // Copyright (c) 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quiche/quic/core/quic_legacy_version_encapsulator.h" |
| |
| #include "quiche/quic/core/crypto/crypto_handshake_message.h" |
| #include "quiche/quic/core/crypto/crypto_protocol.h" |
| #include "quiche/quic/core/quic_utils.h" |
| #include "quiche/quic/platform/api/quic_bug_tracker.h" |
| |
| namespace quic { |
| |
| QuicLegacyVersionEncapsulator::QuicLegacyVersionEncapsulator( |
| QuicPacketBuffer packet_buffer) |
| : packet_buffer_(packet_buffer) {} |
| |
| QuicLegacyVersionEncapsulator::~QuicLegacyVersionEncapsulator() {} |
| |
| // static |
| QuicByteCount QuicLegacyVersionEncapsulator::GetMinimumOverhead( |
| absl::string_view sni) { |
| // The number 52 is the sum of: |
| // - Flags (1 byte) |
| // - Server Connection ID (8 bytes) |
| // - Version (4 bytes) |
| // - Packet Number (1 byte) |
| // - Message Authentication Hash (12 bytes) |
| // - Frame Type (1 byte) |
| // - Stream ID (1 byte) |
| // - ClientHello tag (4 bytes) |
| // - ClientHello num tags (2 bytes) |
| // - Padding (2 bytes) |
| // - SNI tag (4 bytes) |
| // - SNI end offset (4 bytes) |
| // - QLVE tag (4 bytes) |
| // - QLVE end offset (4 bytes) |
| return 52 + sni.length(); |
| } |
| |
| QuicPacketBuffer QuicLegacyVersionEncapsulator::GetPacketBuffer() { |
| return packet_buffer_; |
| } |
| |
| void QuicLegacyVersionEncapsulator::OnSerializedPacket( |
| SerializedPacket serialized_packet) { |
| if (encrypted_length_ != 0) { |
| unrecoverable_failure_encountered_ = true; |
| QUIC_BUG(quic_bug_10615_1) << "OnSerializedPacket called twice"; |
| return; |
| } |
| if (serialized_packet.encrypted_length == 0) { |
| unrecoverable_failure_encountered_ = true; |
| QUIC_BUG(quic_bug_10615_2) << "OnSerializedPacket called with empty packet"; |
| return; |
| } |
| encrypted_length_ = serialized_packet.encrypted_length; |
| } |
| |
| void QuicLegacyVersionEncapsulator::OnUnrecoverableError( |
| QuicErrorCode error, const std::string& error_details) { |
| unrecoverable_failure_encountered_ = true; |
| QUIC_BUG(quic_bug_10615_3) << "QuicLegacyVersionEncapsulator received error " |
| << error << ": " << error_details; |
| } |
| |
| bool QuicLegacyVersionEncapsulator::ShouldGeneratePacket( |
| HasRetransmittableData /*retransmittable*/, IsHandshake /*handshake*/) { |
| return true; |
| } |
| |
| const QuicFrames |
| QuicLegacyVersionEncapsulator::MaybeBundleAckOpportunistically() { |
| // We do not want to ever include any ACKs here, return an empty array. |
| return QuicFrames(); |
| } |
| |
| SerializedPacketFate QuicLegacyVersionEncapsulator::GetSerializedPacketFate( |
| bool /*is_mtu_discovery*/, EncryptionLevel /*encryption_level*/) { |
| return SEND_TO_WRITER; |
| } |
| |
| // static |
| QuicPacketLength QuicLegacyVersionEncapsulator::Encapsulate( |
| absl::string_view sni, absl::string_view inner_packet, |
| const QuicConnectionId& server_connection_id, QuicTime creation_time, |
| QuicByteCount outer_max_packet_length, char* out) { |
| if (outer_max_packet_length > kMaxOutgoingPacketSize) { |
| outer_max_packet_length = kMaxOutgoingPacketSize; |
| } |
| CryptoHandshakeMessage outer_chlo; |
| outer_chlo.set_tag(kCHLO); |
| outer_chlo.SetStringPiece(kSNI, sni); |
| outer_chlo.SetStringPiece(kQLVE, inner_packet); |
| const QuicData& serialized_outer_chlo = outer_chlo.GetSerialized(); |
| QUICHE_DCHECK(!LegacyVersionForEncapsulation().UsesCryptoFrames()); |
| QUICHE_DCHECK(LegacyVersionForEncapsulation().UsesQuicCrypto()); |
| QuicStreamFrame outer_stream_frame( |
| QuicUtils::GetCryptoStreamId( |
| LegacyVersionForEncapsulation().transport_version), |
| /*fin=*/false, |
| /*offset=*/0, serialized_outer_chlo.AsStringPiece()); |
| QuicFramer outer_framer( |
| ParsedQuicVersionVector{LegacyVersionForEncapsulation()}, creation_time, |
| Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength); |
| outer_framer.SetInitialObfuscators(server_connection_id); |
| char outer_encrypted_packet[kMaxOutgoingPacketSize]; |
| QuicPacketBuffer outer_packet_buffer(outer_encrypted_packet, nullptr); |
| QuicLegacyVersionEncapsulator creator_delegate(outer_packet_buffer); |
| QuicPacketCreator outer_creator(server_connection_id, &outer_framer, |
| &creator_delegate); |
| outer_creator.SetMaxPacketLength(outer_max_packet_length); |
| outer_creator.set_encryption_level(ENCRYPTION_INITIAL); |
| outer_creator.SetTransmissionType(NOT_RETRANSMISSION); |
| if (!outer_creator.AddPaddedSavedFrame(QuicFrame(outer_stream_frame), |
| NOT_RETRANSMISSION)) { |
| QUIC_BUG(quic_bug_10615_4) |
| << "Failed to add Legacy Version Encapsulation stream frame " |
| "(max packet length is " |
| << outer_creator.max_packet_length() << ") " << outer_stream_frame; |
| return 0; |
| } |
| outer_creator.FlushCurrentPacket(); |
| const QuicPacketLength encrypted_length = creator_delegate.encrypted_length_; |
| if (creator_delegate.unrecoverable_failure_encountered_ || |
| encrypted_length == 0) { |
| QUIC_BUG(quic_bug_10615_5) |
| << "Failed to perform Legacy Version Encapsulation of " |
| << inner_packet.length() << " bytes"; |
| return 0; |
| } |
| if (encrypted_length > kMaxOutgoingPacketSize) { |
| QUIC_BUG(quic_bug_10615_6) |
| << "Legacy Version Encapsulation outer creator generated a " |
| "packet with unexpected length " |
| << encrypted_length; |
| return 0; |
| } |
| |
| QUIC_DLOG(INFO) << "Successfully performed Legacy Version Encapsulation from " |
| << inner_packet.length() << " bytes to " << encrypted_length; |
| |
| // Replace our current packet with the encapsulated one. |
| memcpy(out, outer_encrypted_packet, encrypted_length); |
| return encrypted_length; |
| } |
| |
| } // namespace quic |