Update QUIC TLS transport parameters to IETF draft 18
Our current implementation supported the transport parameter format from draft 08. Since the IETF has substantially redesigned them since then (variable length integers, no mandatory parameters), we created a new class to handle variable length integer parameters.
gfe-relnote: protected by disabled flag quic_supports_tls_handshake
PiperOrigin-RevId: 244077190
Change-Id: I38c5e0ba0aa64f62a64532315a19c2aac717fa20
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index 60fb22c..84022fe 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -4,184 +4,514 @@
#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+#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 {
-namespace {
+// 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,
-// Values of the TransportParameterId enum as defined in
-// draft-ietf-quic-transport-08 section 7.4. When parameters are encoded, one of
-// these enum values is used to indicate which parameter is encoded.
-enum TransportParameterId : uint16_t {
- kInitialMaxStreamDataId = 0,
- kInitialMaxDataId = 1,
- kInitialMaxBidiStreamsId = 2,
- kIdleTimeoutId = 3,
- kMaxOutgoingPacketSizeId = 5,
- kStatelessResetTokenId = 6,
- kAckDelayExponentId = 7,
- kInitialMaxUniStreamsId = 8,
-
- kMaxKnownParameterId = 9,
+ kGoogleQuicParam = 18257, // Used for non-standard Google-specific params.
};
-// Value for the TransportParameterId to use for non-standard Google QUIC params
-// in Transport Parameters.
-const uint16_t kGoogleQuicParamId = 18257;
+namespace {
// The following constants define minimum and maximum allowed values for some of
-// the parameters. These come from draft-ietf-quic-transport-08 section 7.4.1.
-const uint16_t kMaxAllowedIdleTimeout = 600;
-const uint16_t kMinAllowedMaxPacketSize = 1200;
-const uint16_t kMaxAllowedMaxPacketSize = 65527;
-const uint8_t kMaxAllowedAckDelayExponent = 20;
+// 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;
-static_assert(kMaxKnownParameterId <= 32, "too many parameters to bit pack");
-
-// The initial_max_stream_data, initial_max_data, and idle_timeout parameters
-// are always required to be present. When parsing the extension, a bitmask is
-// used to keep track of which parameter have been seen so far, and that bitmask
-// will be compared to this mask to check that all of the required parameters
-// were present.
-static constexpr uint16_t kRequiredParamsMask = (1 << kInitialMaxStreamDataId) |
- (1 << kInitialMaxDataId) |
- (1 << kIdleTimeoutId);
+std::string TransportParameterIdToString(
+ TransportParameters::TransportParameterId param_id) {
+ switch (param_id) {
+ case kOriginalConnectionId:
+ return "original_connection_id";
+ case kIdleTimeout:
+ return "idle_timeout";
+ case kStatelessResetToken:
+ return "stateless_reset_token";
+ case kMaxPacketSize:
+ return "max_packet_size";
+ case kInitialMaxData:
+ return "initial_max_data";
+ case kInitialMaxStreamDataBidiLocal:
+ return "initial_max_stream_data_bidi_local";
+ case kInitialMaxStreamDataBidiRemote:
+ return "initial_max_stream_data_bidi_remote";
+ case kInitialMaxStreamDataUni:
+ return "initial_max_stream_data_uni";
+ case kInitialMaxStreamsBidi:
+ return "initial_max_streams_bidi";
+ case kInitialMaxStreamsUni:
+ return "initial_max_streams_uni";
+ case kAckDelayExponent:
+ return "ack_delay_exponent";
+ case kMaxAckDelay:
+ return "max_ack_delay";
+ case kDisableMigration:
+ return "disable_migration";
+ case kPreferredAddress:
+ return "preferred_address";
+ case kGoogleQuicParam:
+ return "google";
+ }
+ return "Unknown(" + QuicTextUtils::Uint64ToString(param_id) + ")";
+}
} // namespace
-TransportParameters::TransportParameters() = default;
+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::~TransportParameters() = default;
+TransportParameters::IntegerParameter::IntegerParameter(
+ TransportParameters::TransportParameterId param_id)
+ : TransportParameters::IntegerParameter::IntegerParameter(
+ param_id,
+ 0,
+ 0,
+ kVarInt62MaxValue) {}
-bool TransportParameters::is_valid() const {
- if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) {
+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;
}
- if (perspective == Perspective::IS_SERVER &&
- stateless_reset_token.size() != 16) {
+ 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 (idle_timeout > kMaxAllowedIdleTimeout ||
- (max_packet_size.present &&
- (max_packet_size.value > kMaxAllowedMaxPacketSize ||
- max_packet_size.value < kMinAllowedMaxPacketSize)) ||
- (ack_delay_exponent.present &&
- ack_delay_exponent.value > kMaxAllowedAckDelayExponent)) {
+ 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_seconds.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_seconds(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_seconds.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.is_valid()) {
+ if (!in.AreValid()) {
+ QUIC_DLOG(ERROR) << "Not serializing invalid transport parameters " << in;
return false;
}
bssl::ScopedCBB cbb;
- // 28 is the minimum size that the serialized TransportParameters can be,
- // which is when it is for a client and only the required parameters are
- // present. The CBB will grow to fit larger serializations.
- if (!CBB_init(cbb.get(), 28) || !CBB_add_u32(cbb.get(), in.version)) {
+ // Empirically transport parameters generally fit within 128 bytes.
+ // The CBB will grow to fit larger serializations if required.
+ if (!CBB_init(cbb.get(), 128) || !CBB_add_u32(cbb.get(), in.version)) {
+ QUIC_BUG << "Failed to write version for " << in;
return false;
}
CBB versions;
if (in.perspective == Perspective::IS_SERVER) {
if (!CBB_add_u8_length_prefixed(cbb.get(), &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;
}
}
}
- CBB params, initial_max_stream_data_param, initial_max_data_param,
- idle_timeout_param;
- // required parameters
- if (!CBB_add_u16_length_prefixed(cbb.get(), ¶ms) ||
- // initial_max_stream_data
- !CBB_add_u16(¶ms, kInitialMaxStreamDataId) ||
- !CBB_add_u16_length_prefixed(¶ms, &initial_max_stream_data_param) ||
- !CBB_add_u32(&initial_max_stream_data_param,
- in.initial_max_stream_data) ||
- // initial_max_data
- !CBB_add_u16(¶ms, kInitialMaxDataId) ||
- !CBB_add_u16_length_prefixed(¶ms, &initial_max_data_param) ||
- !CBB_add_u32(&initial_max_data_param, in.initial_max_data) ||
- // idle_timeout
- !CBB_add_u16(¶ms, kIdleTimeoutId) ||
- !CBB_add_u16_length_prefixed(¶ms, &idle_timeout_param) ||
- !CBB_add_u16(&idle_timeout_param, in.idle_timeout)) {
+ 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, 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_seconds.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()) {
- if (!CBB_add_u16(¶ms, kStatelessResetTokenId) ||
+ DCHECK_EQ(kStatelessResetTokenLength, in.stateless_reset_token.size());
+ DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
+ if (!CBB_add_u16(¶ms, 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;
}
}
- CBB initial_max_bidi_streams_param;
- if (in.initial_max_bidi_streams.present) {
- if (!CBB_add_u16(¶ms, kInitialMaxBidiStreamsId) ||
- !CBB_add_u16_length_prefixed(¶ms,
- &initial_max_bidi_streams_param) ||
- !CBB_add_u16(&initial_max_bidi_streams_param,
- in.initial_max_bidi_streams.value)) {
+ 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, kDisableMigration) ||
+ !CBB_add_u16(¶ms, 0u)) { // 0 is the length of this parameter.
+ QUIC_BUG << "Failed to write disable_migration for " << in;
return false;
}
}
- CBB initial_max_uni_streams_param;
- if (in.initial_max_uni_streams.present) {
- if (!CBB_add_u16(¶ms, kInitialMaxUniStreamsId) ||
- !CBB_add_u16_length_prefixed(¶ms, &initial_max_uni_streams_param) ||
- !CBB_add_u16(&initial_max_uni_streams_param,
- in.initial_max_uni_streams.value)) {
+
+ // 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, 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_u16_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;
}
}
- CBB max_packet_size_param;
- if (in.max_packet_size.present) {
- if (!CBB_add_u16(¶ms, kMaxOutgoingPacketSizeId) ||
- !CBB_add_u16_length_prefixed(¶ms, &max_packet_size_param) ||
- !CBB_add_u16(&max_packet_size_param, in.max_packet_size.value)) {
- return false;
- }
- }
- CBB ack_delay_exponent_param;
- if (in.ack_delay_exponent.present) {
- if (!CBB_add_u16(¶ms, kAckDelayExponentId) ||
- !CBB_add_u16_length_prefixed(¶ms, &ack_delay_exponent_param) ||
- !CBB_add_u8(&ack_delay_exponent_param, in.ack_delay_exponent.value)) {
- 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, kGoogleQuicParamId) ||
+ if (!CBB_add_u16(¶ms, 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;
}
}
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;
}
@@ -192,17 +522,22 @@
CBS cbs;
CBS_init(&cbs, in, in_len);
if (!CBS_get_u32(&cbs, &out->version)) {
+ QUIC_DLOG(ERROR) << "Failed to parse transport parameter version";
return false;
}
if (perspective == Perspective::IS_SERVER) {
CBS versions;
if (!CBS_get_u8_length_prefixed(&cbs, &versions) ||
CBS_len(&versions) % 4 != 0) {
+ QUIC_DLOG(ERROR)
+ << "Failed to parse transport parameter supported versions";
return false;
}
while (CBS_len(&versions) > 0) {
QuicVersionLabel version;
if (!CBS_get_u32(&versions, &version)) {
+ QUIC_DLOG(ERROR)
+ << "Failed to parse transport parameter supported version";
return false;
}
out->supported_versions.push_back(version);
@@ -210,93 +545,176 @@
}
out->perspective = perspective;
- uint32_t present_params = 0;
- bool has_google_quic_params = false;
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) {
uint16_t param_id;
CBS value;
- if (!CBS_get_u16(¶ms, ¶m_id) ||
- !CBS_get_u16_length_prefixed(¶ms, &value)) {
+ if (!CBS_get_u16(¶ms, ¶m_id)) {
+ QUIC_DLOG(ERROR) << "Failed to parse transport parameter ID";
return false;
}
- if (param_id < kMaxKnownParameterId) {
- uint16_t mask = 1 << param_id;
- if (present_params & mask) {
- return false;
- }
- present_params |= mask;
+ if (!CBS_get_u16_length_prefixed(¶ms, &value)) {
+ QUIC_DLOG(ERROR) << "Failed to parse length of transport parameter "
+ << param_id;
+ return false;
}
- switch (param_id) {
- case kInitialMaxStreamDataId:
- if (!CBS_get_u32(&value, &out->initial_max_stream_data) ||
- CBS_len(&value) != 0) {
+ bool parse_success = true;
+ switch (static_cast<TransportParameters::TransportParameterId>(param_id)) {
+ case kOriginalConnectionId:
+ if (!out->original_connection_id.IsEmpty()) {
+ QUIC_DLOG(ERROR) << "Received a second original connection ID";
return false;
}
- break;
- case kInitialMaxDataId:
- if (!CBS_get_u32(&value, &out->initial_max_data) ||
- CBS_len(&value) != 0) {
+ if (CBS_len(&value) > static_cast<size_t>(kQuicMaxConnectionIdLength)) {
+ QUIC_DLOG(ERROR) << "Received original connection ID of "
+ << "invalid length " << CBS_len(&value);
return false;
}
- break;
- case kInitialMaxBidiStreamsId:
- if (!CBS_get_u16(&value, &out->initial_max_bidi_streams.value) ||
- CBS_len(&value) != 0) {
- return false;
- }
- out->initial_max_bidi_streams.present = true;
- break;
- case kIdleTimeoutId:
- if (!CBS_get_u16(&value, &out->idle_timeout) || CBS_len(&value) != 0) {
- 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 kMaxOutgoingPacketSizeId:
- if (!CBS_get_u16(&value, &out->max_packet_size.value) ||
- CBS_len(&value) != 0) {
+ case kIdleTimeout:
+ parse_success = out->idle_timeout_seconds.ReadFromCbs(&value);
+ break;
+ case kStatelessResetToken:
+ if (!out->stateless_reset_token.empty()) {
+ QUIC_DLOG(ERROR) << "Received a second stateless reset token";
return false;
}
- out->max_packet_size.present = true;
- break;
- case kStatelessResetTokenId:
- if (CBS_len(&value) == 0) {
+ 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 kAckDelayExponentId:
- if (!CBS_get_u8(&value, &out->ack_delay_exponent.value) ||
- CBS_len(&value) != 0) {
- return false;
- }
- out->ack_delay_exponent.present = true;
+ case kMaxPacketSize:
+ parse_success = out->max_packet_size.ReadFromCbs(&value);
break;
- case kInitialMaxUniStreamsId:
- if (!CBS_get_u16(&value, &out->initial_max_uni_streams.value) ||
- CBS_len(&value) != 0) {
- return false;
- }
- out->initial_max_uni_streams.present = true;
+ case kInitialMaxData:
+ parse_success = out->initial_max_data.ReadFromCbs(&value);
break;
- case kGoogleQuicParamId:
- if (has_google_quic_params) {
+ case kInitialMaxStreamDataBidiLocal:
+ parse_success =
+ out->initial_max_stream_data_bidi_local.ReadFromCbs(&value);
+ break;
+ case kInitialMaxStreamDataBidiRemote:
+ parse_success =
+ out->initial_max_stream_data_bidi_remote.ReadFromCbs(&value);
+ break;
+ case kInitialMaxStreamDataUni:
+ parse_success = out->initial_max_stream_data_uni.ReadFromCbs(&value);
+ break;
+ case kInitialMaxStreamsBidi:
+ parse_success = out->initial_max_streams_bidi.ReadFromCbs(&value);
+ break;
+ case kInitialMaxStreamsUni:
+ parse_success = out->initial_max_streams_uni.ReadFromCbs(&value);
+ break;
+ case kAckDelayExponent:
+ parse_success = out->ack_delay_exponent.ReadFromCbs(&value);
+ break;
+ case kMaxAckDelay:
+ parse_success = out->max_ack_delay.ReadFromCbs(&value);
+ break;
+ case kDisableMigration:
+ if (out->disable_migration) {
+ QUIC_DLOG(ERROR) << "Received a second disable migration";
return false;
}
- has_google_quic_params = true;
+ 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 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_u16_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 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);
}
+ if (!parse_success) {
+ return false;
+ }
}
- if ((present_params & kRequiredParamsMask) != kRequiredParamsMask) {
- 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 out->is_valid();
+ return ok;
}
} // namespace quic
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h
index b8abc0b..251d141 100644
--- a/quic/core/crypto/transport_parameters.h
+++ b/quic/core/crypto/transport_parameters.h
@@ -8,71 +8,178 @@
#include <memory>
#include <vector>
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.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"
namespace quic {
// TransportParameters contains parameters for QUIC's transport layer that are
-// indicated during the TLS handshake. This struct is a mirror of the struct in
-// section 6.4 of draft-ietf-quic-transport-11.
+// exchanged during the TLS handshake. This struct is a mirror of the struct in
+// the "Transport Parameter Encoding" section of draft-ietf-quic-transport.
+// This struct currently uses the values from draft 18.
struct QUIC_EXPORT_PRIVATE TransportParameters {
+ // The identifier used to differentiate transport parameters.
+ enum TransportParameterId : uint16_t;
+ // Represents an individual QUIC transport parameter that only encodes a
+ // variable length integer. Can only be created inside the constructor for
+ // TransportParameters.
+ class QUIC_EXPORT_PRIVATE IntegerParameter {
+ public:
+ // Forbid constructing and copying apart from TransportParameters.
+ IntegerParameter() = delete;
+ IntegerParameter(const IntegerParameter&) = delete;
+ IntegerParameter& operator=(const IntegerParameter&) = delete;
+ // Sets the value of this transport parameter.
+ void set_value(uint64_t value);
+ // Gets the value of this transport parameter.
+ uint64_t value() const;
+ // Validates whether the current value is valid.
+ bool IsValid() const;
+ // Writes to a crypto byte buffer, used during serialization. Does not write
+ // anything if the value is equal to the parameter's default value.
+ // Returns whether the write was successful.
+ bool WriteToCbb(CBB* parent_cbb) const;
+ // Reads from a crypto byte string, used during parsing.
+ // Returns whether the read was successful.
+ bool ReadFromCbs(CBS* const value_cbs);
+ // operator<< allows easily logging integer transport parameters.
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const IntegerParameter& param);
+
+ private:
+ friend class TransportParameters;
+ // Constructors for initial setup used by TransportParameters only.
+ // This constructor sets |default_value| and |min_value| to 0, and
+ // |max_value| to kVarInt62MaxValue.
+ explicit IntegerParameter(TransportParameterId param_id);
+ IntegerParameter(TransportParameterId param_id,
+ uint64_t default_value,
+ uint64_t min_value,
+ uint64_t max_value);
+ // Human-readable string representation.
+ std::string ToString(bool for_use_in_list) const;
+
+ // Number used to indicate this transport parameter.
+ TransportParameterId param_id_;
+ // Current value of the transport parameter.
+ uint64_t value_;
+ // Default value of this transport parameter, as per IETF specification.
+ const uint64_t default_value_;
+ // Minimum value of this transport parameter, as per IETF specification.
+ const uint64_t min_value_;
+ // Maximum value of this transport parameter, as per IETF specification.
+ const uint64_t max_value_;
+ // Ensures this parameter is not parsed twice in the same message.
+ bool has_been_read_from_cbs_;
+ };
+
+ // Represents the preferred_address transport parameter that a server can
+ // send to clients.
+ struct QUIC_EXPORT_PRIVATE PreferredAddress {
+ PreferredAddress();
+ ~PreferredAddress();
+
+ QuicSocketAddress ipv4_socket_address;
+ QuicSocketAddress ipv6_socket_address;
+ QuicConnectionId connection_id;
+ std::vector<uint8_t> stateless_reset_token;
+
+ // Allows easily logging.
+ std::string ToString() const;
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const TransportParameters& params);
+ };
+
TransportParameters();
~TransportParameters();
- // When |perspective| is Perspective::IS_CLIENT, this struct is being used in
- // the client_hello handshake message; when it is Perspective::IS_SERVER, it
- // is being used in the encrypted_extensions handshake message.
+ // Represents the sender of the transport parameters. When |perspective| is
+ // Perspective::IS_CLIENT, this struct is being used in the client_hello
+ // handshake message; when it is Perspective::IS_SERVER, it is being used in
+ // the encrypted_extensions handshake message.
Perspective perspective;
// When Perspective::IS_CLIENT, |version| is the initial version offered by
// the client (before any version negotiation packets) for this connection.
// When Perspective::IS_SERVER, |version| is the version that is in use.
- QuicVersionLabel version = 0;
-
- // Server-only parameters:
+ QuicVersionLabel version;
// |supported_versions| contains a list of all versions that the server would
// send in a version negotiation packet. It is not used if |perspective ==
// Perspective::IS_CLIENT|.
QuicVersionLabelVector supported_versions;
- // See section 6.4.1 of draft-ietf-quic-transport-11 for definition.
+ // The value of the Destination Connection ID field from the first
+ // Initial packet sent by the client.
+ QuicConnectionId original_connection_id;
+
+ // Idle timeout expressed in seconds.
+ IntegerParameter idle_timeout_seconds;
+
+ // Stateless reset token used in verifying stateless resets.
std::vector<uint8_t> stateless_reset_token;
- // Required parameters. See section 6.4.1 of draft-ietf-quic-transport-11 for
- // definitions.
- uint32_t initial_max_stream_data = 0;
- uint32_t initial_max_data = 0;
- uint16_t idle_timeout = 0;
+ // Limits the size of packets that the endpoint is willing to receive.
+ // This indicates that packets larger than this limit will be dropped.
+ IntegerParameter max_packet_size;
- template <typename T>
- struct OptionalParam {
- bool present = false;
- T value;
- };
+ // Contains the initial value for the maximum amount of data that can
+ // be sent on the connection.
+ IntegerParameter initial_max_data;
- // Optional parameters. See section 6.4.1 of draft-ietf-quic-transport-11 for
- // definitions.
- OptionalParam<uint16_t> initial_max_bidi_streams;
- OptionalParam<uint16_t> initial_max_uni_streams;
- OptionalParam<uint16_t> max_packet_size;
- OptionalParam<uint8_t> ack_delay_exponent;
+ // Initial flow control limit for locally-initiated bidirectional streams.
+ IntegerParameter initial_max_stream_data_bidi_local;
+
+ // Initial flow control limit for peer-initiated bidirectional streams.
+ IntegerParameter initial_max_stream_data_bidi_remote;
+
+ // Initial flow control limit for unidirectional streams.
+ IntegerParameter initial_max_stream_data_uni;
+
+ // Initial maximum number of bidirectional streams the peer may initiate.
+ IntegerParameter initial_max_streams_bidi;
+
+ // Initial maximum number of unidirectional streams the peer may initiate.
+ IntegerParameter initial_max_streams_uni;
+
+ // Exponent used to decode the ACK Delay field in ACK frames.
+ IntegerParameter ack_delay_exponent;
+
+ // Maximum amount of time in milliseconds by which the endpoint will
+ // delay sending acknowledgments.
+ IntegerParameter max_ack_delay;
+
+ // Indicates lack of support for connection migration.
+ bool disable_migration;
+
+ // Used to effect a change in server address at the end of the handshake.
+ std::unique_ptr<PreferredAddress> preferred_address;
// Transport parameters used by Google QUIC but not IETF QUIC. This is
// serialized into a TransportParameter struct with a TransportParameterId of
- // 18257.
+ // kGoogleQuicParamId.
std::unique_ptr<CryptoHandshakeMessage> google_quic_params;
- // Returns true if the contents of this struct are valid.
- bool is_valid() const;
+ // Validates whether transport parameters are valid according to
+ // the specification.
+ bool AreValid() const;
+
+ // Allows easily logging transport parameters.
+ std::string ToString() const;
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const TransportParameters& params);
};
// Serializes a TransportParameters struct into the format for sending it in a
-// TLS extension. The serialized bytes are put in |*out|, and this function
-// returns true on success or false if |TransportParameters::is_valid| returns
-// false.
+// TLS extension. The serialized bytes are written to |*out|. Returns if the
+// parameters are valid and serialization succeeded.
QUIC_EXPORT_PRIVATE bool SerializeTransportParameters(
const TransportParameters& in,
std::vector<uint8_t>* out);
@@ -80,8 +187,7 @@
// Parses bytes from the quic_transport_parameters TLS extension and writes the
// parsed parameters into |*out|. Input is read from |in| for |in_len| bytes.
// |perspective| indicates whether the input came from a client or a server.
-// This method returns true if the input was successfully parsed, and false if
-// it could not be parsed.
+// This method returns true if the input was successfully parsed.
// TODO(nharper): Write fuzz tests for this method.
QUIC_EXPORT_PRIVATE bool ParseTransportParameters(const uint8_t* in,
size_t in_len,
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc
index 23b18e5..cf36581 100644
--- a/quic/core/crypto/transport_parameters_test.cc
+++ b/quic/core/crypto/transport_parameters_test.cc
@@ -4,31 +4,90 @@
#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+#include <cstring>
+
#include "third_party/boringssl/src/include/openssl/mem.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
namespace quic {
namespace test {
+namespace {
+const QuicVersionLabel kFakeVersionLabel = 0x01234567;
+const QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF;
+const QuicConnectionId kFakeOriginalConnectionId = TestConnectionId(0x1337);
+const uint64_t kFakeIdleTimeout = 12;
+const uint8_t kFakeStatelessResetTokenData[16] = {
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F};
+const std::vector<uint8_t> kFakeStatelessResetToken(
+ kFakeStatelessResetTokenData,
+ kFakeStatelessResetTokenData + sizeof(kFakeStatelessResetTokenData));
+const uint64_t kFakeMaxPacketSize = 9001;
+const uint64_t kFakeInitialMaxData = 101;
+const uint64_t kFakeInitialMaxStreamDataBidiLocal = 2001;
+const uint64_t kFakeInitialMaxStreamDataBidiRemote = 2002;
+const uint64_t kFakeInitialMaxStreamDataUni = 3000;
+const uint64_t kFakeInitialMaxStreamsBidi = 21;
+const uint64_t kFakeInitialMaxStreamsUni = 22;
+const uint64_t kFakeAckDelayExponent = 10;
+const uint64_t kFakeMaxAckDelay = 51;
+const bool kFakeDisableMigration = true;
+const in_addr kFakeV4Address = {QuicEndian::HostToNet32(0x41424344)};
+// clang-format off
+const in6_addr kFakeV6Address = {{{
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}}};
+// clang-format on
+const QuicSocketAddress kFakeV4SocketAddress(QuicIpAddress(kFakeV4Address),
+ 0x4884);
+const QuicSocketAddress kFakeV6SocketAddress(QuicIpAddress(kFakeV6Address),
+ 0x6336);
+const QuicConnectionId kFakePreferredConnectionId = TestConnectionId(0xBEEF);
+const uint8_t kFakePreferredStatelessResetTokenData[16] = {
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F};
+const std::vector<uint8_t> kFakePreferredStatelessResetToken(
+ kFakePreferredStatelessResetTokenData,
+ kFakePreferredStatelessResetTokenData +
+ sizeof(kFakeStatelessResetTokenData));
+
+std::unique_ptr<TransportParameters::PreferredAddress>
+CreateFakePreferredAddress() {
+ TransportParameters::PreferredAddress preferred_address;
+ preferred_address.ipv4_socket_address = kFakeV4SocketAddress;
+ preferred_address.ipv6_socket_address = kFakeV6SocketAddress;
+ preferred_address.connection_id = kFakePreferredConnectionId;
+ preferred_address.stateless_reset_token = kFakePreferredStatelessResetToken;
+ return QuicMakeUnique<TransportParameters::PreferredAddress>(
+ preferred_address);
+}
+
+} // namespace
class TransportParametersTest : public QuicTest {};
TEST_F(TransportParametersTest, RoundTripClient) {
TransportParameters orig_params;
orig_params.perspective = Perspective::IS_CLIENT;
- orig_params.initial_max_stream_data = 12;
- orig_params.initial_max_data = 34;
- orig_params.idle_timeout = 56;
- orig_params.initial_max_bidi_streams.present = true;
- orig_params.initial_max_bidi_streams.value = 2000;
- orig_params.initial_max_uni_streams.present = true;
- orig_params.initial_max_uni_streams.value = 3000;
- orig_params.max_packet_size.present = true;
- orig_params.max_packet_size.value = 9001;
- orig_params.ack_delay_exponent.present = true;
- orig_params.ack_delay_exponent.value = 10;
- orig_params.version = 0xff000005;
+ orig_params.version = kFakeVersionLabel;
+ orig_params.idle_timeout_seconds.set_value(kFakeIdleTimeout);
+ orig_params.max_packet_size.set_value(kFakeMaxPacketSize);
+ orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+ orig_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ orig_params.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ orig_params.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+ orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+ orig_params.ack_delay_exponent.set_value(kFakeAckDelayExponent);
+ orig_params.max_ack_delay.set_value(kFakeMaxAckDelay);
+ orig_params.disable_migration = kFakeDisableMigration;
std::vector<uint8_t> serialized;
ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
@@ -37,35 +96,52 @@
ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(),
Perspective::IS_CLIENT, &new_params));
- EXPECT_EQ(new_params.initial_max_stream_data,
- orig_params.initial_max_stream_data);
- EXPECT_EQ(new_params.initial_max_data, orig_params.initial_max_data);
- EXPECT_EQ(new_params.idle_timeout, orig_params.idle_timeout);
- EXPECT_EQ(new_params.version, orig_params.version);
- EXPECT_TRUE(new_params.initial_max_bidi_streams.present);
- EXPECT_EQ(new_params.initial_max_bidi_streams.value,
- orig_params.initial_max_bidi_streams.value);
- EXPECT_TRUE(new_params.initial_max_uni_streams.present);
- EXPECT_EQ(new_params.initial_max_uni_streams.value,
- orig_params.initial_max_uni_streams.value);
- EXPECT_TRUE(new_params.max_packet_size.present);
- EXPECT_EQ(new_params.max_packet_size.value,
- orig_params.max_packet_size.value);
- EXPECT_TRUE(new_params.ack_delay_exponent.present);
- EXPECT_EQ(new_params.ack_delay_exponent.value,
- orig_params.ack_delay_exponent.value);
+ EXPECT_EQ(Perspective::IS_CLIENT, new_params.perspective);
+ EXPECT_EQ(kFakeVersionLabel, new_params.version);
+ EXPECT_TRUE(new_params.supported_versions.empty());
+ EXPECT_EQ(EmptyQuicConnectionId(), new_params.original_connection_id);
+ EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value());
+ EXPECT_TRUE(new_params.stateless_reset_token.empty());
+ EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value());
+ EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal,
+ new_params.initial_max_stream_data_bidi_local.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiRemote,
+ new_params.initial_max_stream_data_bidi_remote.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataUni,
+ new_params.initial_max_stream_data_uni.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsBidi,
+ new_params.initial_max_streams_bidi.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsUni,
+ new_params.initial_max_streams_uni.value());
+ EXPECT_EQ(kFakeAckDelayExponent, new_params.ack_delay_exponent.value());
+ EXPECT_EQ(kFakeMaxAckDelay, new_params.max_ack_delay.value());
+ EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration);
}
TEST_F(TransportParametersTest, RoundTripServer) {
TransportParameters orig_params;
orig_params.perspective = Perspective::IS_SERVER;
- orig_params.initial_max_stream_data = 12;
- orig_params.initial_max_data = 34;
- orig_params.idle_timeout = 56;
- orig_params.stateless_reset_token.resize(16);
- orig_params.version = 0xff000005;
- orig_params.supported_versions.push_back(0xff000005);
- orig_params.supported_versions.push_back(0xff000004);
+ orig_params.version = kFakeVersionLabel;
+ orig_params.supported_versions.push_back(kFakeVersionLabel);
+ orig_params.supported_versions.push_back(kFakeVersionLabel2);
+ orig_params.original_connection_id = kFakeOriginalConnectionId;
+ orig_params.idle_timeout_seconds.set_value(kFakeIdleTimeout);
+ orig_params.stateless_reset_token = kFakeStatelessResetToken;
+ orig_params.max_packet_size.set_value(kFakeMaxPacketSize);
+ orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+ orig_params.initial_max_stream_data_bidi_local.set_value(
+ kFakeInitialMaxStreamDataBidiLocal);
+ orig_params.initial_max_stream_data_bidi_remote.set_value(
+ kFakeInitialMaxStreamDataBidiRemote);
+ orig_params.initial_max_stream_data_uni.set_value(
+ kFakeInitialMaxStreamDataUni);
+ orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+ orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+ orig_params.ack_delay_exponent.set_value(kFakeAckDelayExponent);
+ orig_params.max_ack_delay.set_value(kFakeMaxAckDelay);
+ orig_params.disable_migration = kFakeDisableMigration;
+ orig_params.preferred_address = CreateFakePreferredAddress();
std::vector<uint8_t> serialized;
ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
@@ -74,107 +150,170 @@
ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(),
Perspective::IS_SERVER, &new_params));
- EXPECT_EQ(new_params.initial_max_stream_data,
- orig_params.initial_max_stream_data);
- EXPECT_EQ(new_params.initial_max_data, orig_params.initial_max_data);
- EXPECT_EQ(new_params.idle_timeout, orig_params.idle_timeout);
- EXPECT_EQ(new_params.stateless_reset_token,
- orig_params.stateless_reset_token);
- EXPECT_EQ(new_params.version, orig_params.version);
- ASSERT_EQ(new_params.supported_versions, orig_params.supported_versions);
+ EXPECT_EQ(Perspective::IS_SERVER, new_params.perspective);
+ EXPECT_EQ(kFakeVersionLabel, new_params.version);
+ EXPECT_EQ(2, new_params.supported_versions.size());
+ EXPECT_EQ(kFakeVersionLabel, new_params.supported_versions[0]);
+ EXPECT_EQ(kFakeVersionLabel2, new_params.supported_versions[1]);
+ EXPECT_EQ(kFakeOriginalConnectionId, new_params.original_connection_id);
+ EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value());
+ EXPECT_EQ(kFakeStatelessResetToken, new_params.stateless_reset_token);
+ EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value());
+ EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal,
+ new_params.initial_max_stream_data_bidi_local.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiRemote,
+ new_params.initial_max_stream_data_bidi_remote.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataUni,
+ new_params.initial_max_stream_data_uni.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsBidi,
+ new_params.initial_max_streams_bidi.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsUni,
+ new_params.initial_max_streams_uni.value());
+ EXPECT_EQ(kFakeAckDelayExponent, new_params.ack_delay_exponent.value());
+ EXPECT_EQ(kFakeMaxAckDelay, new_params.max_ack_delay.value());
+ EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration);
+ ASSERT_NE(nullptr, new_params.preferred_address.get());
+ EXPECT_EQ(kFakeV4SocketAddress,
+ new_params.preferred_address->ipv4_socket_address);
+ EXPECT_EQ(kFakeV6SocketAddress,
+ new_params.preferred_address->ipv6_socket_address);
+ EXPECT_EQ(kFakePreferredConnectionId,
+ new_params.preferred_address->connection_id);
+ EXPECT_EQ(kFakePreferredStatelessResetToken,
+ new_params.preferred_address->stateless_reset_token);
}
TEST_F(TransportParametersTest, IsValid) {
- TransportParameters empty_params;
- empty_params.perspective = Perspective::IS_CLIENT;
- EXPECT_TRUE(empty_params.is_valid());
-
{
TransportParameters params;
params.perspective = Perspective::IS_CLIENT;
- EXPECT_TRUE(params.is_valid());
- params.idle_timeout = 600;
- EXPECT_TRUE(params.is_valid());
- params.idle_timeout = 601;
- EXPECT_FALSE(params.is_valid());
+ EXPECT_TRUE(params.AreValid());
}
{
TransportParameters params;
params.perspective = Perspective::IS_CLIENT;
- EXPECT_TRUE(params.is_valid());
- params.max_packet_size.present = true;
- params.max_packet_size.value = 0;
- EXPECT_FALSE(params.is_valid());
- params.max_packet_size.value = 1200;
- EXPECT_TRUE(params.is_valid());
- params.max_packet_size.value = 65527;
- EXPECT_TRUE(params.is_valid());
- params.max_packet_size.value = 65535;
- EXPECT_FALSE(params.is_valid());
+ EXPECT_TRUE(params.AreValid());
+ params.idle_timeout_seconds.set_value(601);
+ EXPECT_TRUE(params.AreValid());
}
{
TransportParameters params;
params.perspective = Perspective::IS_CLIENT;
- EXPECT_TRUE(params.is_valid());
- params.ack_delay_exponent.present = true;
- params.ack_delay_exponent.value = 0;
- EXPECT_TRUE(params.is_valid());
- params.ack_delay_exponent.value = 20;
- EXPECT_TRUE(params.is_valid());
- params.ack_delay_exponent.value = 21;
- EXPECT_FALSE(params.is_valid());
+ EXPECT_TRUE(params.AreValid());
+ params.max_packet_size.set_value(0);
+ EXPECT_FALSE(params.AreValid());
+ params.max_packet_size.set_value(1199);
+ EXPECT_FALSE(params.AreValid());
+ params.max_packet_size.set_value(1200);
+ EXPECT_TRUE(params.AreValid());
+ params.max_packet_size.set_value(65535);
+ EXPECT_TRUE(params.AreValid());
+ params.max_packet_size.set_value(9999999);
+ EXPECT_TRUE(params.AreValid());
}
-}
-
-TEST_F(TransportParametersTest, NoServerParamsWithoutStatelessResetToken) {
- TransportParameters orig_params;
- orig_params.perspective = Perspective::IS_SERVER;
- orig_params.initial_max_stream_data = 12;
- orig_params.initial_max_data = 34;
- orig_params.idle_timeout = 56;
- orig_params.version = 0xff000005;
- orig_params.supported_versions.push_back(0xff000005);
- orig_params.supported_versions.push_back(0xff000004);
-
- std::vector<uint8_t> out;
- ASSERT_FALSE(SerializeTransportParameters(orig_params, &out));
+ {
+ TransportParameters params;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.AreValid());
+ params.ack_delay_exponent.set_value(0);
+ EXPECT_TRUE(params.AreValid());
+ params.ack_delay_exponent.set_value(20);
+ EXPECT_TRUE(params.AreValid());
+ params.ack_delay_exponent.set_value(21);
+ EXPECT_FALSE(params.AreValid());
+ }
}
TEST_F(TransportParametersTest, NoClientParamsWithStatelessResetToken) {
TransportParameters orig_params;
orig_params.perspective = Perspective::IS_CLIENT;
- orig_params.initial_max_stream_data = 12;
- orig_params.initial_max_data = 34;
- orig_params.idle_timeout = 56;
- orig_params.stateless_reset_token.resize(16);
- orig_params.version = 0xff000005;
+ orig_params.version = kFakeVersionLabel;
+ orig_params.idle_timeout_seconds.set_value(kFakeIdleTimeout);
+ orig_params.stateless_reset_token = kFakeStatelessResetToken;
+ orig_params.max_packet_size.set_value(kFakeMaxPacketSize);
std::vector<uint8_t> out;
- ASSERT_FALSE(SerializeTransportParameters(orig_params, &out));
+ EXPECT_FALSE(SerializeTransportParameters(orig_params, &out));
}
TEST_F(TransportParametersTest, ParseClientParams) {
+ // clang-format off
const uint8_t kClientParams[] = {
- 0xff, 0x00, 0x00, 0x05, // initial version
- 0x00, 0x16, // length parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x0c, // value
- // initial_max_data
- 0x00, 0x01, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x22, // value
- // idle_timeout
+ 0x01, 0x23, 0x45, 0x67, // initial version
+ 0x00, 0x3B, // length of the parameters array that follows
+ // idle_timeout_seconds
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
+ // max_packet_size
0x00, 0x03, // parameter id
0x00, 0x02, // length
- 0x00, 0x38, // value
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x00, 0x04, // parameter id
+ 0x00, 0x02, // length
+ 0x40, 0x65, // value
+ // initial_max_stream_data_bidi_local
+ 0x00, 0x05, // parameter id
+ 0x00, 0x02, // length
+ 0x47, 0xD1, // value
+ // initial_max_stream_data_bidi_remote
+ 0x00, 0x06, // parameter id
+ 0x00, 0x02, // length
+ 0x47, 0xD2, // value
+ // initial_max_stream_data_uni
+ 0x00, 0x07, // parameter id
+ 0x00, 0x02, // length
+ 0x4B, 0xB8, // value
+ // initial_max_streams_bidi
+ 0x00, 0x08, // parameter id
+ 0x00, 0x01, // length
+ 0x15, // value
+ // initial_max_streams_uni
+ 0x00, 0x09, // parameter id
+ 0x00, 0x01, // length
+ 0x16, // value
+ // ack_delay_exponent
+ 0x00, 0x0a, // parameter id
+ 0x00, 0x01, // length
+ 0x0a, // value
+ // max_ack_delay
+ 0x00, 0x0b, // parameter id
+ 0x00, 0x01, // length
+ 0x33, // value
+ // disable_migration
+ 0x00, 0x0c, // parameter id
+ 0x00, 0x00, // length
};
+ // clang-format on
- TransportParameters out_params;
+ TransportParameters new_params;
ASSERT_TRUE(ParseTransportParameters(kClientParams,
QUIC_ARRAYSIZE(kClientParams),
- Perspective::IS_CLIENT, &out_params));
+ Perspective::IS_CLIENT, &new_params));
+
+ EXPECT_EQ(Perspective::IS_CLIENT, new_params.perspective);
+ EXPECT_EQ(kFakeVersionLabel, new_params.version);
+ EXPECT_TRUE(new_params.supported_versions.empty());
+ EXPECT_EQ(EmptyQuicConnectionId(), new_params.original_connection_id);
+ EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value());
+ EXPECT_TRUE(new_params.stateless_reset_token.empty());
+ EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value());
+ EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal,
+ new_params.initial_max_stream_data_bidi_local.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiRemote,
+ new_params.initial_max_stream_data_bidi_remote.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataUni,
+ new_params.initial_max_stream_data_uni.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsBidi,
+ new_params.initial_max_streams_bidi.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsUni,
+ new_params.initial_max_streams_uni.value());
+ EXPECT_EQ(kFakeAckDelayExponent, new_params.ack_delay_exponent.value());
+ EXPECT_EQ(kFakeMaxAckDelay, new_params.max_ack_delay.value());
+ EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration);
}
TEST_F(TransportParametersTest, ParseClientParamsFailsWithStatelessResetToken) {
@@ -182,234 +321,239 @@
// clang-format off
const uint8_t kClientParamsWithFullToken[] = {
- 0xff, 0x00, 0x00, 0x05, // initial version
- 0x00, 0x2a, // length parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x0c, // value
- // initial_max_data
- 0x00, 0x01, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x22, // value
- // idle_timeout
+ 0x01, 0x23, 0x45, 0x67, // initial version
+ 0x00, 0x25, // length parameters array that follows
+ // idle_timeout_seconds
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
+ // stateless_reset_token
+ 0x00, 0x02,
+ 0x00, 0x10,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ // max_packet_size
0x00, 0x03, // parameter id
0x00, 0x02, // length
- 0x00, 0x38, // value
- // stateless_reset_token
- 0x00, 0x06, // parameter id
- 0x00, 0x10, // length
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x00, 0x04, // parameter id
+ 0x00, 0x02, // length
+ 0x40, 0x65, // value
};
// clang-format on
- ASSERT_FALSE(ParseTransportParameters(
+ EXPECT_FALSE(ParseTransportParameters(
kClientParamsWithFullToken, QUIC_ARRAYSIZE(kClientParamsWithFullToken),
Perspective::IS_CLIENT, &out_params));
+ // clang-format off
const uint8_t kClientParamsWithEmptyToken[] = {
- 0xff, 0x00, 0x00, 0x05, // initial version
- 0x00, 0x1a, // length parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x0c, // value
- // initial_max_data
- 0x00, 0x01, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x22, // value
- // idle_timeout
+ 0x01, 0x23, 0x45, 0x67, // initial version
+ 0x00, 0x15, // length parameters array that follows
+ // idle_timeout_seconds
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
+ // stateless_reset_token
+ 0x00, 0x02,
+ 0x00, 0x00,
+ // max_packet_size
0x00, 0x03, // parameter id
0x00, 0x02, // length
- 0x00, 0x38, // value
- // stateless_reset_token
- 0x00, 0x06, // parameter id
- 0x00, 0x00, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x00, 0x04, // parameter id
+ 0x00, 0x02, // length
+ 0x40, 0x65, // value
};
+ // clang-format on
- ASSERT_FALSE(ParseTransportParameters(
+ EXPECT_FALSE(ParseTransportParameters(
kClientParamsWithEmptyToken, QUIC_ARRAYSIZE(kClientParamsWithEmptyToken),
Perspective::IS_CLIENT, &out_params));
}
-TEST_F(TransportParametersTest, ParseClientParametersWithInvalidParams) {
- TransportParameters out_params;
-
+TEST_F(TransportParametersTest, ParseClientParametersRepeated) {
+ // clang-format off
const uint8_t kClientParamsRepeated[] = {
- 0xff, 0x00, 0x00, 0x05, // initial version
- 0x00, 0x1c, // length parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x0c, // value
- // initial_max_data
- 0x00, 0x01, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x22, // value
- // idle_timeout
+ 0x01, 0x23, 0x45, 0x67, // initial version
+ 0x00, 0x14, // length parameters array that follows
+ // idle_timeout_seconds
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
+ // stateless_reset_token
+ 0x00, 0x02,
+ 0x00, 0x00,
+ // max_packet_size
0x00, 0x03, // parameter id
0x00, 0x02, // length
- 0x00, 0x38, // value
- // idle_timeout (repeat)
- 0x00, 0x03, // parameter id
- 0x00, 0x02, // length
- 0x00, 0x38, // value
+ 0x63, 0x29, // value
+ // idle_timeout_seconds (repeated)
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
};
- ASSERT_FALSE(ParseTransportParameters(kClientParamsRepeated,
+ // clang-format on
+ TransportParameters out_params;
+ EXPECT_FALSE(ParseTransportParameters(kClientParamsRepeated,
QUIC_ARRAYSIZE(kClientParamsRepeated),
Perspective::IS_CLIENT, &out_params));
-
- const uint8_t kClientParamsMissing[] = {
- 0xff, 0x00, 0x00, 0x05, // initial version
- 0x00, 0x10, // length parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x0c, // value
- // initial_max_data
- 0x00, 0x01, // parameter id
- 0x00, 0x04, // length
- 0x00, 0x00, 0x00, 0x22, // value
- };
- ASSERT_FALSE(ParseTransportParameters(kClientParamsMissing,
- QUIC_ARRAYSIZE(kClientParamsMissing),
- Perspective::IS_CLIENT, &out_params));
}
TEST_F(TransportParametersTest, ParseServerParams) {
// clang-format off
const uint8_t kServerParams[] = {
- 0xff, 0x00, 0x00, 0x05, // negotiated_version
+ 0x01, 0x23, 0x45, 0x67, // negotiated_version
0x08, // length of supported versions array
- 0xff, 0x00, 0x00, 0x05,
- 0xff, 0x00, 0x00, 0x04,
- 0x00, 0x2a, // length of parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x0c,
- // initial_max_data
- 0x00, 0x01,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x22,
- // idle_timeout
- 0x00, 0x03,
- 0x00, 0x02,
- 0x00, 0x38,
+ 0x01, 0x23, 0x45, 0x67,
+ 0x89, 0xab, 0xcd, 0xef,
+ 0x00, 0x91, // length of parameters array that follows
+ // original_connection_id
+ 0x00, 0x00, // parameter id
+ 0x00, 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37,
+ // idle_timeout_seconds
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
// stateless_reset_token
- 0x00, 0x06,
+ 0x00, 0x02,
0x00, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ // max_packet_size
+ 0x00, 0x03, // parameter id
+ 0x00, 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x00, 0x04, // parameter id
+ 0x00, 0x02, // length
+ 0x40, 0x65, // value
+ // initial_max_stream_data_bidi_local
+ 0x00, 0x05, // parameter id
+ 0x00, 0x02, // length
+ 0x47, 0xD1, // value
+ // initial_max_stream_data_bidi_remote
+ 0x00, 0x06, // parameter id
+ 0x00, 0x02, // length
+ 0x47, 0xD2, // value
+ // initial_max_stream_data_uni
+ 0x00, 0x07, // parameter id
+ 0x00, 0x02, // length
+ 0x4B, 0xB8, // value
+ // initial_max_streams_bidi
+ 0x00, 0x08, // parameter id
+ 0x00, 0x01, // length
+ 0x15, // value
+ // initial_max_streams_uni
+ 0x00, 0x09, // parameter id
+ 0x00, 0x01, // length
+ 0x16, // value
+ // ack_delay_exponent
+ 0x00, 0x0a, // parameter id
+ 0x00, 0x01, // length
+ 0x0a, // value
+ // max_ack_delay
+ 0x00, 0x0b, // parameter id
+ 0x00, 0x01, // length
+ 0x33, // value
+ // disable_migration
+ 0x00, 0x0c, // parameter id
+ 0x00, 0x00, // length
+ // preferred_address
+ 0x00, 0x0d, // parameter id
+ 0x00, 0x32, // length
+ 0x41, 0x42, 0x43, 0x44, // IPv4 address
+ 0x48, 0x84, // IPv4 port
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // IPv6 address
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x63, 0x36, // IPv6 port
+ 0x00, 0x08, // connection ID length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // connection ID
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // stateless reset token
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
};
// clang-format on
- TransportParameters out_params;
+ TransportParameters new_params;
ASSERT_TRUE(ParseTransportParameters(kServerParams,
QUIC_ARRAYSIZE(kServerParams),
- Perspective::IS_SERVER, &out_params));
+ Perspective::IS_SERVER, &new_params));
+
+ EXPECT_EQ(Perspective::IS_SERVER, new_params.perspective);
+ EXPECT_EQ(kFakeVersionLabel, new_params.version);
+ EXPECT_EQ(2, new_params.supported_versions.size());
+ EXPECT_EQ(kFakeVersionLabel, new_params.supported_versions[0]);
+ EXPECT_EQ(kFakeVersionLabel2, new_params.supported_versions[1]);
+ EXPECT_EQ(kFakeOriginalConnectionId, new_params.original_connection_id);
+ EXPECT_EQ(kFakeIdleTimeout, new_params.idle_timeout_seconds.value());
+ EXPECT_EQ(kFakeStatelessResetToken, new_params.stateless_reset_token);
+ EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value());
+ EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal,
+ new_params.initial_max_stream_data_bidi_local.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataBidiRemote,
+ new_params.initial_max_stream_data_bidi_remote.value());
+ EXPECT_EQ(kFakeInitialMaxStreamDataUni,
+ new_params.initial_max_stream_data_uni.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsBidi,
+ new_params.initial_max_streams_bidi.value());
+ EXPECT_EQ(kFakeInitialMaxStreamsUni,
+ new_params.initial_max_streams_uni.value());
+ EXPECT_EQ(kFakeAckDelayExponent, new_params.ack_delay_exponent.value());
+ EXPECT_EQ(kFakeMaxAckDelay, new_params.max_ack_delay.value());
+ EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration);
+ ASSERT_NE(nullptr, new_params.preferred_address.get());
+ EXPECT_EQ(kFakeV4SocketAddress,
+ new_params.preferred_address->ipv4_socket_address);
+ EXPECT_EQ(kFakeV6SocketAddress,
+ new_params.preferred_address->ipv6_socket_address);
+ EXPECT_EQ(kFakePreferredConnectionId,
+ new_params.preferred_address->connection_id);
+ EXPECT_EQ(kFakePreferredStatelessResetToken,
+ new_params.preferred_address->stateless_reset_token);
}
-TEST_F(TransportParametersTest, ParseServerParamsWithoutToken) {
- // clang-format off
- const uint8_t kServerParams[] = {
- 0xff, 0x00, 0x00, 0x05, // negotiated_version
- 0x08, // length of supported versions array
- 0xff, 0x00, 0x00, 0x05,
- 0xff, 0x00, 0x00, 0x04,
- 0x00, 0x16, // length of parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x0c,
- // initial_max_data
- 0x00, 0x01,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x22,
- // idle_timeout
- 0x00, 0x03,
- 0x00, 0x02,
- 0x00, 0x38,
- };
- // clang-format on
-
- TransportParameters out_params;
- ASSERT_FALSE(ParseTransportParameters(kServerParams,
- QUIC_ARRAYSIZE(kServerParams),
- Perspective::IS_SERVER, &out_params));
-}
-
-TEST_F(TransportParametersTest, ParseServerParametersWithInvalidParams) {
- TransportParameters out_params;
-
+TEST_F(TransportParametersTest, ParseServerParametersRepeated) {
// clang-format off
const uint8_t kServerParamsRepeated[] = {
- 0xff, 0x00, 0x00, 0x05, // negotiated_version
+ 0x01, 0x23, 0x45, 0x67, // negotiated_version
0x08, // length of supported versions array
- 0xff, 0x00, 0x00, 0x05,
- 0xff, 0x00, 0x00, 0x04,
- 0x00, 0x30, // length of parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x0c,
- // initial_max_data
- 0x00, 0x01,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x22,
- // idle_timeout
- 0x00, 0x03,
- 0x00, 0x02,
- 0x00, 0x38,
- // idle_timeout (repeat)
- 0x00, 0x03,
- 0x00, 0x02,
- 0x00, 0x38,
+ 0x01, 0x23, 0x45, 0x67,
+ 0x89, 0xab, 0xcd, 0xef,
+ 0x00, 0x2A, // length of parameters array that follows
+ // original_connection_id
+ 0x00, 0x00, // parameter id
+ 0x00, 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37,
+ // idle_timeout_seconds
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
// stateless_reset_token
- 0x00, 0x06,
+ 0x00, 0x02,
0x00, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ // idle_timeout_seconds (repeated)
+ 0x00, 0x01, // parameter id
+ 0x00, 0x01, // length
+ 0x0c, // value
};
// clang-format on
- ASSERT_FALSE(ParseTransportParameters(kServerParamsRepeated,
- QUIC_ARRAYSIZE(kServerParamsRepeated),
- Perspective::IS_SERVER, &out_params));
- // clang-format off
- const uint8_t kServerParamsMissing[] = {
- 0xff, 0x00, 0x00, 0x05, // negotiated_version
- 0x08, // length of supported versions array
- 0xff, 0x00, 0x00, 0x05,
- 0xff, 0x00, 0x00, 0x04,
- 0x00, 0x24, // length of parameters array that follows
- // initial_max_stream_data
- 0x00, 0x00,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x0c,
- // initial_max_data
- 0x00, 0x01,
- 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x22,
- // stateless_reset_token
- 0x00, 0x06,
- 0x00, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- };
- // clang-format on
- ASSERT_FALSE(ParseTransportParameters(kServerParamsMissing,
- QUIC_ARRAYSIZE(kServerParamsMissing),
+ TransportParameters out_params;
+ EXPECT_FALSE(ParseTransportParameters(kServerParamsRepeated,
+ QUIC_ARRAYSIZE(kServerParamsRepeated),
Perspective::IS_SERVER, &out_params));
}
TEST_F(TransportParametersTest, CryptoHandshakeMessageRoundtrip) {
TransportParameters orig_params;
orig_params.perspective = Perspective::IS_CLIENT;
- orig_params.initial_max_stream_data = 12;
- orig_params.initial_max_data = 34;
- orig_params.idle_timeout = 56;
+ orig_params.max_packet_size.set_value(kFakeMaxPacketSize);
orig_params.google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>();
const std::string kTestString = "test string";
@@ -434,6 +578,7 @@
EXPECT_EQ(new_params.google_quic_params->GetUint32(1337, &test_value),
QUIC_NO_ERROR);
EXPECT_EQ(test_value, kTestValue);
+ EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value());
}
} // namespace test
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index 25b549c..ef4fdad 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -5,10 +5,12 @@
#include "net/third_party/quiche/src/quic/core/quic_config.h"
#include <algorithm>
+#include <cstring>
#include <string>
#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
@@ -17,7 +19,9 @@
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
namespace quic {
@@ -746,25 +750,47 @@
}
bool QuicConfig::FillTransportParameters(TransportParameters* params) const {
- params->initial_max_stream_data =
- initial_stream_flow_control_window_bytes_.GetSendValue();
- params->initial_max_data =
- initial_session_flow_control_window_bytes_.GetSendValue();
+ params->idle_timeout_seconds.set_value(
+ idle_network_timeout_seconds_.GetUint32());
- uint32_t idle_timeout = idle_network_timeout_seconds_.GetUint32();
- if (idle_timeout > std::numeric_limits<uint16_t>::max()) {
- QUIC_BUG << "idle network timeout set too large";
- return false;
+ if (stateless_reset_token_.HasSendValue()) {
+ QuicUint128 stateless_reset_token = stateless_reset_token_.GetSendValue();
+ params->stateless_reset_token.assign(
+ reinterpret_cast<const char*>(&stateless_reset_token),
+ reinterpret_cast<const char*>(&stateless_reset_token) +
+ sizeof(stateless_reset_token));
}
- params->idle_timeout = idle_timeout;
- uint32_t initial_max_streams = max_incoming_dynamic_streams_.GetSendValue();
- if (initial_max_streams > std::numeric_limits<uint16_t>::max()) {
- QUIC_BUG << "max incoming streams set too large";
- return false;
+ params->max_packet_size.set_value(kMaxIncomingPacketSize);
+ params->initial_max_data.set_value(
+ initial_session_flow_control_window_bytes_.GetSendValue());
+ params->initial_max_stream_data_bidi_local.set_value(
+ initial_stream_flow_control_window_bytes_.GetSendValue());
+ params->initial_max_stream_data_bidi_remote.set_value(
+ initial_stream_flow_control_window_bytes_.GetSendValue());
+ params->initial_max_stream_data_uni.set_value(
+ initial_stream_flow_control_window_bytes_.GetSendValue());
+ params->initial_max_streams_bidi.set_value(
+ max_incoming_dynamic_streams_.GetSendValue());
+ params->initial_max_streams_uni.set_value(
+ max_incoming_dynamic_streams_.GetSendValue());
+ params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs);
+ params->disable_migration =
+ connection_migration_disabled_.HasSendValue() &&
+ connection_migration_disabled_.GetSendValue() != 0;
+
+ if (alternate_server_address_.HasSendValue()) {
+ TransportParameters::PreferredAddress preferred_address;
+ QuicSocketAddress socket_address = alternate_server_address_.GetSendValue();
+ if (socket_address.host().IsIPv6()) {
+ preferred_address.ipv6_socket_address = socket_address;
+ } else {
+ preferred_address.ipv4_socket_address = socket_address;
+ }
+ params->preferred_address =
+ QuicMakeUnique<TransportParameters::PreferredAddress>(
+ preferred_address);
}
- params->initial_max_bidi_streams.present = true;
- params->initial_max_bidi_streams.value = initial_max_streams;
if (!params->google_quic_params) {
params->google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>();
@@ -773,6 +799,7 @@
initial_round_trip_time_us_.ToHandshakeMessage(
params->google_quic_params.get());
connection_options_.ToHandshakeMessage(params->google_quic_params.get());
+
return true;
}
@@ -780,12 +807,61 @@
const TransportParameters& params,
HelloType hello_type,
std::string* error_details) {
+ uint64_t idle_timeout_seconds = params.idle_timeout_seconds.value();
+ if (idle_timeout_seconds > kMaximumIdleTimeoutSecs) {
+ idle_timeout_seconds = kMaximumIdleTimeoutSecs;
+ }
QuicErrorCode error = idle_network_timeout_seconds_.ReceiveValue(
- params.idle_timeout, hello_type, error_details);
+ idle_timeout_seconds, hello_type, error_details);
if (error != QUIC_NO_ERROR) {
DCHECK(!error_details->empty());
return error;
}
+
+ if (!params.stateless_reset_token.empty()) {
+ QuicUint128 stateless_reset_token;
+ if (params.stateless_reset_token.size() != sizeof(stateless_reset_token)) {
+ QUIC_BUG << "Bad stateless reset token length "
+ << params.stateless_reset_token.size();
+ *error_details = "Bad stateless reset token length";
+ return QUIC_INTERNAL_ERROR;
+ }
+ memcpy(&stateless_reset_token, params.stateless_reset_token.data(),
+ params.stateless_reset_token.size());
+ stateless_reset_token_.SetReceivedValue(stateless_reset_token);
+ }
+
+ if (params.max_packet_size.value() < kMaxOutgoingPacketSize) {
+ // TODO(dschinazi) act on this.
+ QUIC_DLOG(ERROR) << "Ignoring peer's requested max packet size of "
+ << params.max_packet_size.value();
+ }
+
+ initial_session_flow_control_window_bytes_.SetReceivedValue(
+ std::min<uint64_t>(params.initial_max_data.value(),
+ std::numeric_limits<uint32_t>::max()));
+
+ max_incoming_dynamic_streams_.SetReceivedValue(
+ std::min<uint64_t>(params.initial_max_streams_bidi.value(),
+ std::numeric_limits<uint32_t>::max()));
+
+ initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ std::min<uint64_t>(params.initial_max_stream_data_bidi_local.value(),
+ std::numeric_limits<uint32_t>::max()));
+
+ connection_migration_disabled_.SetReceivedValue(
+ params.disable_migration ? 1u : 0u);
+
+ if (params.preferred_address != nullptr) {
+ if (params.preferred_address->ipv6_socket_address.port() != 0) {
+ alternate_server_address_.SetReceivedValue(
+ params.preferred_address->ipv6_socket_address);
+ } else if (params.preferred_address->ipv4_socket_address.port() != 0) {
+ alternate_server_address_.SetReceivedValue(
+ params.preferred_address->ipv4_socket_address);
+ }
+ }
+
const CryptoHandshakeMessage* peer_params = params.google_quic_params.get();
if (peer_params != nullptr) {
error =
@@ -808,18 +884,6 @@
}
}
- initial_stream_flow_control_window_bytes_.SetReceivedValue(
- params.initial_max_stream_data);
- initial_session_flow_control_window_bytes_.SetReceivedValue(
- params.initial_max_data);
- if (params.initial_max_bidi_streams.present) {
- max_incoming_dynamic_streams_.SetReceivedValue(
- params.initial_max_bidi_streams.value);
- } else {
- // An absent value for initial_max_bidi_streams is treated as a value of 0.
- max_incoming_dynamic_streams_.SetReceivedValue(0);
- }
-
return QUIC_NO_ERROR;
}