Skip to content

Commit 3c7e8e2

Browse files
committed
quinn-rs#2057: Write transport parameters in random order.
1 parent 78cd86c commit 3c7e8e2

File tree

1 file changed

+131
-60
lines changed

1 file changed

+131
-60
lines changed

quinn-proto/src/transport_parameters.rs

+131-60
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::{
@@ -104,6 +104,12 @@ macro_rules! make_struct {
104104
/// of transport parameter extensions.
105105
/// When present, it is included during serialization but ignored during deserialization.
106106
pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
107+
108+
/// Defines the order in which transport parameters are serialized.
109+
///
110+
/// This field is initialized only for outgoing `TransportParameters` instances and
111+
/// is set to `None` for `TransportParameters` received from a peer.
112+
pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
107113
}
108114

109115
// We deliberately don't implement the `Default` trait, since that would be public, and
@@ -126,6 +132,7 @@ macro_rules! make_struct {
126132
stateless_reset_token: None,
127133
preferred_address: None,
128134
grease_transport_parameter: None,
135+
write_order: None,
129136
}
130137
}
131138
}
@@ -168,6 +175,11 @@ impl TransportParameters {
168175
VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
169176
),
170177
grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
178+
write_order: Some({
179+
let mut order = std::array::from_fn(|i| i as u8);
180+
order.shuffle(rng);
181+
order
182+
}),
171183
..Self::default()
172184
}
173185
}
@@ -295,68 +307,100 @@ impl From<UnexpectedEnd> for Error {
295307
impl TransportParameters {
296308
/// Encode `TransportParameters` into buffer
297309
pub fn write<W: BufMut>(&self, w: &mut W) {
298-
macro_rules! write_params {
299-
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
300-
$(
301-
if self.$name.0 != $default {
302-
w.write_var(TransportParameterId::$id as u64);
303-
w.write(VarInt::try_from(self.$name.size()).unwrap());
304-
w.write(self.$name);
310+
for idx in self
311+
.write_order
312+
.as_ref()
313+
.unwrap_or(&std::array::from_fn(|i| i as u8))
314+
{
315+
let id = TransportParameterId::SUPPORTED[*idx as usize];
316+
match id {
317+
TransportParameterId::ReservedTransportParameter => {
318+
if let Some(param) = self.grease_transport_parameter {
319+
param.write(w);
305320
}
306-
)*
307-
}
308-
}
309-
apply_params!(write_params);
310-
311-
if let Some(param) = self.grease_transport_parameter {
312-
param.write(w);
313-
}
314-
315-
if let Some(ref x) = self.stateless_reset_token {
316-
w.write_var(0x02);
317-
w.write_var(16);
318-
w.put_slice(x);
319-
}
320-
321-
if self.disable_active_migration {
322-
w.write_var(0x0c);
323-
w.write_var(0);
324-
}
325-
326-
if let Some(x) = self.max_datagram_frame_size {
327-
w.write_var(0x20);
328-
w.write_var(x.size() as u64);
329-
w.write(x);
330-
}
331-
332-
if let Some(ref x) = self.preferred_address {
333-
w.write_var(0x000d);
334-
w.write_var(x.wire_size() as u64);
335-
x.write(w);
336-
}
337-
338-
for &(tag, cid) in &[
339-
(0x00, &self.original_dst_cid),
340-
(0x0f, &self.initial_src_cid),
341-
(0x10, &self.retry_src_cid),
342-
] {
343-
if let Some(ref cid) = *cid {
344-
w.write_var(tag);
345-
w.write_var(cid.len() as u64);
346-
w.put_slice(cid);
321+
}
322+
TransportParameterId::StatelessResetToken => {
323+
if let Some(ref x) = self.stateless_reset_token {
324+
w.write_var(id as u64);
325+
w.write_var(16);
326+
w.put_slice(x);
327+
}
328+
}
329+
TransportParameterId::DisableActiveMigration => {
330+
if self.disable_active_migration {
331+
w.write_var(id as u64);
332+
w.write_var(0);
333+
}
334+
}
335+
TransportParameterId::MaxDatagramFrameSize => {
336+
if let Some(x) = self.max_datagram_frame_size {
337+
w.write_var(id as u64);
338+
w.write_var(x.size() as u64);
339+
w.write(x);
340+
}
341+
}
342+
TransportParameterId::PreferredAddress => {
343+
if let Some(ref x) = self.preferred_address {
344+
w.write_var(id as u64);
345+
w.write_var(x.wire_size() as u64);
346+
x.write(w);
347+
}
348+
}
349+
TransportParameterId::OriginalDestinationConnectionId => {
350+
if let Some(ref cid) = self.original_dst_cid {
351+
w.write_var(id as u64);
352+
w.write_var(cid.len() as u64);
353+
w.put_slice(cid);
354+
}
355+
}
356+
TransportParameterId::InitialSourceConnectionId => {
357+
if let Some(ref cid) = self.initial_src_cid {
358+
w.write_var(id as u64);
359+
w.write_var(cid.len() as u64);
360+
w.put_slice(cid);
361+
}
362+
}
363+
TransportParameterId::RetrySourceConnectionId => {
364+
if let Some(ref cid) = self.retry_src_cid {
365+
w.write_var(id as u64);
366+
w.write_var(cid.len() as u64);
367+
w.put_slice(cid);
368+
}
369+
}
370+
TransportParameterId::GreaseQuicBit => {
371+
if self.grease_quic_bit {
372+
w.write_var(id as u64);
373+
w.write_var(0);
374+
}
375+
}
376+
TransportParameterId::MinAckDelayDraft07 => {
377+
if let Some(x) = self.min_ack_delay {
378+
w.write_var(id as u64);
379+
w.write_var(x.size() as u64);
380+
w.write(x);
381+
}
382+
}
383+
id => {
384+
macro_rules! write_params {
385+
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
386+
match id {
387+
$(TransportParameterId::$id => {
388+
if self.$name.0 != $default {
389+
w.write_var(id as u64);
390+
w.write(VarInt::try_from(self.$name.size()).unwrap());
391+
w.write(self.$name);
392+
}
393+
})*,
394+
_ => {
395+
unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
396+
}
397+
}
398+
}
399+
}
400+
apply_params!(write_params);
401+
}
347402
}
348403
}
349-
350-
if self.grease_quic_bit {
351-
w.write_var(0x2ab2);
352-
w.write_var(0);
353-
}
354-
355-
if let Some(x) = self.min_ack_delay {
356-
w.write_var(0xff04de1b);
357-
w.write_var(x.size() as u64);
358-
w.write(x);
359-
}
360404
}
361405

362406
/// Decode `TransportParameters` from buffer
@@ -598,6 +642,33 @@ pub(crate) enum TransportParameterId {
598642
MinAckDelayDraft07 = 0xFF04DE1B,
599643
}
600644

645+
impl TransportParameterId {
646+
/// Array with all supported transport parameter IDs
647+
const SUPPORTED: [Self; 21] = [
648+
Self::MaxIdleTimeout,
649+
Self::MaxUdpPayloadSize,
650+
Self::InitialMaxData,
651+
Self::InitialMaxStreamDataBidiLocal,
652+
Self::InitialMaxStreamDataBidiRemote,
653+
Self::InitialMaxStreamDataUni,
654+
Self::InitialMaxStreamsBidi,
655+
Self::InitialMaxStreamsUni,
656+
Self::AckDelayExponent,
657+
Self::MaxAckDelay,
658+
Self::ActiveConnectionIdLimit,
659+
Self::ReservedTransportParameter,
660+
Self::StatelessResetToken,
661+
Self::DisableActiveMigration,
662+
Self::MaxDatagramFrameSize,
663+
Self::PreferredAddress,
664+
Self::OriginalDestinationConnectionId,
665+
Self::InitialSourceConnectionId,
666+
Self::RetrySourceConnectionId,
667+
Self::GreaseQuicBit,
668+
Self::MinAckDelayDraft07,
669+
];
670+
}
671+
601672
impl std::cmp::PartialEq<u64> for TransportParameterId {
602673
fn eq(&self, other: &u64) -> bool {
603674
*other == (*self as u64)

0 commit comments

Comments
 (0)