use super::ack::AckPacket;
use super::handshake::HandshakePacket;
use super::misc::{
AckAckPacket, CongestionWarningPacket, DropReqPacket, KeepAlivePacket, PeerErrorPacket,
ShutdownPacket,
};
use super::nak::NakPacket;
use super::{Error, Result, SRT_HEADER_LEN, be32, put_be32};
pub const CONTROL_TYPE_HANDSHAKE: u16 = 0x0000;
pub const CONTROL_TYPE_KEEPALIVE: u16 = 0x0001;
pub const CONTROL_TYPE_ACK: u16 = 0x0002;
pub const CONTROL_TYPE_NAK: u16 = 0x0003;
pub const CONTROL_TYPE_CONGESTION_WARNING: u16 = 0x0004;
pub const CONTROL_TYPE_SHUTDOWN: u16 = 0x0005;
pub const CONTROL_TYPE_ACKACK: u16 = 0x0006;
pub const CONTROL_TYPE_DROPREQ: u16 = 0x0007;
pub const CONTROL_TYPE_PEERERROR: u16 = 0x0008;
pub const CONTROL_TYPE_USER_DEFINED: u16 = 0x7FFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum ControlType {
Handshake,
KeepAlive,
Ack,
Nak,
CongestionWarning,
Shutdown,
AckAck,
DropReq,
PeerError,
UserDefined,
Reserved(u16),
}
impl ControlType {
pub fn from_bits(v: u16) -> Self {
match v {
CONTROL_TYPE_HANDSHAKE => ControlType::Handshake,
CONTROL_TYPE_KEEPALIVE => ControlType::KeepAlive,
CONTROL_TYPE_ACK => ControlType::Ack,
CONTROL_TYPE_NAK => ControlType::Nak,
CONTROL_TYPE_CONGESTION_WARNING => ControlType::CongestionWarning,
CONTROL_TYPE_SHUTDOWN => ControlType::Shutdown,
CONTROL_TYPE_ACKACK => ControlType::AckAck,
CONTROL_TYPE_DROPREQ => ControlType::DropReq,
CONTROL_TYPE_PEERERROR => ControlType::PeerError,
CONTROL_TYPE_USER_DEFINED => ControlType::UserDefined,
other => ControlType::Reserved(other),
}
}
pub fn to_bits(self) -> u16 {
match self {
ControlType::Handshake => CONTROL_TYPE_HANDSHAKE,
ControlType::KeepAlive => CONTROL_TYPE_KEEPALIVE,
ControlType::Ack => CONTROL_TYPE_ACK,
ControlType::Nak => CONTROL_TYPE_NAK,
ControlType::CongestionWarning => CONTROL_TYPE_CONGESTION_WARNING,
ControlType::Shutdown => CONTROL_TYPE_SHUTDOWN,
ControlType::AckAck => CONTROL_TYPE_ACKACK,
ControlType::DropReq => CONTROL_TYPE_DROPREQ,
ControlType::PeerError => CONTROL_TYPE_PEERERROR,
ControlType::UserDefined => CONTROL_TYPE_USER_DEFINED,
ControlType::Reserved(v) => v,
}
}
pub fn name(&self) -> &'static str {
match self {
ControlType::Handshake => "handshake",
ControlType::KeepAlive => "keep-alive",
ControlType::Ack => "ACK",
ControlType::Nak => "NAK",
ControlType::CongestionWarning => "congestion warning",
ControlType::Shutdown => "shutdown",
ControlType::AckAck => "ACKACK",
ControlType::DropReq => "message drop request",
ControlType::PeerError => "peer error",
ControlType::UserDefined => "user-defined",
ControlType::Reserved(_) => "reserved",
}
}
}
broadcast_common::impl_spec_display!(ControlType, Reserved);
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct UserDefinedPacket<'a> {
pub control_type: u16,
pub subtype: u16,
pub type_specific_info: u32,
pub timestamp: u32,
pub dest_socket_id: u32,
pub cif: &'a [u8],
}
impl<'a> UserDefinedPacket<'a> {
pub fn as_key_material(&self) -> Result<super::KeyMaterial<'a>> {
super::KeyMaterial::parse(self.cif)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum ControlPacket<'a> {
Handshake(HandshakePacket<'a>),
KeepAlive(KeepAlivePacket),
Ack(AckPacket),
Nak(NakPacket<'a>),
CongestionWarning(CongestionWarningPacket),
Shutdown(ShutdownPacket),
AckAck(AckAckPacket),
DropReq(DropReqPacket),
PeerError(PeerErrorPacket),
UserDefined(UserDefinedPacket<'a>),
}
fn check_reserved_u16(what: &'static str, v: u16) -> Result<()> {
if v != 0 {
return Err(Error::ReservedFieldNotZero {
what,
value: u64::from(v),
});
}
Ok(())
}
fn check_reserved_u32(what: &'static str, v: u32) -> Result<()> {
if v != 0 {
return Err(Error::ReservedFieldNotZero {
what,
value: u64::from(v),
});
}
Ok(())
}
fn check_no_cif(what: &'static str, cif: &[u8]) -> Result<()> {
if !cif.is_empty() {
return Err(Error::UnexpectedTrailingBytes {
what,
extra: cif.len(),
});
}
Ok(())
}
impl<'a> ControlPacket<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < SRT_HEADER_LEN {
return Err(Error::BufferTooShort {
need: SRT_HEADER_LEN,
have: bytes.len(),
what: "SRT control packet header",
});
}
let word0 = be32(bytes, 0);
if word0 & super::F_BIT == 0 {
return Err(Error::WrongPacketKind {
expected: "control packet (F=1)",
});
}
let control_type_bits = ((word0 >> 16) & 0x7FFF) as u16;
let subtype = (word0 & 0xFFFF) as u16;
let type_specific_info = be32(bytes, 4);
let timestamp = be32(bytes, 8);
let dest_socket_id = be32(bytes, 12);
let cif = &bytes[SRT_HEADER_LEN..];
let control_type = ControlType::from_bits(control_type_bits);
Ok(match control_type {
ControlType::Handshake => {
check_reserved_u16("Subtype", subtype)?;
check_reserved_u32("Type-specific Information", type_specific_info)?;
ControlPacket::Handshake(HandshakePacket::parse_cif(
timestamp,
dest_socket_id,
cif,
)?)
}
ControlType::KeepAlive => {
check_reserved_u16("Subtype", subtype)?;
check_reserved_u32("Type-specific Information", type_specific_info)?;
check_no_cif("keep-alive CIF", cif)?;
ControlPacket::KeepAlive(KeepAlivePacket {
timestamp,
dest_socket_id,
})
}
ControlType::Ack => {
check_reserved_u16("Subtype", subtype)?;
ControlPacket::Ack(AckPacket::parse_cif(
type_specific_info,
timestamp,
dest_socket_id,
cif,
)?)
}
ControlType::Nak => {
check_reserved_u16("Subtype", subtype)?;
check_reserved_u32("Type-specific Information", type_specific_info)?;
ControlPacket::Nak(NakPacket::parse_cif(timestamp, dest_socket_id, cif))
}
ControlType::CongestionWarning => {
check_reserved_u16("Subtype", subtype)?;
check_reserved_u32("Type-specific Information", type_specific_info)?;
check_no_cif("congestion warning CIF", cif)?;
ControlPacket::CongestionWarning(CongestionWarningPacket {
timestamp,
dest_socket_id,
})
}
ControlType::Shutdown => {
check_reserved_u16("Subtype", subtype)?;
check_reserved_u32("Type-specific Information", type_specific_info)?;
check_no_cif("shutdown CIF", cif)?;
ControlPacket::Shutdown(ShutdownPacket {
timestamp,
dest_socket_id,
})
}
ControlType::AckAck => {
check_reserved_u16("Subtype", subtype)?;
check_no_cif("ACKACK CIF", cif)?;
ControlPacket::AckAck(AckAckPacket {
ack_number: type_specific_info,
timestamp,
dest_socket_id,
})
}
ControlType::DropReq => {
check_reserved_u16("Subtype", subtype)?;
ControlPacket::DropReq(DropReqPacket::parse_cif(
type_specific_info,
timestamp,
dest_socket_id,
cif,
)?)
}
ControlType::PeerError => {
check_reserved_u16("Subtype", subtype)?;
check_no_cif("peer error CIF", cif)?;
ControlPacket::PeerError(PeerErrorPacket {
error_code: type_specific_info,
timestamp,
dest_socket_id,
})
}
ControlType::UserDefined | ControlType::Reserved(_) => {
ControlPacket::UserDefined(UserDefinedPacket {
control_type: control_type_bits,
subtype,
type_specific_info,
timestamp,
dest_socket_id,
cif,
})
}
})
}
pub fn control_type(&self) -> ControlType {
match self {
ControlPacket::Handshake(_) => ControlType::Handshake,
ControlPacket::KeepAlive(_) => ControlType::KeepAlive,
ControlPacket::Ack(_) => ControlType::Ack,
ControlPacket::Nak(_) => ControlType::Nak,
ControlPacket::CongestionWarning(_) => ControlType::CongestionWarning,
ControlPacket::Shutdown(_) => ControlType::Shutdown,
ControlPacket::AckAck(_) => ControlType::AckAck,
ControlPacket::DropReq(_) => ControlType::DropReq,
ControlPacket::PeerError(_) => ControlType::PeerError,
ControlPacket::UserDefined(u) => ControlType::from_bits(u.control_type),
}
}
fn subtype(&self) -> u16 {
match self {
ControlPacket::UserDefined(u) => u.subtype,
_ => 0,
}
}
fn word1(&self) -> u32 {
match self {
ControlPacket::Handshake(_)
| ControlPacket::KeepAlive(_)
| ControlPacket::Nak(_)
| ControlPacket::CongestionWarning(_)
| ControlPacket::Shutdown(_) => 0,
ControlPacket::Ack(a) => a.ack_number,
ControlPacket::AckAck(a) => a.ack_number,
ControlPacket::DropReq(d) => d.message_number,
ControlPacket::PeerError(p) => p.error_code,
ControlPacket::UserDefined(u) => u.type_specific_info,
}
}
fn timestamp(&self) -> u32 {
match self {
ControlPacket::Handshake(h) => h.timestamp,
ControlPacket::KeepAlive(k) => k.timestamp,
ControlPacket::Ack(a) => a.timestamp,
ControlPacket::Nak(n) => n.timestamp,
ControlPacket::CongestionWarning(c) => c.timestamp,
ControlPacket::Shutdown(s) => s.timestamp,
ControlPacket::AckAck(a) => a.timestamp,
ControlPacket::DropReq(d) => d.timestamp,
ControlPacket::PeerError(p) => p.timestamp,
ControlPacket::UserDefined(u) => u.timestamp,
}
}
fn dest_socket_id(&self) -> u32 {
match self {
ControlPacket::Handshake(h) => h.dest_socket_id,
ControlPacket::KeepAlive(k) => k.dest_socket_id,
ControlPacket::Ack(a) => a.dest_socket_id,
ControlPacket::Nak(n) => n.dest_socket_id,
ControlPacket::CongestionWarning(c) => c.dest_socket_id,
ControlPacket::Shutdown(s) => s.dest_socket_id,
ControlPacket::AckAck(a) => a.dest_socket_id,
ControlPacket::DropReq(d) => d.dest_socket_id,
ControlPacket::PeerError(p) => p.dest_socket_id,
ControlPacket::UserDefined(u) => u.dest_socket_id,
}
}
fn cif_len(&self) -> usize {
match self {
ControlPacket::Handshake(h) => h.cif_len(),
ControlPacket::KeepAlive(_)
| ControlPacket::CongestionWarning(_)
| ControlPacket::Shutdown(_)
| ControlPacket::AckAck(_)
| ControlPacket::PeerError(_) => 0,
ControlPacket::Ack(a) => a.cif_len(),
ControlPacket::Nak(n) => n.cif_len(),
ControlPacket::DropReq(d) => d.cif_len(),
ControlPacket::UserDefined(u) => u.cif.len(),
}
}
pub fn serialized_len(&self) -> usize {
SRT_HEADER_LEN + self.cif_len()
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
let control_type_bits = self.control_type().to_bits();
if control_type_bits > 0x7FFF {
return Err(Error::FieldTooWide {
what: "Control Type",
value: u64::from(control_type_bits),
bits: 15,
});
}
let word0 = super::F_BIT | (u32::from(control_type_bits) << 16) | u32::from(self.subtype());
put_be32(buf, 0, word0);
put_be32(buf, 4, self.word1());
put_be32(buf, 8, self.timestamp());
put_be32(buf, 12, self.dest_socket_id());
let cif = &mut buf[SRT_HEADER_LEN..len];
match self {
ControlPacket::Handshake(h) => {
h.write_cif(cif)?;
}
ControlPacket::KeepAlive(_)
| ControlPacket::CongestionWarning(_)
| ControlPacket::Shutdown(_)
| ControlPacket::AckAck(_)
| ControlPacket::PeerError(_) => {}
ControlPacket::Ack(a) => {
a.write_cif(cif)?;
}
ControlPacket::Nak(n) => {
n.write_cif(cif);
}
ControlPacket::DropReq(d) => {
d.write_cif(cif);
}
ControlPacket::UserDefined(u) => {
cif.copy_from_slice(u.cif);
}
}
Ok(len)
}
}