Skip to main content

ant_quic/
transport_error.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8use std::fmt;
9
10use bytes::{Buf, BufMut};
11
12use crate::{
13    coding::{self, BufExt, BufMutExt},
14    frame,
15};
16
17/// Transport-level errors occur when a peer violates the protocol specification
18#[derive(Debug, Clone, Eq, PartialEq)]
19pub struct Error {
20    /// Type of error
21    pub code: Code,
22    /// Frame type that triggered the error
23    pub frame: Option<frame::FrameType>,
24    /// Human-readable explanation of the reason
25    pub reason: String,
26}
27
28impl fmt::Display for Error {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        self.code.fmt(f)?;
31        if let Some(frame) = self.frame {
32            write!(f, " in {frame}")?;
33        }
34        if !self.reason.is_empty() {
35            write!(f, ": {}", self.reason)?;
36        }
37        Ok(())
38    }
39}
40
41impl std::error::Error for Error {}
42
43impl From<Code> for Error {
44    fn from(x: Code) -> Self {
45        Self {
46            code: x,
47            frame: None,
48            reason: "".to_string(),
49        }
50    }
51}
52
53/// Transport-level error code
54#[derive(Copy, Clone, Eq, PartialEq)]
55pub struct Code(u64);
56
57impl Code {
58    /// Create QUIC error code from TLS alert code
59    pub fn crypto(code: u8) -> Self {
60        Self(0x100 | u64::from(code))
61    }
62}
63
64impl coding::Codec for Code {
65    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
66        Ok(Self(buf.get_var()?))
67    }
68    fn encode<B: BufMut>(&self, buf: &mut B) {
69        if buf.write_var(self.0).is_err() {
70            tracing::error!("VarInt overflow while encoding TransportErrorCode");
71            debug_assert!(false, "VarInt overflow while encoding TransportErrorCode");
72        }
73    }
74}
75
76impl From<Code> for u64 {
77    fn from(x: Code) -> Self {
78        x.0
79    }
80}
81
82macro_rules! errors {
83    {$($name:ident($val:expr_2021) $desc:expr_2021;)*} => {
84        #[allow(non_snake_case, unused)]
85        impl Error {
86            $(
87            pub(crate) fn $name<T>(reason: T) -> Self where T: Into<String> {
88                Self {
89                    code: Code::$name,
90                    frame: None,
91                    reason: reason.into(),
92                }
93            }
94            )*
95        }
96
97        impl Code {
98            $(#[doc = $desc] pub const $name: Self = Code($val);)*
99        }
100
101        impl fmt::Debug for Code {
102            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103                match self.0 {
104                    $($val => f.write_str(stringify!($name)),)*
105                    x if (0x100..0x200).contains(&x) => write!(f, "Code::crypto({:02x})", self.0 as u8),
106                    _ => write!(f, "Code({:x})", self.0),
107                }
108            }
109        }
110
111        impl fmt::Display for Code {
112            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113                match self.0 {
114                    $($val => f.write_str($desc),)*
115                    // We're trying to be abstract over the crypto protocol, so human-readable descriptions here is tricky.
116                    _ if self.0 >= 0x100 && self.0 < 0x200 => write!(f, "the cryptographic handshake failed: error {}", self.0 & 0xFF),
117                    _ => f.write_str("unknown error"),
118                }
119            }
120        }
121    }
122}
123
124errors! {
125    NO_ERROR(0x0) "the connection is being closed abruptly in the absence of any error";
126    INTERNAL_ERROR(0x1) "the endpoint encountered an internal error and cannot continue with the connection";
127    CONNECTION_REFUSED(0x2) "the server refused to accept a new connection";
128    FLOW_CONTROL_ERROR(0x3) "received more data than permitted in advertised data limits";
129    STREAM_LIMIT_ERROR(0x4) "received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type";
130    STREAM_STATE_ERROR(0x5) "received a frame for a stream that was not in a state that permitted that frame";
131    FINAL_SIZE_ERROR(0x6) "received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established";
132    FRAME_ENCODING_ERROR(0x7) "received a frame that was badly formatted";
133    TRANSPORT_PARAMETER_ERROR(0x8) "received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error";
134    CONNECTION_ID_LIMIT_ERROR(0x9) "the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit";
135    PROTOCOL_VIOLATION(0xA) "detected an error with protocol compliance that was not covered by more specific error codes";
136    INVALID_TOKEN(0xB) "received an invalid Retry Token in a client Initial";
137    APPLICATION_ERROR(0xC) "the application or application protocol caused the connection to be closed during the handshake";
138    CRYPTO_BUFFER_EXCEEDED(0xD) "received more data in CRYPTO frames than can be buffered";
139    KEY_UPDATE_ERROR(0xE) "key update error";
140    AEAD_LIMIT_REACHED(0xF) "the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm";
141    NO_VIABLE_PATH(0x10) "no viable network path exists";
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use crate::coding::Codec;
148    use bytes::BytesMut;
149
150    #[test]
151    fn transport_error_display() {
152        let err = Error::from(Code::PROTOCOL_VIOLATION);
153        let display = format!("{err}");
154        assert!(!display.is_empty());
155    }
156
157    #[test]
158    fn transport_error_display_with_frame() {
159        // Use the raw FrameType constructor with a known frame type value
160        let err = Error {
161            code: Code::INTERNAL_ERROR,
162            frame: Some(frame::FrameType(0x00)), // PADDING frame
163            reason: "test reason".into(),
164        };
165        let display = format!("{err}");
166        assert!(display.contains("test reason"));
167    }
168
169    #[test]
170    fn transport_error_from_code() {
171        let err: Error = Code::NO_ERROR.into();
172        assert_eq!(err.code, Code::NO_ERROR);
173        assert_eq!(err.frame, None);
174        assert!(err.reason.is_empty());
175    }
176
177    #[test]
178    fn transport_error_is_error() {
179        let err = Error::from(Code::FLOW_CONTROL_ERROR);
180        let err_ref: &dyn std::error::Error = &err;
181        assert!(!format!("{err_ref}").is_empty());
182    }
183
184    #[test]
185    fn transport_error_convenience_constructors() {
186        // The errors! macro generates pub(crate) functions with UPPER_CASE names
187        let e = Error::NO_ERROR(String::new());
188        assert_eq!(e.code, Code::NO_ERROR);
189
190        let e = Error::INTERNAL_ERROR("oops");
191        assert_eq!(e.code, Code::INTERNAL_ERROR);
192        assert_eq!(e.reason, "oops");
193
194        let e = Error::CONNECTION_REFUSED("refused");
195        assert_eq!(e.code, Code::CONNECTION_REFUSED);
196
197        let e = Error::FLOW_CONTROL_ERROR("too much data");
198        assert_eq!(e.code, Code::FLOW_CONTROL_ERROR);
199
200        let e = Error::STREAM_LIMIT_ERROR("too many streams");
201        assert_eq!(e.code, Code::STREAM_LIMIT_ERROR);
202
203        let e = Error::STREAM_STATE_ERROR("bad state");
204        assert_eq!(e.code, Code::STREAM_STATE_ERROR);
205
206        let e = Error::FINAL_SIZE_ERROR("size mismatch");
207        assert_eq!(e.code, Code::FINAL_SIZE_ERROR);
208
209        let e = Error::FRAME_ENCODING_ERROR("bad frame");
210        assert_eq!(e.code, Code::FRAME_ENCODING_ERROR);
211
212        let e = Error::TRANSPORT_PARAMETER_ERROR("bad params");
213        assert_eq!(e.code, Code::TRANSPORT_PARAMETER_ERROR);
214
215        let e = Error::CONNECTION_ID_LIMIT_ERROR("too many CIDs");
216        assert_eq!(e.code, Code::CONNECTION_ID_LIMIT_ERROR);
217
218        let e = Error::PROTOCOL_VIOLATION("violation");
219        assert_eq!(e.code, Code::PROTOCOL_VIOLATION);
220
221        let e = Error::INVALID_TOKEN("bad token");
222        assert_eq!(e.code, Code::INVALID_TOKEN);
223
224        let e = Error::APPLICATION_ERROR("app error");
225        assert_eq!(e.code, Code::APPLICATION_ERROR);
226
227        let e = Error::CRYPTO_BUFFER_EXCEEDED("buffer full");
228        assert_eq!(e.code, Code::CRYPTO_BUFFER_EXCEEDED);
229
230        let e = Error::KEY_UPDATE_ERROR("key update");
231        assert_eq!(e.code, Code::KEY_UPDATE_ERROR);
232
233        let e = Error::AEAD_LIMIT_REACHED("aead limit");
234        assert_eq!(e.code, Code::AEAD_LIMIT_REACHED);
235
236        let e = Error::NO_VIABLE_PATH("no path");
237        assert_eq!(e.code, Code::NO_VIABLE_PATH);
238    }
239
240    #[test]
241    fn code_crypto_constructor() {
242        let code = Code::crypto(0x2C);
243        let val: u64 = code.into();
244        assert_eq!(val, 0x12C); // 0x100 | 0x2C
245    }
246
247    #[test]
248    fn code_from_u64_and_back() {
249        let code = Code::NO_ERROR;
250        let val: u64 = code.into();
251        assert_eq!(val, 0x0);
252
253        let code = Code::NO_VIABLE_PATH;
254        let val: u64 = code.into();
255        assert_eq!(val, 0x10);
256    }
257
258    #[test]
259    fn code_debug_no_error() {
260        assert_eq!(format!("{:?}", Code::NO_ERROR), "NO_ERROR");
261    }
262
263    #[test]
264    fn code_debug_crypto() {
265        assert_eq!(format!("{:?}", Code::crypto(0x2C)), "Code::crypto(2c)");
266    }
267
268    #[test]
269    fn code_debug_unknown() {
270        assert_eq!(format!("{:?}", Code(0x1234)), "Code(1234)");
271    }
272
273    #[test]
274    fn code_display_no_error() {
275        assert_eq!(
276            format!("{}", Code::NO_ERROR),
277            "the connection is being closed abruptly in the absence of any error"
278        );
279    }
280
281    #[test]
282    fn code_display_crypto() {
283        let display = format!("{}", Code::crypto(0x2C));
284        assert!(display.contains("cryptographic handshake"));
285    }
286
287    #[test]
288    fn code_display_unknown() {
289        assert_eq!(format!("{}", Code(0x1234)), "unknown error");
290    }
291
292    #[test]
293    fn transport_error_code_encoding_roundtrip() {
294        let codes = [
295            Code::NO_ERROR,
296            Code::PROTOCOL_VIOLATION,
297            Code::INTERNAL_ERROR,
298            Code::crypto(0x2C),
299            Code(0x1234),
300        ];
301
302        for code in &codes {
303            let mut buf = BytesMut::new();
304            code.encode(&mut buf);
305            let mut read = buf.freeze();
306            let decoded = Code::decode(&mut read).unwrap();
307            assert_eq!(&decoded, code);
308        }
309    }
310
311    #[test]
312    fn transport_error_code_equality() {
313        assert_eq!(Code::NO_ERROR, Code::NO_ERROR);
314        assert_ne!(Code::NO_ERROR, Code::INTERNAL_ERROR);
315        assert_eq!(Code::crypto(0x2C), Code::crypto(0x2C));
316        assert_ne!(Code::crypto(0x2C), Code::crypto(0x2D));
317    }
318
319    #[test]
320    fn transport_error_equality() {
321        let e1 = Error::INTERNAL_ERROR("test");
322        let e2 = Error::INTERNAL_ERROR("test");
323        assert_eq!(e1, e2);
324
325        let e3 = Error::INTERNAL_ERROR("different");
326        assert_ne!(e1, e3);
327    }
328
329    #[test]
330    fn transport_error_clone() {
331        let e = Error::PROTOCOL_VIOLATION("clone test");
332        let cloned = e.clone();
333        assert_eq!(e, cloned);
334    }
335
336    #[test]
337    fn transport_error_debug_format() {
338        let e = Error::from(Code::INTERNAL_ERROR);
339        let debug = format!("{e:?}");
340        assert!(debug.contains("INTERNAL_ERROR"));
341    }
342
343    #[test]
344    fn transport_error_debug_with_frame_and_reason() {
345        let e = Error {
346            code: Code::PROTOCOL_VIOLATION,
347            frame: Some(frame::FrameType(0x01)),
348            reason: "bad thing".into(),
349        };
350        let debug = format!("{e:?}");
351        assert!(debug.contains("PROTOCOL_VIOLATION"));
352        assert!(debug.contains("bad thing"));
353    }
354
355    #[test]
356    fn code_debug_all_variants() {
357        // Every standard code should have a named Debug representation
358        assert_eq!(format!("{:?}", Code::NO_ERROR), "NO_ERROR");
359        assert_eq!(format!("{:?}", Code::INTERNAL_ERROR), "INTERNAL_ERROR");
360        assert_eq!(
361            format!("{:?}", Code::CONNECTION_REFUSED),
362            "CONNECTION_REFUSED"
363        );
364        assert_eq!(
365            format!("{:?}", Code::FLOW_CONTROL_ERROR),
366            "FLOW_CONTROL_ERROR"
367        );
368        assert_eq!(
369            format!("{:?}", Code::STREAM_LIMIT_ERROR),
370            "STREAM_LIMIT_ERROR"
371        );
372        assert_eq!(
373            format!("{:?}", Code::STREAM_STATE_ERROR),
374            "STREAM_STATE_ERROR"
375        );
376        assert_eq!(format!("{:?}", Code::FINAL_SIZE_ERROR), "FINAL_SIZE_ERROR");
377        assert_eq!(
378            format!("{:?}", Code::FRAME_ENCODING_ERROR),
379            "FRAME_ENCODING_ERROR"
380        );
381        assert_eq!(
382            format!("{:?}", Code::TRANSPORT_PARAMETER_ERROR),
383            "TRANSPORT_PARAMETER_ERROR"
384        );
385        assert_eq!(
386            format!("{:?}", Code::CONNECTION_ID_LIMIT_ERROR),
387            "CONNECTION_ID_LIMIT_ERROR"
388        );
389        assert_eq!(
390            format!("{:?}", Code::PROTOCOL_VIOLATION),
391            "PROTOCOL_VIOLATION"
392        );
393        assert_eq!(format!("{:?}", Code::INVALID_TOKEN), "INVALID_TOKEN");
394        assert_eq!(
395            format!("{:?}", Code::APPLICATION_ERROR),
396            "APPLICATION_ERROR"
397        );
398        assert_eq!(
399            format!("{:?}", Code::CRYPTO_BUFFER_EXCEEDED),
400            "CRYPTO_BUFFER_EXCEEDED"
401        );
402        assert_eq!(format!("{:?}", Code::KEY_UPDATE_ERROR), "KEY_UPDATE_ERROR");
403        assert_eq!(
404            format!("{:?}", Code::AEAD_LIMIT_REACHED),
405            "AEAD_LIMIT_REACHED"
406        );
407        assert_eq!(format!("{:?}", Code::NO_VIABLE_PATH), "NO_VIABLE_PATH");
408    }
409
410    #[test]
411    fn code_display_all_variants() {
412        // Every standard code should display a human-readable description
413        let displays = [
414            (Code::NO_ERROR, "connection"),
415            (Code::INTERNAL_ERROR, "internal error"),
416            (Code::CONNECTION_REFUSED, "refused"),
417            (Code::FLOW_CONTROL_ERROR, "data limits"),
418            (Code::STREAM_LIMIT_ERROR, "stream limit"),
419            (Code::STREAM_STATE_ERROR, "stream"),
420            (Code::FINAL_SIZE_ERROR, "final size"),
421            (Code::FRAME_ENCODING_ERROR, "badly formatted"),
422            (Code::TRANSPORT_PARAMETER_ERROR, "transport parameters"),
423            (Code::CONNECTION_ID_LIMIT_ERROR, "connection IDs"),
424            (Code::PROTOCOL_VIOLATION, "protocol compliance"),
425            (Code::INVALID_TOKEN, "invalid Retry Token"),
426            (Code::APPLICATION_ERROR, "application"),
427            (Code::CRYPTO_BUFFER_EXCEEDED, "CRYPTO frames"),
428            (Code::KEY_UPDATE_ERROR, "key update"),
429            (Code::AEAD_LIMIT_REACHED, "AEAD"),
430            (Code::NO_VIABLE_PATH, "no viable network path"),
431        ];
432
433        for (code, expected_fragment) in &displays {
434            let display = format!("{code}");
435            assert!(
436                display.contains(expected_fragment),
437                "Code {code:?} display '{display}' should contain '{expected_fragment}'"
438            );
439        }
440    }
441}