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}