use std::collections::VecDeque;
use std::net::{SocketAddr, UdpSocket};
use std::sync::Arc;
use std::time::{Duration, Instant};
use rustc_hash::{FxHashMap, FxHashSet};
use super::quic::CongestionController;
pub use super::quic::parse_varint;
use zenith_tls::quic::{
apply_header_protection, decrypt_packet, remove_header_protection,
QuicCryptoError, QuicCryptoSession, QuicVersion,
};
use zenith_tls::CertGeneration;
use zenith_tls::fingerprint::Ja3Fingerprint;
use rustls::quic::Keys;
pub const QUIC_VERSION_V1: u32 = 0x00000001;
pub const QUIC_VERSION_V2: u32 = 0x6b3343cf;
pub const LONG_HEADER_FLAG: u8 = 0x80;
pub const LONG_TYPE_INITIAL: u8 = 0x00;
pub const LONG_TYPE_0RTT: u8 = 0x20;
pub const LONG_TYPE_HANDSHAKE: u8 = 0x40;
pub const LONG_TYPE_RETRY: u8 = 0x60;
pub const FRAME_PADDING: u64 = 0x00;
pub const FRAME_PING: u64 = 0x01;
pub const FRAME_ACK: u64 = 0x02;
pub const FRAME_ACK_ECN: u64 = 0x03;
pub const FRAME_RESET_STREAM: u64 = 0x04;
pub const FRAME_STOP_SENDING: u64 = 0x05;
pub const FRAME_CRYPTO: u64 = 0x06;
pub const FRAME_NEW_TOKEN: u64 = 0x07;
pub const FRAME_STREAM: u64 = 0x08;
pub const FRAME_MAX_DATA: u64 = 0x10;
pub const FRAME_MAX_STREAM_DATA: u64 = 0x11;
pub const FRAME_MAX_STREAMS_BIDI: u64 = 0x12;
pub const FRAME_MAX_STREAMS_UNI: u64 = 0x13;
pub const FRAME_DATA_BLOCKED: u64 = 0x14;
pub const FRAME_STREAM_DATA_BLOCKED: u64 = 0x15;
pub const FRAME_STREAMS_BLOCKED_BIDI: u64 = 0x16;
pub const FRAME_STREAMS_BLOCKED_UNI: u64 = 0x17;
pub const FRAME_NEW_CONNECTION_ID: u64 = 0x18;
pub const FRAME_RETIRE_CONNECTION_ID: u64 = 0x19;
pub const FRAME_PATH_CHALLENGE: u64 = 0x1a;
pub const FRAME_PATH_RESPONSE: u64 = 0x1b;
pub const FRAME_CONNECTION_CLOSE: u64 = 0x1c;
pub const FRAME_CONNECTION_CLOSE_APP: u64 = 0x1d;
pub const FRAME_HANDSHAKE_DONE: u64 = 0x1e;
const MAX_CRYPTO_BUFFER_SIZE: usize = 65536;
#[derive(Debug, thiserror::Error)]
pub enum QuicServerError {
#[error("crypto error: {0}")]
Crypto(#[from] QuicCryptoError),
#[error("rustls error: {0}")]
Rustls(#[from] rustls::Error),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("packet parse error: {0}")]
PacketParse(String),
#[error("handshake not complete")]
HandshakeNotComplete,
#[error("recv timeout")]
Timeout,
#[error("connection closed")]
ConnectionClosed,
#[error("flow control error: {0}")]
FlowControl(String),
#[error("secure random source unavailable: {0}")]
RandomUnavailable(String),
}
#[inline]
fn packet_number_len(first_byte: u8) -> usize {
((first_byte & 0x03) + 1) as usize
}
pub fn encode_varint(v: u64) -> Vec<u8> {
assert!(
v <= zenith_foundation::varint::MAX_VARINT_VALUE,
"QUIC varint 值域契约违反: {v} >= 2^62"
);
let mut out = Vec::with_capacity(zenith_foundation::varint::MAX_VARINT_SIZE);
let _ = zenith_foundation::varint::encode_varint(v, &mut out);
out
}
#[inline]
fn push_varint(out: &mut Vec<u8>, v: u64) {
assert!(
v <= zenith_foundation::varint::MAX_VARINT_VALUE,
"QUIC varint 值域契约违反: {v} >= 2^62"
);
let mut buf = [0u8; zenith_foundation::varint::MAX_VARINT_SIZE];
if let Ok(n) = zenith_foundation::varint::encode_varint_buf(v, &mut buf) {
out.extend_from_slice(&buf[..n]);
}
}
#[derive(Debug, Clone)]
pub struct ParsedLongHeader {
pub first_byte: u8,
pub version: u32,
pub dcid: Vec<u8>,
pub scid: Vec<u8>,
pub token: Vec<u8>,
pub length: u64,
pub packet_number_raw: u64,
pub packet_number_len: usize,
pub pn_offset: usize,
pub payload_offset: usize,
pub payload_len: usize,
pub long_frame: u8,
}
pub fn parse_long_header_full(buf: &[u8]) -> Result<ParsedLongHeader, QuicServerError> {
if buf.len() < 6 {
return Err(QuicServerError::PacketParse("packet too short".into()));
}
let first = buf[0];
if (first & 0x80) == 0 {
return Err(QuicServerError::PacketParse("not a long header".into()));
}
let version = u32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
if version != 0 && (first & 0x40) == 0 {
return Err(QuicServerError::PacketParse(
"long header fixed bit (0x40) must be set".into(),
));
}
let mut off = 5;
if off >= buf.len() {
return Err(QuicServerError::PacketParse("dcid len missing".into()));
}
let dcid_len = buf[off] as usize;
off += 1;
let dcid_end = off
.checked_add(dcid_len)
.ok_or_else(|| QuicServerError::PacketParse("dcid offset overflow".into()))?;
if dcid_len > 20 || dcid_end > buf.len() {
return Err(QuicServerError::PacketParse("dcid out of bounds".into()));
}
let dcid = buf[off..dcid_end].to_vec();
off = dcid_end;
if off >= buf.len() {
return Err(QuicServerError::PacketParse("scid len missing".into()));
}
let scid_len = buf[off] as usize;
off += 1;
let scid_end = off
.checked_add(scid_len)
.ok_or_else(|| QuicServerError::PacketParse("scid offset overflow".into()))?;
if scid_len > 20 || scid_end > buf.len() {
return Err(QuicServerError::PacketParse("scid out of bounds".into()));
}
let scid = buf[off..scid_end].to_vec();
off = scid_end;
let long_frame = (first & 0x30) >> 4;
let mut token = Vec::new();
if long_frame == 0 && version != 0 {
if off >= buf.len() {
return Err(QuicServerError::PacketParse("token len missing".into()));
}
let (token_len, token_len_bytes) = parse_varint(&buf[off..])
.ok_or_else(|| QuicServerError::PacketParse("token varint parse failed".into()))?;
off += token_len_bytes;
let token_end = off
.checked_add(token_len as usize)
.ok_or_else(|| QuicServerError::PacketParse("token offset overflow".into()))?;
if token_end > buf.len() {
return Err(QuicServerError::PacketParse("token out of bounds".into()));
}
token = buf[off..token_end].to_vec();
off = token_end;
}
if off >= buf.len() {
return Err(QuicServerError::PacketParse("length missing".into()));
}
let (length, length_bytes) = parse_varint(&buf[off..])
.ok_or_else(|| QuicServerError::PacketParse("length varint parse failed".into()))?;
off += length_bytes;
let pn_len = packet_number_len(first);
let pn_end = off
.checked_add(pn_len)
.ok_or_else(|| QuicServerError::PacketParse("pn offset overflow".into()))?;
if pn_end > buf.len() {
return Err(QuicServerError::PacketParse("pn out of bounds".into()));
}
let mut pn: u64 = 0;
for &b in &buf[off..pn_end] {
pn = (pn << 8) | (b as u64);
}
let pn_offset = off;
off = pn_end;
let length_usize = length as usize;
if length_usize < pn_len {
return Err(QuicServerError::PacketParse("length less than pn_len".into()));
}
let payload_len = length_usize - pn_len;
let payload_end = off
.checked_add(payload_len)
.ok_or_else(|| QuicServerError::PacketParse("payload offset overflow".into()))?;
if payload_end > buf.len() {
return Err(QuicServerError::PacketParse("payload out of bounds".into()));
}
Ok(ParsedLongHeader {
first_byte: first,
version,
dcid,
scid,
token,
length,
packet_number_raw: pn,
packet_number_len: pn_len,
pn_offset,
payload_offset: off,
payload_len,
long_frame,
})
}
#[derive(Debug, Clone)]
pub struct ParsedShortHeader {
pub first_byte: u8,
pub dcid: Vec<u8>,
pub pn_offset: usize,
pub packet_number_len: usize,
pub packet_number_raw: u64,
pub payload_offset: usize,
}
pub fn parse_short_header(
buf: &[u8],
dcid_len: usize,
) -> Result<ParsedShortHeader, QuicServerError> {
if buf.is_empty() {
return Err(QuicServerError::PacketParse("empty short header".into()));
}
let first = buf[0];
if (first & 0x80) != 0 {
return Err(QuicServerError::PacketParse("not a short header".into()));
}
let pn_len = packet_number_len(first);
let mut off: usize = 1;
let dcid_end = off
.checked_add(dcid_len)
.ok_or_else(|| QuicServerError::PacketParse("dcid offset overflow".into()))?;
if dcid_end > buf.len() {
return Err(QuicServerError::PacketParse("dcid out of bounds".into()));
}
let dcid = buf[off..dcid_end].to_vec();
off = dcid_end;
let pn_end = off
.checked_add(pn_len)
.ok_or_else(|| QuicServerError::PacketParse("pn offset overflow".into()))?;
if pn_end > buf.len() {
return Err(QuicServerError::PacketParse("pn out of bounds".into()));
}
let mut pn: u64 = 0;
for &b in &buf[off..pn_end] {
pn = (pn << 8) | (b as u64);
}
let pn_offset = off;
off = pn_end;
Ok(ParsedShortHeader {
first_byte: first,
dcid,
pn_offset,
packet_number_len: pn_len,
packet_number_raw: pn,
payload_offset: off,
})
}
pub fn decode_packet_number(
truncated: u64,
pn_len: usize,
largest_pn: u64,
) -> u64 {
let expected_pn = largest_pn + 1;
let pn_nbits = pn_len * 8;
let pn_win = 1u64 << pn_nbits;
let pn_hwin = pn_win / 2;
let pn_mask = pn_win - 1;
let candidate_pn = (expected_pn & !pn_mask) | (truncated & pn_mask);
if candidate_pn + pn_hwin <= expected_pn && candidate_pn < (1u64 << 62) - pn_win {
candidate_pn + pn_win
} else if candidate_pn > expected_pn + pn_hwin && candidate_pn >= pn_win {
candidate_pn - pn_win
} else {
candidate_pn
}
}
#[derive(Debug, Clone)]
pub enum QuicFrame {
Padding,
Ping,
Ack {
largest_acked: u64,
ack_delay: u64,
ack_ranges: Vec<(u64, u64)>,
},
ResetStream {
stream_id: u64,
error_code: u64,
final_size: u64,
},
StopSending {
stream_id: u64,
error_code: u64,
},
Crypto {
offset: u64,
data: Vec<u8>,
},
NewToken {
token: Vec<u8>,
},
Stream {
stream_id: u64,
offset: u64,
fin: bool,
data: Vec<u8>,
},
MaxData {
max_data: u64,
},
MaxStreamData {
stream_id: u64,
max_stream_data: u64,
},
MaxStreamsBidi {
max_streams: u64,
},
MaxStreamsUni {
max_streams: u64,
},
DataBlocked {
limit: u64,
},
StreamDataBlocked {
stream_id: u64,
limit: u64,
},
StreamsBlockedBidi {
limit: u64,
},
StreamsBlockedUni {
limit: u64,
},
NewConnectionId {
sequence_number: u64,
retire_prior_to: u64,
connection_id: Vec<u8>,
stateless_reset_token: [u8; 16],
},
RetireConnectionId {
sequence_number: u64,
},
PathChallenge {
data: [u8; 8],
},
PathResponse {
data: [u8; 8],
},
ConnectionClose {
error_code: u64,
frame_type: u64,
reason: Vec<u8>,
},
HandshakeDone,
Unknown {
frame_type: u64,
data: Vec<u8>,
},
}
pub fn parse_frame(buf: &[u8]) -> Option<(QuicFrame, usize)> {
if buf.is_empty() {
return None;
}
let (frame_type, ft_len) = parse_varint(buf)?;
let mut off = ft_len;
let frame = match frame_type {
FRAME_PADDING => {
QuicFrame::Padding
}
FRAME_PING => QuicFrame::Ping,
FRAME_ACK | FRAME_ACK_ECN => {
let (largest_acked, n) = parse_varint(&buf[off..])?;
off += n;
let (ack_delay, n) = parse_varint(&buf[off..])?;
off += n;
let (ack_range_count, n) = parse_varint(&buf[off..])?;
off += n;
let (first_ack_range, n) = parse_varint(&buf[off..])?;
off += n;
let mut ack_ranges = Vec::new();
let mut lowest = largest_acked.checked_sub(first_ack_range)?;
ack_ranges.push((lowest, largest_acked));
for _ in 0..ack_range_count {
let (gap, n) = parse_varint(&buf[off..])?;
off += n;
let (range_len, n) = parse_varint(&buf[off..])?;
off += n;
let high = lowest.checked_sub(gap)?.checked_sub(2)?;
let low = high.checked_sub(range_len)?;
ack_ranges.push((low, high));
lowest = low;
}
if frame_type == FRAME_ACK_ECN {
for _ in 0..3 {
let (_, n) = parse_varint(&buf[off..])?;
off += n;
}
}
QuicFrame::Ack {
largest_acked,
ack_delay,
ack_ranges,
}
}
FRAME_RESET_STREAM => {
let (stream_id, n) = parse_varint(&buf[off..])?;
off += n;
let (error_code, n) = parse_varint(&buf[off..])?;
off += n;
let (final_size, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::ResetStream {
stream_id,
error_code,
final_size,
}
}
FRAME_STOP_SENDING => {
let (stream_id, n) = parse_varint(&buf[off..])?;
off += n;
let (error_code, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::StopSending { stream_id, error_code }
}
FRAME_CRYPTO => {
let (offset, n) = parse_varint(&buf[off..])?;
off += n;
let (length, n) = parse_varint(&buf[off..])?;
off += n;
const MAX_CRYPTO_FRAME_LEN: usize = 16384;
let length_usize = usize::try_from(length)
.ok()
.filter(|&l| l <= MAX_CRYPTO_FRAME_LEN)
.unwrap_or(MAX_CRYPTO_FRAME_LEN);
let end = match off.checked_add(length_usize) {
Some(e) if e <= buf.len() => e,
_ => return None,
};
let data = buf[off..end].to_vec();
off = end;
QuicFrame::Crypto { offset, data }
}
FRAME_NEW_TOKEN => {
let (length, n) = parse_varint(&buf[off..])?;
off += n;
if off + length as usize > buf.len() {
return None;
}
let token = buf[off..off + length as usize].to_vec();
off += length as usize;
QuicFrame::NewToken { token }
}
t if (0x08..=0x0f).contains(&t) => {
let has_fin = (t & 0x01) != 0;
let has_len = (t & 0x02) != 0;
let has_off = (t & 0x04) != 0;
let (stream_id, n) = parse_varint(&buf[off..])?;
off += n;
let offset = if has_off {
let (o, n) = parse_varint(&buf[off..])?;
off += n;
o
} else {
0
};
let length = if has_len {
let (l, n) = parse_varint(&buf[off..])?;
off += n;
l as usize
} else {
buf.len() - off
};
if off + length > buf.len() {
return None;
}
let data = buf[off..off + length].to_vec();
off += length;
QuicFrame::Stream {
stream_id,
offset,
fin: has_fin,
data,
}
}
FRAME_MAX_DATA => {
let (max_data, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::MaxData { max_data }
}
FRAME_MAX_STREAM_DATA => {
let (stream_id, n) = parse_varint(&buf[off..])?;
off += n;
let (max_stream_data, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::MaxStreamData {
stream_id,
max_stream_data,
}
}
FRAME_MAX_STREAMS_BIDI => {
let (max_streams, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::MaxStreamsBidi { max_streams }
}
FRAME_MAX_STREAMS_UNI => {
let (max_streams, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::MaxStreamsUni { max_streams }
}
FRAME_DATA_BLOCKED => {
let (limit, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::DataBlocked { limit }
}
FRAME_STREAM_DATA_BLOCKED => {
let (stream_id, n) = parse_varint(&buf[off..])?;
off += n;
let (limit, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::StreamDataBlocked { stream_id, limit }
}
FRAME_STREAMS_BLOCKED_BIDI => {
let (limit, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::StreamsBlockedBidi { limit }
}
FRAME_STREAMS_BLOCKED_UNI => {
let (limit, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::StreamsBlockedUni { limit }
}
FRAME_NEW_CONNECTION_ID => {
let (sequence_number, n) = parse_varint(&buf[off..])?;
off += n;
let (retire_prior_to, n) = parse_varint(&buf[off..])?;
off += n;
if off >= buf.len() {
return None;
}
let cid_len = buf[off] as usize;
off += 1;
if off + cid_len > buf.len() {
return None;
}
let connection_id = buf[off..off + cid_len].to_vec();
off += cid_len;
if off + 16 > buf.len() {
return None;
}
let mut stateless_reset_token = [0u8; 16];
stateless_reset_token.copy_from_slice(&buf[off..off + 16]);
off += 16;
QuicFrame::NewConnectionId {
sequence_number,
retire_prior_to,
connection_id,
stateless_reset_token,
}
}
FRAME_RETIRE_CONNECTION_ID => {
let (sequence_number, n) = parse_varint(&buf[off..])?;
off += n;
QuicFrame::RetireConnectionId { sequence_number }
}
FRAME_PATH_CHALLENGE => {
if off + 8 > buf.len() {
return None;
}
let mut data = [0u8; 8];
data.copy_from_slice(&buf[off..off + 8]);
off += 8;
QuicFrame::PathChallenge { data }
}
FRAME_PATH_RESPONSE => {
if off + 8 > buf.len() {
return None;
}
let mut data = [0u8; 8];
data.copy_from_slice(&buf[off..off + 8]);
off += 8;
QuicFrame::PathResponse { data }
}
FRAME_CONNECTION_CLOSE | FRAME_CONNECTION_CLOSE_APP => {
let (error_code, n) = parse_varint(&buf[off..])?;
off += n;
let frame_type_field: u64 = if frame_type == FRAME_CONNECTION_CLOSE {
let (ft, n) = parse_varint(&buf[off..])?;
off += n;
ft
} else {
0
};
let (reason_len, n) = parse_varint(&buf[off..])?;
off += n;
if off + reason_len as usize > buf.len() {
return None;
}
let reason = buf[off..off + reason_len as usize].to_vec();
off += reason_len as usize;
QuicFrame::ConnectionClose {
error_code,
frame_type: frame_type_field,
reason,
}
}
FRAME_HANDSHAKE_DONE => QuicFrame::HandshakeDone,
_ => {
return None;
}
};
Some((frame, off))
}
pub fn parse_frames(mut buf: &[u8]) -> Vec<QuicFrame> {
let mut frames = Vec::new();
while !buf.is_empty() {
match parse_frame(buf) {
Some((frame, consumed)) => {
frames.push(frame);
buf = &buf[consumed..];
}
None => break,
}
}
frames
}
pub fn build_crypto_frame(offset: u64, data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x06); push_varint(&mut out, offset);
push_varint(&mut out, data.len() as u64);
out.extend_from_slice(data);
out
}
fn scan_client_hello_extensions(ch: &[u8]) -> Vec<u16> {
let mut exts = Vec::new();
let mut p = 4 + 2 + 32;
if ch.len() < p + 1 {
return exts;
}
let sid_len = ch[p] as usize;
p += 1 + sid_len;
if ch.len() < p + 2 {
return exts;
}
let cs_len = ((ch[p] as usize) << 8) | (ch[p + 1] as usize);
p += 2 + cs_len;
if ch.len() < p + 1 {
return exts;
}
let comp_len = ch[p] as usize;
p += 1 + comp_len;
if ch.len() < p + 2 {
return exts;
}
let ext_total = ((ch[p] as usize) << 8) | (ch[p + 1] as usize);
p += 2;
let end = (p + ext_total).min(ch.len());
while p + 4 <= end {
let et = ((ch[p] as u16) << 8) | (ch[p + 1] as u16);
let el = ((ch[p + 2] as usize) << 8) | (ch[p + 3] as usize);
exts.push(et);
p += 4 + el;
}
exts
}
pub fn build_ack_frame(largest_acked: u64, ack_delay: u64, ack_range: u64) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x02); push_varint(&mut out, largest_acked);
push_varint(&mut out, ack_delay);
push_varint(&mut out, 0); push_varint(&mut out, ack_range); out
}
pub fn build_stream_frame(stream_id: u64, offset: u64, fin: bool, data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let mut frame_type: u8 = 0x08 | 0x02 | 0x04; if fin {
frame_type |= 0x01;
}
out.push(frame_type);
push_varint(&mut out, stream_id);
push_varint(&mut out, offset);
push_varint(&mut out, data.len() as u64);
out.extend_from_slice(data);
out
}
pub fn build_handshake_done_frame() -> Vec<u8> {
vec![0x1e]
}
pub fn build_path_challenge_frame(data: &[u8; 8]) -> Vec<u8> {
let mut out = Vec::with_capacity(9);
out.push(FRAME_PATH_CHALLENGE as u8);
out.extend_from_slice(data);
out
}
pub fn build_path_response_frame(data: &[u8; 8]) -> Vec<u8> {
let mut out = Vec::with_capacity(9);
out.push(FRAME_PATH_RESPONSE as u8);
out.extend_from_slice(data);
out
}
pub fn build_connection_close_app(error_code: u64, reason: &str) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x1d); push_varint(&mut out, error_code);
push_varint(&mut out, reason.len() as u64);
out.extend_from_slice(reason.as_bytes());
out
}
pub fn build_connection_close_transport(error_code: u64, frame_type: u64, reason: &str) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x1c); push_varint(&mut out, error_code);
push_varint(&mut out, frame_type);
push_varint(&mut out, reason.len() as u64);
out.extend_from_slice(reason.as_bytes());
out
}
pub fn build_padding_frame(n: usize) -> Vec<u8> {
vec![0u8; n]
}
pub fn build_ping_frame() -> Vec<u8> {
vec![0x01]
}
#[derive(Clone)]
pub struct QuicServerConfig {
pub bind_addr: SocketAddr,
pub rustls_config: Arc<rustls::ServerConfig>,
pub version: QuicVersion,
pub scid_len: usize,
pub scid: Vec<u8>,
pub idle_timeout_ms: u64,
pub max_recv_bytes: usize,
}
impl std::fmt::Debug for QuicServerConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QuicServerConfig")
.field("bind_addr", &self.bind_addr)
.field("version", &self.version)
.field("scid_len", &self.scid_len)
.field("idle_timeout_ms", &self.idle_timeout_ms)
.finish()
}
}
impl QuicServerConfig {
pub fn new(
bind_addr: SocketAddr,
rustls_config: Arc<rustls::ServerConfig>,
version: QuicVersion,
) -> Result<Self, QuicServerError> {
let scid_len = 8usize;
let mut scid = vec![0u8; scid_len];
if !zenith_foundation::random::try_fill_random(&mut scid) {
return Err(QuicServerError::RandomUnavailable(
"CSPRNG 填充服务端 SCID 失败".into(),
));
}
Ok(Self {
bind_addr,
rustls_config,
version,
scid_len,
scid,
idle_timeout_ms: 10_000,
max_recv_bytes: 65535,
})
}
pub fn from_pem(
bind_addr: SocketAddr,
cert_pem: &[u8],
key_pem: &[u8],
alpn: Vec<Vec<u8>>,
version: QuicVersion,
) -> Result<Self, QuicServerError> {
let generation = CertGeneration::from_pem(cert_pem, key_pem)
.map_err(|e| QuicServerError::PacketParse(format!("cert load: {e}")))?;
let rustls_config = generation
.to_server_config(alpn)
.map_err(|e| QuicServerError::PacketParse(format!("server config: {e}")))?;
Self::new(bind_addr, rustls_config, version)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuicServerState {
WaitInitial,
HandshakeSent,
HandshakeDone,
Established,
Closed,
}
#[derive(Debug, Clone, Copy)]
pub struct SentPacket {
pub pn: u64,
pub bytes: u64,
pub sent_at: Instant,
pub ack_eliciting: bool,
}
#[derive(Debug, Default)]
pub struct StreamRecv {
segments: std::collections::BTreeMap<u64, Vec<u8>>,
delivered_end: u64,
fin_offset: Option<u64>,
fin_delivered: bool,
}
impl StreamRecv {
fn insert(&mut self, offset: u64, data: &[u8]) -> bool {
let mut start = offset;
let mut chunk = data;
if start < self.delivered_end {
let skip = (self.delivered_end - start) as usize;
if skip >= chunk.len() {
return false;
}
chunk = &chunk[skip..];
start = self.delivered_end;
}
if let Some((&prev_off, prev)) = self.segments.range(..=start).next_back() {
let prev_end = prev_off + prev.len() as u64;
if prev_end > start {
let overlap = (prev_end - start) as usize;
if overlap >= chunk.len() {
return false;
}
chunk = &chunk[overlap..];
start = prev_end;
}
}
let mut end = start + chunk.len() as u64;
if let Some((&next_off, _)) = self.segments.range(start..).next()
&& next_off < end
{
end = next_off;
}
if end > start {
self.segments
.insert(start, chunk[..(end - start) as usize].to_vec());
true
} else {
false
}
}
fn take_contiguous(&mut self) -> Option<Vec<u8>> {
let mut out = Vec::new();
while let Some((&off, _)) = self.segments.range(..=self.delivered_end).next_back() {
let Some(data) = self.segments.remove(&off) else {
break;
};
let seg_end = off + data.len() as u64;
if seg_end <= self.delivered_end {
continue; }
let skip = (self.delivered_end - off) as usize;
out.extend_from_slice(&data[skip..]);
self.delivered_end = seg_end;
}
if out.is_empty() {
None
} else {
Some(out)
}
}
fn take_fin(&mut self) -> bool {
if self.fin_delivered {
return false;
}
if let Some(fin_off) = self.fin_offset
&& self.delivered_end >= fin_off
{
self.fin_delivered = true;
return true;
}
false
}
}
pub struct QuicConnection {
pub client_addr: SocketAddr,
pub client_scid: Vec<u8>,
pub client_dcid: Vec<u8>,
pub server_scid: Vec<u8>,
pub server_dcid: Vec<u8>,
pub crypto_session: QuicCryptoSession,
pub initial_keys: Keys,
pub handshake_keys: Option<Keys>,
pub one_rtt_keys: Option<Keys>,
pub pending_err_ack: Option<Vec<u8>>,
pub state: QuicServerState,
pub next_initial_pn: u64,
pub next_handshake_pn: u64,
pub next_1rtt_pn: u64,
pub largest_initial_pn: u64,
pub largest_handshake_pn: u64,
pub largest_1rtt_pn: u64,
pub initial_crypto_rx: Vec<u8>,
pub initial_crypto_consumed: usize,
pub handshake_crypto_rx: Vec<u8>,
pub handshake_crypto_consumed: usize,
pub initial_crypto_tx_offset: u64,
pub handshake_crypto_tx_offset: u64,
pub stream_rx: FxHashMap<u64, StreamRecv>,
pub server_opened_streams: FxHashSet<u64>,
pub established_at: Option<Instant>,
pub handshake_done_sent: bool,
pub congestion: CongestionController,
pub sent_1rtt: VecDeque<SentPacket>,
pub max_tracked_sent: usize,
pub fc_stream_end: FxHashMap<u64, u64>,
pub fc_conn_consumed: u64,
pub pending_path_response: Vec<[u8; 8]>,
pub one_rtt_crypto_tx_offset: u64,
pub zero_rtt_keys: Option<rustls::quic::DirectionalKeys>,
pub early_stream_rx: FxHashMap<u64, StreamRecv>,
pub key_phase: bool,
pub key_update_count: u64,
pub initial_flight: Vec<Vec<u8>>,
pub handshake_flight: Vec<Vec<u8>>,
pub dup_stream_sids: Vec<u64>,
pub sni: Option<String>,
pub client_fingerprint: Option<Ja3Fingerprint>,
}
impl std::fmt::Debug for QuicConnection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QuicConnection")
.field("client_addr", &self.client_addr)
.field("client_scid_len", &self.client_scid.len())
.field("client_dcid_len", &self.client_dcid.len())
.field("server_scid_len", &self.server_scid.len())
.field("server_dcid_len", &self.server_dcid.len())
.field("state", &self.state)
.field("next_initial_pn", &self.next_initial_pn)
.field("next_handshake_pn", &self.next_handshake_pn)
.field("next_1rtt_pn", &self.next_1rtt_pn)
.field("handshake_done", &self.crypto_session.is_handshake_done())
.finish()
}
}
impl QuicConnection {
const FC_MAX_DATA: u64 = 1_048_576;
const FC_MAX_STREAM_DATA: u64 = 262_144;
const FC_MAX_STREAM_ID: u64 = 400;
pub fn new(
client_addr: SocketAddr,
client_scid: Vec<u8>,
client_dcid: Vec<u8>,
server_scid: Vec<u8>,
rustls_config: Arc<rustls::ServerConfig>,
version: QuicVersion,
) -> Result<Self, QuicServerError> {
let transport_params = build_default_transport_params(&client_dcid, &server_scid);
let (crypto_session, initial_keys) = QuicCryptoSession::new(
rustls_config,
version,
&client_dcid,
transport_params,
)?;
Ok(Self {
client_addr,
client_scid,
client_dcid,
server_scid: server_scid.clone(),
server_dcid: server_scid, crypto_session,
initial_keys,
handshake_keys: None,
one_rtt_keys: None,
pending_err_ack: None,
state: QuicServerState::WaitInitial,
next_initial_pn: 0,
next_handshake_pn: 0,
next_1rtt_pn: 0,
largest_initial_pn: 0,
largest_handshake_pn: 0,
largest_1rtt_pn: 0,
initial_crypto_rx: Vec::new(),
initial_crypto_consumed: 0,
handshake_crypto_rx: Vec::new(),
handshake_crypto_consumed: 0,
initial_crypto_tx_offset: 0,
handshake_crypto_tx_offset: 0,
stream_rx: FxHashMap::default(),
server_opened_streams: FxHashSet::default(),
established_at: None,
handshake_done_sent: false,
congestion: CongestionController::new(),
sent_1rtt: VecDeque::new(),
max_tracked_sent: 1024,
fc_stream_end: FxHashMap::default(),
fc_conn_consumed: 0,
pending_path_response: Vec::new(),
one_rtt_crypto_tx_offset: 0,
zero_rtt_keys: None,
early_stream_rx: FxHashMap::default(),
key_phase: false,
key_update_count: 0,
initial_flight: Vec::new(),
handshake_flight: Vec::new(),
dup_stream_sids: Vec::new(),
sni: None,
client_fingerprint: None,
})
}
pub fn is_handshake_done(&self) -> bool {
self.crypto_session.is_handshake_done()
}
#[inline]
pub fn sni(&self) -> Option<&str> {
self.sni.as_deref()
}
pub fn take_client_fingerprint(&mut self) -> Option<Ja3Fingerprint> {
self.client_fingerprint.take()
}
pub fn is_established(&self) -> bool {
self.state == QuicServerState::Established
}
pub fn handle_initial_packet(
&mut self,
packet: &[u8],
) -> Result<Vec<Vec<u8>>, QuicServerError> {
let first_byte_raw = packet.first().copied().unwrap_or(0);
tracing::debug!(len = packet.len(), first_byte = format_args!("{first_byte_raw:#04x}"), "QUIC Initial recv");
let parsed = parse_long_header_full(packet)?;
tracing::debug!(
version = format_args!("{:#010x}", parsed.version),
dcid = ?parsed.dcid, scid = ?parsed.scid, token_len = parsed.token.len(),
length = parsed.length, pn_len = parsed.packet_number_len,
payload_offset = parsed.payload_offset, payload_len = parsed.payload_len,
"QUIC Initial parsed"
);
if parsed.long_frame != 0 {
return Err(QuicServerError::PacketParse(format!(
"not an Initial packet: long_frame={}",
parsed.long_frame
)));
}
let pn_offset = parsed.pn_offset;
let length_value = parsed.length as usize;
let packet_end = pn_offset
.checked_add(length_value)
.ok_or_else(|| QuicServerError::PacketParse("packet_end overflow".into()))?;
if packet_end > packet.len() {
return Err(QuicServerError::PacketParse("payload out of bounds".into()));
}
let mut buf = packet[..packet_end].to_vec();
let hp_key = &*self.initial_keys.remote.header;
let sample_len = hp_key.sample_len();
let sample_start = pn_offset
.checked_add(4) .ok_or_else(|| QuicServerError::PacketParse("sample_start overflow".into()))?;
let sample_end = sample_start
.checked_add(sample_len)
.ok_or_else(|| QuicServerError::PacketParse("sample_end overflow".into()))?;
if sample_end > buf.len() {
return Err(QuicServerError::PacketParse("hp sample too short".into()));
}
let sample = buf[sample_start..sample_end].to_vec();
let mut first_byte = buf[0];
{
let pn_bytes = &mut buf[pn_offset..sample_start];
remove_header_protection(hp_key, &sample, &mut first_byte, pn_bytes)?;
}
buf[0] = first_byte;
let real_pn_len = ((first_byte & 0x03) as usize) + 1;
let header_end = pn_offset
.checked_add(real_pn_len)
.ok_or_else(|| QuicServerError::PacketParse("header_end overflow".into()))?;
if header_end > packet_end {
return Err(QuicServerError::PacketParse("header_end out of bounds".into()));
}
let mut pn_plain: u64 = 0;
for i in 0..real_pn_len {
pn_plain = (pn_plain << 8) | (buf[pn_offset + i] as u64);
}
let pn = decode_packet_number(
pn_plain,
real_pn_len,
self.largest_initial_pn,
);
self.largest_initial_pn = pn;
let ciphertext_len = packet_end
.checked_sub(header_end)
.ok_or_else(|| QuicServerError::PacketParse("ciphertext_len underflow".into()))?;
let (header, payload) = buf.split_at_mut(header_end);
let header: &[u8] = header;
let payload = &mut payload[..ciphertext_len];
tracing::debug!(aad_len = header.len(), ciphertext_len, "QUIC Initial AEAD decrypt");
let decrypted = decrypt_packet(
&*self.initial_keys.remote.packet,
pn,
header,
payload,
).map_err(|e| {
tracing::warn!(error = ?e, "QUIC Initial decrypt failed");
e
})?;
let frames = parse_frames(decrypted);
let had_crypto = frames.iter().any(|f| matches!(f, QuicFrame::Crypto { .. }));
tracing::debug!(frame_count = frames.len(), "QUIC Initial frames parsed");
for frame in &frames {
if let QuicFrame::Crypto { offset, data } = frame {
let off = *offset as usize;
let end = off
.checked_add(data.len())
.ok_or_else(|| QuicServerError::PacketParse("crypto offset overflow".into()))?;
if end > self.initial_crypto_rx.len() {
if end > MAX_CRYPTO_BUFFER_SIZE {
return Err(QuicServerError::PacketParse(
"CRYPTO buffer exceeds maximum size".into(),
));
}
self.initial_crypto_rx.resize(end, 0);
}
self.initial_crypto_rx[off..end].copy_from_slice(data);
}
}
let prev_consumed = self.initial_crypto_consumed;
let consumed = self.initial_crypto_consumed;
let available = self.initial_crypto_rx.len();
if available > consumed {
let new_data = &self.initial_crypto_rx[consumed..available];
tracing::debug!(crypto_offset = consumed, len = new_data.len(), "QUIC Initial feed rustls");
if self.initial_crypto_rx.len() >= 4 && self.initial_crypto_rx[0] == 0x01 {
let ch_len = ((self.initial_crypto_rx[1] as usize) << 16)
| ((self.initial_crypto_rx[2] as usize) << 8)
| (self.initial_crypto_rx[3] as usize);
if let Some(ch_end) = 4usize.checked_add(ch_len)
&& self.initial_crypto_rx.len() >= ch_end {
let exts = scan_client_hello_extensions(&self.initial_crypto_rx[..ch_end]);
tracing::debug!(extensions = format_args!("{exts:04x?}"), "QUIC ClientHello extensions");
if !exts.contains(&0x0039) {
return Err(QuicServerError::Crypto(
QuicCryptoError::KeyDerivation(
"missing_extension: client hello lacks quic_transport_parameters extension (0x0039)".into(),
),
));
}
if self.sni.is_none()
&& let Some(sni) = zenith_tls::TlsAcceptor::peek_sni_quic(
&self.initial_crypto_rx[..ch_end],
) {
tracing::debug!(sni, "QUIC ClientHello SNI extracted");
self.sni = Some(sni.to_ascii_lowercase());
}
if self.client_fingerprint.is_none()
&& let Some(fp) = Ja3Fingerprint::from_quic_client_hello(
&self.initial_crypto_rx[..ch_end],
) {
tracing::debug!(
ja3 = %fp.ja3_hash, ja4 = %fp.ja4_hash,
"QUIC ClientHello JA3/JA4 fingerprint captured"
);
self.client_fingerprint = Some(fp);
}
}
}
self.crypto_session.process_handshake_data(new_data)?;
self.initial_crypto_consumed = available;
}
let mut output_packets = Vec::new();
let version = self.version();
let saved_initial_pn = self.next_initial_pn;
loop {
let result = match self.crypto_session.write_handshake() {
Ok(r) => r,
Err(e) => {
self.next_initial_pn = saved_initial_pn;
return Err(e.into());
}
};
match result {
Some((tls_data, new_keys)) => {
tracing::debug!(
tls_data_len = tls_data.len(),
has_new_keys = new_keys.is_some(),
"QUIC Initial write_hs"
);
if !tls_data.is_empty() {
if let Some(keys) = new_keys {
if tls_data.len() < 4 {
return Err(QuicServerError::PacketParse("tls_data too short".into()));
}
let _msg_type = tls_data[0];
let msg_len = ((tls_data[1] as usize) << 16)
| ((tls_data[2] as usize) << 8)
| (tls_data[3] as usize);
let server_hello_end = 4usize
.checked_add(msg_len)
.ok_or_else(|| QuicServerError::PacketParse("ServerHello len overflow".into()))?;
if server_hello_end > tls_data.len() {
return Err(QuicServerError::PacketParse(
"ServerHello out of bounds".into(),
));
}
let server_hello = &tls_data[..server_hello_end];
let remaining = &tls_data[server_hello_end..];
tracing::debug!(
server_hello_len = server_hello.len(),
remaining_len = remaining.len(),
"QUIC Initial split ServerHello"
);
let init_off = self.initial_crypto_tx_offset;
let crypto_frame = build_crypto_frame(init_off, server_hello);
self.initial_crypto_tx_offset = self
.initial_crypto_tx_offset
.checked_add(server_hello.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("initial crypto tx offset overflow".into()))?;
let ack_frame = build_ack_frame(self.largest_initial_pn, 0, 0);
let mut init_payload = Vec::with_capacity(ack_frame.len() + crypto_frame.len());
init_payload.extend_from_slice(&ack_frame);
init_payload.extend_from_slice(&crypto_frame);
let initial_packet = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
0, &self.initial_keys,
&mut self.next_initial_pn,
&init_payload,
)?;
output_packets.push(initial_packet);
self.handshake_keys = Some(keys);
if !remaining.is_empty() {
let hs_off = self.handshake_crypto_tx_offset;
let crypto_frame = build_crypto_frame(hs_off, remaining);
self.handshake_crypto_tx_offset = self
.handshake_crypto_tx_offset
.checked_add(remaining.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("handshake crypto tx offset overflow".into()))?;
let handshake_packet = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
2, self.handshake_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_handshake_pn,
&crypto_frame,
)?;
output_packets.push(handshake_packet);
}
self.state = QuicServerState::HandshakeSent;
} else {
if let Some(hs_keys) = self.handshake_keys.as_ref() {
let hs_off = self.handshake_crypto_tx_offset;
let crypto_frame = build_crypto_frame(hs_off, &tls_data);
let handshake_packet = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
2, hs_keys,
&mut self.next_handshake_pn,
&crypto_frame,
)?;
self.handshake_crypto_tx_offset = self
.handshake_crypto_tx_offset
.checked_add(tls_data.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("handshake crypto tx offset overflow".into()))?;
output_packets.push(handshake_packet);
} else {
let init_off = self.initial_crypto_tx_offset;
let crypto_frame = build_crypto_frame(init_off, &tls_data);
self.initial_crypto_tx_offset = self
.initial_crypto_tx_offset
.checked_add(tls_data.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("initial crypto tx offset overflow".into()))?;
let ack_frame = build_ack_frame(self.largest_initial_pn, 0, 0);
let mut init_payload = Vec::with_capacity(ack_frame.len() + crypto_frame.len());
init_payload.extend_from_slice(&ack_frame);
init_payload.extend_from_slice(&crypto_frame);
let initial_packet = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
0,
&self.initial_keys,
&mut self.next_initial_pn,
&init_payload,
)?;
output_packets.push(initial_packet);
}
}
}
}
None => break,
}
}
if self.one_rtt_keys.is_none()
&& let Some(keys) = self.crypto_session.take_one_rtt_keys()
{
self.one_rtt_keys = Some(keys);
if let Some(tp) = self.crypto_session.peer_transport_params() {
if let Err(e) = validate_peer_transport_params(tp) {
tracing::warn!("peer transport params invalid: {}", e);
return Err(QuicServerError::PacketParse(format!(
"TRANSPORT_PARAMETER_ERROR: {e}"
)));
}
}
tracing::debug!("QUIC 1-RTT keys extracted (waiting for client Finished)");
}
let crypto_advanced = self.initial_crypto_consumed > prev_consumed;
if crypto_advanced && !output_packets.is_empty() {
self.initial_flight = output_packets.clone();
} else if had_crypto && !crypto_advanced && !self.initial_flight.is_empty() {
tracing::debug!(
flight_len = self.initial_flight.len(),
"QUIC Initial CRYPTO retransmission detected → retransmit initial flight"
);
output_packets = self.initial_flight.clone();
}
Ok(output_packets)
}
pub fn handle_0rtt_packet(
&mut self,
packet: &[u8],
) -> Result<Vec<Vec<u8>>, QuicServerError> {
let parsed = parse_long_header_full(packet)?;
if parsed.long_frame != 1 {
return Err(QuicServerError::PacketParse(format!(
"not a 0-RTT packet: long_frame={}",
parsed.long_frame
)));
}
if self.zero_rtt_keys.is_none() {
match self.crypto_session.zero_rtt_keys() {
Some(keys) => {
self.zero_rtt_keys = Some(keys);
tracing::debug!("QUIC 0-RTT keys derived (client attempting early data)");
}
None => {
tracing::debug!("QUIC 0-RTT packet dropped: no early secret");
return Ok(Vec::new());
}
}
}
let keys = self
.zero_rtt_keys
.as_ref()
.ok_or_else(|| QuicServerError::PacketParse("0-RTT keys unavailable".into()))?;
let pn_offset = parsed.pn_offset;
let length_value = parsed.length as usize;
let packet_end = pn_offset
.checked_add(length_value)
.ok_or_else(|| QuicServerError::PacketParse("packet_end overflow".into()))?;
if packet_end > packet.len() {
return Err(QuicServerError::PacketParse("payload out of bounds".into()));
}
let mut buf = packet[..packet_end].to_vec();
let hp_key = &*keys.header;
let sample_len = hp_key.sample_len();
let sample_start = pn_offset
.checked_add(4) .ok_or_else(|| QuicServerError::PacketParse("sample_start overflow".into()))?;
let sample_end = sample_start
.checked_add(sample_len)
.ok_or_else(|| QuicServerError::PacketParse("sample_end overflow".into()))?;
if sample_end > buf.len() {
return Err(QuicServerError::PacketParse("hp sample too short".into()));
}
let sample = buf[sample_start..sample_end].to_vec();
let mut first_byte = buf[0];
{
let pn_bytes = &mut buf[pn_offset..sample_start];
remove_header_protection(hp_key, &sample, &mut first_byte, pn_bytes)?;
}
buf[0] = first_byte;
let real_pn_len = ((first_byte & 0x03) as usize) + 1;
let header_end = pn_offset
.checked_add(real_pn_len)
.ok_or_else(|| QuicServerError::PacketParse("header_end overflow".into()))?;
if header_end > packet_end {
return Err(QuicServerError::PacketParse("header_end out of bounds".into()));
}
let mut pn_plain: u64 = 0;
for i in 0..real_pn_len {
pn_plain = (pn_plain << 8) | (buf[pn_offset + i] as u64);
}
let pn = decode_packet_number(pn_plain, real_pn_len, self.largest_1rtt_pn);
self.largest_1rtt_pn = self.largest_1rtt_pn.max(pn);
let ciphertext_len = packet_end
.checked_sub(header_end)
.ok_or_else(|| QuicServerError::PacketParse("ciphertext_len underflow".into()))?;
let (header, payload) = buf.split_at_mut(header_end);
let header: &[u8] = header;
let payload = &mut payload[..ciphertext_len];
let decrypted = decrypt_packet(&*keys.packet, pn, header, payload)?;
let frames = parse_frames(decrypted);
tracing::debug!(pn, decrypted_len = decrypted.len(), frame_count = frames.len(), "QUIC 0-RTT packet");
for frame in &frames {
match frame {
QuicFrame::Stream {
stream_id,
offset,
fin,
data,
} => {
let sid = *stream_id;
let end = offset.saturating_add(data.len() as u64);
if end > Self::FC_MAX_STREAM_DATA {
return Err(QuicServerError::FlowControl(format!(
"0-RTT stream {sid} end={end} > max_stream_data={}",
Self::FC_MAX_STREAM_DATA
)));
}
let prev_end = self.fc_stream_end.get(&sid).copied().unwrap_or(0);
if end > prev_end {
self.fc_conn_consumed =
self.fc_conn_consumed.saturating_add(end - prev_end);
self.fc_stream_end.insert(sid, end);
}
if self.fc_conn_consumed > Self::FC_MAX_DATA {
return Err(QuicServerError::FlowControl(format!(
"0-RTT connection consumed={} > max_data={}",
self.fc_conn_consumed,
Self::FC_MAX_DATA
)));
}
let stream_buf = self.early_stream_rx.entry(sid).or_default();
stream_buf.insert(*offset, data);
if *fin {
stream_buf.fin_offset = Some(end);
}
tracing::debug!(stream_id = sid, offset = *offset, data_len = data.len(), fin = *fin, "QUIC 0-RTT STREAM");
}
QuicFrame::Padding | QuicFrame::Ping => {}
other => {
return Err(QuicServerError::PacketParse(format!(
"illegal frame in 0-RTT packet: {other:?} (RFC 9001 §5.7)"
)));
}
}
}
Ok(Vec::new())
}
pub fn take_early_stream_data(&mut self, stream_id: u64) -> Option<Vec<u8>> {
self.early_stream_rx
.get_mut(&stream_id)
.and_then(StreamRecv::take_contiguous)
}
pub fn take_early_stream_fin(&mut self, stream_id: u64) -> bool {
self.early_stream_rx
.get_mut(&stream_id)
.is_some_and(StreamRecv::take_fin)
}
pub fn handle_handshake_packet(
&mut self,
packet: &[u8],
) -> Result<Vec<Vec<u8>>, QuicServerError> {
let keys = self
.handshake_keys
.as_ref()
.ok_or(QuicServerError::PacketParse("no handshake keys".into()))?;
let parsed = parse_long_header_full(packet)?;
if parsed.long_frame != 2 {
return Err(QuicServerError::PacketParse(format!(
"not a Handshake packet: long_frame={}",
parsed.long_frame
)));
}
let pn_offset = parsed.pn_offset;
let length_value = parsed.length as usize;
let packet_end = pn_offset
.checked_add(length_value)
.ok_or_else(|| QuicServerError::PacketParse("packet_end overflow".into()))?;
if packet_end > packet.len() {
return Err(QuicServerError::PacketParse("payload out of bounds".into()));
}
let mut buf = packet[..packet_end].to_vec();
let hp_key = &*keys.remote.header;
let sample_len = hp_key.sample_len();
let sample_start = pn_offset
.checked_add(4) .ok_or_else(|| QuicServerError::PacketParse("sample_start overflow".into()))?;
let sample_end = sample_start
.checked_add(sample_len)
.ok_or_else(|| QuicServerError::PacketParse("sample_end overflow".into()))?;
if sample_end > buf.len() {
return Err(QuicServerError::PacketParse("hp sample too short".into()));
}
let sample = buf[sample_start..sample_end].to_vec();
let mut first_byte = buf[0];
{
let pn_bytes = &mut buf[pn_offset..sample_start];
remove_header_protection(hp_key, &sample, &mut first_byte, pn_bytes)?;
}
buf[0] = first_byte;
let real_pn_len = ((first_byte & 0x03) as usize) + 1;
if first_byte & 0x0C != 0 {
return Err(QuicServerError::PacketParse(
"PROTOCOL_VIOLATION: reserved bits in Handshake header are non-zero".into(),
));
}
let header_end = pn_offset
.checked_add(real_pn_len)
.ok_or_else(|| QuicServerError::PacketParse("header_end overflow".into()))?;
if header_end > packet_end {
return Err(QuicServerError::PacketParse("header_end out of bounds".into()));
}
let mut pn_plain: u64 = 0;
for i in 0..real_pn_len {
pn_plain = (pn_plain << 8) | (buf[pn_offset + i] as u64);
}
let pn = decode_packet_number(
pn_plain,
real_pn_len,
self.largest_handshake_pn,
);
self.largest_handshake_pn = pn;
let ciphertext_len = packet_end
.checked_sub(header_end)
.ok_or_else(|| QuicServerError::PacketParse("ciphertext_len underflow".into()))?;
let (header, payload) = buf.split_at_mut(header_end);
let header: &[u8] = header;
let payload = &mut payload[..ciphertext_len];
let decrypted = decrypt_packet(&*keys.remote.packet, pn, header, payload)?;
let frames = parse_frames(decrypted);
let had_crypto = frames.iter().any(|f| matches!(f, QuicFrame::Crypto { .. }));
tracing::debug!(pn, decrypted_len = decrypted.len(), frame_count = frames.len(), "QUIC Handshake packet");
if frames.iter().any(|f| matches!(f, QuicFrame::PathChallenge { .. })) {
return Err(QuicServerError::PacketParse(
"PROTOCOL_VIOLATION: PATH_CHALLENGE in Handshake packet".into(),
));
}
for frame in &frames {
if let QuicFrame::Crypto { offset, data } = frame {
let off = *offset as usize;
let end = off
.checked_add(data.len())
.ok_or_else(|| QuicServerError::PacketParse("crypto offset overflow".into()))?;
if end > self.handshake_crypto_rx.len() {
if end > MAX_CRYPTO_BUFFER_SIZE {
return Err(QuicServerError::PacketParse(
"CRYPTO buffer exceeds maximum size".into(),
));
}
self.handshake_crypto_rx.resize(end, 0);
}
self.handshake_crypto_rx[off..end].copy_from_slice(data);
}
}
let prev_hs_consumed = self.handshake_crypto_consumed;
let hs_consumed = self.handshake_crypto_consumed;
let hs_available = self.handshake_crypto_rx.len();
if hs_available > hs_consumed {
let new_data = &self.handshake_crypto_rx[hs_consumed..hs_available];
self.crypto_session.process_handshake_data(new_data)?;
self.handshake_crypto_consumed = hs_available;
}
let mut output_packets = Vec::new();
let version = self.version();
loop {
let result = self.crypto_session.write_handshake()?;
if self.one_rtt_keys.is_none()
&& let Some(keys) = self.crypto_session.take_one_rtt_keys()
{
self.one_rtt_keys = Some(keys);
tracing::debug!("QUIC HS 1-RTT keys taken");
if !self.handshake_done_sent && !self.crypto_session.is_handshaking() {
self.state = QuicServerState::HandshakeDone;
let hd_frame = build_handshake_done_frame();
tracing::debug!(pn = self.next_1rtt_pn, "QUIC handshake complete, sending HANDSHAKE_DONE");
let packet = QuicConnection::build_short_packet(
&self.client_scid,
self.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_1rtt_pn,
&hd_frame,
self.key_phase,
)?;
output_packets.push(packet);
self.handshake_done_sent = true;
}
}
let use_one_rtt =
self.one_rtt_keys.is_some() && !self.crypto_session.is_handshaking();
match result {
Some((tls_data, _new_keys)) => {
if !tls_data.is_empty() {
if use_one_rtt {
let crypto_frame =
build_crypto_frame(self.one_rtt_crypto_tx_offset, &tls_data);
self.one_rtt_crypto_tx_offset = self
.one_rtt_crypto_tx_offset
.checked_add(tls_data.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("1rtt crypto tx offset overflow".into()))?;
tracing::debug!(len = tls_data.len(), "QUIC issuing NewSessionTicket in 1-RTT CRYPTO");
let packet = QuicConnection::build_short_packet(
&self.client_scid,
self.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_1rtt_pn,
&crypto_frame,
self.key_phase,
)?;
output_packets.push(packet);
} else {
let hs_off = self.handshake_crypto_tx_offset;
let crypto_frame = build_crypto_frame(hs_off, &tls_data);
self.handshake_crypto_tx_offset = self
.handshake_crypto_tx_offset
.checked_add(tls_data.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("handshake crypto tx offset overflow".into()))?;
let handshake_packet = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
2,
self.handshake_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_handshake_pn,
&crypto_frame,
)?;
output_packets.push(handshake_packet);
}
}
}
None => break,
}
}
if self.one_rtt_keys.is_none()
&& let Some(keys) = self.crypto_session.take_one_rtt_keys()
{
self.one_rtt_keys = Some(keys);
tracing::debug!("QUIC HS 1-RTT keys taken");
}
if !self.handshake_done_sent && self.one_rtt_keys.is_some() && !self.crypto_session.is_handshaking() {
self.state = QuicServerState::HandshakeDone;
let hd_frame = build_handshake_done_frame();
let one_rtt_keys = self
.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
tracing::debug!(pn = self.next_1rtt_pn, "QUIC handshake complete, sending HANDSHAKE_DONE");
let packet = QuicConnection::build_short_packet(
&self.client_scid,
one_rtt_keys,
&mut self.next_1rtt_pn,
&hd_frame,
self.key_phase,
)?;
output_packets.push(packet);
self.handshake_done_sent = true;
}
if self.handshake_done_sent && self.one_rtt_keys.is_some() {
loop {
match self.crypto_session.write_handshake() {
Ok(Some((tls_data, _))) if !tls_data.is_empty() => {
let crypto_frame =
build_crypto_frame(self.one_rtt_crypto_tx_offset, &tls_data);
self.one_rtt_crypto_tx_offset = self
.one_rtt_crypto_tx_offset
.checked_add(tls_data.len() as u64)
.ok_or_else(|| QuicServerError::PacketParse("1rtt crypto tx offset overflow".into()))?;
tracing::debug!(len = tls_data.len(), "QUIC issuing NewSessionTicket in 1-RTT CRYPTO");
let packet = QuicConnection::build_short_packet(
&self.client_scid,
self.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_1rtt_pn,
&crypto_frame,
self.key_phase,
)?;
output_packets.push(packet);
}
_ => break,
}
}
}
let hs_crypto_advanced = self.handshake_crypto_consumed > prev_hs_consumed;
if hs_crypto_advanced && !output_packets.is_empty() {
self.handshake_flight = output_packets.clone();
} else if had_crypto && !hs_crypto_advanced && !self.handshake_flight.is_empty() {
tracing::debug!(
flight_len = self.handshake_flight.len(),
"QUIC Handshake CRYPTO retransmission detected → retransmit handshake flight"
);
output_packets = self.handshake_flight.clone();
}
let ack_frame = build_ack_frame(pn, 0, 0);
let ack_packet = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
2, self.handshake_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_handshake_pn,
&ack_frame,
)?;
output_packets.push(ack_packet);
Ok(output_packets)
}
pub fn handle_short_packet(
&mut self,
packet: &[u8],
) -> Result<Vec<Vec<u8>>, QuicServerError> {
let pn_offset = parse_short_header(packet, self.server_dcid.len())?.pn_offset;
let mut buf = packet.to_vec();
let mut first_byte = buf[0];
{
let keys = self
.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
let hp_key = &*keys.remote.header;
let sample_len = hp_key.sample_len();
let sample_start = pn_offset
.checked_add(4) .ok_or_else(|| QuicServerError::PacketParse("sample_start overflow".into()))?;
let sample_end = sample_start
.checked_add(sample_len)
.ok_or_else(|| QuicServerError::PacketParse("sample_end overflow".into()))?;
if sample_end > buf.len() {
return Err(QuicServerError::PacketParse("hp sample too short".into()));
}
let sample = buf[sample_start..sample_end].to_vec();
let pn_bytes = &mut buf[pn_offset..sample_start];
remove_header_protection(hp_key, &sample, &mut first_byte, pn_bytes)?;
}
buf[0] = first_byte;
let packet_key_phase = (first_byte & 0x04) != 0;
if packet_key_phase != self.key_phase {
let new_keys = self.crypto_session.next_packet_keys()?;
let keys_mut = self
.one_rtt_keys
.as_mut()
.ok_or(QuicServerError::HandshakeNotComplete)?;
keys_mut.local.packet = new_keys.local;
keys_mut.remote.packet = new_keys.remote;
self.key_phase = packet_key_phase;
self.key_update_count = self
.key_update_count
.checked_add(1)
.ok_or_else(|| QuicServerError::PacketParse("key update count overflow".into()))?;
tracing::debug!(
count = self.key_update_count,
phase = self.key_phase,
"QUIC key update (peer initiated)"
);
}
let real_pn_len = ((first_byte & 0x03) as usize) + 1;
if first_byte & 0x0C != 0 {
let ack_frame = build_ack_frame(0, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"PROTOCOL_VIOLATION: reserved bits in Short header are non-zero".into(),
));
}
let header_end = pn_offset
.checked_add(real_pn_len)
.ok_or_else(|| QuicServerError::PacketParse("header_end overflow".into()))?;
if header_end > buf.len() {
return Err(QuicServerError::PacketParse("header_end out of bounds".into()));
}
let mut pn_plain: u64 = 0;
for i in 0..real_pn_len {
pn_plain = (pn_plain << 8) | (buf[pn_offset + i] as u64);
}
let pn = decode_packet_number(
pn_plain,
real_pn_len,
self.largest_1rtt_pn,
);
self.largest_1rtt_pn = self.largest_1rtt_pn.max(pn);
let (header, payload) = buf.split_at_mut(header_end);
let header: &[u8] = header;
let keys = self
.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
let decrypted = decrypt_packet(&*keys.remote.packet, pn, header, payload)?;
let frames = parse_frames(decrypted);
let mut total_consumed = 0usize;
let mut tmp_buf: &[u8] = decrypted;
loop {
match parse_frame(tmp_buf) {
Some((_, c)) => { total_consumed += c; tmp_buf = &tmp_buf[c..]; }
None => break,
}
}
let unparsed = decrypted.len() - total_consumed;
let has_unparsed_error = unparsed > 0
&& !decrypted[total_consumed..].iter().all(|&b| b == 0x00);
{
let mut kinds = String::with_capacity(64);
let mut padding = 0u32;
for f in &frames {
match f {
QuicFrame::Stream { .. } => kinds.push('S'),
QuicFrame::Ack { .. } => kinds.push('A'),
QuicFrame::Padding => padding += 1,
QuicFrame::Ping => kinds.push('P'),
QuicFrame::Crypto { .. } => kinds.push('C'),
QuicFrame::HandshakeDone => kinds.push('H'),
QuicFrame::ConnectionClose { .. } => kinds.push('X'),
QuicFrame::PathChallenge { .. } => kinds.push('c'),
QuicFrame::PathResponse { .. } => kinds.push('r'),
_ => kinds.push('?'),
}
}
tracing::debug!(pn, frames = %kinds, padding, "QUIC 1-RTT pkt frames");
}
let mut output_packets = Vec::new();
let mut needs_ack = false;
for frame in &frames {
match frame {
QuicFrame::Stream {
stream_id,
offset,
fin,
data,
} => {
let sid = *stream_id;
if sid >= Self::FC_MAX_STREAM_ID {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(format!(
"STREAM_LIMIT_ERROR: stream_id={sid} >= max_stream_id={}",
Self::FC_MAX_STREAM_ID
)));
}
if sid % 4 == 3 {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(format!(
"STREAM_STATE_ERROR: STREAM frame on send-only stream {sid}"
)));
}
if sid % 4 == 1 && !self.stream_rx.contains_key(&sid) {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(format!(
"STREAM_STATE_ERROR: STREAM on locally-initiated non-existing stream {sid}"
)));
}
let end = offset.saturating_add(data.len() as u64);
if end > Self::FC_MAX_STREAM_DATA {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::FlowControl(format!(
"stream {sid} end={end} > max_stream_data={}",
Self::FC_MAX_STREAM_DATA
)));
}
let prev_end = self.fc_stream_end.get(&sid).copied().unwrap_or(0);
if end > prev_end {
self.fc_conn_consumed =
self.fc_conn_consumed.saturating_add(end - prev_end);
self.fc_stream_end.insert(sid, end);
}
if self.fc_conn_consumed > Self::FC_MAX_DATA {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::FlowControl(format!(
"connection consumed={} > max_data={}",
self.fc_conn_consumed,
Self::FC_MAX_DATA
)));
}
let stream_buf = self.stream_rx.entry(sid).or_default();
if !stream_buf.insert(*offset, data) {
if !self.dup_stream_sids.contains(&sid) {
self.dup_stream_sids.push(sid);
}
}
if *fin {
stream_buf.fin_offset = Some(end);
}
tracing::debug!(stream_id = sid, offset = *offset, data_len = data.len(), fin = *fin, "QUIC 1-RTT STREAM");
needs_ack = true;
}
QuicFrame::Crypto { data, .. } => {
if let Err(e) = self.crypto_session.process_handshake_data(data) {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(e.into());
}
needs_ack = true;
}
QuicFrame::HandshakeDone => {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"PROTOCOL_VIOLATION: client must not send HANDSHAKE_DONE".into(),
));
}
QuicFrame::ConnectionClose {
error_code,
frame_type,
reason,
} => {
tracing::debug!(
"QUIC 1-RTT CONNECTION_CLOSE received from peer: error_code=0x{error_code:x} ({error_code}), frame_type=0x{frame_type:x}, reason={:?}",
String::from_utf8_lossy(reason)
);
self.state = QuicServerState::Closed;
return Ok(Vec::new());
}
QuicFrame::NewToken { .. } => {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"PROTOCOL_VIOLATION: client must not send NEW_TOKEN".into(),
));
}
QuicFrame::Ping => {
needs_ack = true;
}
QuicFrame::Ack { ack_ranges, .. } => {
self.process_1rtt_ack(ack_ranges);
}
QuicFrame::PathChallenge { data } => {
tracing::debug!(data = ?data, "QUIC 1-RTT PATH_CHALLENGE → PATH_RESPONSE");
let resp_frame = build_path_response_frame(data);
let pkt = QuicConnection::build_short_packet(
&self.client_scid,
self.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_1rtt_pn,
&resp_frame,
self.key_phase,
)?;
output_packets.push(pkt);
needs_ack = true; }
QuicFrame::PathResponse { data } => {
self.pending_path_response.push(*data);
}
QuicFrame::ResetStream { stream_id, .. } => {
let sid = *stream_id;
if sid % 4 >= 2 {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
format!("STREAM_STATE_ERROR: RESET_STREAM on unidirectional stream {sid}"),
));
}
needs_ack = true;
}
QuicFrame::StopSending { stream_id, .. } => {
let sid = *stream_id;
if sid % 4 == 2 {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
format!("STREAM_STATE_ERROR: STOP_SENDING on send-only stream {sid}"),
));
}
if sid % 4 != 0 && !self.server_opened_streams.contains(&sid) {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
format!("STREAM_STATE_ERROR: STOP_SENDING on non-existing stream {sid}"),
));
}
needs_ack = true;
}
QuicFrame::MaxStreamData { stream_id, .. } => {
let sid = *stream_id;
if sid % 4 == 2 {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
format!("STREAM_STATE_ERROR: MAX_STREAM_DATA on send-only stream {sid}"),
));
}
if sid % 4 != 0 && !self.server_opened_streams.contains(&sid) {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
format!("STREAM_STATE_ERROR: MAX_STREAM_DATA on non-existing stream {sid}"),
));
}
needs_ack = true;
}
f @ QuicFrame::MaxStreamsBidi { .. } | f @ QuicFrame::MaxStreamsUni { .. } => {
let max_streams = match &f {
QuicFrame::MaxStreamsBidi { max_streams } => *max_streams,
QuicFrame::MaxStreamsUni { max_streams } => *max_streams,
_ => 0,
};
if max_streams > (1u64 << 60) {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"FRAME_ENCODING_ERROR: MAX_STREAMS value exceeds 2^60".into(),
));
}
needs_ack = true;
}
f @ QuicFrame::StreamsBlockedBidi { .. } | f @ QuicFrame::StreamsBlockedUni { .. } => {
let limit = match &f {
QuicFrame::StreamsBlockedBidi { limit } => *limit,
QuicFrame::StreamsBlockedUni { limit } => *limit,
_ => 0,
};
if limit > (1u64 << 60) {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"FRAME_ENCODING_ERROR: STREAMS_BLOCKED value exceeds 2^60".into(),
));
}
needs_ack = true;
}
QuicFrame::NewConnectionId { sequence_number, retire_prior_to, connection_id, .. } => {
if retire_prior_to > sequence_number {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
format!("FRAME_ENCODING_ERROR: NEW_CONNECTION_ID Retire_Prior_To {retire_prior_to} > Sequence_Number {sequence_number}"),
));
}
if connection_id.is_empty() {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"FRAME_ENCODING_ERROR: NEW_CONNECTION_ID with 0-byte CID".into(),
));
}
needs_ack = true;
}
_ => {
needs_ack = true;
}
}
}
if has_unparsed_error {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"FRAME_ENCODING_ERROR: unparsed frame bytes".into(),
));
}
if frames.is_empty() {
let ack_frame = build_ack_frame(pn, 0, 0);
self.pending_err_ack = Some(ack_frame);
return Err(QuicServerError::PacketParse(
"PROTOCOL_VIOLATION: no frames in 1-RTT packet".into(),
));
}
if self.state == QuicServerState::HandshakeDone {
self.state = QuicServerState::Established;
self.established_at = Some(Instant::now());
}
if needs_ack {
let ack_frame = build_ack_frame(pn, 0, 0);
let ack_packet = QuicConnection::build_short_packet(
&self.client_scid,
self.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?,
&mut self.next_1rtt_pn,
&ack_frame,
self.key_phase,
)?;
output_packets.push(ack_packet);
}
Ok(output_packets)
}
pub fn build_long_packet(
version: u32,
client_scid: &[u8],
server_scid: &[u8],
long_frame: u8,
keys: &Keys,
next_pn: &mut u64,
payload: &[u8],
) -> Result<Vec<u8>, QuicServerError> {
let pn = *next_pn;
*next_pn = pn
.checked_add(1)
.ok_or_else(|| QuicServerError::PacketParse("packet number overflow".into()))?;
let pn_len: usize = 4;
let pn_bytes: [u8; 4] = (pn as u32).to_be_bytes();
let payload_len = payload.len();
let tag_len = keys.local.packet.tag_len();
let length_field = pn_len
.checked_add(payload_len)
.and_then(|v| v.checked_add(tag_len))
.ok_or_else(|| QuicServerError::PacketParse("length field overflow".into()))?;
let mut first_byte: u8 = 0x80 | 0x40; first_byte |= (long_frame & 0x03) << 4; first_byte |= ((pn_len - 1) as u8) & 0x03;
let mut header = Vec::new();
header.push(first_byte);
header.extend_from_slice(&version.to_be_bytes());
header.push(client_scid.len() as u8);
header.extend_from_slice(client_scid);
header.push(server_scid.len() as u8);
header.extend_from_slice(server_scid);
if long_frame == 0 {
header.push(0); }
push_varint(&mut header, length_field as u64);
header.extend_from_slice(&pn_bytes);
let mut plaintext = Vec::with_capacity(payload_len + tag_len);
plaintext.extend_from_slice(payload);
plaintext.extend(std::iter::repeat_n(0u8, tag_len));
let tag = keys
.local
.packet
.encrypt_in_place(pn, &header, &mut plaintext[..payload_len])?;
let tag_end = payload_len
.checked_add(tag_len)
.ok_or_else(|| QuicServerError::PacketParse("tag end overflow".into()))?;
plaintext[payload_len..tag_end].copy_from_slice(tag.as_ref());
let mut packet = header.clone();
packet.extend_from_slice(&plaintext);
let hp_key = &*keys.local.header;
let sample_len = hp_key.sample_len();
let sample_start = header.len(); let sample_end = sample_start
.checked_add(sample_len)
.ok_or_else(|| QuicServerError::PacketParse("sample end overflow".into()))?;
if sample_end <= packet.len() {
let sample = packet[sample_start..sample_end].to_vec();
let pn_offset_in_packet = header.len() - pn_len;
let mut first_byte = packet[0];
let pn_end = pn_offset_in_packet
.checked_add(pn_len)
.ok_or_else(|| QuicServerError::PacketParse("pn end overflow".into()))?;
{
let pn_bytes = &mut packet[pn_offset_in_packet..pn_end];
let _ = apply_header_protection(hp_key, &sample, &mut first_byte, pn_bytes);
}
packet[0] = first_byte;
} else {
return Err(QuicServerError::PacketParse(
"hp sample too short, refuse to send unprotected packet".into(),
));
}
tracing::debug!(long_frame, pn, packet_len = packet.len(), payload_len, "QUIC build long packet");
Ok(packet)
}
pub fn build_short_packet(
server_scid: &[u8],
keys: &Keys,
next_pn: &mut u64,
payload: &[u8],
key_phase: bool,
) -> Result<Vec<u8>, QuicServerError> {
let pn = *next_pn;
*next_pn = pn
.checked_add(1)
.ok_or_else(|| QuicServerError::PacketParse("packet number overflow".into()))?;
let pn_len = 4;
let pn_bytes: [u8; 4] = (pn as u32).to_be_bytes();
tracing::debug!(pn, payload = %format!("{:02x?}", payload), "QUIC SEND 1-RTT plaintext");
let payload_len = payload.len();
let tag_len = keys.local.packet.tag_len();
let mut first_byte: u8 = 0x40; if key_phase {
first_byte |= 0x04; }
first_byte |= ((pn_len - 1) as u8) & 0x03;
let mut header = Vec::new();
header.push(first_byte);
header.extend_from_slice(server_scid);
header.extend_from_slice(&pn_bytes);
let mut plaintext = Vec::with_capacity(payload_len + tag_len);
plaintext.extend_from_slice(payload);
plaintext.extend(std::iter::repeat_n(0u8, tag_len));
let tag = keys
.local
.packet
.encrypt_in_place(pn, &header, &mut plaintext[..payload_len])?;
let tag_end = payload_len
.checked_add(tag_len)
.ok_or_else(|| QuicServerError::PacketParse("tag end overflow".into()))?;
plaintext[payload_len..tag_end].copy_from_slice(tag.as_ref());
let mut packet = header.clone();
packet.extend_from_slice(&plaintext);
let hp_key = &*keys.local.header;
let sample_len = hp_key.sample_len();
let header_len = packet.len() - plaintext.len();
let sample_start = header_len;
let sample_end = sample_start
.checked_add(sample_len)
.ok_or_else(|| QuicServerError::PacketParse("sample end overflow".into()))?;
if sample_end <= packet.len() {
let sample = packet[sample_start..sample_end].to_vec();
let pn_offset_in_packet = header_len - pn_len;
let mut first_byte = packet[0];
let pn_end = pn_offset_in_packet
.checked_add(pn_len)
.ok_or_else(|| QuicServerError::PacketParse("pn end overflow".into()))?;
{
let pn_bytes = &mut packet[pn_offset_in_packet..pn_end];
let _ = apply_header_protection(hp_key, &sample, &mut first_byte, pn_bytes);
}
packet[0] = first_byte;
} else {
return Err(QuicServerError::PacketParse(
"hp sample too short, refuse to send unprotected packet".into(),
));
}
Ok(packet)
}
pub fn peer_alert_code(&self) -> u64 {
self.crypto_session
.alert()
.map(|a| 0x0100 + u8::from(a) as u64)
.unwrap_or(0x0100)
}
pub fn version(&self) -> u32 {
match self.crypto_session.version() {
QuicVersion::V1 => QUIC_VERSION_V1,
QuicVersion::V2 => QUIC_VERSION_V2,
}
}
pub fn build_stream_packet(
&mut self,
stream_id: u64,
offset: u64,
fin: bool,
data: &[u8],
) -> Result<Vec<u8>, QuicServerError> {
let keys = self
.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
let stream_frame = build_stream_frame(stream_id, offset, fin, data);
self.server_opened_streams.insert(stream_id);
let pn = self.next_1rtt_pn;
let packet = QuicConnection::build_short_packet(
&self.client_scid,
keys,
&mut self.next_1rtt_pn,
&stream_frame,
self.key_phase,
)?;
let bytes = packet.len() as u64;
self.congestion.on_send(bytes);
self.track_sent(SentPacket {
pn,
bytes,
sent_at: Instant::now(),
ack_eliciting: true,
});
Ok(packet)
}
pub fn build_1rtt_packet(&mut self, frame_bytes: &[u8]) -> Result<Vec<u8>, QuicServerError> {
let keys = self
.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
let pkt = QuicConnection::build_short_packet(
&self.client_scid,
keys,
&mut self.next_1rtt_pn,
frame_bytes,
self.key_phase,
)?;
Ok(pkt)
}
pub fn build_initial_cc_packet(
&mut self,
frame_bytes: &[u8],
) -> Result<Vec<u8>, QuicServerError> {
let version = self.version();
let pkt = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
0, &self.initial_keys,
&mut self.next_initial_pn,
frame_bytes,
)?;
Ok(pkt)
}
pub fn build_handshake_cc_packet(
&mut self,
frame_bytes: &[u8],
) -> Result<Vec<u8>, QuicServerError> {
let version = self.version();
let keys = self
.handshake_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
let pkt = QuicConnection::build_long_packet(
version,
&self.client_scid,
&self.server_scid,
2, keys,
&mut self.next_handshake_pn,
frame_bytes,
)?;
Ok(pkt)
}
pub fn build_1rtt_cc_packet(
&mut self,
ack_frame: &[u8],
cc_frame: &[u8],
) -> Result<Vec<u8>, QuicServerError> {
let mut payload = Vec::with_capacity(ack_frame.len() + cc_frame.len());
payload.extend_from_slice(ack_frame);
payload.extend_from_slice(cc_frame);
let keys = self
.one_rtt_keys
.as_ref()
.ok_or(QuicServerError::HandshakeNotComplete)?;
let pkt = QuicConnection::build_short_packet(
&self.client_scid,
keys,
&mut self.next_1rtt_pn,
&payload,
self.key_phase,
)?;
Ok(pkt)
}
pub fn initiate_key_update(&mut self) -> Result<(), QuicServerError> {
let new_keys = self.crypto_session.next_packet_keys()?;
let keys_mut = self
.one_rtt_keys
.as_mut()
.ok_or(QuicServerError::HandshakeNotComplete)?;
keys_mut.local.packet = new_keys.local;
keys_mut.remote.packet = new_keys.remote;
self.key_phase = !self.key_phase;
self.key_update_count = self
.key_update_count
.checked_add(1)
.ok_or_else(|| QuicServerError::PacketParse("key update count overflow".into()))?;
tracing::debug!(
count = self.key_update_count,
phase = self.key_phase,
"QUIC key update (locally initiated)"
);
Ok(())
}
fn track_sent(&mut self, pkt: SentPacket) {
if self.sent_1rtt.len() >= self.max_tracked_sent {
self.sent_1rtt.pop_front();
}
self.sent_1rtt.push_back(pkt);
}
fn process_1rtt_ack(&mut self, ack_ranges: &[(u64, u64)]) {
let now = Instant::now();
let mut newly_acked_bytes: u64 = 0;
let mut best_sample: Option<(u64, u64)> = None;
let mut i = 0;
while i < self.sent_1rtt.len() {
let pkt = self.sent_1rtt[i];
let acked = ack_ranges
.iter()
.any(|(low, high)| pkt.pn >= *low && pkt.pn <= *high);
if acked {
let Some(pkt) = self.sent_1rtt.remove(i) else {
break;
};
if pkt.ack_eliciting {
newly_acked_bytes = newly_acked_bytes.saturating_add(pkt.bytes);
let rtt = now
.saturating_duration_since(pkt.sent_at)
.as_micros()
.max(1) as u64;
if best_sample.is_none_or(|(p, _)| pkt.pn > p) {
best_sample = Some((pkt.pn, rtt));
}
}
} else {
i += 1;
}
}
if newly_acked_bytes > 0 {
let rtt = best_sample.map(|(_, r)| r).unwrap_or(if self.congestion.srtt > 0 {
self.congestion.srtt
} else {
1000
});
self.congestion.on_ack(newly_acked_bytes, rtt);
self.congestion.reset_pto();
}
}
#[inline]
pub fn congestion_available_bytes(&self) -> u64 {
self.congestion.available_bytes()
}
pub fn take_stream_data(&mut self, stream_id: u64) -> Option<Vec<u8>> {
self.stream_rx
.get_mut(&stream_id)
.and_then(StreamRecv::take_contiguous)
}
pub fn is_stream_fin(&self, stream_id: u64) -> bool {
self.stream_rx
.get(&stream_id)
.is_some_and(|r| r.fin_offset.is_some())
}
pub fn take_stream_fin(&mut self, stream_id: u64) -> bool {
self.stream_rx
.get_mut(&stream_id)
.is_some_and(StreamRecv::take_fin)
}
}
pub fn build_default_transport_params(client_dcid: &[u8], server_scid: &[u8]) -> Vec<u8> {
let mut params = Vec::new();
push_bytes_param(&mut params, 0x00, client_dcid);
push_bytes_param(&mut params, 0x0f, server_scid);
push_int_param(&mut params, 0x01, 10_000);
push_int_param(&mut params, 0x03, 1452);
push_int_param(&mut params, 0x04, 1_048_576);
push_int_param(&mut params, 0x05, 262_144);
push_int_param(&mut params, 0x06, 262_144);
push_int_param(&mut params, 0x07, 262_144);
push_int_param(&mut params, 0x08, 100);
push_int_param(&mut params, 0x09, 100);
push_int_param(&mut params, 0x0e, 2);
params
}
#[inline]
fn push_int_param(out: &mut Vec<u8>, id: u64, value: u64) {
let mut vb = [0u8; zenith_foundation::varint::MAX_VARINT_SIZE];
let vn = match zenith_foundation::varint::encode_varint_buf(value, &mut vb) {
Ok(n) => n,
Err(_) => return, };
push_varint(out, id);
push_varint(out, vn as u64);
out.extend_from_slice(&vb[..vn]);
}
#[inline]
fn push_bytes_param(out: &mut Vec<u8>, id: u64, value: &[u8]) {
push_varint(out, id);
push_varint(out, value.len() as u64);
out.extend_from_slice(value);
}
fn validate_peer_transport_params(buf: &[u8]) -> Result<(), String> {
let mut off = 0usize;
let mut has_initial_scid = false;
while off < buf.len() {
let (id, n) = parse_varint(&buf[off..]).ok_or("tp id parse")?;
off += n;
let (len, n) = parse_varint(&buf[off..]).ok_or("tp len parse")?;
off += n;
let val_end = off.checked_add(len as usize).ok_or("tp length overflow")?;
if val_end > buf.len() {
return Err("tp value extends past buffer".into());
}
let value = &buf[off..val_end];
off = val_end;
match id {
0x0f => {
has_initial_scid = true;
}
0x00 => {
return Err("client must not send original_destination_connection_id".into());
}
0x02 => {
return Err("client must not send stateless_reset_token".into());
}
0x0d => {
return Err("client must not send preferred_address".into());
}
0x10 => {
return Err("client must not send retry_source_connection_id".into());
}
0x03 => {
let (v, _) = parse_varint(value).ok_or("max_udp_payload_size parse")?;
if v < 1200 {
return Err(format!("max_udp_payload_size={v} < 1200"));
}
}
0x0a => {
let (v, _) = parse_varint(value).ok_or("ack_delay_exponent parse")?;
if v > 20 {
return Err(format!("ack_delay_exponent={v} > 20"));
}
}
0x0b => {
let (v, _) = parse_varint(value).ok_or("max_ack_delay parse")?;
if v >= (1 << 14) {
return Err(format!("max_ack_delay={v} >= 2^14"));
}
}
_ => {}
}
}
if !has_initial_scid {
return Err("missing initial_source_connection_id".into());
}
Ok(())
}
pub struct QuicServer {
socket: UdpSocket,
config: QuicServerConfig,
}
impl std::fmt::Debug for QuicServer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QuicServer")
.field("bind_addr", &self.config.bind_addr)
.field("version", &self.config.version)
.finish()
}
}
impl QuicServer {
pub fn bind(config: QuicServerConfig) -> Result<Self, QuicServerError> {
let socket = UdpSocket::bind(config.bind_addr)?;
socket.set_nonblocking(false)?;
Ok(Self { socket, config })
}
pub fn accept(&self) -> Result<QuicConnection, QuicServerError> {
let mut buf = vec![0u8; self.config.max_recv_bytes];
let mut established = false;
let mut conn: Option<QuicConnection> = None;
let deadline = Instant::now() + Duration::from_millis(self.config.idle_timeout_ms);
while !established && Instant::now() < deadline {
self.socket.set_read_timeout(Some(Duration::from_millis(1000)))?;
match self.socket.recv_from(&mut buf) {
Ok((n, client_addr)) => {
let packet = &buf[..n];
if packet.is_empty() {
continue;
}
let first_byte = packet[0];
let is_long = (first_byte & 0x80) != 0;
if conn.is_none() {
if !is_long {
continue; }
let parsed = match parse_long_header_full(packet) {
Ok(p) => p,
Err(_) => continue,
};
if parsed.long_frame != 0 {
continue; }
let server_scid = self.config.scid.clone();
let new_conn = match QuicConnection::new(
client_addr,
parsed.scid.clone(), parsed.dcid.clone(), server_scid,
self.config.rustls_config.clone(),
self.config.version,
) {
Ok(c) => c,
Err(_) => continue,
};
conn = Some(new_conn);
}
let Some(conn) = conn.as_mut() else {
continue;
};
let output_packets = if is_long {
let parsed = match parse_long_header_full(packet) {
Ok(p) => p,
Err(_) => continue,
};
match parsed.long_frame {
0 => match conn.handle_initial_packet(packet) {
Ok(pkts) => pkts,
Err(e) => {
tracing::warn!(error = %e, "QUIC handle_initial_packet failed");
Vec::new()
}
},
2 => match conn.handle_handshake_packet(packet) {
Ok(pkts) => pkts,
Err(e) => {
tracing::warn!(error = %e, "QUIC handle_handshake_packet failed");
Vec::new()
}
},
_ => Vec::new(),
}
} else {
match conn.handle_short_packet(packet) {
Ok(pkts) => pkts,
Err(e) => {
tracing::warn!(error = %e, "QUIC handle_short_packet failed");
Vec::new()
}
}
};
for pkt in output_packets {
match self.socket.send_to(&pkt, conn.client_addr) {
Ok(n) => tracing::debug!(peer = %conn.client_addr, len = n, "QUIC send"),
Err(e) => tracing::warn!(peer = %conn.client_addr, error = %e, "QUIC send failed"),
}
}
if conn.is_handshake_done() && conn.state == QuicServerState::Established {
established = true;
}
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::TimedOut =>
{
continue;
}
Err(e) => return Err(QuicServerError::Io(e)),
}
}
conn.ok_or(QuicServerError::Timeout)
}
pub fn recv_packet(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr), QuicServerError> {
self.socket.set_read_timeout(Some(Duration::from_millis(self.config.idle_timeout_ms)))?;
match self.socket.recv_from(buf) {
Ok((n, addr)) => Ok((n, addr)),
Err(e) => Err(QuicServerError::Io(e)),
}
}
pub fn send_to(&self, buf: &[u8], addr: SocketAddr) -> Result<usize, QuicServerError> {
Ok(self.socket.send_to(buf, addr)?)
}
pub fn socket(&self) -> &UdpSocket {
&self.socket
}
pub fn config(&self) -> &QuicServerConfig {
&self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_frame_ack_underflow_rejected() {
let mut hostile = vec![FRAME_ACK as u8];
push_varint(&mut hostile, 1); push_varint(&mut hostile, 0); push_varint(&mut hostile, 0); push_varint(&mut hostile, 5); assert!(parse_frame(&hostile).is_none(), "first_ack_range 下溢必须拒绝");
let mut hostile2 = vec![FRAME_ACK as u8];
push_varint(&mut hostile2, 3); push_varint(&mut hostile2, 0);
push_varint(&mut hostile2, 1); push_varint(&mut hostile2, 2); push_varint(&mut hostile2, 10); push_varint(&mut hostile2, 0); assert!(parse_frame(&hostile2).is_none(), "gap 下溢必须拒绝");
let mut hostile3 = vec![FRAME_ACK as u8];
push_varint(&mut hostile3, 10);
push_varint(&mut hostile3, 0);
push_varint(&mut hostile3, 1);
push_varint(&mut hostile3, 2); push_varint(&mut hostile3, 1); push_varint(&mut hostile3, 6); assert!(parse_frame(&hostile3).is_none(), "range_len 下溢必须拒绝");
let mut okf = vec![FRAME_ACK as u8];
push_varint(&mut okf, 10);
push_varint(&mut okf, 0);
push_varint(&mut okf, 0);
push_varint(&mut okf, 2);
assert!(matches!(parse_frame(&okf), Some((QuicFrame::Ack { .. }, _))));
}
#[test]
fn test_varint_encode_decode() {
for v in [0u64, 1, 63, 64, 16383, 16384, 1_073_741_823, 1_073_741_824] {
let encoded = encode_varint(v);
let (decoded, _) = parse_varint(&encoded).expect("parse should succeed");
assert_eq!(decoded, v, "varint round-trip failed for {}", v);
}
}
#[test]
fn test_encode_varint_rfc9000_appendix_a() {
assert_eq!(encode_varint(37), vec![0x25]);
assert_eq!(encode_varint(15_293), vec![0x7b, 0xbd]);
assert_eq!(encode_varint(494_878_333), vec![0x9d, 0x7f, 0x3e, 0x7d]);
assert_eq!(
encode_varint(151_288_809_941_952_652),
vec![0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c]
);
let (v, n) = parse_varint(&[0x40, 0x25]).expect("parse should succeed");
assert_eq!(v, 37);
assert_eq!(n, 2);
assert_eq!(encode_varint(63), vec![0x3f]);
assert_eq!(encode_varint(64), vec![0x40, 0x40]);
assert_eq!(encode_varint(16_383), vec![0x7f, 0xff]);
assert_eq!(encode_varint(16_384), vec![0x80, 0x00, 0x40, 0x00]);
assert_eq!(encode_varint(1_073_741_823), vec![0xbf, 0xff, 0xff, 0xff]);
assert_eq!(
encode_varint(1_073_741_824),
vec![0xc0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00]
);
assert_eq!(encode_varint((1u64 << 62) - 1), vec![0xff; 8]);
}
fn dummy_rustls_config() -> Arc<rustls::ServerConfig> {
let cfg = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_cert_resolver(Arc::new(
rustls::server::ResolvesServerCertUsingSni::new(),
));
Arc::new(cfg)
}
#[test]
fn test_scid_random_not_all_zero() {
let bind_addr: SocketAddr = "127.0.0.1:0".parse().expect("valid addr");
let cfg = QuicServerConfig::new(bind_addr, dummy_rustls_config(), QuicVersion::V1)
.expect("config should build (CSPRNG available)");
assert_eq!(cfg.scid_len, 8);
assert_eq!(cfg.scid.len(), 8);
assert!(
cfg.scid.iter().any(|&b| b != 0),
"SCID 不得为全零(可枚举将导致连接 ID 猜测)"
);
}
#[test]
fn test_scid_random_unique_per_config() {
let bind_addr: SocketAddr = "127.0.0.1:0".parse().expect("valid addr");
let c1 = QuicServerConfig::new(bind_addr, dummy_rustls_config(), QuicVersion::V1)
.expect("config should build");
let c2 = QuicServerConfig::new(bind_addr, dummy_rustls_config(), QuicVersion::V1)
.expect("config should build");
assert_ne!(
c1.scid, c2.scid,
"两次构造的 SCID 必须不相等(CSPRNG 随机化,RFC 9000 §7.2)"
);
let debug = format!("{:?}", c1);
assert!(debug.contains("QuicServerConfig"));
let c3 = c1.clone();
assert_eq!(c1.scid, c3.scid);
}
#[test]
fn test_packet_number_len() {
assert_eq!(packet_number_len(0b00), 1);
assert_eq!(packet_number_len(0b01), 2);
assert_eq!(packet_number_len(0b10), 3);
assert_eq!(packet_number_len(0b11), 4);
}
#[test]
fn test_decode_packet_number_simple() {
let pn = decode_packet_number(0, 4, 0);
assert_eq!(pn, 0);
let pn = decode_packet_number(0, 1, 0);
assert_eq!(pn, 0);
}
#[test]
fn test_build_crypto_frame() {
let data = b"hello";
let frame = build_crypto_frame(0, data);
assert_eq!(frame[0], 0x06); assert_eq!(frame[1], 0); assert_eq!(frame[2], 5); assert_eq!(&frame[3..], data);
}
#[test]
fn test_build_ack_frame() {
let frame = build_ack_frame(0, 0, 0);
assert_eq!(frame[0], 0x02); assert_eq!(frame[1], 0); assert_eq!(frame[2], 0); assert_eq!(frame[3], 0); assert_eq!(frame[4], 0); }
#[test]
fn test_build_stream_frame() {
let data = b"hello";
let frame = build_stream_frame(0, 0, true, data);
assert_eq!(frame[0], 0x0f);
assert_eq!(frame[1], 0); assert_eq!(frame[2], 0); assert_eq!(frame[3], 5); assert_eq!(&frame[4..], data);
}
#[test]
fn test_parse_initial_header() {
let mut buf = Vec::new();
buf.push(0xC3);
buf.extend_from_slice(&QUIC_VERSION_V1.to_be_bytes());
buf.push(8);
buf.extend_from_slice(&[0u8; 8]);
buf.push(8);
buf.extend_from_slice(&[1u8; 8]);
buf.push(0);
buf.extend_from_slice(&encode_varint(14));
buf.extend_from_slice(&[0u8, 0, 0, 0]);
buf.extend_from_slice(&[0u8; 10]);
let parsed = parse_long_header_full(&buf).expect("parse should succeed");
assert_eq!(parsed.version, QUIC_VERSION_V1);
assert_eq!(parsed.dcid, vec![0u8; 8]);
assert_eq!(parsed.scid, vec![1u8; 8]);
assert_eq!(parsed.long_frame, 0); assert_eq!(parsed.packet_number_len, 4);
assert_eq!(parsed.payload_len, 10);
}
#[test]
fn test_transport_params_encode() {
let client_dcid = vec![0u8; 8];
let server_scid = vec![1u8; 8];
let params = build_default_transport_params(&client_dcid, &server_scid);
assert!(params.len() > 50, "transport params too short: {}", params.len());
assert!(params.starts_with(&[0x00, 0x08]), "missing original_destination_connection_id");
}
}