noq_proto/
transport_error.rs

1use std::{fmt, sync::Arc};
2
3use bytes::{Buf, BufMut};
4
5use crate::{
6    VarInt,
7    coding::{self, BufExt, BufMutExt, Decodable, Encodable},
8    frame::MaybeFrame,
9};
10
11/// Transport-level errors occur when a peer violates the protocol specification
12///
13/// # Note
14///
15/// The `PartialEq` implementation for this type performs comparison on the `code` field only
16#[derive(Debug, Clone)]
17#[non_exhaustive]
18pub struct Error {
19    /// Type of error
20    pub code: Code,
21    /// Frame type that triggered the error
22    pub frame: MaybeFrame,
23    /// Human-readable explanation of the reason
24    pub reason: String,
25    /// An underlying crypto (e.g. TLS) layer error
26    pub crypto: Option<Arc<dyn std::error::Error + Send + Sync>>,
27}
28
29impl Error {
30    /// Construct an error with a code and a reason
31    pub fn new(code: Code, reason: String) -> Self {
32        Self {
33            code,
34            frame: MaybeFrame::None,
35            reason,
36            crypto: None,
37        }
38    }
39}
40
41impl PartialEq for Error {
42    fn eq(&self, other: &Self) -> bool {
43        self.code == other.code
44    }
45}
46
47impl Eq for Error {}
48
49impl fmt::Display for Error {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        self.code.fmt(f)?;
52        if self.frame != MaybeFrame::None {
53            write!(f, " in {}", self.frame)?;
54        }
55        if !self.reason.is_empty() {
56            write!(f, ": {}", self.reason)?;
57        }
58        Ok(())
59    }
60}
61
62impl std::error::Error for Error {}
63
64/// Transport-level error code
65#[derive(Copy, Clone, Eq, PartialEq)]
66#[cfg_attr(test, derive(test_strategy::Arbitrary))]
67pub struct Code(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
68
69impl Code {
70    /// Create QUIC error code from TLS alert code
71    pub fn crypto(code: u8) -> Self {
72        Self(0x100 | u64::from(code))
73    }
74}
75
76impl Decodable for Code {
77    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
78        Ok(Self(buf.get_var()?))
79    }
80}
81
82impl Encodable for Code {
83    fn encode<B: BufMut>(&self, buf: &mut B) {
84        buf.write_var(self.0)
85    }
86}
87
88impl From<Code> for u64 {
89    fn from(x: Code) -> Self {
90        x.0
91    }
92}
93
94impl From<VarInt> for Code {
95    fn from(value: VarInt) -> Self {
96        Self(value.0)
97    }
98}
99
100impl From<Code> for VarInt {
101    fn from(value: Code) -> Self {
102        Self(value.0)
103    }
104}
105
106macro_rules! errors {
107    {$($name:ident($val:expr) $desc:expr;)*} => {
108        #[allow(non_snake_case, unused)]
109        impl Error {
110            $(
111            pub(crate) fn $name<T>(reason: T) -> Self where T: Into<String> {
112                Self::new(Code::$name, reason.into())
113            }
114            )*
115        }
116
117        impl Code {
118            $(#[doc = $desc] pub const $name: Self = Code($val);)*
119        }
120
121        impl fmt::Debug for Code {
122            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123                match self.0 {
124                    $($val => f.write_str(stringify!($name)),)*
125                    x if (0x100..0x200).contains(&x) => write!(f, "Code::crypto({:02x})", self.0 as u8),
126                    _ => write!(f, "Code({:x})", self.0),
127                }
128            }
129        }
130
131        impl fmt::Display for Code {
132            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133                match self.0 {
134                    $($val => f.write_str($desc),)*
135                    // We're trying to be abstract over the crypto protocol, so human-readable descriptions here is tricky.
136                    _ if self.0 >= 0x100 && self.0 < 0x200 => write!(f, "the cryptographic handshake failed: error {}", self.0 & 0xFF),
137                    _ => f.write_str("unknown error"),
138                }
139            }
140        }
141    }
142}
143
144errors! {
145    NO_ERROR(0x0) "the connection is being closed abruptly in the absence of any error";
146    INTERNAL_ERROR(0x1) "the endpoint encountered an internal error and cannot continue with the connection";
147    CONNECTION_REFUSED(0x2) "the server refused to accept a new connection";
148    FLOW_CONTROL_ERROR(0x3) "received more data than permitted in advertised data limits";
149    STREAM_LIMIT_ERROR(0x4) "received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type";
150    STREAM_STATE_ERROR(0x5) "received a frame for a stream that was not in a state that permitted that frame";
151    FINAL_SIZE_ERROR(0x6) "received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established";
152    FRAME_ENCODING_ERROR(0x7) "received a frame that was badly formatted";
153    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";
154    CONNECTION_ID_LIMIT_ERROR(0x9) "the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit";
155    PROTOCOL_VIOLATION(0xA) "detected an error with protocol compliance that was not covered by more specific error codes";
156    INVALID_TOKEN(0xB) "received an invalid Retry Token in a client Initial";
157    APPLICATION_ERROR(0xC) "the application or application protocol caused the connection to be closed during the handshake";
158    CRYPTO_BUFFER_EXCEEDED(0xD) "received more data in CRYPTO frames than can be buffered";
159    KEY_UPDATE_ERROR(0xE) "key update error";
160    AEAD_LIMIT_REACHED(0xF) "the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm";
161    NO_VIABLE_PATH(0x10) "no viable network path exists";
162    APPLICATION_ABANDON_PATH(0x3e) "Path abandoned at the application's request";
163    PATH_RESOURCE_LIMIT_REACHED(0x3e75) "Path abandoned due to resource limitations in the transport";
164    PATH_UNSTABLE_OR_POOR(0x3e76) "Path abandoned due to unstable interfaces";
165    NO_CID_AVAILABLE_FOR_PATH(0x3e77) "Path abandoned due to no available connection IDs for the path";
166}