| // Copyright (c) 2018 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 "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| #include "third_party/boringssl/src/include/openssl/bytestring.h" | |
| #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_types.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_versions.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" | |
| namespace quic { | |
| // Values of the TransportParameterId enum as defined in the | |
| // "Transport Parameter Encoding" section of draft-ietf-quic-transport. | |
| // When parameters are encoded, one of these enum values is used to indicate | |
| // which parameter is encoded. The supported draft version is noted in | |
| // transport_parameters.h. | |
| enum TransportParameters::TransportParameterId : uint16_t { | |
| kOriginalConnectionId = 0, | |
| kIdleTimeout = 1, | |
| kStatelessResetToken = 2, | |
| kMaxPacketSize = 3, | |
| kInitialMaxData = 4, | |
| kInitialMaxStreamDataBidiLocal = 5, | |
| kInitialMaxStreamDataBidiRemote = 6, | |
| kInitialMaxStreamDataUni = 7, | |
| kInitialMaxStreamsBidi = 8, | |
| kInitialMaxStreamsUni = 9, | |
| kAckDelayExponent = 0xa, | |
| kMaxAckDelay = 0xb, | |
| kDisableMigration = 0xc, | |
| kPreferredAddress = 0xd, | |
| kGoogleQuicParam = 18257, // Used for non-standard Google-specific params. | |
| kGoogleQuicVersion = | |
| 18258, // Used to transmit version and supported_versions. | |
| }; | |
| namespace { | |
| // The following constants define minimum and maximum allowed values for some of | |
| // the parameters. These come from the "Transport Parameter Definitions" | |
| // section of draft-ietf-quic-transport. | |
| const uint64_t kMinMaxPacketSizeTransportParam = 1200; | |
| const uint64_t kDefaultMaxPacketSizeTransportParam = 65527; | |
| const uint64_t kMaxAckDelayExponentTransportParam = 20; | |
| const uint64_t kDefaultAckDelayExponentTransportParam = 3; | |
| const uint64_t kMaxMaxAckDelayTransportParam = 16383; | |
| const uint64_t kDefaultMaxAckDelayTransportParam = 25; | |
| const size_t kStatelessResetTokenLength = 16; | |
| std::string TransportParameterIdToString( | |
| TransportParameters::TransportParameterId param_id) { | |
| switch (param_id) { | |
| case TransportParameters::kOriginalConnectionId: | |
| return "original_connection_id"; | |
| case TransportParameters::kIdleTimeout: | |
| return "idle_timeout"; | |
| case TransportParameters::kStatelessResetToken: | |
| return "stateless_reset_token"; | |
| case TransportParameters::kMaxPacketSize: | |
| return "max_packet_size"; | |
| case TransportParameters::kInitialMaxData: | |
| return "initial_max_data"; | |
| case TransportParameters::kInitialMaxStreamDataBidiLocal: | |
| return "initial_max_stream_data_bidi_local"; | |
| case TransportParameters::kInitialMaxStreamDataBidiRemote: | |
| return "initial_max_stream_data_bidi_remote"; | |
| case TransportParameters::kInitialMaxStreamDataUni: | |
| return "initial_max_stream_data_uni"; | |
| case TransportParameters::kInitialMaxStreamsBidi: | |
| return "initial_max_streams_bidi"; | |
| case TransportParameters::kInitialMaxStreamsUni: | |
| return "initial_max_streams_uni"; | |
| case TransportParameters::kAckDelayExponent: | |
| return "ack_delay_exponent"; | |
| case TransportParameters::kMaxAckDelay: | |
| return "max_ack_delay"; | |
| case TransportParameters::kDisableMigration: | |
| return "disable_migration"; | |
| case TransportParameters::kPreferredAddress: | |
| return "preferred_address"; | |
| case TransportParameters::kGoogleQuicParam: | |
| return "google"; | |
| case TransportParameters::kGoogleQuicVersion: | |
| return "google-version"; | |
| } | |
| return "Unknown(" + QuicTextUtils::Uint64ToString(param_id) + ")"; | |
| } | |
| } // namespace | |
| TransportParameters::IntegerParameter::IntegerParameter( | |
| TransportParameters::TransportParameterId param_id, | |
| uint64_t default_value, | |
| uint64_t min_value, | |
| uint64_t max_value) | |
| : param_id_(param_id), | |
| value_(default_value), | |
| default_value_(default_value), | |
| min_value_(min_value), | |
| max_value_(max_value), | |
| has_been_read_from_cbs_(false) { | |
| DCHECK_LE(min_value, default_value); | |
| DCHECK_LE(default_value, max_value); | |
| DCHECK_LE(max_value, kVarInt62MaxValue); | |
| } | |
| TransportParameters::IntegerParameter::IntegerParameter( | |
| TransportParameters::TransportParameterId param_id) | |
| : TransportParameters::IntegerParameter::IntegerParameter( | |
| param_id, | |
| 0, | |
| 0, | |
| kVarInt62MaxValue) {} | |
| void TransportParameters::IntegerParameter::set_value(uint64_t value) { | |
| value_ = value; | |
| } | |
| uint64_t TransportParameters::IntegerParameter::value() const { | |
| return value_; | |
| } | |
| bool TransportParameters::IntegerParameter::IsValid() const { | |
| return min_value_ <= value_ && value_ <= max_value_; | |
| } | |
| bool TransportParameters::IntegerParameter::WriteToCbb(CBB* parent_cbb) const { | |
| DCHECK(IsValid()); | |
| if (value_ == default_value_) { | |
| // Do not write if the value is default. | |
| return true; | |
| } | |
| uint8_t encoded_data[sizeof(uint64_t)] = {}; | |
| QuicDataWriter writer(sizeof(encoded_data), | |
| reinterpret_cast<char*>(encoded_data)); | |
| writer.WriteVarInt62(value_); | |
| const uint16_t value_length = writer.length(); | |
| DCHECK_LE(value_length, sizeof(encoded_data)); | |
| const bool ok = CBB_add_u16(parent_cbb, param_id_) && | |
| CBB_add_u16(parent_cbb, value_length) && | |
| CBB_add_bytes(parent_cbb, encoded_data, value_length); | |
| QUIC_BUG_IF(!ok) << "Failed to write " << this; | |
| return ok; | |
| } | |
| bool TransportParameters::IntegerParameter::ReadFromCbs(CBS* const value_cbs) { | |
| if (has_been_read_from_cbs_) { | |
| QUIC_DLOG(ERROR) << "Received a second " | |
| << TransportParameterIdToString(param_id_); | |
| return false; | |
| } | |
| has_been_read_from_cbs_ = true; | |
| QuicDataReader reader(reinterpret_cast<const char*>(CBS_data(value_cbs)), | |
| CBS_len(value_cbs)); | |
| QuicVariableLengthIntegerLength value_length = reader.PeekVarInt62Length(); | |
| if (value_length == 0 || !reader.ReadVarInt62(&value_)) { | |
| QUIC_DLOG(ERROR) << "Failed to parse value for " | |
| << TransportParameterIdToString(param_id_); | |
| return false; | |
| } | |
| if (!reader.IsDoneReading()) { | |
| QUIC_DLOG(ERROR) << "Received unexpected " << reader.BytesRemaining() | |
| << " bytes after parsing " << this; | |
| return false; | |
| } | |
| if (!CBS_skip(value_cbs, value_length)) { | |
| QUIC_BUG << "Failed to advance CBS past value for " << this; | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::string TransportParameters::IntegerParameter::ToString( | |
| bool for_use_in_list) const { | |
| if (for_use_in_list && value_ == default_value_) { | |
| return ""; | |
| } | |
| std::string rv = for_use_in_list ? " " : ""; | |
| rv += TransportParameterIdToString(param_id_) + " "; | |
| rv += QuicTextUtils::Uint64ToString(value_); | |
| if (!IsValid()) { | |
| rv += " (Invalid)"; | |
| } | |
| return rv; | |
| } | |
| std::ostream& operator<<(std::ostream& os, | |
| const TransportParameters::IntegerParameter& param) { | |
| os << param.ToString(/*for_use_in_list=*/false); | |
| return os; | |
| } | |
| TransportParameters::PreferredAddress::PreferredAddress() | |
| : ipv4_socket_address(QuicIpAddress::Any4(), 0), | |
| ipv6_socket_address(QuicIpAddress::Any6(), 0), | |
| connection_id(EmptyQuicConnectionId()), | |
| stateless_reset_token(kStatelessResetTokenLength, 0) {} | |
| TransportParameters::PreferredAddress::~PreferredAddress() {} | |
| std::ostream& operator<<( | |
| std::ostream& os, | |
| const TransportParameters::PreferredAddress& preferred_address) { | |
| os << preferred_address.ToString(); | |
| return os; | |
| } | |
| std::string TransportParameters::PreferredAddress::ToString() const { | |
| return "[" + ipv4_socket_address.ToString() + " " + | |
| ipv6_socket_address.ToString() + " connection_id " + | |
| connection_id.ToString() + " stateless_reset_token " + | |
| QuicTextUtils::HexEncode( | |
| reinterpret_cast<const char*>(stateless_reset_token.data()), | |
| stateless_reset_token.size()) + | |
| "]"; | |
| } | |
| std::ostream& operator<<(std::ostream& os, const TransportParameters& params) { | |
| os << params.ToString(); | |
| return os; | |
| } | |
| std::string TransportParameters::ToString() const { | |
| std::string rv = "["; | |
| if (perspective == Perspective::IS_SERVER) { | |
| rv += "Server"; | |
| } else { | |
| rv += "Client"; | |
| } | |
| if (version != 0) { | |
| rv += " version " + QuicVersionLabelToString(version); | |
| } | |
| if (!supported_versions.empty()) { | |
| rv += " supported_versions " + | |
| QuicVersionLabelVectorToString(supported_versions); | |
| } | |
| if (!original_connection_id.IsEmpty()) { | |
| rv += " " + TransportParameterIdToString(kOriginalConnectionId) + " " + | |
| original_connection_id.ToString(); | |
| } | |
| rv += idle_timeout_milliseconds.ToString(/*for_use_in_list=*/true); | |
| if (!stateless_reset_token.empty()) { | |
| rv += " " + TransportParameterIdToString(kStatelessResetToken) + " " + | |
| QuicTextUtils::HexEncode( | |
| reinterpret_cast<const char*>(stateless_reset_token.data()), | |
| stateless_reset_token.size()); | |
| } | |
| rv += max_packet_size.ToString(/*for_use_in_list=*/true); | |
| rv += initial_max_data.ToString(/*for_use_in_list=*/true); | |
| rv += initial_max_stream_data_bidi_local.ToString(/*for_use_in_list=*/true); | |
| rv += initial_max_stream_data_bidi_remote.ToString(/*for_use_in_list=*/true); | |
| rv += initial_max_stream_data_uni.ToString(/*for_use_in_list=*/true); | |
| rv += initial_max_streams_bidi.ToString(/*for_use_in_list=*/true); | |
| rv += initial_max_streams_uni.ToString(/*for_use_in_list=*/true); | |
| rv += ack_delay_exponent.ToString(/*for_use_in_list=*/true); | |
| rv += max_ack_delay.ToString(/*for_use_in_list=*/true); | |
| if (disable_migration) { | |
| rv += " " + TransportParameterIdToString(kDisableMigration); | |
| } | |
| if (preferred_address) { | |
| rv += " " + TransportParameterIdToString(kPreferredAddress) + " " + | |
| preferred_address->ToString(); | |
| } | |
| if (google_quic_params) { | |
| rv += " " + TransportParameterIdToString(kGoogleQuicParam); | |
| } | |
| rv += "]"; | |
| return rv; | |
| } | |
| TransportParameters::TransportParameters() | |
| : version(0), | |
| original_connection_id(EmptyQuicConnectionId()), | |
| idle_timeout_milliseconds(kIdleTimeout), | |
| max_packet_size(kMaxPacketSize, | |
| kDefaultMaxPacketSizeTransportParam, | |
| kMinMaxPacketSizeTransportParam, | |
| kVarInt62MaxValue), | |
| initial_max_data(kInitialMaxData), | |
| initial_max_stream_data_bidi_local(kInitialMaxStreamDataBidiLocal), | |
| initial_max_stream_data_bidi_remote(kInitialMaxStreamDataBidiRemote), | |
| initial_max_stream_data_uni(kInitialMaxStreamDataUni), | |
| initial_max_streams_bidi(kInitialMaxStreamsBidi), | |
| initial_max_streams_uni(kInitialMaxStreamsUni), | |
| ack_delay_exponent(kAckDelayExponent, | |
| kDefaultAckDelayExponentTransportParam, | |
| 0, | |
| kMaxAckDelayExponentTransportParam), | |
| max_ack_delay(kMaxAckDelay, | |
| kDefaultMaxAckDelayTransportParam, | |
| 0, | |
| kMaxMaxAckDelayTransportParam), | |
| disable_migration(false) | |
| // Important note: any new transport parameters must be added | |
| // to TransportParameters::AreValid, SerializeTransportParameters and | |
| // ParseTransportParameters. | |
| {} | |
| bool TransportParameters::AreValid() const { | |
| DCHECK(perspective == Perspective::IS_CLIENT || | |
| perspective == Perspective::IS_SERVER); | |
| if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) { | |
| QUIC_DLOG(ERROR) << "Client cannot send stateless reset token"; | |
| return false; | |
| } | |
| if (perspective == Perspective::IS_CLIENT && | |
| !original_connection_id.IsEmpty()) { | |
| QUIC_DLOG(ERROR) << "Client cannot send original connection ID"; | |
| return false; | |
| } | |
| if (!stateless_reset_token.empty() && | |
| stateless_reset_token.size() != kStatelessResetTokenLength) { | |
| QUIC_DLOG(ERROR) << "Stateless reset token has bad length " | |
| << stateless_reset_token.size(); | |
| return false; | |
| } | |
| if (perspective == Perspective::IS_CLIENT && preferred_address) { | |
| QUIC_DLOG(ERROR) << "Client cannot send preferred address"; | |
| return false; | |
| } | |
| if (preferred_address && preferred_address->stateless_reset_token.size() != | |
| kStatelessResetTokenLength) { | |
| QUIC_DLOG(ERROR) | |
| << "Preferred address stateless reset token has bad length " | |
| << preferred_address->stateless_reset_token.size(); | |
| return false; | |
| } | |
| if (preferred_address && | |
| (!preferred_address->ipv4_socket_address.host().IsIPv4() || | |
| !preferred_address->ipv6_socket_address.host().IsIPv6())) { | |
| QUIC_BUG << "Preferred address family failure"; | |
| return false; | |
| } | |
| const bool ok = idle_timeout_milliseconds.IsValid() && | |
| max_packet_size.IsValid() && initial_max_data.IsValid() && | |
| initial_max_stream_data_bidi_local.IsValid() && | |
| initial_max_stream_data_bidi_remote.IsValid() && | |
| initial_max_stream_data_uni.IsValid() && | |
| initial_max_streams_bidi.IsValid() && | |
| initial_max_streams_uni.IsValid() && | |
| ack_delay_exponent.IsValid() && max_ack_delay.IsValid(); | |
| if (!ok) { | |
| QUIC_DLOG(ERROR) << "Invalid transport parameters " << *this; | |
| } | |
| return ok; | |
| } | |
| TransportParameters::~TransportParameters() = default; | |
| bool SerializeTransportParameters(const TransportParameters& in, | |
| std::vector<uint8_t>* out) { | |
| if (!in.AreValid()) { | |
| QUIC_DLOG(ERROR) << "Not serializing invalid transport parameters " << in; | |
| return false; | |
| } | |
| if (in.version == 0 || (in.perspective == Perspective::IS_SERVER && | |
| in.supported_versions.empty())) { | |
| QUIC_DLOG(ERROR) << "Refusing to serialize without versions"; | |
| return false; | |
| } | |
| bssl::ScopedCBB cbb; | |
| // Empirically transport parameters generally fit within 128 bytes. | |
| // The CBB will grow to fit larger serializations if required. | |
| if (!CBB_init(cbb.get(), 128)) { | |
| QUIC_BUG << "Failed to initialize CBB for " << in; | |
| return false; | |
| } | |
| CBB params; | |
| // Add length of the transport parameters list. | |
| if (!CBB_add_u16_length_prefixed(cbb.get(), ¶ms)) { | |
| QUIC_BUG << "Failed to write parameter length for " << in; | |
| return false; | |
| } | |
| // original_connection_id | |
| CBB original_connection_id_param; | |
| if (!in.original_connection_id.IsEmpty()) { | |
| DCHECK_EQ(Perspective::IS_SERVER, in.perspective); | |
| if (!CBB_add_u16(¶ms, TransportParameters::kOriginalConnectionId) || | |
| !CBB_add_u16_length_prefixed(¶ms, &original_connection_id_param) || | |
| !CBB_add_bytes( | |
| &original_connection_id_param, | |
| reinterpret_cast<const uint8_t*>(in.original_connection_id.data()), | |
| in.original_connection_id.length())) { | |
| QUIC_BUG << "Failed to write original_connection_id " | |
| << in.original_connection_id << " for " << in; | |
| return false; | |
| } | |
| } | |
| if (!in.idle_timeout_milliseconds.WriteToCbb(¶ms)) { | |
| QUIC_BUG << "Failed to write idle_timeout for " << in; | |
| return false; | |
| } | |
| // stateless_reset_token | |
| CBB stateless_reset_token_param; | |
| if (!in.stateless_reset_token.empty()) { | |
| DCHECK_EQ(kStatelessResetTokenLength, in.stateless_reset_token.size()); | |
| DCHECK_EQ(Perspective::IS_SERVER, in.perspective); | |
| if (!CBB_add_u16(¶ms, TransportParameters::kStatelessResetToken) || | |
| !CBB_add_u16_length_prefixed(¶ms, &stateless_reset_token_param) || | |
| !CBB_add_bytes(&stateless_reset_token_param, | |
| in.stateless_reset_token.data(), | |
| in.stateless_reset_token.size())) { | |
| QUIC_BUG << "Failed to write stateless_reset_token of length " | |
| << in.stateless_reset_token.size() << " for " << in; | |
| return false; | |
| } | |
| } | |
| if (!in.max_packet_size.WriteToCbb(¶ms) || | |
| !in.initial_max_data.WriteToCbb(¶ms) || | |
| !in.initial_max_stream_data_bidi_local.WriteToCbb(¶ms) || | |
| !in.initial_max_stream_data_bidi_remote.WriteToCbb(¶ms) || | |
| !in.initial_max_stream_data_uni.WriteToCbb(¶ms) || | |
| !in.initial_max_streams_bidi.WriteToCbb(¶ms) || | |
| !in.initial_max_streams_uni.WriteToCbb(¶ms) || | |
| !in.ack_delay_exponent.WriteToCbb(¶ms) || | |
| !in.max_ack_delay.WriteToCbb(¶ms)) { | |
| QUIC_BUG << "Failed to write integers for " << in; | |
| return false; | |
| } | |
| // disable_migration | |
| if (in.disable_migration) { | |
| if (!CBB_add_u16(¶ms, TransportParameters::kDisableMigration) || | |
| !CBB_add_u16(¶ms, 0u)) { // 0 is the length of this parameter. | |
| QUIC_BUG << "Failed to write disable_migration for " << in; | |
| return false; | |
| } | |
| } | |
| // preferred_address | |
| CBB preferred_address_params, preferred_address_connection_id_param; | |
| if (in.preferred_address) { | |
| std::string v4_address_bytes = | |
| in.preferred_address->ipv4_socket_address.host().ToPackedString(); | |
| std::string v6_address_bytes = | |
| in.preferred_address->ipv6_socket_address.host().ToPackedString(); | |
| if (v4_address_bytes.length() != 4 || v6_address_bytes.length() != 16 || | |
| in.preferred_address->stateless_reset_token.size() != | |
| kStatelessResetTokenLength) { | |
| QUIC_BUG << "Bad lengths " << *in.preferred_address; | |
| return false; | |
| } | |
| if (!CBB_add_u16(¶ms, TransportParameters::kPreferredAddress) || | |
| !CBB_add_u16_length_prefixed(¶ms, &preferred_address_params) || | |
| !CBB_add_bytes( | |
| &preferred_address_params, | |
| reinterpret_cast<const uint8_t*>(v4_address_bytes.data()), | |
| v4_address_bytes.length()) || | |
| !CBB_add_u16(&preferred_address_params, | |
| in.preferred_address->ipv4_socket_address.port()) || | |
| !CBB_add_bytes( | |
| &preferred_address_params, | |
| reinterpret_cast<const uint8_t*>(v6_address_bytes.data()), | |
| v6_address_bytes.length()) || | |
| !CBB_add_u16(&preferred_address_params, | |
| in.preferred_address->ipv6_socket_address.port()) || | |
| !CBB_add_u8_length_prefixed(&preferred_address_params, | |
| &preferred_address_connection_id_param) || | |
| !CBB_add_bytes(&preferred_address_connection_id_param, | |
| reinterpret_cast<const uint8_t*>( | |
| in.preferred_address->connection_id.data()), | |
| in.preferred_address->connection_id.length()) || | |
| !CBB_add_bytes(&preferred_address_params, | |
| in.preferred_address->stateless_reset_token.data(), | |
| in.preferred_address->stateless_reset_token.size())) { | |
| QUIC_BUG << "Failed to write preferred_address for " << in; | |
| return false; | |
| } | |
| } | |
| // Google-specific non-standard parameter. | |
| CBB google_quic_params; | |
| if (in.google_quic_params) { | |
| const QuicData& serialized_google_quic_params = | |
| in.google_quic_params->GetSerialized(); | |
| if (!CBB_add_u16(¶ms, TransportParameters::kGoogleQuicParam) || | |
| !CBB_add_u16_length_prefixed(¶ms, &google_quic_params) || | |
| !CBB_add_bytes(&google_quic_params, | |
| reinterpret_cast<const uint8_t*>( | |
| serialized_google_quic_params.data()), | |
| serialized_google_quic_params.length())) { | |
| QUIC_BUG << "Failed to write Google params of length " | |
| << serialized_google_quic_params.length() << " for " << in; | |
| return false; | |
| } | |
| } | |
| // Google-specific version extension. | |
| CBB google_version_params; | |
| if (!CBB_add_u16(¶ms, TransportParameters::kGoogleQuicVersion) || | |
| !CBB_add_u16_length_prefixed(¶ms, &google_version_params) || | |
| !CBB_add_u32(&google_version_params, in.version)) { | |
| QUIC_BUG << "Failed to write Google version extension for " << in; | |
| return false; | |
| } | |
| CBB versions; | |
| if (in.perspective == Perspective::IS_SERVER) { | |
| if (!CBB_add_u8_length_prefixed(&google_version_params, &versions)) { | |
| QUIC_BUG << "Failed to write versions length for " << in; | |
| return false; | |
| } | |
| for (QuicVersionLabel version : in.supported_versions) { | |
| if (!CBB_add_u32(&versions, version)) { | |
| QUIC_BUG << "Failed to write supported version for " << in; | |
| return false; | |
| } | |
| } | |
| } | |
| if (!CBB_flush(cbb.get())) { | |
| QUIC_BUG << "Failed to flush CBB for " << in; | |
| return false; | |
| } | |
| out->resize(CBB_len(cbb.get())); | |
| memcpy(out->data(), CBB_data(cbb.get()), CBB_len(cbb.get())); | |
| QUIC_DLOG(INFO) << "Serialized " << in << " as " << CBB_len(cbb.get()) | |
| << " bytes"; | |
| return true; | |
| } | |
| bool ParseTransportParameters(const uint8_t* in, | |
| size_t in_len, | |
| Perspective perspective, | |
| TransportParameters* out) { | |
| out->perspective = perspective; | |
| CBS cbs; | |
| CBS_init(&cbs, in, in_len); | |
| CBS params; | |
| if (!CBS_get_u16_length_prefixed(&cbs, ¶ms)) { | |
| QUIC_DLOG(ERROR) << "Failed to parse the number of transport parameters"; | |
| return false; | |
| } | |
| while (CBS_len(¶ms) > 0) { | |
| TransportParameters::TransportParameterId param_id; | |
| CBS value; | |
| static_assert(sizeof(param_id) == sizeof(uint16_t), "bad size"); | |
| if (!CBS_get_u16(¶ms, reinterpret_cast<uint16_t*>(¶m_id))) { | |
| QUIC_DLOG(ERROR) << "Failed to parse transport parameter ID"; | |
| return false; | |
| } | |
| if (!CBS_get_u16_length_prefixed(¶ms, &value)) { | |
| QUIC_DLOG(ERROR) << "Failed to parse length of transport parameter " | |
| << TransportParameterIdToString(param_id); | |
| return false; | |
| } | |
| bool parse_success = true; | |
| switch (param_id) { | |
| case TransportParameters::kOriginalConnectionId: | |
| if (!out->original_connection_id.IsEmpty()) { | |
| QUIC_DLOG(ERROR) << "Received a second original connection ID"; | |
| return false; | |
| } | |
| if (CBS_len(&value) > static_cast<size_t>(kQuicMaxConnectionIdLength)) { | |
| QUIC_DLOG(ERROR) << "Received original connection ID of " | |
| << "invalid length " << CBS_len(&value); | |
| return false; | |
| } | |
| if (CBS_len(&value) != 0) { | |
| out->original_connection_id.set_length(CBS_len(&value)); | |
| memcpy(out->original_connection_id.mutable_data(), CBS_data(&value), | |
| CBS_len(&value)); | |
| } | |
| break; | |
| case TransportParameters::kIdleTimeout: | |
| parse_success = out->idle_timeout_milliseconds.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kStatelessResetToken: | |
| if (!out->stateless_reset_token.empty()) { | |
| QUIC_DLOG(ERROR) << "Received a second stateless reset token"; | |
| return false; | |
| } | |
| if (CBS_len(&value) != kStatelessResetTokenLength) { | |
| QUIC_DLOG(ERROR) << "Received stateless reset token of " | |
| << "invalid length " << CBS_len(&value); | |
| return false; | |
| } | |
| out->stateless_reset_token.assign(CBS_data(&value), | |
| CBS_data(&value) + CBS_len(&value)); | |
| break; | |
| case TransportParameters::kMaxPacketSize: | |
| parse_success = out->max_packet_size.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kInitialMaxData: | |
| parse_success = out->initial_max_data.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kInitialMaxStreamDataBidiLocal: | |
| parse_success = | |
| out->initial_max_stream_data_bidi_local.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kInitialMaxStreamDataBidiRemote: | |
| parse_success = | |
| out->initial_max_stream_data_bidi_remote.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kInitialMaxStreamDataUni: | |
| parse_success = out->initial_max_stream_data_uni.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kInitialMaxStreamsBidi: | |
| parse_success = out->initial_max_streams_bidi.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kInitialMaxStreamsUni: | |
| parse_success = out->initial_max_streams_uni.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kAckDelayExponent: | |
| parse_success = out->ack_delay_exponent.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kMaxAckDelay: | |
| parse_success = out->max_ack_delay.ReadFromCbs(&value); | |
| break; | |
| case TransportParameters::kDisableMigration: | |
| if (out->disable_migration) { | |
| QUIC_DLOG(ERROR) << "Received a second disable migration"; | |
| return false; | |
| } | |
| if (CBS_len(&value) != 0) { | |
| QUIC_DLOG(ERROR) << "Received disable migration of invalid length " | |
| << CBS_len(&value); | |
| return false; | |
| } | |
| out->disable_migration = true; | |
| break; | |
| case TransportParameters::kPreferredAddress: { | |
| uint16_t ipv4_port, ipv6_port; | |
| in_addr ipv4_address; | |
| in6_addr ipv6_address; | |
| if (!CBS_copy_bytes(&value, reinterpret_cast<uint8_t*>(&ipv4_address), | |
| sizeof(ipv4_address)) || | |
| !CBS_get_u16(&value, &ipv4_port) || | |
| !CBS_copy_bytes(&value, reinterpret_cast<uint8_t*>(&ipv6_address), | |
| sizeof(ipv6_address)) || | |
| !CBS_get_u16(&value, &ipv6_port)) { | |
| QUIC_DLOG(ERROR) << "Failed to parse preferred address IPs and ports"; | |
| return false; | |
| } | |
| TransportParameters::PreferredAddress preferred_address; | |
| preferred_address.ipv4_socket_address = | |
| QuicSocketAddress(QuicIpAddress(ipv4_address), ipv4_port); | |
| preferred_address.ipv6_socket_address = | |
| QuicSocketAddress(QuicIpAddress(ipv6_address), ipv6_port); | |
| if (!preferred_address.ipv4_socket_address.host().IsIPv4() || | |
| !preferred_address.ipv6_socket_address.host().IsIPv6()) { | |
| QUIC_DLOG(ERROR) << "Received preferred addresses of bad families " | |
| << preferred_address; | |
| return false; | |
| } | |
| CBS connection_id_cbs; | |
| if (!CBS_get_u8_length_prefixed(&value, &connection_id_cbs)) { | |
| QUIC_DLOG(ERROR) | |
| << "Failed to parse length of preferred address connection ID"; | |
| return false; | |
| } | |
| if (CBS_len(&connection_id_cbs) > kQuicMaxConnectionIdLength) { | |
| QUIC_DLOG(ERROR) << "Bad preferred address connection ID length"; | |
| return false; | |
| } | |
| preferred_address.connection_id.set_length(CBS_len(&connection_id_cbs)); | |
| if (preferred_address.connection_id.length() > 0 && | |
| !CBS_copy_bytes(&connection_id_cbs, | |
| reinterpret_cast<uint8_t*>( | |
| preferred_address.connection_id.mutable_data()), | |
| preferred_address.connection_id.length())) { | |
| QUIC_DLOG(ERROR) << "Failed to read preferred address connection ID"; | |
| return false; | |
| } | |
| if (CBS_len(&value) != kStatelessResetTokenLength) { | |
| QUIC_DLOG(ERROR) << "Received preferred address with " | |
| << "invalid remaining length " << CBS_len(&value); | |
| return false; | |
| } | |
| preferred_address.stateless_reset_token.assign( | |
| CBS_data(&value), CBS_data(&value) + CBS_len(&value)); | |
| out->preferred_address = | |
| QuicMakeUnique<TransportParameters::PreferredAddress>( | |
| preferred_address); | |
| } break; | |
| case TransportParameters::kGoogleQuicParam: { | |
| if (out->google_quic_params) { | |
| QUIC_DLOG(ERROR) << "Received a second Google parameter"; | |
| return false; | |
| } | |
| QuicStringPiece serialized_params( | |
| reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value)); | |
| out->google_quic_params = CryptoFramer::ParseMessage(serialized_params); | |
| } break; | |
| case TransportParameters::kGoogleQuicVersion: { | |
| if (!CBS_get_u32(&value, &out->version)) { | |
| QUIC_DLOG(ERROR) << "Failed to parse Google version extension"; | |
| return false; | |
| } | |
| if (perspective == Perspective::IS_SERVER) { | |
| CBS versions; | |
| if (!CBS_get_u8_length_prefixed(&value, &versions) || | |
| CBS_len(&versions) % 4 != 0) { | |
| QUIC_DLOG(ERROR) | |
| << "Failed to parse Google supported versions length"; | |
| return false; | |
| } | |
| while (CBS_len(&versions) > 0) { | |
| QuicVersionLabel version; | |
| if (!CBS_get_u32(&versions, &version)) { | |
| QUIC_DLOG(ERROR) << "Failed to parse Google supported version"; | |
| return false; | |
| } | |
| out->supported_versions.push_back(version); | |
| } | |
| } | |
| } break; | |
| } | |
| if (!parse_success) { | |
| return false; | |
| } | |
| } | |
| const bool ok = out->AreValid(); | |
| if (ok) { | |
| QUIC_DLOG(INFO) << "Parsed transport parameters " << *out << " from " | |
| << in_len << " bytes"; | |
| } else { | |
| QUIC_DLOG(ERROR) << "Transport parameter validity check failed " << *out | |
| << " from " << in_len << " bytes"; | |
| } | |
| return ok; | |
| } | |
| } // namespace quic |