| // 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, |
| kActiveConnectionIdLimit = 0xe, |
| |
| 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::kActiveConnectionIdLimit: |
| return "active_connection_id_limit"; |
| 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(); |
| } |
| rv += active_connection_id_limit.ToString(/*for_use_in_list=*/true); |
| 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), |
| active_connection_id_limit(kActiveConnectionIdLimit) |
| // 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() && |
| active_connection_id_limit.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) || |
| !in.active_connection_id_limit.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::kActiveConnectionIdLimit: |
| parse_success = out->active_connection_id_limit.ReadFromCbs(&value); |
| 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 |