#![cfg_attr(not(fuzzing), warn(missing_docs))]
#![cfg_attr(test, allow(dead_code))]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::too_many_arguments)]
use std::{
fmt,
net::{IpAddr, SocketAddr},
ops,
time::Duration,
};
mod cid_queue;
#[doc(hidden)]
pub mod coding;
mod constant_time;
mod packet;
mod range_set;
#[cfg(all(test, feature = "rustls"))]
mod tests;
pub mod transport_parameters;
mod varint;
pub use varint::{VarInt, VarIntBoundsExceeded};
mod connection;
pub use crate::connection::{
BytesSource, Chunk, Chunks, ConnectionError, ConnectionStats, Event, FinishError, ReadError,
ReadableError, RecvStream, SendDatagramError, SendStream, StreamEvent, Streams, UnknownStream,
WriteError, Written,
};
mod config;
pub use config::{ConfigError, TransportConfig};
pub mod crypto;
#[cfg(feature = "rustls")]
pub use crypto::types::*;
mod frame;
use crate::frame::Frame;
pub use crate::frame::{ApplicationClose, ConnectionClose, Datagram};
mod endpoint;
pub use crate::endpoint::{ConnectError, ConnectionHandle, DatagramEvent};
mod shared;
pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
mod transport_error;
pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
pub mod congestion;
mod cid_generator;
pub use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
mod token;
use token::{ResetToken, RetryToken};
pub mod generic {
pub use crate::{
config::{ClientConfig, EndpointConfig, ServerConfig},
connection::{Connection, Datagrams},
endpoint::Endpoint,
};
}
#[cfg(feature = "rustls")]
mod rustls_impls {
use crate::{crypto, generic};
pub type Connection = generic::Connection<crypto::rustls::TlsSession>;
pub type ClientConfig = generic::ClientConfig<crypto::rustls::TlsSession>;
pub type Datagrams<'a> = generic::Datagrams<'a, crypto::rustls::TlsSession>;
pub type Endpoint = generic::Endpoint<crypto::rustls::TlsSession>;
pub type ServerConfig = generic::ServerConfig<crypto::rustls::TlsSession>;
pub type EndpointConfig = generic::EndpointConfig<crypto::rustls::TlsSession>;
}
#[cfg(feature = "rustls")]
pub use crate::rustls_impls::*;
#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
#[doc(hidden)]
#[cfg(fuzzing)]
pub mod fuzzing {
pub use crate::connection::{
FinishError, Retransmits, SendStream, State as ConnectionState, Streams, StreamsState,
};
pub use crate::frame::ResetStream;
pub use crate::packet::PartialDecode;
pub use crate::transport_parameters::TransportParameters;
use crate::MAX_CID_SIZE;
use arbitrary::{Arbitrary, Result, Unstructured};
pub use bytes::{BufMut, BytesMut};
impl Arbitrary for TransportParameters {
fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
Ok(TransportParameters {
initial_max_streams_bidi: u.arbitrary()?,
initial_max_streams_uni: u.arbitrary()?,
ack_delay_exponent: u.arbitrary()?,
max_udp_payload_size: u.arbitrary()?,
..TransportParameters::default()
})
}
}
#[derive(Debug)]
pub struct PacketParams {
pub local_cid_len: usize,
pub buf: BytesMut,
}
impl Arbitrary for PacketParams {
fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
let local_cid_len: usize = u.int_in_range(0..=MAX_CID_SIZE)?;
let bytes: Vec<u8> = Vec::arbitrary(u)?;
let mut buf = BytesMut::new();
buf.put_slice(&bytes[..]);
Ok(PacketParams { local_cid_len, buf })
}
}
}
pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] =
&[0xff00_001d, 0xff00_001e, 0xff00_001f, 0xff00_0020];
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Side {
Client = 0,
Server = 1,
}
impl Side {
#[inline]
pub fn is_client(self) -> bool {
self == Side::Client
}
#[inline]
pub fn is_server(self) -> bool {
self == Side::Server
}
}
impl ops::Not for Side {
type Output = Side;
fn not(self) -> Side {
match self {
Side::Client => Side::Server,
Side::Server => Side::Client,
}
}
}
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Dir {
Bi = 0,
Uni = 1,
}
impl Dir {
fn iter() -> impl Iterator<Item = Self> {
[Dir::Bi, Dir::Uni].iter().cloned()
}
}
impl fmt::Display for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::Dir::*;
f.pad(match *self {
Bi => "bidirectional",
Uni => "unidirectional",
})
}
}
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct StreamId(#[doc(hidden)] pub u64);
impl fmt::Display for StreamId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let initiator = match self.initiator() {
Side::Client => "client",
Side::Server => "server",
};
let dir = match self.dir() {
Dir::Uni => "uni",
Dir::Bi => "bi",
};
write!(
f,
"{} {}directional stream {}",
initiator,
dir,
self.index()
)
}
}
impl StreamId {
pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
StreamId(index << 2 | (dir as u64) << 1 | initiator as u64)
}
pub fn initiator(self) -> Side {
if self.0 & 0x1 == 0 {
Side::Client
} else {
Side::Server
}
}
pub fn dir(self) -> Dir {
if self.0 & 0x2 == 0 {
Dir::Bi
} else {
Dir::Uni
}
}
pub fn index(self) -> u64 {
self.0 >> 2
}
}
impl Into<VarInt> for StreamId {
fn into(self) -> VarInt {
unsafe { VarInt::from_u64_unchecked(self.0) }
}
}
impl From<VarInt> for StreamId {
fn from(v: VarInt) -> Self {
Self(v.0)
}
}
impl coding::Codec for StreamId {
fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<StreamId> {
VarInt::decode(buf).map(|x| StreamId(x.into_inner()))
}
fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
VarInt::from_u64(self.0).unwrap().encode(buf);
}
}
#[derive(Debug)]
pub struct Transmit {
pub destination: SocketAddr,
pub ecn: Option<EcnCodepoint>,
pub contents: Vec<u8>,
pub segment_size: Option<usize>,
pub src_ip: Option<IpAddr>,
}
const LOC_CID_COUNT: u64 = 8;
const RESET_TOKEN_SIZE: usize = 16;
const MAX_CID_SIZE: usize = 20;
const MIN_INITIAL_SIZE: u16 = 1200;
const MIN_MTU: u16 = 1232;
const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
const MAX_STREAM_COUNT: u64 = 1 << 60;