Skip to main content

simoom_quic/
params.rs

1//! Transport parameters (RFC 9000 §18): encoding, decoding and validation.
2
3use crate::coding::{put_varint, varint_len, Reader};
4use crate::error::{TransportError, TRANSPORT_PARAMETER_ERROR};
5use crate::frame::MAX_STREAMS_LIMIT;
6
7/// A connection ID of up to 20 bytes, stored inline.
8#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
9pub struct Cid {
10    len: u8,
11    b: [u8; 20],
12}
13
14impl Cid {
15    /// Panics if `s` is longer than 20 bytes.
16    pub fn new(s: &[u8]) -> Self {
17        let mut b = [0u8; 20];
18        b[..s.len()].copy_from_slice(s);
19        Cid { len: s.len() as u8, b }
20    }
21    pub fn as_slice(&self) -> &[u8] {
22        &self.b[..self.len as usize]
23    }
24    pub fn len(&self) -> usize {
25        self.len as usize
26    }
27    pub fn is_empty(&self) -> bool {
28        self.len == 0
29    }
30}
31
32impl std::ops::Deref for Cid {
33    type Target = [u8];
34    fn deref(&self) -> &[u8] {
35        self.as_slice()
36    }
37}
38
39impl std::fmt::Debug for Cid {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        for b in self.as_slice() {
42            write!(f, "{b:02x}")?;
43        }
44        Ok(())
45    }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct PreferredAddress {
50    pub v4: [u8; 6],
51    pub v6: [u8; 18],
52    pub cid: Cid,
53    pub reset_token: [u8; 16],
54}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct TransportParams {
58    pub original_destination_connection_id: Option<Cid>,
59    /// Milliseconds; 0 means no idle timeout.
60    pub max_idle_timeout: u64,
61    pub stateless_reset_token: Option<[u8; 16]>,
62    pub max_udp_payload_size: u64,
63    pub initial_max_data: u64,
64    pub initial_max_stream_data_bidi_local: u64,
65    pub initial_max_stream_data_bidi_remote: u64,
66    pub initial_max_stream_data_uni: u64,
67    pub initial_max_streams_bidi: u64,
68    pub initial_max_streams_uni: u64,
69    pub ack_delay_exponent: u64,
70    /// Milliseconds.
71    pub max_ack_delay: u64,
72    pub disable_active_migration: bool,
73    pub preferred_address: Option<PreferredAddress>,
74    pub active_connection_id_limit: u64,
75    pub initial_source_connection_id: Option<Cid>,
76    pub retry_source_connection_id: Option<Cid>,
77    /// `version_information` (RFC 9368 §3): the chosen version and the available versions.
78    pub version_information: Option<(u32, Vec<u32>)>,
79}
80
81impl Default for TransportParams {
82    /// The protocol defaults (what an absent parameter means).
83    fn default() -> Self {
84        TransportParams {
85            original_destination_connection_id: None,
86            max_idle_timeout: 0,
87            stateless_reset_token: None,
88            max_udp_payload_size: 65527,
89            initial_max_data: 0,
90            initial_max_stream_data_bidi_local: 0,
91            initial_max_stream_data_bidi_remote: 0,
92            initial_max_stream_data_uni: 0,
93            initial_max_streams_bidi: 0,
94            initial_max_streams_uni: 0,
95            ack_delay_exponent: 3,
96            max_ack_delay: 25,
97            disable_active_migration: false,
98            preferred_address: None,
99            active_connection_id_limit: 2,
100            initial_source_connection_id: None,
101            retry_source_connection_id: None,
102            version_information: None,
103        }
104    }
105}
106
107fn put_int(out: &mut Vec<u8>, id: u64, v: u64, default: u64) {
108    if v != default {
109        put_varint(out, id);
110        put_varint(out, varint_len(v) as u64);
111        put_varint(out, v);
112    }
113}
114
115fn put_bytes(out: &mut Vec<u8>, id: u64, b: &[u8]) {
116    put_varint(out, id);
117    put_varint(out, b.len() as u64);
118    out.extend_from_slice(b);
119}
120
121impl TransportParams {
122    pub fn encode(&self, out: &mut Vec<u8>) {
123        let d = TransportParams::default();
124        if let Some(c) = &self.original_destination_connection_id {
125            put_bytes(out, 0x00, c);
126        }
127        put_int(out, 0x01, self.max_idle_timeout, 0);
128        if let Some(t) = &self.stateless_reset_token {
129            put_bytes(out, 0x02, t);
130        }
131        put_int(out, 0x03, self.max_udp_payload_size, d.max_udp_payload_size);
132        put_int(out, 0x04, self.initial_max_data, 0);
133        put_int(out, 0x05, self.initial_max_stream_data_bidi_local, 0);
134        put_int(out, 0x06, self.initial_max_stream_data_bidi_remote, 0);
135        put_int(out, 0x07, self.initial_max_stream_data_uni, 0);
136        put_int(out, 0x08, self.initial_max_streams_bidi, 0);
137        put_int(out, 0x09, self.initial_max_streams_uni, 0);
138        put_int(out, 0x0a, self.ack_delay_exponent, 3);
139        put_int(out, 0x0b, self.max_ack_delay, 25);
140        if self.disable_active_migration {
141            put_bytes(out, 0x0c, &[]);
142        }
143        if let Some(p) = &self.preferred_address {
144            let mut b = Vec::with_capacity(61);
145            b.extend_from_slice(&p.v4);
146            b.extend_from_slice(&p.v6);
147            b.push(p.cid.len() as u8);
148            b.extend_from_slice(&p.cid);
149            b.extend_from_slice(&p.reset_token);
150            put_bytes(out, 0x0d, &b);
151        }
152        put_int(out, 0x0e, self.active_connection_id_limit, 2);
153        if let Some(c) = &self.initial_source_connection_id {
154            put_bytes(out, 0x0f, c);
155        }
156        if let Some(c) = &self.retry_source_connection_id {
157            put_bytes(out, 0x10, c);
158        }
159        if let Some((chosen, available)) = &self.version_information {
160            put_bytes(out, crate::vn::VERSION_INFORMATION, &crate::vn::encode(*chosen, available));
161        }
162    }
163
164    /// Decodes and validates parameters sent by a peer; `from_server` says who sent them.
165    pub fn decode(buf: &[u8], from_server: bool) -> Result<Self, TransportError> {
166        let err = |r: &'static str| TransportError::new(TRANSPORT_PARAMETER_ERROR, r);
167        let mut p = TransportParams::default();
168        let mut seen = 0u32;
169        let mut r = Reader::new(buf);
170        while !r.is_empty() {
171            let id = r.varint().ok_or(err("truncated parameter"))?;
172            let v = r.vbytes().ok_or(err("truncated parameter"))?;
173            if id <= 0x10 {
174                if seen & (1 << id) != 0 {
175                    return Err(err("duplicate parameter"));
176                }
177                seen |= 1 << id;
178                if !from_server && matches!(id, 0x00 | 0x02 | 0x0d | 0x10) {
179                    return Err(err("server-only parameter from a client"));
180                }
181            }
182            let int = || -> Result<u64, TransportError> {
183                let mut vr = Reader::new(v);
184                let x = vr.varint().ok_or(err("bad integer"))?;
185                if !vr.is_empty() {
186                    return Err(err("bad integer"));
187                }
188                Ok(x)
189            };
190            let cid = || -> Result<Cid, TransportError> {
191                if v.len() > 20 {
192                    return Err(err("connection ID too long"));
193                }
194                Ok(Cid::new(v))
195            };
196            match id {
197                0x00 => p.original_destination_connection_id = Some(cid()?),
198                0x01 => p.max_idle_timeout = int()?,
199                0x02 => p.stateless_reset_token = Some(v.try_into().map_err(|_| err("bad reset token"))?),
200                0x03 => {
201                    p.max_udp_payload_size = int()?;
202                    if p.max_udp_payload_size < 1200 {
203                        return Err(err("max_udp_payload_size below 1200"));
204                    }
205                }
206                0x04 => p.initial_max_data = int()?,
207                0x05 => p.initial_max_stream_data_bidi_local = int()?,
208                0x06 => p.initial_max_stream_data_bidi_remote = int()?,
209                0x07 => p.initial_max_stream_data_uni = int()?,
210                0x08 | 0x09 => {
211                    let n = int()?;
212                    if n > MAX_STREAMS_LIMIT {
213                        return Err(err("stream limit above 2^60"));
214                    }
215                    if id == 0x08 {
216                        p.initial_max_streams_bidi = n
217                    } else {
218                        p.initial_max_streams_uni = n
219                    }
220                }
221                0x0a => {
222                    p.ack_delay_exponent = int()?;
223                    if p.ack_delay_exponent > 20 {
224                        return Err(err("ack_delay_exponent above 20"));
225                    }
226                }
227                0x0b => {
228                    p.max_ack_delay = int()?;
229                    if p.max_ack_delay >= 1 << 14 {
230                        return Err(err("max_ack_delay too large"));
231                    }
232                }
233                0x0c => {
234                    if !v.is_empty() {
235                        return Err(err("disable_active_migration not empty"));
236                    }
237                    p.disable_active_migration = true;
238                }
239                0x0d => {
240                    let mut pr = Reader::new(v);
241                    let v4 = pr.bytes(6).ok_or(err("bad preferred_address"))?.try_into().unwrap();
242                    let v6 = pr.bytes(18).ok_or(err("bad preferred_address"))?.try_into().unwrap();
243                    let n = pr.u8().ok_or(err("bad preferred_address"))? as usize;
244                    if n == 0 || n > 20 {
245                        return Err(err("bad preferred_address"));
246                    }
247                    let c = Cid::new(pr.bytes(n).ok_or(err("bad preferred_address"))?);
248                    let t = pr.bytes(16).ok_or(err("bad preferred_address"))?.try_into().unwrap();
249                    if !pr.is_empty() {
250                        return Err(err("bad preferred_address"));
251                    }
252                    p.preferred_address = Some(PreferredAddress { v4, v6, cid: c, reset_token: t });
253                }
254                0x0e => {
255                    p.active_connection_id_limit = int()?;
256                    if p.active_connection_id_limit < 2 {
257                        return Err(err("active_connection_id_limit below 2"));
258                    }
259                }
260                0x0f => p.initial_source_connection_id = Some(cid()?),
261                0x10 => p.retry_source_connection_id = Some(cid()?),
262                crate::vn::VERSION_INFORMATION => {
263                    if p.version_information.is_some() {
264                        return Err(err("duplicate parameter"));
265                    }
266                    p.version_information = Some(crate::vn::decode(v).ok_or(err("bad version_information"))?);
267                }
268                _ => {} // Unknown and reserved parameters are ignored (RFC 9000 §18.1).
269            }
270        }
271        if p.initial_source_connection_id.is_none() {
272            return Err(err("missing initial_source_connection_id"));
273        }
274        if from_server && p.original_destination_connection_id.is_none() {
275            return Err(err("missing original_destination_connection_id"));
276        }
277        Ok(p)
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn roundtrip_and_validation() {
287        let p = TransportParams {
288            original_destination_connection_id: Some(Cid::new(&[1, 2, 3, 4, 5, 6, 7, 8])),
289            max_idle_timeout: 30_000,
290            stateless_reset_token: Some([9; 16]),
291            max_udp_payload_size: 1452,
292            initial_max_data: 1 << 20,
293            initial_max_stream_data_bidi_local: 1 << 16,
294            initial_max_stream_data_bidi_remote: 1 << 16,
295            initial_max_stream_data_uni: 1 << 16,
296            initial_max_streams_bidi: 100,
297            initial_max_streams_uni: 3,
298            ack_delay_exponent: 5,
299            max_ack_delay: 20,
300            disable_active_migration: true,
301            preferred_address: Some(PreferredAddress {
302                v4: [127, 0, 0, 1, 1, 187],
303                v6: [0; 18],
304                cid: Cid::new(&[7; 8]),
305                reset_token: [3; 16],
306            }),
307            active_connection_id_limit: 4,
308            initial_source_connection_id: Some(Cid::new(&[5; 8])),
309            retry_source_connection_id: Some(Cid::new(&[6; 8])),
310            version_information: Some((crate::VERSION_2, vec![crate::VERSION_2, crate::VERSION_1])),
311        };
312        let mut b = Vec::new();
313        p.encode(&mut b);
314        assert_eq!(TransportParams::decode(&b, true).unwrap(), p);
315        // A client may not send server-only parameters.
316        assert_eq!(TransportParams::decode(&b, false).unwrap_err().code, TRANSPORT_PARAMETER_ERROR);
317
318        let bad = |extra: &[u8]| {
319            let mut b = vec![0x0f, 0x01, 0x07];
320            b.extend_from_slice(extra);
321            TransportParams::decode(&b, false)
322        };
323        assert!(bad(&[]).is_ok());
324        assert!(bad(&[0x03, 0x02, 0x44, 0xaf]).is_err()); // 1199
325        assert!(bad(&[0x0a, 0x01, 21]).is_err());
326        assert!(bad(&[0x0b, 0x04, 0x80, 0, 0x40, 0]).is_err());
327        assert!(bad(&[0x0e, 0x01, 1]).is_err());
328        assert!(bad(&[0x01, 0x02, 0x40]).is_err()); // truncated value
329        assert!(bad(&[0x01, 0x02, 5, 5]).is_err()); // trailing bytes in an integer
330        assert!(bad(&[0x0f, 0x00]).is_err()); // duplicate
331        assert!(bad(&[0x0c, 0x01, 0]).is_err());
332        assert!(bad(&[0x7f, 0xff, 0x02, 1, 2]).is_ok()); // unknown ignored
333        assert!(TransportParams::decode(&[], false).is_err()); // no initial_source_connection_id
334        assert!(bad(&[0x11, 0x05, 0, 0, 0, 1, 2]).is_err()); // version_information not 4-aligned
335        assert!(bad(&[0x11, 0x04, 0, 0, 0, 0]).is_err()); // chosen version 0
336    }
337}