blob: 11d2293bee53e71563aa0efab9696905183a688e [file] [log] [blame]
// 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 "quic/core/quic_legacy_version_encapsulator.h"
#include "quic/core/crypto/crypto_handshake_message.h"
#include "quic/core/crypto/crypto_protocol.h"
#include "quic/core/quic_utils.h"
#include "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