use std::{
collections::HashMap, fmt::Display, marker::PhantomData, net::SocketAddr, time::Duration,
};
use bytes::Bytes;
use derive_builder::Builder;
use derive_more::{From, Into, LowerHex};
use qbase::{
frame::{
AckFrame, ConnectionCloseFrame, CryptoFrame, DatagramFrame, EncodeSize, Frame,
GetFrameType, MaxStreamsFrame, NewTokenFrame, PathChallengeFrame, PathResponseFrame,
PingFrame, ReliableFrame, StreamCtlFrame, StreamFrame, StreamsBlockedFrame,
},
net::addr::BoundAddr,
packet::header::{
GetDcid, GetScid,
long::{HandshakeHeader, InitialHeader, ZeroRttHeader},
short::OneRttHeader,
},
util::ContinuousData,
varint::VarInt,
};
use serde::{Deserialize, Serialize};
pub mod connectivity;
pub mod recovery;
pub mod security;
pub mod transport;
use crate::{BeSpecificEventData, HexString, RawInfo};
#[derive(Debug, Clone, From, Into, PartialEq, Eq)]
pub struct QuicVersion(u32);
impl Serialize for QuicVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[serde_with::serde_as]
#[derive(Serialize)]
struct Helper(#[serde_as(as = "serde_with::hex::Hex")] [u8; 4]);
Helper(self.0.to_be_bytes()).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for QuicVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[serde_with::serde_as]
#[derive(Deserialize)]
struct Helper(#[serde_as(as = "serde_with::hex::Hex")] [u8; 4]);
Helper::deserialize(deserializer).map(|b| Self(u32::from_be_bytes(b.0)))
}
}
#[derive(Default, Debug, LowerHex, From, Into, Clone, Copy, PartialEq, Eq)]
pub struct ConnectionID(qbase::cid::ConnectionId);
impl Serialize for ConnectionID {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[serde_with::serde_as]
#[derive(Serialize)]
struct Helper<'b>(#[serde_as(as = "serde_with::hex::Hex")] &'b [u8]);
Helper(self.0.as_ref()).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ConnectionID {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[serde_with::serde_as]
#[derive(Deserialize)]
struct Helper(#[serde_as(as = "serde_with::hex::Hex")] Vec<u8>);
let bytes = Helper::deserialize(deserializer)?.0;
if bytes.len() > qbase::cid::MAX_CID_SIZE {
return Err(serde::de::Error::custom("ConnectionID too long"));
}
Ok(Self(qbase::cid::ConnectionId::from_slice(&bytes)))
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Owner {
Local,
Remote,
}
#[derive(Debug, Clone, From, Into, Serialize, Deserialize, PartialEq, Eq)]
#[serde(transparent)]
pub struct IPAddress(String);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub enum IpVersion {
V4,
V6,
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[builder(
default,
setter(into, strip_option),
build_fn(private, name = "fallible_build")
)]
pub struct PathEndpointInfo {
ip_v4: Option<IPAddress>,
ip_v6: Option<IPAddress>,
port_v4: Option<u16>,
port_v6: Option<u16>,
conenction_ids: Vec<ConnectionID>,
}
impl From<SocketAddr> for PathEndpointInfo {
fn from(value: SocketAddr) -> Self {
match value {
SocketAddr::V4(addr) => crate::build!(PathEndpointInfo {
ip_v4: addr.ip().to_string(),
port_v4: addr.port(),
}),
SocketAddr::V6(addr) => crate::build!(PathEndpointInfo {
ip_v6: addr.ip().to_string(),
port_v6: addr.port(),
}),
}
}
}
impl From<BoundAddr> for PathEndpointInfo {
fn from(value: BoundAddr) -> Self {
match value {
BoundAddr::Internet(socket_addr) => socket_addr.into(),
_ => crate::build!(Self {}),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum PacketType {
Initial,
Handshake,
#[serde(rename = "0RTT")]
ZeroRTT,
#[serde(rename = "1RTT")]
OneRTT,
Retry,
VersionNegotiation,
StatelessReset,
Unknown,
}
impl From<qbase::packet::Type> for PacketType {
fn from(r#type: qbase::packet::Type) -> Self {
match r#type {
qbase::packet::r#type::Type::Long(long) => match long {
qbase::packet::r#type::long::Type::VersionNegotiation => {
PacketType::VersionNegotiation
}
qbase::packet::r#type::long::Type::V1(
qbase::packet::r#type::long::Version::INITIAL,
) => PacketType::Initial,
qbase::packet::r#type::long::Type::V1(
qbase::packet::r#type::long::Version::HANDSHAKE,
) => PacketType::Handshake,
qbase::packet::r#type::long::Type::V1(
qbase::packet::r#type::long::Version::ZERO_RTT,
) => PacketType::ZeroRTT,
qbase::packet::r#type::long::Type::V1(
qbase::packet::r#type::long::Version::RETRY,
) => PacketType::Retry,
},
qbase::packet::r#type::Type::Short(_one_rtt) => PacketType::OneRTT,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum PacketNumberSpace {
Initial,
Handshake,
ApplicationData,
}
impl From<qbase::Epoch> for PacketNumberSpace {
fn from(value: qbase::Epoch) -> Self {
match value {
qbase::Epoch::Initial => Self::Initial,
qbase::Epoch::Handshake => Self::Handshake,
qbase::Epoch::Data => Self::ApplicationData,
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct PacketHeader {
#[builder(default)]
#[serde(default)]
quic_bit: bool,
packet_type: PacketType,
#[builder(default)]
packet_number: Option<u64>,
#[builder(default)]
flags: Option<u8>,
#[builder(default)]
token: Option<Token>,
#[builder(default)]
length: Option<u16>,
#[builder(default)]
version: Option<QuicVersion>,
#[builder(default)]
scil: Option<u8>,
#[builder(default)]
dcil: Option<u8>,
#[builder(default)]
scid: Option<ConnectionID>,
#[builder(default)]
dcid: Option<ConnectionID>,
}
impl PacketHeaderBuilder {
pub fn initial(&mut self, header: &InitialHeader) -> &mut Self {
crate::build!(@field self,
packet_type: PacketType::Initial,
?token: Token::try_from(header).ok(),
scil: header.scid().len() as u8,
scid: { *header.scid() },
dcil: header.dcid().len() as u8,
dcid: { *header.dcid() }
);
self
}
pub fn handshake(&mut self, header: &HandshakeHeader) -> &mut Self {
self.packet_type(PacketType::Handshake)
.scil(header.scid().len() as u8)
.scid(*header.scid())
.dcil(header.dcid().len() as u8)
.dcid(*header.dcid())
}
pub fn zero_rtt(&mut self, header: &ZeroRttHeader) -> &mut Self {
self.packet_type(PacketType::ZeroRTT)
.scil(header.scid().len() as u8)
.scid(*header.scid())
.dcil(header.dcid().len() as u8)
.dcid(*header.dcid())
}
pub fn one_rtt(&mut self, header: &OneRttHeader) -> &mut Self {
self.packet_type(PacketType::OneRTT)
.dcil(header.dcid().len() as u8)
.dcid(*header.dcid())
}
}
impl From<&InitialHeader> for PacketHeaderBuilder {
fn from(header: &InitialHeader) -> Self {
let mut builder = PacketHeader::builder();
builder.initial(header);
builder
}
}
impl From<&HandshakeHeader> for PacketHeaderBuilder {
fn from(header: &HandshakeHeader) -> Self {
let mut builder = PacketHeader::builder();
builder.handshake(header);
builder
}
}
impl From<&ZeroRttHeader> for PacketHeaderBuilder {
fn from(header: &ZeroRttHeader) -> Self {
let mut builder = PacketHeader::builder();
builder.zero_rtt(header);
builder
}
}
impl From<&OneRttHeader> for PacketHeaderBuilder {
fn from(header: &OneRttHeader) -> Self {
let mut builder = PacketHeader::builder();
builder.one_rtt(header);
builder
}
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[builder(
default,
setter(into, strip_option),
build_fn(private, name = "fallible_build")
)]
#[serde(default)]
pub struct Token {
pub r#type: Option<TokenType>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
details: HashMap<String, serde_json::Value>,
raw: Option<RawInfo>,
}
impl<H: 'static> TryFrom<&qbase::packet::header::LongHeader<H>> for Token {
type Error = ();
fn try_from(header: &qbase::packet::header::LongHeader<H>) -> Result<Self, Self::Error> {
use qbase::packet::header::RetryHeader;
let header: &dyn core::any::Any = header;
if let Some(initial) = header.downcast_ref::<InitialHeader>() {
if initial.token().is_empty() {
return Err(());
}
return Ok(crate::build!(Token {
raw: initial.token(),
}));
}
if let Some(retry) = header.downcast_ref::<RetryHeader>() {
return Ok(crate::build!(Token {
r#type: TokenType::Retry,
raw: retry.token(),
}));
}
Err(())
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TokenType {
Retry,
Resumption,
}
#[serde_with::serde_as]
#[derive(Debug, Clone, Copy, From, Into, Serialize, Deserialize, PartialEq, Eq)]
pub struct StatelessResetToken(#[serde_as(as = "serde_with::hex::Hex")] [u8; 16]);
impl From<qbase::token::ResetToken> for StatelessResetToken {
fn from(value: qbase::token::ResetToken) -> Self {
Self(*value)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KeyType {
ServerInitialSecret,
ClientInitialSecret,
ServerHandshakeSecret,
ClientHandshakeSecret,
#[serde(rename = "server_0rtt_secret")]
Server0RttSecret,
#[serde(rename = "client_0rtt_secret")]
Client0RttSecret,
#[serde(rename = "server_1rtt_secret")]
Server1RttSecret,
#[serde(rename = "client_1rtt_secret")]
Client1RttSecret,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ECN {
#[serde(rename = "Not-ECT")]
NotEct,
#[serde(rename = "ECT(1)")]
Ect1,
#[serde(rename = "ECT(0)")]
Ect0,
CE,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "frame_type")]
#[serde(rename_all = "snake_case")]
pub enum QuicFrame {
Padding {
length: Option<u32>,
payload_length: u32,
},
Ping {
length: Option<u32>,
payload_length: Option<u32>,
},
Ack {
ack_delay: Option<f32>,
acked_ranges: Vec<[u64; 2]>,
ect1: Option<u64>,
ect0: Option<u64>,
ce: Option<u64>,
length: Option<u32>,
payload_length: Option<u32>,
},
ResetStream {
stream_id: u64,
error_code: ApplicationCode,
final_size: u64,
length: Option<u32>,
payload_length: Option<u32>,
},
StopSending {
stream_id: u64,
error_code: ApplicationCode,
length: Option<u32>,
payload_length: Option<u32>,
},
Crypto {
offset: u64,
length: u64,
payload_length: Option<u32>,
raw: Option<RawInfo>,
},
NewToken {
token: Token,
},
Stream {
stream_id: u64,
offset: u64,
length: u64,
#[serde(default)]
fin: bool,
raw: Option<RawInfo>,
},
MaxData {
maximum: u64,
},
MaxStreamData {
stream_id: u64,
maximum: u64,
},
MaxStreams {
stream_type: StreamType,
maximum: u64,
},
DataBlocked {
limit: u64,
},
StreamDataBlocked {
stream_id: u64,
limit: u64,
},
StreamsBlocked {
stream_type: StreamType,
limit: u64,
},
NewConnectionId {
sequence_number: u32,
retire_prior_to: u32,
connection_id_length: Option<u8>,
connection_id: ConnectionID,
stateless_reset_token: Option<StatelessResetToken>,
},
RetireConnectionId {
sequence_number: u32,
},
PathChallenge {
data: Option<HexString>,
},
PathResponse {
data: Option<HexString>,
},
ConnectionClose {
error_space: Option<ConnectionCloseErrorSpace>,
error_code: Option<ConnectionCloseErrorCode>,
reason: Option<String>,
reason_bytes: Option<HexString>,
trigger_frame_type: Option<ConnectionCloseTriggerFrameType>,
},
HandshakeDone {},
Unknow {
frame_type_bytes: u64,
raw: Option<RawInfo>,
},
Datagram {
length: Option<u64>,
raw: Option<RawInfo>,
},
}
impl From<&PingFrame> for QuicFrame {
fn from(frame: &PingFrame) -> Self {
QuicFrame::Ping {
length: Some(frame.encoding_size() as u32),
payload_length: Some(0),
}
}
}
impl<D: ContinuousData + ?Sized> From<(&CryptoFrame, &D)> for QuicFrame {
fn from((frame, data): (&CryptoFrame, &D)) -> Self {
let payload_length = frame.len();
let length = frame.encoding_size() as u64 + payload_length;
QuicFrame::Crypto {
offset: frame.offset(),
length,
payload_length: Some(payload_length as _),
raw: Some(crate::build!(RawInfo {
length,
payload_length,
data,
})),
}
}
}
impl From<&CryptoFrame> for QuicFrame {
fn from(frame: &CryptoFrame) -> Self {
let payload_length = frame.len();
let length = frame.encoding_size() as u64 + payload_length;
QuicFrame::Crypto {
offset: frame.offset(),
length,
payload_length: Some(payload_length as _),
raw: Some(crate::build!(RawInfo {
length,
payload_length,
})),
}
}
}
impl<D: ContinuousData + ?Sized> From<(&StreamFrame, &D)> for QuicFrame {
fn from((frame, data): (&StreamFrame, &D)) -> Self {
let payload_length = frame.len();
let length = frame.encoding_size() + payload_length;
QuicFrame::Stream {
stream_id: frame.stream_id().into(),
offset: frame.offset(),
length: payload_length as u64,
fin: frame.is_fin(),
raw: Some(crate::build!(RawInfo {
length: length as u64,
payload_length: payload_length as u64,
data: data,
})),
}
}
}
impl From<&StreamFrame> for QuicFrame {
fn from(frame: &StreamFrame) -> Self {
let payload_length = frame.len();
let length = frame.encoding_size() + payload_length;
QuicFrame::Stream {
stream_id: frame.stream_id().into(),
offset: frame.offset(),
length: payload_length as u64,
fin: frame.is_fin(),
raw: Some(crate::build!(RawInfo {
length: length as u64,
payload_length: payload_length as u64,
})),
}
}
}
impl<D: ContinuousData + ?Sized> From<(&DatagramFrame, &D)> for QuicFrame {
fn from((frame, data): (&DatagramFrame, &D)) -> Self {
let payload_length = frame.len().into_inner();
let length = frame.encoding_size() as u64 + payload_length;
QuicFrame::Datagram {
length: Some(payload_length as _),
raw: Some(crate::build!(RawInfo {
length,
payload_length,
data: data,
})),
}
}
}
impl From<&DatagramFrame> for QuicFrame {
fn from(frame: &DatagramFrame) -> Self {
let payload_length = frame.len().into_inner();
let length = frame.encoding_size() as u64 + payload_length;
QuicFrame::Datagram {
length: Some(payload_length as _),
raw: Some(crate::build!(RawInfo {
length,
payload_length,
})),
}
}
}
impl From<&PathChallengeFrame> for QuicFrame {
fn from(frame: &PathChallengeFrame) -> Self {
QuicFrame::PathChallenge {
data: Some(Bytes::from_owner(frame.to_vec()).into()),
}
}
}
impl From<&PathResponseFrame> for QuicFrame {
fn from(frame: &PathResponseFrame) -> Self {
QuicFrame::PathResponse {
data: Some(Bytes::from_owner(frame.to_vec()).into()),
}
}
}
impl From<&AckFrame> for QuicFrame {
fn from(frame: &AckFrame) -> Self {
Self::Ack {
ack_delay: Some(Duration::from_micros(frame.delay()).as_secs_f32() * 1000.0),
acked_ranges: frame
.ranges()
.iter()
.fold(
(
frame.largest() - frame.first_range(),
vec![[frame.largest() - frame.first_range(), frame.largest()]],
),
|(previous_smallest, mut acked_ranges), (gap, ack)| {
let largest = previous_smallest - gap.into_inner() - 2;
let smallest = largest - ack.into_inner();
acked_ranges.push([smallest, largest]);
(smallest, acked_ranges)
},
)
.1,
ect1: frame.ecn().map(|ecn| ecn.ect1()),
ect0: frame.ecn().map(|ecn| ecn.ect0()),
ce: frame.ecn().map(|ecn| ecn.ce()),
length: Some(frame.encoding_size() as u32),
payload_length: None,
}
}
}
impl From<&ReliableFrame> for QuicFrame {
fn from(frame: &ReliableFrame) -> Self {
match frame {
ReliableFrame::NewToken(new_token_frame) => new_token_frame.into(),
ReliableFrame::MaxData(max_data_frame) => QuicFrame::MaxData {
maximum: max_data_frame.max_data(),
},
ReliableFrame::DataBlocked(data_blocked_frame) => QuicFrame::DataBlocked {
limit: data_blocked_frame.limit(),
},
ReliableFrame::NewConnectionId(new_connection_id_frame) => QuicFrame::NewConnectionId {
sequence_number: new_connection_id_frame.sequence() as u32,
retire_prior_to: new_connection_id_frame.retire_prior_to() as u32,
connection_id_length: Some(new_connection_id_frame.connection_id().len() as u8),
connection_id: (*new_connection_id_frame.connection_id()).into(),
stateless_reset_token: Some((**new_connection_id_frame.reset_token()).into()),
},
ReliableFrame::RetireConnectionId(retire_connection_id_frame) => {
QuicFrame::RetireConnectionId {
sequence_number: retire_connection_id_frame.sequence() as u32,
}
}
ReliableFrame::HandshakeDone(_handshake_done_frame) => QuicFrame::HandshakeDone {},
ReliableFrame::AddAddress(frame) => QuicFrame::Unknow {
frame_type_bytes: VarInt::from(frame.frame_type()).into_inner() as _,
raw: None,
},
ReliableFrame::RemoveAddress(frame) => QuicFrame::Unknow {
frame_type_bytes: VarInt::from(frame.frame_type()).into_inner() as _,
raw: None,
},
ReliableFrame::PunchMeNow(frame) => QuicFrame::Unknow {
frame_type_bytes: VarInt::from(frame.frame_type()).into_inner() as _,
raw: None,
},
ReliableFrame::PunchDone(frame) => QuicFrame::Unknow {
frame_type_bytes: VarInt::from(frame.frame_type()).into_inner() as _,
raw: None,
},
ReliableFrame::StreamCtl(stream_ctl_frame) => QuicFrame::from(stream_ctl_frame),
}
}
}
impl From<&NewTokenFrame> for QuicFrame {
fn from(value: &NewTokenFrame) -> Self {
QuicFrame::NewToken {
token: crate::build!(Token {
r#type: TokenType::Retry,
raw: RawInfo {
length: value.encoding_size() as u64,
payload_length: value.token().len() as u64,
data: value.token(),
},
}),
}
}
}
impl From<&StreamCtlFrame> for QuicFrame {
fn from(frame: &StreamCtlFrame) -> Self {
match frame {
StreamCtlFrame::ResetStream(reset_stream_frame) => QuicFrame::ResetStream {
stream_id: reset_stream_frame.stream_id().id(),
error_code: (reset_stream_frame.app_error_code() as u32).into(),
final_size: reset_stream_frame.final_size(),
length: None,
payload_length: None,
},
StreamCtlFrame::StopSending(stop_sending_frame) => QuicFrame::StopSending {
stream_id: stop_sending_frame.stream_id().id(),
error_code: (stop_sending_frame.app_err_code() as u32).into(),
length: None,
payload_length: None,
},
StreamCtlFrame::MaxStreamData(max_stream_data_frame) => QuicFrame::MaxStreamData {
stream_id: max_stream_data_frame.stream_id().id(),
maximum: max_stream_data_frame.max_stream_data(),
},
StreamCtlFrame::MaxStreams(max_streams_frame) => match max_streams_frame {
MaxStreamsFrame::Bi(maximum) => QuicFrame::MaxStreams {
stream_type: StreamType::Bidirectional,
maximum: maximum.into_inner(),
},
MaxStreamsFrame::Uni(maximum) => QuicFrame::MaxStreams {
stream_type: StreamType::Unidirectional,
maximum: maximum.into_inner(),
},
},
StreamCtlFrame::StreamDataBlocked(stream_data_blocked_frame) => {
QuicFrame::StreamDataBlocked {
stream_id: stream_data_blocked_frame.stream_id().id(),
limit: stream_data_blocked_frame.maximum_stream_data(),
}
}
StreamCtlFrame::StreamsBlocked(streams_blocked_frame) => match streams_blocked_frame {
StreamsBlockedFrame::Bi(limit) => QuicFrame::StreamsBlocked {
stream_type: StreamType::Bidirectional,
limit: limit.into_inner(),
},
StreamsBlockedFrame::Uni(limit) => QuicFrame::StreamsBlocked {
stream_type: StreamType::Unidirectional,
limit: limit.into_inner(),
},
},
}
}
}
impl From<&ConnectionCloseFrame> for QuicFrame {
fn from(frame: &ConnectionCloseFrame) -> Self {
Self::ConnectionClose {
error_space: Some(match &frame {
ConnectionCloseFrame::App(..) => ConnectionCloseErrorSpace::Application,
ConnectionCloseFrame::Quic(..) => ConnectionCloseErrorSpace::Transport,
}),
error_code: match &frame {
ConnectionCloseFrame::App(frame) => {
Some(ApplicationCode::from(frame.error_code() as u32).into())
}
ConnectionCloseFrame::Quic(frame) => {
Some(connectivity::ConnectionCode::from(frame.error_kind()).into())
}
},
reason: match &frame {
ConnectionCloseFrame::App(frame) => Some(frame.reason().to_owned()),
ConnectionCloseFrame::Quic(frame) => Some(frame.reason().to_owned()),
},
reason_bytes: None,
trigger_frame_type: match &frame {
ConnectionCloseFrame::Quic(frame) => {
Some((VarInt::from(frame.frame_type()).into_inner()).into())
}
ConnectionCloseFrame::App(..) => None,
},
}
}
}
impl<D: ContinuousData> From<&Frame<D>> for QuicFrame {
fn from(frame: &Frame<D>) -> Self {
match frame {
Frame::Padding(..) => QuicFrame::Padding {
length: Some(1),
payload_length: 1,
},
Frame::Ping(..) => QuicFrame::Ping {
length: Some(1),
payload_length: Some(1),
},
Frame::Ack(frame) => frame.into(),
Frame::Close(frame) => frame.into(),
Frame::NewToken(frame) => frame.into(),
Frame::MaxData(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::DataBlocked(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::NewConnectionId(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::RetireConnectionId(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::HandshakeDone(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::AddAddress(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::RemoveAddress(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::PunchMeNow(frame) => (&ReliableFrame::from(*frame)).into(),
Frame::PathChallenge(frame) => frame.into(),
Frame::PathResponse(frame) => frame.into(),
Frame::StreamCtl(frame) => frame.into(),
Frame::Stream(frame, bytes) if bytes.is_empty() => (frame, bytes).into(),
Frame::Crypto(frame, bytes) if bytes.is_empty() => (frame, bytes).into(),
Frame::Datagram(frame, bytes) if bytes.is_empty() => (frame, bytes).into(),
Frame::Stream(frame, bytes) => (frame, bytes).into(),
Frame::Crypto(frame, bytes) => (frame, bytes).into(),
Frame::Datagram(frame, bytes) => (frame, bytes).into(),
Frame::PunchHello(frame) => QuicFrame::Unknow {
frame_type_bytes: VarInt::from(frame.frame_type()).into_inner() as _,
raw: None,
},
Frame::PunchDone(frame) => QuicFrame::Unknow {
frame_type_bytes: VarInt::from(frame.frame_type()).into_inner() as _,
raw: None,
},
}
}
}
#[derive(Debug)]
pub struct QuicFramesCollector<E> {
event: PhantomData<E>,
frames: Vec<QuicFrame>,
}
impl<E> QuicFramesCollector<E> {
pub fn new() -> Self {
Self {
event: PhantomData,
frames: Vec::new(),
}
}
}
impl<E> Default for QuicFramesCollector<E> {
fn default() -> Self {
Self::new()
}
}
impl<E, F> Extend<F> for QuicFramesCollector<E>
where
E: BeSpecificEventData,
F: Into<QuicFrame>,
{
fn extend<T: IntoIterator<Item = F>>(&mut self, iter: T) {
if !crate::telemetry::Span::current().filter_event(E::scheme()) {
return;
}
for frame in iter.into_iter().map(Into::into) {
if let Some(last) = self.frames.last_mut() {
match last {
QuicFrame::Padding {
length,
payload_length,
} => {
*last = QuicFrame::Padding {
length: length.map(|length| length + 1),
payload_length: *payload_length + 1,
};
continue;
}
QuicFrame::Ping {
length,
payload_length,
} => {
*last = QuicFrame::Ping {
length: length.map(|length| length + 1),
payload_length: payload_length.map(|length| length + 1),
};
continue;
}
_ => {}
}
}
self.frames.push(frame);
}
}
}
impl<E> From<QuicFramesCollector<E>> for Vec<QuicFrame> {
fn from(value: QuicFramesCollector<E>) -> Self {
value.frames
}
}
#[derive(Debug, Clone, From, Serialize, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ApplicationCode {
ApplicationError(ApplicationError),
Value(u32),
}
impl From<ApplicationCode> for ConnectionCloseErrorCode {
fn from(value: ApplicationCode) -> Self {
match value {
ApplicationCode::ApplicationError(error) => {
ConnectionCloseErrorCode::ApplicationError(error)
}
ApplicationCode::Value(value) => ConnectionCloseErrorCode::Value(value as _),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum StreamType {
Unidirectional,
Bidirectional,
}
impl From<qbase::sid::Dir> for StreamType {
fn from(dir: qbase::sid::Dir) -> Self {
match dir {
qbase::sid::Dir::Bi => Self::Bidirectional,
qbase::sid::Dir::Uni => Self::Unidirectional,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionCloseErrorSpace {
Transport,
Application,
}
#[derive(Debug, Clone, From, Serialize, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ConnectionCloseErrorCode {
TransportError(TransportError),
CryptoError(CryptoError),
ApplicationError(ApplicationError),
Value(u64),
}
#[derive(Debug, Clone, Serialize, From, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ConnectionCloseTriggerFrameType {
Id(u64),
Text(String),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TransportError {
NoError,
InternalError,
ConnectionRefused,
FlowControlError,
StreamLimitError,
StreamStateError,
FinalSizeError,
FrameEncodingError,
TransportParameterError,
ConnectionIdLimitError,
ProtocolViolation,
InvalidToken,
ApplicationError,
CryptoBufferExceeded,
KeyUpdateError,
AeadLimitReached,
NoViablePath,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct ApplicationError(String);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CryptoError(u8);
impl Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "crypto_error_0x1{:02x}", self.0)
}
}
impl Serialize for CryptoError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for CryptoError {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
string.strip_prefix("crypto_error_0x1").map_or_else(
|| Err(serde::de::Error::custom("invalid crypto error")),
|s| {
u8::from_str_radix(s, 16)
.map(CryptoError)
.map_err(serde::de::Error::custom)
},
)
}
}
crate::gen_builder_method! {
PathEndpointInfoBuilder => PathEndpointInfo;
PacketHeaderBuilder => PacketHeader;
TokenBuilder => Token;
}
mod rollback {
use super::*;
use crate::{build, legacy::quic as legacy};
impl From<IPAddress> for legacy::IPAddress {
#[inline]
fn from(value: IPAddress) -> Self {
legacy::IPAddress::from(value.0)
}
}
impl From<IpVersion> for legacy::IPVersion {
#[inline]
fn from(value: IpVersion) -> Self {
match value {
IpVersion::V4 => legacy::IPVersion::V4,
IpVersion::V6 => legacy::IPVersion::V6,
}
}
}
impl From<ConnectionID> for legacy::ConnectionID {
#[inline]
fn from(value: ConnectionID) -> Self {
legacy::ConnectionID::from(HexString::from(Bytes::from(value.0.to_vec())))
}
}
impl From<Owner> for legacy::Owner {
#[inline]
fn from(value: Owner) -> Self {
match value {
Owner::Local => legacy::Owner::Local,
Owner::Remote => legacy::Owner::Remote,
}
}
}
impl From<PacketType> for legacy::PacketType {
#[inline]
fn from(value: PacketType) -> Self {
match value {
PacketType::Initial => legacy::PacketType::Initial,
PacketType::Handshake => legacy::PacketType::Handshake,
PacketType::ZeroRTT => legacy::PacketType::ZeroRTT,
PacketType::OneRTT => legacy::PacketType::OneRTT,
PacketType::Retry => legacy::PacketType::Retry,
PacketType::VersionNegotiation => legacy::PacketType::VersionNegotiation,
PacketType::StatelessReset => legacy::PacketType::StatelessReset,
PacketType::Unknown => legacy::PacketType::Unknown,
}
}
}
impl From<PacketNumberSpace> for legacy::PacketNumberSpace {
#[inline]
fn from(value: PacketNumberSpace) -> Self {
match value {
PacketNumberSpace::Initial => legacy::PacketNumberSpace::Initial,
PacketNumberSpace::Handshake => legacy::PacketNumberSpace::Handshake,
PacketNumberSpace::ApplicationData => legacy::PacketNumberSpace::ApplicationData,
}
}
}
impl From<TokenType> for legacy::TokenType {
#[inline]
fn from(value: TokenType) -> Self {
match value {
TokenType::Retry => legacy::TokenType::Retry,
TokenType::Resumption => legacy::TokenType::Resumption,
}
}
}
impl From<Token> for legacy::Token {
#[inline]
fn from(value: Token) -> Self {
build!(legacy::Token {
?r#type: value.r#type,
details: value.details,
?length: value.raw.as_ref().and_then(|raw| raw.length.map(|length| length as u32)),
?data: value.raw.and_then(|raw| raw.data)
})
}
}
impl From<StatelessResetToken> for legacy::Token {
#[inline]
fn from(value: StatelessResetToken) -> Self {
build!(legacy::Token {
r#type: TokenType::Resumption,
details: HashMap::new(),
length: 16u32,
data: { Bytes::from_owner(value.0.to_vec()) }
})
}
}
impl From<PacketHeader> for legacy::PacketHeader {
fn from(value: PacketHeader) -> Self {
build!(legacy::PacketHeader {
packet_type: value.packet_type,
?packet_number: value.packet_number,
?flags: value.flags,
?token: value.token,
?length: value.length,
?version: value.version,
?scil: value.scil,
?dcil: value.dcil,
?scid: value.scid,
?dcid: value.dcid
})
}
}
impl From<TransportError> for legacy::TransportError {
#[inline]
fn from(value: TransportError) -> Self {
match value {
TransportError::NoError => legacy::TransportError::NoError,
TransportError::InternalError => legacy::TransportError::InternalError,
TransportError::ConnectionRefused => legacy::TransportError::ConnectionRefused,
TransportError::FlowControlError => legacy::TransportError::FlowControlError,
TransportError::StreamLimitError => legacy::TransportError::StreamLimitError,
TransportError::StreamStateError => legacy::TransportError::StreamStateError,
TransportError::FinalSizeError => legacy::TransportError::FinalSizeError,
TransportError::FrameEncodingError => legacy::TransportError::FrameEncodingError,
TransportError::TransportParameterError => {
legacy::TransportError::TransportParameterError
}
TransportError::ConnectionIdLimitError => {
legacy::TransportError::ConnectionIdLimitError
}
TransportError::ProtocolViolation => legacy::TransportError::ProtocolViolation,
TransportError::InvalidToken => legacy::TransportError::InvalidToken,
TransportError::ApplicationError => legacy::TransportError::ApplicationError,
TransportError::CryptoBufferExceeded => {
legacy::TransportError::CryptoBufferExceeded
}
TransportError::KeyUpdateError => legacy::TransportError::KeyUpdateError,
TransportError::AeadLimitReached => legacy::TransportError::AeadLimitReached,
TransportError::NoViablePath => legacy::TransportError::NoViablePath,
}
}
}
impl From<StreamType> for legacy::StreamType {
#[inline]
fn from(value: StreamType) -> Self {
match value {
StreamType::Unidirectional => legacy::StreamType::Unidirectional,
StreamType::Bidirectional => legacy::StreamType::Bidirectional,
}
}
}
impl From<ConnectionCloseErrorSpace> for legacy::ConnectionCloseErrorSpace {
#[inline]
fn from(value: ConnectionCloseErrorSpace) -> Self {
match value {
ConnectionCloseErrorSpace::Transport => {
legacy::ConnectionCloseErrorSpace::Transport
}
ConnectionCloseErrorSpace::Application => {
legacy::ConnectionCloseErrorSpace::Application
}
}
}
}
impl TryFrom<ConnectionCloseErrorCode> for legacy::ConnectionCloseErrorCode {
type Error = ();
#[inline]
fn try_from(value: ConnectionCloseErrorCode) -> Result<Self, ()> {
match value {
ConnectionCloseErrorCode::TransportError(error) => Ok(
legacy::ConnectionCloseErrorCode::TransportError(error.into()),
),
ConnectionCloseErrorCode::CryptoError(_error) => Err(()),
ConnectionCloseErrorCode::ApplicationError(error) => Ok(
legacy::ConnectionCloseErrorCode::ApplicationError(error.into()),
),
ConnectionCloseErrorCode::Value(value) => {
Ok(legacy::ConnectionCloseErrorCode::Value(value))
}
}
}
}
impl From<ConnectionCloseTriggerFrameType> for legacy::ConnectionCloseTriggerFrameType {
#[inline]
fn from(value: ConnectionCloseTriggerFrameType) -> Self {
match value {
ConnectionCloseTriggerFrameType::Id(id) => {
legacy::ConnectionCloseTriggerFrameType::Id(id)
}
ConnectionCloseTriggerFrameType::Text(text) => {
legacy::ConnectionCloseTriggerFrameType::Text(text)
}
}
}
}
impl From<QuicFrame> for legacy::QuicFrame {
fn from(value: QuicFrame) -> Self {
match value {
QuicFrame::Padding {
length,
payload_length,
} => legacy::QuicFrame::Padding {
length,
payload_length,
},
QuicFrame::Ping {
length,
payload_length,
} => legacy::QuicFrame::Ping {
length,
payload_length,
},
QuicFrame::Ack {
ack_delay,
acked_ranges,
ect1,
ect0,
ce,
length,
payload_length,
} => legacy::QuicFrame::Ack {
ack_delay,
acked_ranges,
ect1,
ect0,
ce,
length,
payload_length,
},
QuicFrame::ResetStream {
stream_id,
error_code,
final_size,
length,
payload_length,
} => legacy::QuicFrame::ResetStream {
stream_id,
error_code: error_code.into(),
final_size,
length,
payload_length,
},
QuicFrame::StopSending {
stream_id,
error_code,
length,
payload_length,
} => legacy::QuicFrame::StopSending {
stream_id,
error_code: error_code.into(),
length,
payload_length,
},
QuicFrame::Crypto {
offset,
length,
payload_length,
raw: _,
} => legacy::QuicFrame::Crypto {
offset,
length,
payload_length,
},
QuicFrame::NewToken { token } => legacy::QuicFrame::NewToken {
token: token.into(),
},
QuicFrame::Stream {
stream_id,
offset,
length,
fin,
raw,
} => legacy::QuicFrame::Stream {
stream_id,
offset,
length,
fin,
raw,
},
QuicFrame::MaxData { maximum } => legacy::QuicFrame::MaxData { maximum },
QuicFrame::MaxStreamData { stream_id, maximum } => {
legacy::QuicFrame::MaxStreamData { stream_id, maximum }
}
QuicFrame::MaxStreams {
stream_type,
maximum,
} => legacy::QuicFrame::MaxStreams {
stream_type: stream_type.into(),
maximum,
},
QuicFrame::DataBlocked { limit } => legacy::QuicFrame::DataBlocked { limit },
QuicFrame::StreamDataBlocked { stream_id, limit } => {
legacy::QuicFrame::StreamDataBlocked { stream_id, limit }
}
QuicFrame::StreamsBlocked { stream_type, limit } => {
legacy::QuicFrame::StreamsBlocked {
stream_type: stream_type.into(),
limit,
}
}
QuicFrame::NewConnectionId {
sequence_number,
retire_prior_to,
connection_id_length,
connection_id,
stateless_reset_token,
} => legacy::QuicFrame::NewConnectionId {
sequence_number,
retire_prior_to,
connection_id_length,
connection_id: connection_id.into(),
stateless_reset_token: stateless_reset_token.map(Into::into),
},
QuicFrame::RetireConnectionId { sequence_number } => {
legacy::QuicFrame::RetireConnectionId { sequence_number }
}
QuicFrame::PathChallenge { data } => legacy::QuicFrame::PathChallenge { data },
QuicFrame::PathResponse { data } => legacy::QuicFrame::PathResponse { data },
QuicFrame::ConnectionClose {
error_space,
error_code,
reason,
reason_bytes: _,
trigger_frame_type,
} => legacy::QuicFrame::ConnectionClose {
error_space: error_space.map(Into::into),
raw_error_code: match &error_code {
Some(ConnectionCloseErrorCode::CryptoError(CryptoError(value))) => {
Some(*value as u32)
}
_ => None,
},
error_code: error_code.and_then(|error_code| error_code.try_into().ok()),
reason,
trigger_frame_type: trigger_frame_type.map(Into::into),
},
QuicFrame::HandshakeDone {} => legacy::QuicFrame::HandshakeDone {},
QuicFrame::Unknow {
frame_type_bytes,
raw,
} => legacy::QuicFrame::Unknown {
raw_frame_type: frame_type_bytes,
raw_length: raw
.as_ref()
.and_then(|raw| raw.length.map(|length| length as u32)),
raw: raw.and_then(|raw| raw.data),
},
QuicFrame::Datagram { length, raw } => legacy::QuicFrame::Datagram { length, raw },
}
}
}
impl From<ApplicationError> for legacy::ApplicationError {
#[inline]
fn from(value: ApplicationError) -> Self {
value.0.into()
}
}
impl From<ApplicationCode> for legacy::ApplicationCode {
#[inline]
fn from(value: ApplicationCode) -> Self {
match value {
ApplicationCode::ApplicationError(error) => {
legacy::ApplicationCode::ApplicationError(error.into())
}
ApplicationCode::Value(value) => legacy::ApplicationCode::Value(value),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ack() {
let frame = AckFrame::new(
9u32.into(),
1000u32.into(),
0u32.into(),
vec![(1u32.into(), 1u32.into()), (0u32.into(), 2u32.into())],
None,
);
let encoding_size = frame.encoding_size();
let quic_frame: QuicFrame = (&frame).into();
assert_eq!(
quic_frame,
QuicFrame::Ack {
ack_delay: Some(1.0),
acked_ranges: vec![[9, 9], [5, 6], [1, 3]],
ect1: None,
ect0: None,
ce: None,
length: Some(encoding_size as u32),
payload_length: None,
}
);
}
}