s2n_quic_core/transport/
error.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#![forbid(unsafe_code)]
5
6use crate::{
7    crypto::tls,
8    event::metrics::aggregate,
9    frame::ConnectionClose,
10    varint::{VarInt, VarIntError},
11};
12use core::fmt;
13use s2n_codec::DecoderError;
14
15//= https://www.rfc-editor.org/rfc/rfc9000#section-20
16//# QUIC transport error codes and application error codes are 62-bit
17//# unsigned integers.
18
19/// Transport Errors are 62-bit unsigned integer values indicating a QUIC transport error
20/// has occurred, as defined in [QUIC Transport RFC](https://www.rfc-editor.org/rfc/rfc9000.html#section-20).
21#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
22pub struct Error {
23    /// A 62-bit unsigned integer value indicating the error that occurred
24    pub code: Code,
25    /// If this error was caused by a particular QUIC frame, `frame_type` will contain
26    /// the Frame Type as defined in [QUIC Transport RFC](https://www.rfc-editor.org/rfc/rfc9000.html#name-frame-types-and-formats).
27    pub frame_type: VarInt,
28    /// Additional information about the error that occurred
29    pub reason: &'static str,
30}
31
32#[cfg(feature = "std")]
33impl std::error::Error for Error {}
34
35impl Error {
36    /// Creates a new `Error`
37    pub const fn new(code: VarInt) -> Self {
38        Self {
39            code: Code::new(code),
40            reason: "",
41            frame_type: VarInt::from_u8(0),
42        }
43    }
44
45    /// Updates the `Error` with the specified `frame_type`
46    #[must_use]
47    pub const fn with_frame_type(mut self, frame_type: VarInt) -> Self {
48        self.frame_type = frame_type;
49        self
50    }
51
52    /// Updates the `Error` with the specified `reason`
53    #[must_use]
54    pub const fn with_reason(mut self, reason: &'static str) -> Self {
55        self.reason = reason;
56        self
57    }
58}
59
60impl fmt::Display for Error {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        if !self.reason.is_empty() {
63            self.reason.fmt(f)
64        } else {
65            self.code.fmt(f)
66        }
67    }
68}
69
70impl fmt::Debug for Error {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        let mut d = f.debug_struct("transport::Error");
73
74        d.field("code", &self.code.as_u64());
75
76        if let Some(description) = self.description() {
77            d.field("description", &description);
78        }
79
80        if !self.reason.is_empty() {
81            d.field("reason", &self.reason);
82        }
83
84        d.field("frame_type", &self.frame_type);
85
86        d.finish()
87    }
88}
89
90impl From<Error> for ConnectionClose<'_> {
91    fn from(error: Error) -> Self {
92        ConnectionClose {
93            error_code: error.code.0,
94            frame_type: Some(error.frame_type),
95            reason: Some(error.reason.as_bytes()).filter(|reason| !reason.is_empty()),
96        }
97    }
98}
99
100#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
101pub struct Code(VarInt);
102
103impl Code {
104    #[doc(hidden)]
105    pub const fn new(code: VarInt) -> Self {
106        Self(code)
107    }
108
109    #[inline]
110    pub fn as_u64(self) -> u64 {
111        self.0.as_u64()
112    }
113
114    #[inline]
115    pub fn as_varint(self) -> VarInt {
116        self.0
117    }
118}
119
120impl fmt::Debug for Code {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        let mut d = f.debug_tuple("transport::error::Code");
123
124        d.field(&self.0);
125
126        if let Some(desc) = self.description() {
127            d.field(&desc);
128        }
129
130        d.finish()
131    }
132}
133
134impl fmt::Display for Code {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        if let Some(description) = self.description() {
137            description.fmt(f)
138        } else {
139            write!(f, "error({:x?})", self.as_u64())
140        }
141    }
142}
143
144//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
145//# A value of 0 (equivalent to the mention
146//# of the PADDING frame) is used when the frame type is unknown.
147const UNKNOWN_FRAME_TYPE: u32 = 0;
148
149//= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
150//# CRYPTO_ERROR (0x0100-0x01ff):  The cryptographic handshake failed.  A
151//#    range of 256 values is reserved for carrying error codes specific
152//#    to the cryptographic handshake that is used.  Codes for errors
153//#    occurring when TLS is used for the cryptographic handshake are
154//#    described in Section 4.8 of [QUIC-TLS].
155const CRYPTO_ERROR_RANGE: core::ops::RangeInclusive<u64> = 0x100..=0x1ff;
156
157/// Internal convenience macro for defining standard error codes
158macro_rules! impl_errors {
159    ($($(#[doc = $doc:expr])* $name:ident = $code:literal $(.with_frame_type($frame:expr))?),* $(,)?) => {
160        impl Code {
161            $(
162                $(#[doc = $doc])*
163                pub const $name: Self = Self::new(VarInt::from_u32($code));
164            )*
165
166            pub fn description(&self) -> Option<&'static str> {
167                match self.0.as_u64() {
168                    $(
169                        $code => Some(stringify!($name)),
170                    )*
171                    code if CRYPTO_ERROR_RANGE.contains(&code) => tls::Error::new(code as u8).description(),
172                    _ => None
173                }
174            }
175        }
176
177        impl aggregate::AsVariant for Code {
178            const VARIANTS: &'static [aggregate::info::Variant] = &{
179                use aggregate::info::{Variant, Str};
180
181                const fn count(_v: u64) -> usize {
182                    1
183                }
184
185                const QUIC_VARIANTS: usize = 0 $( + count($code))*;
186
187                const TLS: &'static [Variant] = tls::Error::VARIANTS;
188
189                let mut array = [
190                    Variant { name: Str::new("\0"), id: 0 };
191                    QUIC_VARIANTS + TLS.len() + 1
192                ];
193
194                let mut id = 0;
195
196                $(
197                    array[id] = Variant {
198                        name: Str::new(concat!("QUIC_", stringify!($name), "\0")),
199                        id,
200                    };
201                    id += 1;
202                )*
203
204                let mut tls_idx = 0;
205                while tls_idx < TLS.len() {
206                    let variant = TLS[tls_idx];
207                    array[id] = Variant {
208                        name: variant.name,
209                        id,
210                    };
211                    id += 1;
212                    tls_idx += 1;
213                }
214
215                array[id] = Variant {
216                    name: Str::new("QUIC_UNKNOWN_ERROR\0"),
217                    id,
218                };
219
220                array
221            };
222
223            #[inline]
224            fn variant_idx(&self) -> usize {
225                let mut idx = 0;
226                let code = self.0.as_u64();
227
228                $(
229                    if code == $code {
230                        return idx;
231                    }
232                    idx += 1;
233                )*
234
235                if CRYPTO_ERROR_RANGE.contains(&code) {
236                    return tls::Error::new(code as _).variant_idx() + idx;
237                }
238
239                idx + tls::Error::VARIANTS.len()
240            }
241        }
242
243        impl Error {
244            $(
245                $(#[doc = $doc])*
246                pub const $name: Self = Self::new(VarInt::from_u32($code))
247                    $( .with_frame_type(VarInt::from_u32($frame)) )?;
248            )*
249
250            pub fn description(&self) -> Option<&'static str> {
251                self.code.description()
252            }
253        }
254
255        #[test]
256        fn description_test() {
257            $(
258                assert_eq!(&Error::$name.to_string(), stringify!($name));
259            )*
260            assert_eq!(&Error::from(tls::Error::DECODE_ERROR).to_string(), "DECODE_ERROR");
261        }
262
263        #[test]
264        #[cfg_attr(miri, ignore)]
265        fn variants_test() {
266            use aggregate::AsVariant;
267            insta::assert_debug_snapshot!(Code::VARIANTS);
268
269            let mut seen = std::collections::HashSet::new();
270            for variant in Code::VARIANTS {
271                assert!(seen.insert(variant.id));
272            }
273        }
274    };
275}
276
277impl_errors! {
278    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
279    //# NO_ERROR (0x00):  An endpoint uses this with CONNECTION_CLOSE to
280    //#    signal that the connection is being closed abruptly in the absence
281    //#    of any error.
282    /// An endpoint uses this with CONNECTION_CLOSE to
283    /// signal that the connection is being closed abruptly in the absence
284    /// of any error
285    NO_ERROR = 0x0.with_frame_type(UNKNOWN_FRAME_TYPE),
286
287    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
288    //# INTERNAL_ERROR (0x01):  The endpoint encountered an internal error and
289    //#    cannot continue with the connection.
290    /// The endpoint encountered an internal error
291    /// and cannot continue with the connection.
292    INTERNAL_ERROR = 0x1.with_frame_type(UNKNOWN_FRAME_TYPE),
293
294    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
295    //# CONNECTION_REFUSED (0x02):  The server refused to accept a new
296    //#  connection.
297    /// The server refused to accept a new
298    ///  connection.
299    CONNECTION_REFUSED = 0x2.with_frame_type(UNKNOWN_FRAME_TYPE),
300
301    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
302    //# FLOW_CONTROL_ERROR (0x03):  An endpoint received more data than it
303    //#    permitted in its advertised data limits; see Section 4.
304    /// An endpoint received more data than it
305    /// permitted in its advertised data limits.
306    FLOW_CONTROL_ERROR = 0x3.with_frame_type(UNKNOWN_FRAME_TYPE),
307
308    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
309    //# STREAM_LIMIT_ERROR (0x04):  An endpoint received a frame for a stream
310    //#    identifier that exceeded its advertised stream limit for the
311    //#    corresponding stream type.
312    /// An endpoint received a frame for a stream
313    /// identifier that exceeded its advertised stream limit for the
314    /// corresponding stream type.
315    STREAM_LIMIT_ERROR = 0x4.with_frame_type(UNKNOWN_FRAME_TYPE),
316
317    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
318    //# STREAM_STATE_ERROR (0x05):  An endpoint received a frame for a stream
319    //#    that was not in a state that permitted that frame; see Section 3.
320    /// An endpoint received a frame for a stream
321    /// that was not in a state that permitted that frame.
322    STREAM_STATE_ERROR = 0x5.with_frame_type(UNKNOWN_FRAME_TYPE),
323
324    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
325    //# FINAL_SIZE_ERROR (0x06):  (1) An endpoint received a STREAM frame
326    //#    containing data that exceeded the previously established final
327    //#    size, (2) an endpoint received a STREAM frame or a RESET_STREAM
328    //#    frame containing a final size that was lower than the size of
329    //#    stream data that was already received, or (3) an endpoint received
330    //#    a STREAM frame or a RESET_STREAM frame containing a different
331    //#    final size to the one already established.
332    /// An endpoint received a STREAM frame
333    /// containing data that exceeded the previously established final
334    /// size.
335    FINAL_SIZE_ERROR = 0x6.with_frame_type(UNKNOWN_FRAME_TYPE),
336
337    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
338    //# FRAME_ENCODING_ERROR (0x07):  An endpoint received a frame that was
339    //#   badly formatted -- for instance, a frame of an unknown type or an
340    //#   ACK frame that has more acknowledgment ranges than the remainder
341    //#   of the packet could carry.
342    /// An endpoint received a frame that was
343    /// badly formatted.
344    FRAME_ENCODING_ERROR = 0x7.with_frame_type(UNKNOWN_FRAME_TYPE),
345
346    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
347    //# TRANSPORT_PARAMETER_ERROR (0x08):  An endpoint received transport
348    //#    parameters that were badly formatted, included an invalid value,
349    //#    omitted a mandatory transport parameter, included a forbidden
350    //#    transport parameter, or were otherwise in error.
351    /// An endpoint received transport
352    /// parameters that were badly formatted.
353    TRANSPORT_PARAMETER_ERROR = 0x8.with_frame_type(UNKNOWN_FRAME_TYPE),
354
355    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
356    //# CONNECTION_ID_LIMIT_ERROR (0x09):  The number of connection IDs
357    //#    provided by the peer exceeds the advertised
358    //#    active_connection_id_limit.
359    /// The number of connection IDs
360    /// provided by the peer exceeds the advertised
361    /// active_connection_id_limit.
362    CONNECTION_ID_LIMIT_ERROR = 0x9.with_frame_type(UNKNOWN_FRAME_TYPE),
363
364    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
365    //# PROTOCOL_VIOLATION (0x0a):  An endpoint detected an error with
366    //#    protocol compliance that was not covered by more specific error
367    //#    codes.
368    /// An endpoint detected an error with
369    /// protocol compliance that was not covered by more specific error
370    /// codes.
371    PROTOCOL_VIOLATION = 0xA.with_frame_type(UNKNOWN_FRAME_TYPE),
372
373    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
374    //# INVALID_TOKEN (0x0b):  A server received a client Initial that
375    //#     contained an invalid Token field.
376    /// A server received a client Initial that
377    /// contained an invalid Token field.
378    INVALID_TOKEN = 0xB.with_frame_type(UNKNOWN_FRAME_TYPE),
379
380    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
381    //# APPLICATION_ERROR (0x0c):  The application or application protocol
382    //#    caused the connection to be closed.
383    /// The application or application protocol
384    /// caused the connection to be closed.
385    APPLICATION_ERROR = 0xC,
386
387    //= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
388    //# CRYPTO_BUFFER_EXCEEDED (0x0d):  An endpoint has received more data in
389    //#    CRYPTO frames than it can buffer.
390    /// An endpoint has received more data in
391    /// CRYPTO frames than it can buffer.
392    CRYPTO_BUFFER_EXCEEDED = 0xD.with_frame_type(UNKNOWN_FRAME_TYPE),
393
394    //# KEY_UPDATE_ERROR (0x0e):  An endpoint detected errors in performing
395    //#    key updates; see Section 6 of [QUIC-TLS].
396    /// An endpoint detected errors in performing
397    /// key updates.
398    KEY_UPDATE_ERROR = 0xe.with_frame_type(UNKNOWN_FRAME_TYPE),
399
400    //# AEAD_LIMIT_REACHED (0x0f):  An endpoint has reached the
401    //#    confidentiality or integrity limit for the AEAD algorithm used by
402    //#    the given connection.
403    /// An endpoint has reached the
404    /// confidentiality or integrity limit for the AEAD algorithm used by
405    /// the given connection.
406    AEAD_LIMIT_REACHED = 0xf.with_frame_type(UNKNOWN_FRAME_TYPE),
407}
408
409//= https://www.rfc-editor.org/rfc/rfc9000#section-20.1
410//# CRYPTO_ERROR (0x0100-0x01ff):  The cryptographic handshake failed.  A
411//#   range of 256 values is reserved for carrying error codes specific
412//#   to the cryptographic handshake that is used.  Codes for errors
413//#   occurring when TLS is used for the cryptographic handshake are
414//#   described in Section 4.8 of [QUIC-TLS].
415
416impl Error {
417    /// Creates a crypto-level `TransportError` from a TLS alert code.
418    #[inline]
419    pub const fn crypto_error(code: u8) -> Self {
420        Self::new(VarInt::from_u16(0x100 | (code as u16)))
421            .with_frame_type(VarInt::from_u32(UNKNOWN_FRAME_TYPE))
422    }
423
424    /// If the [`Error`] contains a [`tls::Error`], it is returned
425    #[inline]
426    pub fn try_into_tls_error(self) -> Option<tls::Error> {
427        let code = self.code.as_u64();
428        if (0x100..=0x1ff).contains(&code) {
429            Some(tls::Error::new(code as u8).with_reason(self.reason))
430        } else {
431            None
432        }
433    }
434}
435
436//= https://www.rfc-editor.org/rfc/rfc9000#section-20.2
437//# The management of application error codes is left to application
438//# protocols.  Application protocol error codes are used for the
439//# RESET_STREAM frame (Section 19.4), the STOP_SENDING frame
440//# (Section 19.5), and the CONNECTION_CLOSE frame with a type of 0x1d
441//# (Section 19.19).
442
443impl Error {
444    /// Creates an application-level `Error`
445    #[inline]
446    pub const fn application_error(code: VarInt) -> Self {
447        // Application errors set `frame_type` to `None`
448        Self::new(code)
449    }
450}
451
452/// Implements conversion from decoder errors
453impl From<DecoderError> for Error {
454    fn from(decoder_error: DecoderError) -> Self {
455        match decoder_error {
456            DecoderError::InvariantViolation(reason) => {
457                Self::PROTOCOL_VIOLATION.with_reason(reason)
458            }
459            _ => Self::PROTOCOL_VIOLATION.with_reason("malformed packet"),
460        }
461    }
462}
463
464/// Implements conversion from TLS errors
465/// See `Error::crypto_error` for more details
466impl From<tls::Error> for Error {
467    fn from(tls_error: tls::Error) -> Self {
468        Self::crypto_error(tls_error.code).with_reason(tls_error.reason)
469    }
470}
471
472/// Implements conversion from crypto errors
473/// See `Error::crypto_error` for more details
474impl From<VarIntError> for Error {
475    fn from(_: VarIntError) -> Self {
476        Self::INTERNAL_ERROR.with_reason("varint encoding limit exceeded")
477    }
478}