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}