blob: 7e1cf440138b4e42721abc921ee1527de8ad0160 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright (c) 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
6
dschinazi6cf4d2a2019-04-30 16:20:23 -07007#include <cstdint>
dschinazi52127d72019-04-17 15:12:38 -07008#include <cstring>
vasilvva2ef3012019-09-12 18:32:14 -07009#include <forward_list>
bnc463f2352019-10-10 04:49:34 -070010#include <utility>
dschinazi52127d72019-04-17 15:12:38 -070011
QUICHE teama6ef0a62019-03-07 20:34:33 -050012#include "third_party/boringssl/src/include/openssl/bytestring.h"
13#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
dschinazi52127d72019-04-17 15:12:38 -070014#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
15#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
16#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
17#include "net/third_party/quiche/src/quic/core/quic_types.h"
dschinazi6c84c142019-07-31 09:11:49 -070018#include "net/third_party/quiche/src/quic/core/quic_utils.h"
dschinazi52127d72019-04-17 15:12:38 -070019#include "net/third_party/quiche/src/quic/core/quic_versions.h"
20#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
dmcardle904ef182019-12-13 08:34:33 -080021#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
22#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050023
24namespace quic {
25
dschinazi52127d72019-04-17 15:12:38 -070026// Values of the TransportParameterId enum as defined in the
27// "Transport Parameter Encoding" section of draft-ietf-quic-transport.
28// When parameters are encoded, one of these enum values is used to indicate
29// which parameter is encoded. The supported draft version is noted in
30// transport_parameters.h.
31enum TransportParameters::TransportParameterId : uint16_t {
32 kOriginalConnectionId = 0,
33 kIdleTimeout = 1,
34 kStatelessResetToken = 2,
35 kMaxPacketSize = 3,
36 kInitialMaxData = 4,
37 kInitialMaxStreamDataBidiLocal = 5,
38 kInitialMaxStreamDataBidiRemote = 6,
39 kInitialMaxStreamDataUni = 7,
40 kInitialMaxStreamsBidi = 8,
41 kInitialMaxStreamsUni = 9,
42 kAckDelayExponent = 0xa,
43 kMaxAckDelay = 0xb,
44 kDisableMigration = 0xc,
45 kPreferredAddress = 0xd,
dschinazie9db63c2019-07-17 16:19:42 -070046 kActiveConnectionIdLimit = 0xe,
QUICHE teama6ef0a62019-03-07 20:34:33 -050047
dschinazicd86dd12019-11-14 10:11:13 -080048 kMaxDatagramFrameSize = 0x20,
49
dschinazi52127d72019-04-17 15:12:38 -070050 kGoogleQuicParam = 18257, // Used for non-standard Google-specific params.
dschinazi6cf4d2a2019-04-30 16:20:23 -070051 kGoogleQuicVersion =
52 18258, // Used to transmit version and supported_versions.
QUICHE teama6ef0a62019-03-07 20:34:33 -050053};
54
dschinazi52127d72019-04-17 15:12:38 -070055namespace {
QUICHE teama6ef0a62019-03-07 20:34:33 -050056
57// The following constants define minimum and maximum allowed values for some of
dschinazi52127d72019-04-17 15:12:38 -070058// the parameters. These come from the "Transport Parameter Definitions"
59// section of draft-ietf-quic-transport.
60const uint64_t kMinMaxPacketSizeTransportParam = 1200;
61const uint64_t kDefaultMaxPacketSizeTransportParam = 65527;
62const uint64_t kMaxAckDelayExponentTransportParam = 20;
63const uint64_t kDefaultAckDelayExponentTransportParam = 3;
64const uint64_t kMaxMaxAckDelayTransportParam = 16383;
65const uint64_t kDefaultMaxAckDelayTransportParam = 25;
66const size_t kStatelessResetTokenLength = 16;
QUICHE teama6ef0a62019-03-07 20:34:33 -050067
dschinazi52127d72019-04-17 15:12:38 -070068std::string TransportParameterIdToString(
69 TransportParameters::TransportParameterId param_id) {
70 switch (param_id) {
danzh9424add2019-06-06 14:04:36 -070071 case TransportParameters::kOriginalConnectionId:
dschinazi52127d72019-04-17 15:12:38 -070072 return "original_connection_id";
danzh9424add2019-06-06 14:04:36 -070073 case TransportParameters::kIdleTimeout:
dschinazi52127d72019-04-17 15:12:38 -070074 return "idle_timeout";
danzh9424add2019-06-06 14:04:36 -070075 case TransportParameters::kStatelessResetToken:
dschinazi52127d72019-04-17 15:12:38 -070076 return "stateless_reset_token";
danzh9424add2019-06-06 14:04:36 -070077 case TransportParameters::kMaxPacketSize:
dschinazi52127d72019-04-17 15:12:38 -070078 return "max_packet_size";
danzh9424add2019-06-06 14:04:36 -070079 case TransportParameters::kInitialMaxData:
dschinazi52127d72019-04-17 15:12:38 -070080 return "initial_max_data";
danzh9424add2019-06-06 14:04:36 -070081 case TransportParameters::kInitialMaxStreamDataBidiLocal:
dschinazi52127d72019-04-17 15:12:38 -070082 return "initial_max_stream_data_bidi_local";
danzh9424add2019-06-06 14:04:36 -070083 case TransportParameters::kInitialMaxStreamDataBidiRemote:
dschinazi52127d72019-04-17 15:12:38 -070084 return "initial_max_stream_data_bidi_remote";
danzh9424add2019-06-06 14:04:36 -070085 case TransportParameters::kInitialMaxStreamDataUni:
dschinazi52127d72019-04-17 15:12:38 -070086 return "initial_max_stream_data_uni";
danzh9424add2019-06-06 14:04:36 -070087 case TransportParameters::kInitialMaxStreamsBidi:
dschinazi52127d72019-04-17 15:12:38 -070088 return "initial_max_streams_bidi";
danzh9424add2019-06-06 14:04:36 -070089 case TransportParameters::kInitialMaxStreamsUni:
dschinazi52127d72019-04-17 15:12:38 -070090 return "initial_max_streams_uni";
danzh9424add2019-06-06 14:04:36 -070091 case TransportParameters::kAckDelayExponent:
dschinazi52127d72019-04-17 15:12:38 -070092 return "ack_delay_exponent";
danzh9424add2019-06-06 14:04:36 -070093 case TransportParameters::kMaxAckDelay:
dschinazi52127d72019-04-17 15:12:38 -070094 return "max_ack_delay";
danzh9424add2019-06-06 14:04:36 -070095 case TransportParameters::kDisableMigration:
dschinazi52127d72019-04-17 15:12:38 -070096 return "disable_migration";
danzh9424add2019-06-06 14:04:36 -070097 case TransportParameters::kPreferredAddress:
dschinazi52127d72019-04-17 15:12:38 -070098 return "preferred_address";
dschinazie9db63c2019-07-17 16:19:42 -070099 case TransportParameters::kActiveConnectionIdLimit:
100 return "active_connection_id_limit";
dschinazicd86dd12019-11-14 10:11:13 -0800101 case TransportParameters::kMaxDatagramFrameSize:
102 return "max_datagram_frame_size";
danzh9424add2019-06-06 14:04:36 -0700103 case TransportParameters::kGoogleQuicParam:
dschinazi52127d72019-04-17 15:12:38 -0700104 return "google";
danzh9424add2019-06-06 14:04:36 -0700105 case TransportParameters::kGoogleQuicVersion:
dschinazi6cf4d2a2019-04-30 16:20:23 -0700106 return "google-version";
dschinazi52127d72019-04-17 15:12:38 -0700107 }
dmcardle904ef182019-12-13 08:34:33 -0800108 return "Unknown(" + quiche::QuicheTextUtils::Uint64ToString(param_id) + ")";
dschinazi52127d72019-04-17 15:12:38 -0700109}
QUICHE teama6ef0a62019-03-07 20:34:33 -0500110
111} // namespace
112
dschinazi52127d72019-04-17 15:12:38 -0700113TransportParameters::IntegerParameter::IntegerParameter(
114 TransportParameters::TransportParameterId param_id,
115 uint64_t default_value,
116 uint64_t min_value,
117 uint64_t max_value)
118 : param_id_(param_id),
119 value_(default_value),
120 default_value_(default_value),
121 min_value_(min_value),
122 max_value_(max_value),
123 has_been_read_from_cbs_(false) {
124 DCHECK_LE(min_value, default_value);
125 DCHECK_LE(default_value, max_value);
126 DCHECK_LE(max_value, kVarInt62MaxValue);
127}
QUICHE teama6ef0a62019-03-07 20:34:33 -0500128
dschinazi52127d72019-04-17 15:12:38 -0700129TransportParameters::IntegerParameter::IntegerParameter(
130 TransportParameters::TransportParameterId param_id)
131 : TransportParameters::IntegerParameter::IntegerParameter(
132 param_id,
133 0,
134 0,
135 kVarInt62MaxValue) {}
QUICHE teama6ef0a62019-03-07 20:34:33 -0500136
dschinazi52127d72019-04-17 15:12:38 -0700137void TransportParameters::IntegerParameter::set_value(uint64_t value) {
138 value_ = value;
139}
140
141uint64_t TransportParameters::IntegerParameter::value() const {
142 return value_;
143}
144
145bool TransportParameters::IntegerParameter::IsValid() const {
146 return min_value_ <= value_ && value_ <= max_value_;
147}
148
149bool TransportParameters::IntegerParameter::WriteToCbb(CBB* parent_cbb) const {
150 DCHECK(IsValid());
151 if (value_ == default_value_) {
152 // Do not write if the value is default.
153 return true;
154 }
155 uint8_t encoded_data[sizeof(uint64_t)] = {};
156 QuicDataWriter writer(sizeof(encoded_data),
157 reinterpret_cast<char*>(encoded_data));
158 writer.WriteVarInt62(value_);
159 const uint16_t value_length = writer.length();
160 DCHECK_LE(value_length, sizeof(encoded_data));
161 const bool ok = CBB_add_u16(parent_cbb, param_id_) &&
162 CBB_add_u16(parent_cbb, value_length) &&
163 CBB_add_bytes(parent_cbb, encoded_data, value_length);
164 QUIC_BUG_IF(!ok) << "Failed to write " << this;
165 return ok;
166}
167
168bool TransportParameters::IntegerParameter::ReadFromCbs(CBS* const value_cbs) {
169 if (has_been_read_from_cbs_) {
170 QUIC_DLOG(ERROR) << "Received a second "
171 << TransportParameterIdToString(param_id_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500172 return false;
173 }
dschinazi52127d72019-04-17 15:12:38 -0700174 has_been_read_from_cbs_ = true;
175 QuicDataReader reader(reinterpret_cast<const char*>(CBS_data(value_cbs)),
176 CBS_len(value_cbs));
177 QuicVariableLengthIntegerLength value_length = reader.PeekVarInt62Length();
178 if (value_length == 0 || !reader.ReadVarInt62(&value_)) {
179 QUIC_DLOG(ERROR) << "Failed to parse value for "
180 << TransportParameterIdToString(param_id_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500181 return false;
182 }
dschinazi52127d72019-04-17 15:12:38 -0700183 if (!reader.IsDoneReading()) {
184 QUIC_DLOG(ERROR) << "Received unexpected " << reader.BytesRemaining()
185 << " bytes after parsing " << this;
186 return false;
187 }
188 if (!CBS_skip(value_cbs, value_length)) {
189 QUIC_BUG << "Failed to advance CBS past value for " << this;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500190 return false;
191 }
192 return true;
193}
194
dschinazi52127d72019-04-17 15:12:38 -0700195std::string TransportParameters::IntegerParameter::ToString(
196 bool for_use_in_list) const {
197 if (for_use_in_list && value_ == default_value_) {
198 return "";
199 }
200 std::string rv = for_use_in_list ? " " : "";
201 rv += TransportParameterIdToString(param_id_) + " ";
dmcardle904ef182019-12-13 08:34:33 -0800202 rv += quiche::QuicheTextUtils::Uint64ToString(value_);
dschinazi52127d72019-04-17 15:12:38 -0700203 if (!IsValid()) {
204 rv += " (Invalid)";
205 }
206 return rv;
207}
208
209std::ostream& operator<<(std::ostream& os,
210 const TransportParameters::IntegerParameter& param) {
211 os << param.ToString(/*for_use_in_list=*/false);
212 return os;
213}
214
215TransportParameters::PreferredAddress::PreferredAddress()
216 : ipv4_socket_address(QuicIpAddress::Any4(), 0),
217 ipv6_socket_address(QuicIpAddress::Any6(), 0),
218 connection_id(EmptyQuicConnectionId()),
219 stateless_reset_token(kStatelessResetTokenLength, 0) {}
220
221TransportParameters::PreferredAddress::~PreferredAddress() {}
222
223std::ostream& operator<<(
224 std::ostream& os,
225 const TransportParameters::PreferredAddress& preferred_address) {
226 os << preferred_address.ToString();
227 return os;
228}
229
230std::string TransportParameters::PreferredAddress::ToString() const {
231 return "[" + ipv4_socket_address.ToString() + " " +
232 ipv6_socket_address.ToString() + " connection_id " +
233 connection_id.ToString() + " stateless_reset_token " +
dmcardle904ef182019-12-13 08:34:33 -0800234 quiche::QuicheTextUtils::HexEncode(
dschinazi52127d72019-04-17 15:12:38 -0700235 reinterpret_cast<const char*>(stateless_reset_token.data()),
236 stateless_reset_token.size()) +
237 "]";
238}
239
240std::ostream& operator<<(std::ostream& os, const TransportParameters& params) {
241 os << params.ToString();
242 return os;
243}
244
245std::string TransportParameters::ToString() const {
246 std::string rv = "[";
247 if (perspective == Perspective::IS_SERVER) {
248 rv += "Server";
249 } else {
250 rv += "Client";
251 }
252 if (version != 0) {
253 rv += " version " + QuicVersionLabelToString(version);
254 }
255 if (!supported_versions.empty()) {
256 rv += " supported_versions " +
257 QuicVersionLabelVectorToString(supported_versions);
258 }
259 if (!original_connection_id.IsEmpty()) {
260 rv += " " + TransportParameterIdToString(kOriginalConnectionId) + " " +
261 original_connection_id.ToString();
262 }
dschinazi6cf4d2a2019-04-30 16:20:23 -0700263 rv += idle_timeout_milliseconds.ToString(/*for_use_in_list=*/true);
dschinazi52127d72019-04-17 15:12:38 -0700264 if (!stateless_reset_token.empty()) {
265 rv += " " + TransportParameterIdToString(kStatelessResetToken) + " " +
dmcardle904ef182019-12-13 08:34:33 -0800266 quiche::QuicheTextUtils::HexEncode(
dschinazi52127d72019-04-17 15:12:38 -0700267 reinterpret_cast<const char*>(stateless_reset_token.data()),
268 stateless_reset_token.size());
269 }
270 rv += max_packet_size.ToString(/*for_use_in_list=*/true);
271 rv += initial_max_data.ToString(/*for_use_in_list=*/true);
272 rv += initial_max_stream_data_bidi_local.ToString(/*for_use_in_list=*/true);
273 rv += initial_max_stream_data_bidi_remote.ToString(/*for_use_in_list=*/true);
274 rv += initial_max_stream_data_uni.ToString(/*for_use_in_list=*/true);
275 rv += initial_max_streams_bidi.ToString(/*for_use_in_list=*/true);
276 rv += initial_max_streams_uni.ToString(/*for_use_in_list=*/true);
277 rv += ack_delay_exponent.ToString(/*for_use_in_list=*/true);
278 rv += max_ack_delay.ToString(/*for_use_in_list=*/true);
279 if (disable_migration) {
280 rv += " " + TransportParameterIdToString(kDisableMigration);
281 }
282 if (preferred_address) {
283 rv += " " + TransportParameterIdToString(kPreferredAddress) + " " +
284 preferred_address->ToString();
285 }
dschinazie9db63c2019-07-17 16:19:42 -0700286 rv += active_connection_id_limit.ToString(/*for_use_in_list=*/true);
dschinazicd86dd12019-11-14 10:11:13 -0800287 rv += max_datagram_frame_size.ToString(/*for_use_in_list=*/true);
dschinazi52127d72019-04-17 15:12:38 -0700288 if (google_quic_params) {
289 rv += " " + TransportParameterIdToString(kGoogleQuicParam);
290 }
291 rv += "]";
vasilvva2ef3012019-09-12 18:32:14 -0700292 for (const auto& kv : custom_parameters) {
dmcardle904ef182019-12-13 08:34:33 -0800293 rv += " 0x" + quiche::QuicheTextUtils::Hex(static_cast<uint32_t>(kv.first));
294 rv += "=" + quiche::QuicheTextUtils::HexEncode(kv.second);
vasilvva2ef3012019-09-12 18:32:14 -0700295 }
dschinazi52127d72019-04-17 15:12:38 -0700296 return rv;
297}
298
299TransportParameters::TransportParameters()
300 : version(0),
301 original_connection_id(EmptyQuicConnectionId()),
dschinazi6cf4d2a2019-04-30 16:20:23 -0700302 idle_timeout_milliseconds(kIdleTimeout),
dschinazi52127d72019-04-17 15:12:38 -0700303 max_packet_size(kMaxPacketSize,
304 kDefaultMaxPacketSizeTransportParam,
305 kMinMaxPacketSizeTransportParam,
306 kVarInt62MaxValue),
307 initial_max_data(kInitialMaxData),
308 initial_max_stream_data_bidi_local(kInitialMaxStreamDataBidiLocal),
309 initial_max_stream_data_bidi_remote(kInitialMaxStreamDataBidiRemote),
310 initial_max_stream_data_uni(kInitialMaxStreamDataUni),
311 initial_max_streams_bidi(kInitialMaxStreamsBidi),
312 initial_max_streams_uni(kInitialMaxStreamsUni),
313 ack_delay_exponent(kAckDelayExponent,
314 kDefaultAckDelayExponentTransportParam,
315 0,
316 kMaxAckDelayExponentTransportParam),
317 max_ack_delay(kMaxAckDelay,
318 kDefaultMaxAckDelayTransportParam,
319 0,
320 kMaxMaxAckDelayTransportParam),
dschinazie9db63c2019-07-17 16:19:42 -0700321 disable_migration(false),
dschinazicd86dd12019-11-14 10:11:13 -0800322 active_connection_id_limit(kActiveConnectionIdLimit),
323 max_datagram_frame_size(kMaxDatagramFrameSize)
dschinazi52127d72019-04-17 15:12:38 -0700324// Important note: any new transport parameters must be added
325// to TransportParameters::AreValid, SerializeTransportParameters and
326// ParseTransportParameters.
327{}
328
329bool TransportParameters::AreValid() const {
330 DCHECK(perspective == Perspective::IS_CLIENT ||
331 perspective == Perspective::IS_SERVER);
332 if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) {
333 QUIC_DLOG(ERROR) << "Client cannot send stateless reset token";
334 return false;
335 }
336 if (perspective == Perspective::IS_CLIENT &&
337 !original_connection_id.IsEmpty()) {
338 QUIC_DLOG(ERROR) << "Client cannot send original connection ID";
339 return false;
340 }
341 if (!stateless_reset_token.empty() &&
342 stateless_reset_token.size() != kStatelessResetTokenLength) {
343 QUIC_DLOG(ERROR) << "Stateless reset token has bad length "
344 << stateless_reset_token.size();
345 return false;
346 }
347 if (perspective == Perspective::IS_CLIENT && preferred_address) {
348 QUIC_DLOG(ERROR) << "Client cannot send preferred address";
349 return false;
350 }
351 if (preferred_address && preferred_address->stateless_reset_token.size() !=
352 kStatelessResetTokenLength) {
353 QUIC_DLOG(ERROR)
354 << "Preferred address stateless reset token has bad length "
355 << preferred_address->stateless_reset_token.size();
356 return false;
357 }
358 if (preferred_address &&
359 (!preferred_address->ipv4_socket_address.host().IsIPv4() ||
360 !preferred_address->ipv6_socket_address.host().IsIPv6())) {
361 QUIC_BUG << "Preferred address family failure";
362 return false;
363 }
dschinazicd86dd12019-11-14 10:11:13 -0800364 const bool ok =
365 idle_timeout_milliseconds.IsValid() && max_packet_size.IsValid() &&
366 initial_max_data.IsValid() &&
367 initial_max_stream_data_bidi_local.IsValid() &&
368 initial_max_stream_data_bidi_remote.IsValid() &&
369 initial_max_stream_data_uni.IsValid() &&
370 initial_max_streams_bidi.IsValid() && initial_max_streams_uni.IsValid() &&
371 ack_delay_exponent.IsValid() && max_ack_delay.IsValid() &&
372 active_connection_id_limit.IsValid() && max_datagram_frame_size.IsValid();
dschinazi52127d72019-04-17 15:12:38 -0700373 if (!ok) {
374 QUIC_DLOG(ERROR) << "Invalid transport parameters " << *this;
375 }
376 return ok;
377}
378
379TransportParameters::~TransportParameters() = default;
380
dschinazi6c84c142019-07-31 09:11:49 -0700381bool SerializeTransportParameters(ParsedQuicVersion /*version*/,
382 const TransportParameters& in,
QUICHE teama6ef0a62019-03-07 20:34:33 -0500383 std::vector<uint8_t>* out) {
dschinazi52127d72019-04-17 15:12:38 -0700384 if (!in.AreValid()) {
385 QUIC_DLOG(ERROR) << "Not serializing invalid transport parameters " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500386 return false;
387 }
dschinazi6cf4d2a2019-04-30 16:20:23 -0700388 if (in.version == 0 || (in.perspective == Perspective::IS_SERVER &&
389 in.supported_versions.empty())) {
390 QUIC_DLOG(ERROR) << "Refusing to serialize without versions";
391 return false;
392 }
393
QUICHE teama6ef0a62019-03-07 20:34:33 -0500394 bssl::ScopedCBB cbb;
dschinazi52127d72019-04-17 15:12:38 -0700395 // Empirically transport parameters generally fit within 128 bytes.
396 // The CBB will grow to fit larger serializations if required.
dschinazi6cf4d2a2019-04-30 16:20:23 -0700397 if (!CBB_init(cbb.get(), 128)) {
398 QUIC_BUG << "Failed to initialize CBB for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500399 return false;
400 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500401
dschinazi52127d72019-04-17 15:12:38 -0700402 CBB params;
403 // Add length of the transport parameters list.
404 if (!CBB_add_u16_length_prefixed(cbb.get(), &params)) {
405 QUIC_BUG << "Failed to write parameter length for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500406 return false;
407 }
408
dschinazi52127d72019-04-17 15:12:38 -0700409 // original_connection_id
410 CBB original_connection_id_param;
411 if (!in.original_connection_id.IsEmpty()) {
412 DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
danzh9424add2019-06-06 14:04:36 -0700413 if (!CBB_add_u16(&params, TransportParameters::kOriginalConnectionId) ||
dschinazi52127d72019-04-17 15:12:38 -0700414 !CBB_add_u16_length_prefixed(&params, &original_connection_id_param) ||
415 !CBB_add_bytes(
416 &original_connection_id_param,
417 reinterpret_cast<const uint8_t*>(in.original_connection_id.data()),
418 in.original_connection_id.length())) {
419 QUIC_BUG << "Failed to write original_connection_id "
420 << in.original_connection_id << " for " << in;
421 return false;
422 }
423 }
424
dschinazi6cf4d2a2019-04-30 16:20:23 -0700425 if (!in.idle_timeout_milliseconds.WriteToCbb(&params)) {
dschinazi52127d72019-04-17 15:12:38 -0700426 QUIC_BUG << "Failed to write idle_timeout for " << in;
427 return false;
428 }
429
430 // stateless_reset_token
QUICHE teama6ef0a62019-03-07 20:34:33 -0500431 CBB stateless_reset_token_param;
432 if (!in.stateless_reset_token.empty()) {
dschinazi52127d72019-04-17 15:12:38 -0700433 DCHECK_EQ(kStatelessResetTokenLength, in.stateless_reset_token.size());
434 DCHECK_EQ(Perspective::IS_SERVER, in.perspective);
danzh9424add2019-06-06 14:04:36 -0700435 if (!CBB_add_u16(&params, TransportParameters::kStatelessResetToken) ||
QUICHE teama6ef0a62019-03-07 20:34:33 -0500436 !CBB_add_u16_length_prefixed(&params, &stateless_reset_token_param) ||
437 !CBB_add_bytes(&stateless_reset_token_param,
438 in.stateless_reset_token.data(),
439 in.stateless_reset_token.size())) {
dschinazi52127d72019-04-17 15:12:38 -0700440 QUIC_BUG << "Failed to write stateless_reset_token of length "
441 << in.stateless_reset_token.size() << " for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500442 return false;
443 }
444 }
445
dschinazi52127d72019-04-17 15:12:38 -0700446 if (!in.max_packet_size.WriteToCbb(&params) ||
447 !in.initial_max_data.WriteToCbb(&params) ||
448 !in.initial_max_stream_data_bidi_local.WriteToCbb(&params) ||
449 !in.initial_max_stream_data_bidi_remote.WriteToCbb(&params) ||
450 !in.initial_max_stream_data_uni.WriteToCbb(&params) ||
451 !in.initial_max_streams_bidi.WriteToCbb(&params) ||
452 !in.initial_max_streams_uni.WriteToCbb(&params) ||
453 !in.ack_delay_exponent.WriteToCbb(&params) ||
dschinazie9db63c2019-07-17 16:19:42 -0700454 !in.max_ack_delay.WriteToCbb(&params) ||
dschinazicd86dd12019-11-14 10:11:13 -0800455 !in.active_connection_id_limit.WriteToCbb(&params) ||
456 !in.max_datagram_frame_size.WriteToCbb(&params)) {
dschinazi52127d72019-04-17 15:12:38 -0700457 QUIC_BUG << "Failed to write integers for " << in;
458 return false;
459 }
460
461 // disable_migration
462 if (in.disable_migration) {
danzh9424add2019-06-06 14:04:36 -0700463 if (!CBB_add_u16(&params, TransportParameters::kDisableMigration) ||
dschinazi52127d72019-04-17 15:12:38 -0700464 !CBB_add_u16(&params, 0u)) { // 0 is the length of this parameter.
465 QUIC_BUG << "Failed to write disable_migration for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500466 return false;
467 }
468 }
dschinazi52127d72019-04-17 15:12:38 -0700469
470 // preferred_address
471 CBB preferred_address_params, preferred_address_connection_id_param;
472 if (in.preferred_address) {
473 std::string v4_address_bytes =
474 in.preferred_address->ipv4_socket_address.host().ToPackedString();
475 std::string v6_address_bytes =
476 in.preferred_address->ipv6_socket_address.host().ToPackedString();
477 if (v4_address_bytes.length() != 4 || v6_address_bytes.length() != 16 ||
478 in.preferred_address->stateless_reset_token.size() !=
479 kStatelessResetTokenLength) {
480 QUIC_BUG << "Bad lengths " << *in.preferred_address;
481 return false;
482 }
danzh9424add2019-06-06 14:04:36 -0700483 if (!CBB_add_u16(&params, TransportParameters::kPreferredAddress) ||
dschinazi52127d72019-04-17 15:12:38 -0700484 !CBB_add_u16_length_prefixed(&params, &preferred_address_params) ||
485 !CBB_add_bytes(
486 &preferred_address_params,
487 reinterpret_cast<const uint8_t*>(v4_address_bytes.data()),
488 v4_address_bytes.length()) ||
489 !CBB_add_u16(&preferred_address_params,
490 in.preferred_address->ipv4_socket_address.port()) ||
491 !CBB_add_bytes(
492 &preferred_address_params,
493 reinterpret_cast<const uint8_t*>(v6_address_bytes.data()),
494 v6_address_bytes.length()) ||
495 !CBB_add_u16(&preferred_address_params,
496 in.preferred_address->ipv6_socket_address.port()) ||
dschinazi3b5dc922019-05-01 20:58:30 -0700497 !CBB_add_u8_length_prefixed(&preferred_address_params,
498 &preferred_address_connection_id_param) ||
dschinazi52127d72019-04-17 15:12:38 -0700499 !CBB_add_bytes(&preferred_address_connection_id_param,
500 reinterpret_cast<const uint8_t*>(
501 in.preferred_address->connection_id.data()),
502 in.preferred_address->connection_id.length()) ||
503 !CBB_add_bytes(&preferred_address_params,
504 in.preferred_address->stateless_reset_token.data(),
505 in.preferred_address->stateless_reset_token.size())) {
506 QUIC_BUG << "Failed to write preferred_address for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500507 return false;
508 }
509 }
dschinazi52127d72019-04-17 15:12:38 -0700510
511 // Google-specific non-standard parameter.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500512 CBB google_quic_params;
513 if (in.google_quic_params) {
514 const QuicData& serialized_google_quic_params =
515 in.google_quic_params->GetSerialized();
danzh9424add2019-06-06 14:04:36 -0700516 if (!CBB_add_u16(&params, TransportParameters::kGoogleQuicParam) ||
QUICHE teama6ef0a62019-03-07 20:34:33 -0500517 !CBB_add_u16_length_prefixed(&params, &google_quic_params) ||
518 !CBB_add_bytes(&google_quic_params,
519 reinterpret_cast<const uint8_t*>(
520 serialized_google_quic_params.data()),
521 serialized_google_quic_params.length())) {
dschinazi52127d72019-04-17 15:12:38 -0700522 QUIC_BUG << "Failed to write Google params of length "
523 << serialized_google_quic_params.length() << " for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500524 return false;
525 }
526 }
dschinazi6cf4d2a2019-04-30 16:20:23 -0700527
528 // Google-specific version extension.
529 CBB google_version_params;
danzh9424add2019-06-06 14:04:36 -0700530 if (!CBB_add_u16(&params, TransportParameters::kGoogleQuicVersion) ||
dschinazi6cf4d2a2019-04-30 16:20:23 -0700531 !CBB_add_u16_length_prefixed(&params, &google_version_params) ||
532 !CBB_add_u32(&google_version_params, in.version)) {
533 QUIC_BUG << "Failed to write Google version extension for " << in;
534 return false;
535 }
536 CBB versions;
537 if (in.perspective == Perspective::IS_SERVER) {
538 if (!CBB_add_u8_length_prefixed(&google_version_params, &versions)) {
539 QUIC_BUG << "Failed to write versions length for " << in;
540 return false;
541 }
542 for (QuicVersionLabel version : in.supported_versions) {
543 if (!CBB_add_u32(&versions, version)) {
544 QUIC_BUG << "Failed to write supported version for " << in;
545 return false;
546 }
547 }
548 }
549
vasilvva2ef3012019-09-12 18:32:14 -0700550 auto custom_parameters = std::make_unique<CBB[]>(in.custom_parameters.size());
551 int i = 0;
552 for (const auto& kv : in.custom_parameters) {
553 CBB* custom_parameter = &custom_parameters[i++];
554 QUIC_BUG_IF(kv.first < 0xff00) << "custom_parameters should not be used "
555 "for non-private use parameters";
556 if (!CBB_add_u16(&params, kv.first) ||
557 !CBB_add_u16_length_prefixed(&params, custom_parameter) ||
558 !CBB_add_bytes(custom_parameter,
559 reinterpret_cast<const uint8_t*>(kv.second.data()),
560 kv.second.size())) {
561 QUIC_BUG << "Failed to write custom parameter "
562 << static_cast<int>(kv.first);
563 return false;
564 }
565 }
566
QUICHE teama6ef0a62019-03-07 20:34:33 -0500567 if (!CBB_flush(cbb.get())) {
dschinazi52127d72019-04-17 15:12:38 -0700568 QUIC_BUG << "Failed to flush CBB for " << in;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500569 return false;
570 }
571 out->resize(CBB_len(cbb.get()));
572 memcpy(out->data(), CBB_data(cbb.get()), CBB_len(cbb.get()));
dschinazi52127d72019-04-17 15:12:38 -0700573 QUIC_DLOG(INFO) << "Serialized " << in << " as " << CBB_len(cbb.get())
574 << " bytes";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500575 return true;
576}
577
dschinazi6c84c142019-07-31 09:11:49 -0700578bool ParseTransportParameters(ParsedQuicVersion version,
QUICHE teama6ef0a62019-03-07 20:34:33 -0500579 Perspective perspective,
dschinazi6c84c142019-07-31 09:11:49 -0700580 const uint8_t* in,
581 size_t in_len,
QUICHE teama6ef0a62019-03-07 20:34:33 -0500582 TransportParameters* out) {
dschinazi6cf4d2a2019-04-30 16:20:23 -0700583 out->perspective = perspective;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500584 CBS cbs;
585 CBS_init(&cbs, in, in_len);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500586
QUICHE teama6ef0a62019-03-07 20:34:33 -0500587 CBS params;
588 if (!CBS_get_u16_length_prefixed(&cbs, &params)) {
dschinazi52127d72019-04-17 15:12:38 -0700589 QUIC_DLOG(ERROR) << "Failed to parse the number of transport parameters";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500590 return false;
591 }
dschinazi52127d72019-04-17 15:12:38 -0700592
QUICHE teama6ef0a62019-03-07 20:34:33 -0500593 while (CBS_len(&params) > 0) {
dschinazi6cf4d2a2019-04-30 16:20:23 -0700594 TransportParameters::TransportParameterId param_id;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500595 CBS value;
dschinazi6cf4d2a2019-04-30 16:20:23 -0700596 static_assert(sizeof(param_id) == sizeof(uint16_t), "bad size");
597 if (!CBS_get_u16(&params, reinterpret_cast<uint16_t*>(&param_id))) {
dschinazi52127d72019-04-17 15:12:38 -0700598 QUIC_DLOG(ERROR) << "Failed to parse transport parameter ID";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500599 return false;
600 }
dschinazi52127d72019-04-17 15:12:38 -0700601 if (!CBS_get_u16_length_prefixed(&params, &value)) {
602 QUIC_DLOG(ERROR) << "Failed to parse length of transport parameter "
dschinazi6cf4d2a2019-04-30 16:20:23 -0700603 << TransportParameterIdToString(param_id);
dschinazi52127d72019-04-17 15:12:38 -0700604 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500605 }
dschinazi52127d72019-04-17 15:12:38 -0700606 bool parse_success = true;
dschinazi6cf4d2a2019-04-30 16:20:23 -0700607 switch (param_id) {
dschinazi6c84c142019-07-31 09:11:49 -0700608 case TransportParameters::kOriginalConnectionId: {
dschinazi52127d72019-04-17 15:12:38 -0700609 if (!out->original_connection_id.IsEmpty()) {
610 QUIC_DLOG(ERROR) << "Received a second original connection ID";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500611 return false;
612 }
dschinazi6c84c142019-07-31 09:11:49 -0700613 const size_t connection_id_length = CBS_len(&value);
614 if (!QuicUtils::IsConnectionIdLengthValidForVersion(
615 connection_id_length, version.transport_version)) {
dschinazi52127d72019-04-17 15:12:38 -0700616 QUIC_DLOG(ERROR) << "Received original connection ID of "
dschinazi6c84c142019-07-31 09:11:49 -0700617 << "invalid length " << connection_id_length;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500618 return false;
619 }
dschinazi6c84c142019-07-31 09:11:49 -0700620 out->original_connection_id.set_length(
621 static_cast<uint8_t>(connection_id_length));
622 if (out->original_connection_id.length() != 0) {
dschinazi52127d72019-04-17 15:12:38 -0700623 memcpy(out->original_connection_id.mutable_data(), CBS_data(&value),
dschinazi6c84c142019-07-31 09:11:49 -0700624 out->original_connection_id.length());
QUICHE teama6ef0a62019-03-07 20:34:33 -0500625 }
dschinazi6c84c142019-07-31 09:11:49 -0700626 } break;
danzh9424add2019-06-06 14:04:36 -0700627 case TransportParameters::kIdleTimeout:
dschinazi6cf4d2a2019-04-30 16:20:23 -0700628 parse_success = out->idle_timeout_milliseconds.ReadFromCbs(&value);
dschinazi52127d72019-04-17 15:12:38 -0700629 break;
danzh9424add2019-06-06 14:04:36 -0700630 case TransportParameters::kStatelessResetToken:
dschinazi52127d72019-04-17 15:12:38 -0700631 if (!out->stateless_reset_token.empty()) {
632 QUIC_DLOG(ERROR) << "Received a second stateless reset token";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500633 return false;
634 }
dschinazi52127d72019-04-17 15:12:38 -0700635 if (CBS_len(&value) != kStatelessResetTokenLength) {
636 QUIC_DLOG(ERROR) << "Received stateless reset token of "
637 << "invalid length " << CBS_len(&value);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500638 return false;
639 }
640 out->stateless_reset_token.assign(CBS_data(&value),
641 CBS_data(&value) + CBS_len(&value));
642 break;
danzh9424add2019-06-06 14:04:36 -0700643 case TransportParameters::kMaxPacketSize:
dschinazi52127d72019-04-17 15:12:38 -0700644 parse_success = out->max_packet_size.ReadFromCbs(&value);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500645 break;
danzh9424add2019-06-06 14:04:36 -0700646 case TransportParameters::kInitialMaxData:
dschinazi52127d72019-04-17 15:12:38 -0700647 parse_success = out->initial_max_data.ReadFromCbs(&value);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500648 break;
danzh9424add2019-06-06 14:04:36 -0700649 case TransportParameters::kInitialMaxStreamDataBidiLocal:
dschinazi52127d72019-04-17 15:12:38 -0700650 parse_success =
651 out->initial_max_stream_data_bidi_local.ReadFromCbs(&value);
652 break;
danzh9424add2019-06-06 14:04:36 -0700653 case TransportParameters::kInitialMaxStreamDataBidiRemote:
dschinazi52127d72019-04-17 15:12:38 -0700654 parse_success =
655 out->initial_max_stream_data_bidi_remote.ReadFromCbs(&value);
656 break;
danzh9424add2019-06-06 14:04:36 -0700657 case TransportParameters::kInitialMaxStreamDataUni:
dschinazi52127d72019-04-17 15:12:38 -0700658 parse_success = out->initial_max_stream_data_uni.ReadFromCbs(&value);
659 break;
danzh9424add2019-06-06 14:04:36 -0700660 case TransportParameters::kInitialMaxStreamsBidi:
dschinazi52127d72019-04-17 15:12:38 -0700661 parse_success = out->initial_max_streams_bidi.ReadFromCbs(&value);
662 break;
danzh9424add2019-06-06 14:04:36 -0700663 case TransportParameters::kInitialMaxStreamsUni:
dschinazi52127d72019-04-17 15:12:38 -0700664 parse_success = out->initial_max_streams_uni.ReadFromCbs(&value);
665 break;
danzh9424add2019-06-06 14:04:36 -0700666 case TransportParameters::kAckDelayExponent:
dschinazi52127d72019-04-17 15:12:38 -0700667 parse_success = out->ack_delay_exponent.ReadFromCbs(&value);
668 break;
danzh9424add2019-06-06 14:04:36 -0700669 case TransportParameters::kMaxAckDelay:
dschinazi52127d72019-04-17 15:12:38 -0700670 parse_success = out->max_ack_delay.ReadFromCbs(&value);
671 break;
danzh9424add2019-06-06 14:04:36 -0700672 case TransportParameters::kDisableMigration:
dschinazi52127d72019-04-17 15:12:38 -0700673 if (out->disable_migration) {
674 QUIC_DLOG(ERROR) << "Received a second disable migration";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500675 return false;
676 }
dschinazi52127d72019-04-17 15:12:38 -0700677 if (CBS_len(&value) != 0) {
678 QUIC_DLOG(ERROR) << "Received disable migration of invalid length "
679 << CBS_len(&value);
680 return false;
681 }
682 out->disable_migration = true;
683 break;
danzh9424add2019-06-06 14:04:36 -0700684 case TransportParameters::kPreferredAddress: {
dschinazi52127d72019-04-17 15:12:38 -0700685 uint16_t ipv4_port, ipv6_port;
686 in_addr ipv4_address;
687 in6_addr ipv6_address;
688 if (!CBS_copy_bytes(&value, reinterpret_cast<uint8_t*>(&ipv4_address),
689 sizeof(ipv4_address)) ||
690 !CBS_get_u16(&value, &ipv4_port) ||
691 !CBS_copy_bytes(&value, reinterpret_cast<uint8_t*>(&ipv6_address),
692 sizeof(ipv6_address)) ||
693 !CBS_get_u16(&value, &ipv6_port)) {
694 QUIC_DLOG(ERROR) << "Failed to parse preferred address IPs and ports";
695 return false;
696 }
697 TransportParameters::PreferredAddress preferred_address;
698 preferred_address.ipv4_socket_address =
699 QuicSocketAddress(QuicIpAddress(ipv4_address), ipv4_port);
700 preferred_address.ipv6_socket_address =
701 QuicSocketAddress(QuicIpAddress(ipv6_address), ipv6_port);
702 if (!preferred_address.ipv4_socket_address.host().IsIPv4() ||
703 !preferred_address.ipv6_socket_address.host().IsIPv6()) {
704 QUIC_DLOG(ERROR) << "Received preferred addresses of bad families "
705 << preferred_address;
706 return false;
707 }
708 CBS connection_id_cbs;
dschinazi3b5dc922019-05-01 20:58:30 -0700709 if (!CBS_get_u8_length_prefixed(&value, &connection_id_cbs)) {
dschinazi52127d72019-04-17 15:12:38 -0700710 QUIC_DLOG(ERROR)
711 << "Failed to parse length of preferred address connection ID";
712 return false;
713 }
dschinazi6c84c142019-07-31 09:11:49 -0700714 const size_t connection_id_length = CBS_len(&connection_id_cbs);
715 if (!QuicUtils::IsConnectionIdLengthValidForVersion(
716 connection_id_length, version.transport_version)) {
717 QUIC_DLOG(ERROR) << "Received preferred address connection ID of "
718 << "invalid length " << connection_id_length;
dschinazi52127d72019-04-17 15:12:38 -0700719 return false;
720 }
dschinazi6c84c142019-07-31 09:11:49 -0700721 preferred_address.connection_id.set_length(
722 static_cast<uint8_t>(connection_id_length));
dschinazi52127d72019-04-17 15:12:38 -0700723 if (preferred_address.connection_id.length() > 0 &&
724 !CBS_copy_bytes(&connection_id_cbs,
725 reinterpret_cast<uint8_t*>(
726 preferred_address.connection_id.mutable_data()),
727 preferred_address.connection_id.length())) {
728 QUIC_DLOG(ERROR) << "Failed to read preferred address connection ID";
729 return false;
730 }
731 if (CBS_len(&value) != kStatelessResetTokenLength) {
732 QUIC_DLOG(ERROR) << "Received preferred address with "
733 << "invalid remaining length " << CBS_len(&value);
734 return false;
735 }
736 preferred_address.stateless_reset_token.assign(
737 CBS_data(&value), CBS_data(&value) + CBS_len(&value));
738 out->preferred_address =
vasilvv0fc587f2019-09-06 13:33:08 -0700739 std::make_unique<TransportParameters::PreferredAddress>(
dschinazi52127d72019-04-17 15:12:38 -0700740 preferred_address);
741 } break;
dschinazie9db63c2019-07-17 16:19:42 -0700742 case TransportParameters::kActiveConnectionIdLimit:
743 parse_success = out->active_connection_id_limit.ReadFromCbs(&value);
744 break;
dschinazicd86dd12019-11-14 10:11:13 -0800745 case TransportParameters::kMaxDatagramFrameSize:
746 parse_success = out->max_datagram_frame_size.ReadFromCbs(&value);
747 break;
danzh9424add2019-06-06 14:04:36 -0700748 case TransportParameters::kGoogleQuicParam: {
dschinazi52127d72019-04-17 15:12:38 -0700749 if (out->google_quic_params) {
750 QUIC_DLOG(ERROR) << "Received a second Google parameter";
751 return false;
752 }
dmcardle904ef182019-12-13 08:34:33 -0800753 quiche::QuicheStringPiece serialized_params(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500754 reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value));
755 out->google_quic_params = CryptoFramer::ParseMessage(serialized_params);
dschinazi6cf4d2a2019-04-30 16:20:23 -0700756 } break;
danzh9424add2019-06-06 14:04:36 -0700757 case TransportParameters::kGoogleQuicVersion: {
dschinazi6cf4d2a2019-04-30 16:20:23 -0700758 if (!CBS_get_u32(&value, &out->version)) {
759 QUIC_DLOG(ERROR) << "Failed to parse Google version extension";
760 return false;
761 }
762 if (perspective == Perspective::IS_SERVER) {
763 CBS versions;
764 if (!CBS_get_u8_length_prefixed(&value, &versions) ||
765 CBS_len(&versions) % 4 != 0) {
766 QUIC_DLOG(ERROR)
767 << "Failed to parse Google supported versions length";
768 return false;
769 }
770 while (CBS_len(&versions) > 0) {
771 QuicVersionLabel version;
772 if (!CBS_get_u32(&versions, &version)) {
773 QUIC_DLOG(ERROR) << "Failed to parse Google supported version";
774 return false;
775 }
776 out->supported_versions.push_back(version);
777 }
778 }
779 } break;
vasilvva2ef3012019-09-12 18:32:14 -0700780 default:
781 out->custom_parameters[param_id] = std::string(
782 reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value));
783 break;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500784 }
dschinazi52127d72019-04-17 15:12:38 -0700785 if (!parse_success) {
786 return false;
787 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500788 }
dschinazi52127d72019-04-17 15:12:38 -0700789
790 const bool ok = out->AreValid();
791 if (ok) {
792 QUIC_DLOG(INFO) << "Parsed transport parameters " << *out << " from "
793 << in_len << " bytes";
794 } else {
795 QUIC_DLOG(ERROR) << "Transport parameter validity check failed " << *out
796 << " from " << in_len << " bytes";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500797 }
dschinazi52127d72019-04-17 15:12:38 -0700798 return ok;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500799}
800
801} // namespace quic