1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::{application, frame::Tag, varint::VarInt};
use s2n_codec::{decoder_parameterized_value, Encoder, EncoderValue};
//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
//# An endpoint sends a CONNECTION_CLOSE frame (type=0x1c or 0x1d) to
//# notify its peer that the connection is being closed. The
//# CONNECTION_CLOSE frame with a type of 0x1c is used to signal errors
//# at only the QUIC layer, or the absence of errors (with the NO_ERROR
//# code). The CONNECTION_CLOSE frame with a type of 0x1d is used to
//# signal an error with the application that uses QUIC.
macro_rules! connection_close_tag {
() => {
0x1cu8..=0x1du8
};
}
const QUIC_ERROR_TAG: u8 = 0x1c;
const APPLICATION_ERROR_TAG: u8 = 0x1d;
//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
//# CONNECTION_CLOSE Frame {
//# Type (i) = 0x1c..0x1d,
//# Error Code (i),
//# [Frame Type (i)],
//# Reason Phrase Length (i),
//# Reason Phrase (..),
//# }
//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
//# CONNECTION_CLOSE frames contain the following fields:
//#
//# Error Code: A variable-length integer that indicates the reason for
//# closing this connection. A CONNECTION_CLOSE frame of type 0x1c
//# uses codes from the space defined in Section 20.1. A
//# CONNECTION_CLOSE frame of type 0x1d uses codes defined by the
//# application protocol; see Section 20.2.
//#
//# Frame Type: A variable-length integer encoding the type of frame
//# that triggered the error. A value of 0 (equivalent to the mention
//# of the PADDING frame) is used when the frame type is unknown. The
//# application-specific variant of CONNECTION_CLOSE (type 0x1d) does
//# not include this field.
//#
//# Reason Phrase Length: A variable-length integer specifying the
//# length of the reason phrase in bytes. Because a CONNECTION_CLOSE
//# frame cannot be split between packets, any limits on packet size
//# will also limit the space available for a reason phrase.
//#
//# Reason Phrase: Additional diagnostic information for the closure.
//# This can be zero length if the sender chooses not to give details
//# beyond the Error Code value. This SHOULD be a UTF-8 encoded
//# string [RFC3629], though the frame does not carry information,
//# such as language tags, that would aid comprehension by any entity
//# other than the one that created the text.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ConnectionClose<'a> {
/// A variable length integer error code which indicates the reason
/// for closing this connection.
pub error_code: VarInt,
/// A variable-length integer encoding the type of frame that
/// triggered the error.
pub frame_type: Option<VarInt>,
/// A human-readable explanation for why the connection was closed.
/// This SHOULD be a UTF-8 encoded string.
pub reason: Option<&'a [u8]>,
}
impl<'a> ConnectionClose<'a> {
pub fn tag(&self) -> u8 {
if self.frame_type.is_some() {
QUIC_ERROR_TAG
} else {
APPLICATION_ERROR_TAG
}
}
}
// If a `ConnectionClose` contains no frame type it was sent by an application and contains
// an `ApplicationErrorCode`. Otherwise it is an error on the QUIC layer.
impl<'a> application::error::TryInto for ConnectionClose<'a> {
fn application_error(&self) -> Option<application::Error> {
if self.frame_type.is_none() {
Some(self.error_code.into())
} else {
None
}
}
}
decoder_parameterized_value!(
impl<'a> ConnectionClose<'a> {
fn decode(tag: Tag, buffer: Buffer) -> Result<Self> {
let (error_code, buffer) = buffer.decode()?;
let (frame_type, buffer) = if tag == QUIC_ERROR_TAG {
let (frame_type, buffer) = buffer.decode()?;
(Some(frame_type), buffer)
} else {
(None, buffer)
};
let (reason, buffer) = buffer.decode_slice_with_len_prefix::<VarInt>()?;
let reason = if reason.is_empty() {
None
} else {
// newer versions of clippy complain about redundant slicing
// but we don't know if this is a `&slice` or `&mut slice`
#[allow(clippy::all)]
Some(&reason.into_less_safe_slice()[..])
};
let frame = ConnectionClose {
error_code,
frame_type,
reason,
};
Ok((frame, buffer))
}
}
);
impl<'a> EncoderValue for ConnectionClose<'a> {
fn encode<E: Encoder>(&self, buffer: &mut E) {
buffer.encode(&self.tag());
buffer.encode(&self.error_code);
if let Some(frame_type) = &self.frame_type {
buffer.encode(frame_type);
}
if let Some(reason) = &self.reason {
buffer.encode_with_len_prefix::<VarInt, _>(reason);
} else {
buffer.encode(&0u8);
}
}
}