Skip to content

Commit d169653

Browse files
committed
quinn-rs#2057: Write transport parameters in random order.
1 parent 4f2338d commit d169653

File tree

1 file changed

+126
-60
lines changed

1 file changed

+126
-60
lines changed

quinn-proto/src/transport_parameters.rs

Lines changed: 126 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::{
1212
};
1313

1414
use bytes::{Buf, BufMut};
15-
use rand::{Rng as _, RngCore};
15+
use rand::{seq::SliceRandom as _, Rng as _, RngCore};
1616
use thiserror::Error;
1717

1818
use crate::{
@@ -105,6 +105,31 @@ macro_rules! apply_params {
105105
};
106106
}
107107

108+
/// Array with all supported transport parameter IDs
109+
const SUPPORTED_TRANSPORT_PARAMETERS: [TransportParameterId; 21] = [
110+
TransportParameterId::MaxIdleTimeout,
111+
TransportParameterId::MaxUdpPayloadSize,
112+
TransportParameterId::InitialMaxData,
113+
TransportParameterId::InitialMaxStreamDataBidiLocal,
114+
TransportParameterId::InitialMaxStreamDataBidiRemote,
115+
TransportParameterId::InitialMaxStreamDataUni,
116+
TransportParameterId::InitialMaxStreamsBidi,
117+
TransportParameterId::InitialMaxStreamsUni,
118+
TransportParameterId::AckDelayExponent,
119+
TransportParameterId::MaxAckDelay,
120+
TransportParameterId::ActiveConnectionIdLimit,
121+
TransportParameterId::ReservedTransportParameter,
122+
TransportParameterId::StatelessResetToken,
123+
TransportParameterId::DisableActiveMigration,
124+
TransportParameterId::MaxDatagramFrameSize,
125+
TransportParameterId::PreferredAddress,
126+
TransportParameterId::OriginalDestinationConnectionId,
127+
TransportParameterId::InitialSourceConnectionId,
128+
TransportParameterId::RetrySourceConnectionId,
129+
TransportParameterId::GreaseQuicBit,
130+
TransportParameterId::MinAckDelayDraft07,
131+
];
132+
108133
macro_rules! make_struct {
109134
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
110135
/// Transport parameters used to negotiate connection-level preferences between peers
@@ -145,6 +170,9 @@ macro_rules! make_struct {
145170
/// of transport parameter extensions.
146171
/// When present, it is included during serialization but ignored during deserialization.
147172
pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
173+
174+
/// The order in which transport parameters are serialized
175+
pub(crate) write_order: Option<[u8; SUPPORTED_TRANSPORT_PARAMETERS.len()]>,
148176
}
149177

150178
// We deliberately don't implement the `Default` trait, since that would be public, and
@@ -167,6 +195,7 @@ macro_rules! make_struct {
167195
stateless_reset_token: None,
168196
preferred_address: None,
169197
grease_transport_parameter: None,
198+
write_order: None,
170199
}
171200
}
172201
}
@@ -209,6 +238,11 @@ impl TransportParameters {
209238
VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
210239
),
211240
grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
241+
write_order: Some({
242+
let mut order = std::array::from_fn(|i| i as u8);
243+
order.shuffle(rng);
244+
order
245+
}),
212246
..Self::default()
213247
}
214248
}
@@ -336,68 +370,100 @@ impl From<UnexpectedEnd> for Error {
336370
impl TransportParameters {
337371
/// Encode `TransportParameters` into buffer
338372
pub fn write<W: BufMut>(&self, w: &mut W) {
339-
macro_rules! write_params {
340-
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
341-
$(
342-
if self.$name.0 != $default {
343-
w.write_var(TransportParameterId::$id as u64);
344-
w.write(VarInt::try_from(self.$name.size()).unwrap());
345-
w.write(self.$name);
373+
for idx in self
374+
.write_order
375+
.as_ref()
376+
.unwrap_or(&std::array::from_fn(|i| i as u8))
377+
{
378+
let id = SUPPORTED_TRANSPORT_PARAMETERS[*idx as usize];
379+
match id {
380+
TransportParameterId::ReservedTransportParameter => {
381+
if let Some(param) = self.grease_transport_parameter {
382+
param.write(w);
346383
}
347-
)*
348-
}
349-
}
350-
apply_params!(write_params);
351-
352-
if let Some(param) = self.grease_transport_parameter {
353-
param.write(w);
354-
}
355-
356-
if let Some(ref x) = self.stateless_reset_token {
357-
w.write_var(0x02);
358-
w.write_var(16);
359-
w.put_slice(x);
360-
}
361-
362-
if self.disable_active_migration {
363-
w.write_var(0x0c);
364-
w.write_var(0);
365-
}
366-
367-
if let Some(x) = self.max_datagram_frame_size {
368-
w.write_var(0x20);
369-
w.write_var(x.size() as u64);
370-
w.write(x);
371-
}
372-
373-
if let Some(ref x) = self.preferred_address {
374-
w.write_var(0x000d);
375-
w.write_var(x.wire_size() as u64);
376-
x.write(w);
377-
}
378-
379-
for &(tag, cid) in &[
380-
(0x00, &self.original_dst_cid),
381-
(0x0f, &self.initial_src_cid),
382-
(0x10, &self.retry_src_cid),
383-
] {
384-
if let Some(ref cid) = *cid {
385-
w.write_var(tag);
386-
w.write_var(cid.len() as u64);
387-
w.put_slice(cid);
384+
}
385+
TransportParameterId::StatelessResetToken => {
386+
if let Some(ref x) = self.stateless_reset_token {
387+
w.write_var(id as u64);
388+
w.write_var(16);
389+
w.put_slice(x);
390+
}
391+
}
392+
TransportParameterId::DisableActiveMigration => {
393+
if self.disable_active_migration {
394+
w.write_var(id as u64);
395+
w.write_var(0);
396+
}
397+
}
398+
TransportParameterId::MaxDatagramFrameSize => {
399+
if let Some(x) = self.max_datagram_frame_size {
400+
w.write_var(id as u64);
401+
w.write_var(x.size() as u64);
402+
w.write(x);
403+
}
404+
}
405+
TransportParameterId::PreferredAddress => {
406+
if let Some(ref x) = self.preferred_address {
407+
w.write_var(id as u64);
408+
w.write_var(x.wire_size() as u64);
409+
x.write(w);
410+
}
411+
}
412+
TransportParameterId::OriginalDestinationConnectionId => {
413+
if let Some(ref cid) = self.original_dst_cid {
414+
w.write_var(id as u64);
415+
w.write_var(cid.len() as u64);
416+
w.put_slice(cid);
417+
}
418+
}
419+
TransportParameterId::InitialSourceConnectionId => {
420+
if let Some(ref cid) = self.initial_src_cid {
421+
w.write_var(id as u64);
422+
w.write_var(cid.len() as u64);
423+
w.put_slice(cid);
424+
}
425+
}
426+
TransportParameterId::RetrySourceConnectionId => {
427+
if let Some(ref cid) = self.retry_src_cid {
428+
w.write_var(id as u64);
429+
w.write_var(cid.len() as u64);
430+
w.put_slice(cid);
431+
}
432+
}
433+
TransportParameterId::GreaseQuicBit => {
434+
if self.grease_quic_bit {
435+
w.write_var(id as u64);
436+
w.write_var(0);
437+
}
438+
}
439+
TransportParameterId::MinAckDelayDraft07 => {
440+
if let Some(x) = self.min_ack_delay {
441+
w.write_var(id as u64);
442+
w.write_var(x.size() as u64);
443+
w.write(x);
444+
}
445+
}
446+
id => {
447+
macro_rules! write_params {
448+
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
449+
match id {
450+
$(TransportParameterId::$id => {
451+
if self.$name.0 != $default {
452+
w.write_var(id as u64);
453+
w.write(VarInt::try_from(self.$name.size()).unwrap());
454+
w.write(self.$name);
455+
}
456+
})*,
457+
_ => {
458+
unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
459+
}
460+
}
461+
}
462+
}
463+
apply_params!(write_params);
464+
}
388465
}
389466
}
390-
391-
if self.grease_quic_bit {
392-
w.write_var(0x2ab2);
393-
w.write_var(0);
394-
}
395-
396-
if let Some(x) = self.min_ack_delay {
397-
w.write_var(0xff04de1b);
398-
w.write_var(x.size() as u64);
399-
w.write(x);
400-
}
401467
}
402468

403469
/// Decode `TransportParameters` from buffer

0 commit comments

Comments
 (0)