Skip to main content

srt_runtime/packet/
mod.rs

1//! SRT packet structure — `draft-sharabayko-srt-01` §3 (Packet Structure).
2//!
3//! Every SRT packet is the payload of one UDP datagram (§3, Figure 1). It
4//! starts with a 16-byte SRT header (Figure 2): a leading `F` bit
5//! distinguishes data packets (`F=0`, §3.1) from control packets (`F=1`,
6//! §3.2); the following two header words carry packet-type-specific fields,
7//! and the header always closes with a 32-bit Timestamp and a 32-bit
8//! Destination Socket ID.
9//!
10//! This module is packet **structure** only — parse/serialize of the wire
11//! format. The handshake *state machine* (caller/listener/rendezvous
12//! exchange, §4.3), loss/ARQ handling, TSBPD, congestion control, and the
13//! actual AES key-wrap/unwrap crypto (§6) are explicit follow-ups; see the
14//! crate root docs.
15
16pub mod ack;
17pub mod control;
18pub mod data;
19pub mod handshake;
20pub mod key_material;
21pub mod misc;
22pub mod nak;
23
24pub use ack::{AckCif, AckPacket};
25pub use control::{ControlPacket, ControlType, UserDefinedPacket};
26pub use data::{DataPacket, EncryptionKeyField, PacketPosition};
27pub use handshake::{
28    EncryptionField, ExtensionType, GroupFlags, GroupMembershipExtension, GroupType,
29    HandshakeExtensionBlock, HandshakeExtensionFlags, HandshakeExtensionMessageFlags,
30    HandshakeExtensions, HandshakePacket, HandshakeType, HsExtMessage,
31};
32pub use key_material::{Cipher, KeyMaterial, KmAuth, KmKeyFlag, StreamEncapsulation};
33pub use misc::{
34    AckAckPacket, CongestionWarningPacket, DropReqPacket, KeepAlivePacket, PeerErrorPacket,
35    ShutdownPacket,
36};
37pub use nak::{LossListEntry, NakPacket};
38
39use crate::error::{Error, Result};
40
41/// Length in bytes of the fixed SRT header shared by data and control packets
42/// (`draft-sharabayko-srt-01` §3, Figure 2): 4 header words = 16 bytes, before
43/// any packet-type-specific payload (data) or Control Information Field
44/// (control).
45pub const SRT_HEADER_LEN: usize = 16;
46
47/// The `F` (Packet Type Flag) bit — bit 31 of the first header word. Clear for
48/// a data packet, set for a control packet (§3, Figure 2). Also reused,
49/// bit-for-bit, as the range marker in the NAK loss-list coding (Appendix A).
50pub(crate) const F_BIT: u32 = 0x8000_0000;
51
52/// Mask for a 31-bit packet sequence number (data packet §3.1, NAK loss list
53/// Appendix A): all bits below the `F`/range-marker bit.
54pub(crate) const SEQ_NUMBER_MASK: u32 = 0x7FFF_FFFF;
55
56/// A parsed SRT packet — the payload of one UDP datagram carrying SRT traffic
57/// (`draft-sharabayko-srt-01` §3, Figure 1 / Figure 2).
58#[derive(Debug, Clone, PartialEq)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize))]
60#[non_exhaustive]
61pub enum SrtPacket<'a> {
62    /// `F=0`: a data packet (§3.1).
63    Data(DataPacket<'a>),
64    /// `F=1`: a control packet (§3.2).
65    Control(ControlPacket<'a>),
66}
67
68impl<'a> SrtPacket<'a> {
69    /// Parse one SRT packet from `bytes` — the full payload of one UDP
70    /// datagram carrying SRT traffic. Dispatches on the `F` bit of the first
71    /// header word (§3, Figure 2).
72    pub fn parse(bytes: &'a [u8]) -> Result<Self> {
73        if bytes.len() < SRT_HEADER_LEN {
74            return Err(Error::BufferTooShort {
75                need: SRT_HEADER_LEN,
76                have: bytes.len(),
77                what: "SRT header",
78            });
79        }
80        let word0 = be32(bytes, 0);
81        if word0 & F_BIT != 0 {
82            Ok(SrtPacket::Control(ControlPacket::parse(bytes)?))
83        } else {
84            Ok(SrtPacket::Data(DataPacket::parse(bytes)?))
85        }
86    }
87
88    /// Number of bytes [`Self::serialize_into`] will write.
89    pub fn serialized_len(&self) -> usize {
90        match self {
91            SrtPacket::Data(d) => d.serialized_len(),
92            SrtPacket::Control(c) => c.serialized_len(),
93        }
94    }
95
96    /// Serialize this packet into `buf`. Returns the number of bytes written.
97    pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
98        match self {
99            SrtPacket::Data(d) => d.serialize_into(buf),
100            SrtPacket::Control(c) => c.serialize_into(buf),
101        }
102    }
103}
104
105/// Read a big-endian `u32` at byte offset `off`.
106///
107/// # Panics
108/// Panics if `bytes.len() < off + 4`. Every call site slices/length-checks
109/// first, so this is an internal invariant, not an external input path.
110pub(crate) fn be32(bytes: &[u8], off: usize) -> u32 {
111    u32::from_be_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]])
112}
113
114/// Write a big-endian `u32` at byte offset `off`.
115///
116/// # Panics
117/// Panics if `buf.len() < off + 4`; see [`be32`].
118pub(crate) fn put_be32(buf: &mut [u8], off: usize, value: u32) {
119    buf[off..off + 4].copy_from_slice(&value.to_be_bytes());
120}
121
122/// Read a big-endian `u16` at byte offset `off`. See [`be32`] on panics.
123pub(crate) fn be16(bytes: &[u8], off: usize) -> u16 {
124    u16::from_be_bytes([bytes[off], bytes[off + 1]])
125}
126
127/// Write a big-endian `u16` at byte offset `off`. See [`put_be32`] on panics.
128pub(crate) fn put_be16(buf: &mut [u8], off: usize, value: u16) {
129    buf[off..off + 2].copy_from_slice(&value.to_be_bytes());
130}