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