rust-nex/prudpv1/src/prudp/packet.rs

461 lines
13 KiB
Rust
Raw Normal View History

2025-02-01 17:31:13 +01:00
// no clue why this produces a warning where `#[repr(u16)]` is below,
// the thing is says to do also breaks the code, so we just
// force the compiler to shut up here
#![allow(unused_parens)]
2026-01-20 20:26:44 +01:00
use crate::prudp::packet::PacketOption::{
ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions,
};
use bytemuck::{Pod, Zeroable};
use hmac::{Hmac, Mac};
use log::{error, warn};
use md5::{Digest, Md5};
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::prudp::types_flags::TypesFlags;
use rnex_core::prudp::types_flags::flags::ACK;
use rnex_core::prudp::virtual_port::VirtualPort;
use std::fmt::Debug;
2025-01-19 13:02:15 +01:00
use std::io;
2025-02-01 17:31:13 +01:00
use std::io::{Cursor, Read, Seek, Write};
2026-03-25 00:42:30 +01:00
use std::net::SocketAddr;
use std::net::SocketAddrV4;
2025-01-19 13:02:15 +01:00
use thiserror::Error;
2026-01-20 20:26:44 +01:00
use v_byte_helpers::SwapEndian;
2025-09-21 15:59:27 +02:00
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
2025-01-19 13:02:15 +01:00
type Md5Hmac = Hmac<Md5>;
2025-01-19 13:02:15 +01:00
#[derive(Error, Debug)]
2025-01-20 18:01:54 +01:00
pub enum Error {
2025-01-19 13:02:15 +01:00
#[error("{0}")]
IO(#[from] io::Error),
#[error("invalid magic {0:#06x}")]
InvalidMagic(u16),
#[error("invalid version {0}")]
InvalidVersion(u8),
#[error("invalid option id {0}")]
InvalidOptionId(u8),
#[error("option size {size} doesnt match expected option for given option id {id}")]
2026-01-20 20:26:44 +01:00
InvalidOptionSize { id: u8, size: u8 },
2025-01-19 13:02:15 +01:00
}
pub type Result<T> = std::result::Result<T, Error>;
#[repr(C)]
2025-02-12 18:46:29 +01:00
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)]
pub struct PRUDPV1Header {
2025-01-26 12:09:56 +01:00
pub magic: [u8; 2],
pub version: u8,
2025-01-19 13:02:15 +01:00
pub packet_specific_size: u8,
pub payload_size: u16,
pub source_port: VirtualPort,
pub destination_port: VirtualPort,
pub types_and_flags: TypesFlags,
pub session_id: u8,
pub substream_id: u8,
pub sequence_id: u16,
}
2025-01-26 12:09:56 +01:00
impl Default for PRUDPV1Header {
2025-01-26 12:09:56 +01:00
fn default() -> Self {
2026-01-20 20:26:44 +01:00
Self {
2025-01-26 12:09:56 +01:00
magic: [0xEA, 0xD0],
version: 1,
session_id: 0,
source_port: VirtualPort(0),
sequence_id: 0,
payload_size: 0,
destination_port: VirtualPort(0),
types_and_flags: TypesFlags(0),
packet_specific_size: 0,
2026-01-20 20:26:44 +01:00
substream_id: 0,
2025-01-26 12:09:56 +01:00
}
}
}
2025-02-12 18:46:29 +01:00
#[derive(Debug, Clone, Eq, PartialEq)]
2026-01-20 20:26:44 +01:00
pub enum PacketOption {
SupportedFunctions(u32),
ConnectionSignature([u8; 16]),
FragmentId(u8),
InitialSequenceId(u16),
2026-01-20 20:26:44 +01:00
MaximumSubstreamId(u8),
}
2026-01-20 20:26:44 +01:00
impl PacketOption {
fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self> {
let mut data_cursor = Cursor::new(option_data);
2026-01-20 20:26:44 +01:00
let val = match option_id.into() {
0 => SupportedFunctions(data_cursor.read_struct(IS_BIG_ENDIAN)?),
1 => ConnectionSignature(data_cursor.read_struct(IS_BIG_ENDIAN)?),
2 => FragmentId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
3 => InitialSequenceId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
2026-01-20 20:26:44 +01:00
_ => unreachable!(),
};
Ok(val)
}
fn write_to_stream(&self, stream: &mut impl Write) -> io::Result<()> {
match self {
SupportedFunctions(v) => {
stream.write_all(&[0, size_of_val(v) as u8])?;
stream.write_all(&v.to_le_bytes())?;
}
ConnectionSignature(v) => {
stream.write_all(&[1, size_of_val(v) as u8])?;
stream.write_all(v)?;
}
FragmentId(v) => {
stream.write_all(&[2, size_of_val(v) as u8])?;
stream.write_all(&v.to_le_bytes())?;
}
InitialSequenceId(v) => {
stream.write_all(&[3, size_of_val(v) as u8])?;
stream.write_all(&v.to_le_bytes())?;
}
MaximumSubstreamId(v) => {
stream.write_all(&[4, size_of_val(v) as u8])?;
stream.write_all(&v.to_le_bytes())?;
}
}
Ok(())
}
fn write_size(&self) -> u8 {
match self {
SupportedFunctions(_) => 2 + 4,
ConnectionSignature(_) => 2 + 16,
FragmentId(_) => 2 + 1,
InitialSequenceId(_) => 2 + 2,
MaximumSubstreamId(_) => 2 + 1,
}
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct PRUDPV1Packet {
pub header: PRUDPV1Header,
pub packet_signature: [u8; 16],
2025-01-20 18:01:54 +01:00
pub payload: Vec<u8>,
pub options: Vec<PacketOption>,
2025-01-19 13:02:15 +01:00
}
#[derive(Copy, Clone, Debug)]
// Invariant: can only contain 0, 1, 2, 3 or 4
struct OptionId(u8);
2025-01-20 18:01:54 +01:00
impl OptionId {
fn new(val: u8) -> Result<Self> {
2025-01-19 13:02:15 +01:00
// Invariant is upheld because we only create the object if it doesn't violate the invariant
match val {
0 | 1 | 2 | 3 | 4 => Ok(Self(val)),
2026-01-20 20:26:44 +01:00
_ => Err(Error::InvalidOptionId(val)),
2025-01-19 13:02:15 +01:00
}
}
2025-01-20 18:01:54 +01:00
fn option_type_size(self) -> u8 {
match self.0 {
2025-01-19 13:02:15 +01:00
0 => 4,
1 => 16,
2 => 1,
3 => 2,
4 => 1,
2026-01-20 20:26:44 +01:00
_ => unreachable!(),
2025-01-19 13:02:15 +01:00
}
}
}
2025-01-20 18:01:54 +01:00
impl Into<u8> for OptionId {
2025-01-19 13:02:15 +01:00
fn into(self) -> u8 {
self.0
}
}
impl PRUDPV1Packet {
2025-01-20 18:01:54 +01:00
pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self> {
let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?;
2025-01-19 13:02:15 +01:00
2026-01-20 20:26:44 +01:00
if header.magic[0] != 0xEA || header.magic[1] != 0xD0 {
2025-01-19 13:02:15 +01:00
return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic)));
}
2025-01-20 18:01:54 +01:00
if header.version != 1 {
return Err(Error::InvalidVersion(header.version));
2025-01-19 13:02:15 +01:00
}
let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
2025-02-12 18:46:29 +01:00
//let packet_signature: [u8; 16] = [0; 16];
2025-01-19 13:02:15 +01:00
2025-01-20 18:01:54 +01:00
assert_eq!(reader.stream_position().ok(), Some(14 + 16));
2025-01-19 13:02:15 +01:00
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
reader.read_exact(&mut packet_specific_buffer)?;
//no clue whats up with options but they are broken
let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer);
2025-01-19 13:02:15 +01:00
2025-01-20 18:01:54 +01:00
let mut options = Vec::new();
2025-01-19 13:02:15 +01:00
loop {
2026-01-20 20:26:44 +01:00
let Ok(option_id): io::Result<u8> =
packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
else {
break;
2025-01-19 13:02:15 +01:00
};
2026-01-20 20:26:44 +01:00
let Ok(value_size): io::Result<u8> =
packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
else {
break;
2025-01-19 13:02:15 +01:00
};
if value_size == 0 {
// skip it if its 0 and dont check?
2025-01-20 18:01:54 +01:00
warn!("reading packets options might be going wrong");
2025-01-19 13:02:15 +01:00
continue;
}
2025-01-20 18:01:54 +01:00
2025-01-19 13:02:15 +01:00
let option_id: OptionId = OptionId::new(option_id)?;
2025-01-20 18:01:54 +01:00
if option_id.option_type_size() != value_size {
error!("invalid packet options");
2025-01-19 13:02:15 +01:00
return Err(Error::InvalidOptionSize {
size: value_size,
2025-01-20 18:01:54 +01:00
id: option_id.0,
});
2025-01-19 13:02:15 +01:00
}
2025-01-21 20:33:08 +01:00
let mut option_data = vec![0u8; value_size as usize];
2026-01-20 20:26:44 +01:00
if packet_specific_data_cursor
.read_exact(&mut option_data[..])
.is_err()
{
2025-01-20 18:01:54 +01:00
error!("unable to read options");
2025-01-19 13:02:15 +01:00
break;
}
2025-01-20 18:01:54 +01:00
options.push(PacketOption::from(option_id, &option_data)?);
}
2026-01-20 20:26:44 +01:00
let mut payload = vec![0u8; header.payload_size as usize];
2025-01-19 13:02:15 +01:00
reader.read_exact(&mut payload)?;
2025-01-19 13:02:15 +01:00
2025-01-20 18:01:54 +01:00
Ok(Self {
header,
packet_signature,
2025-01-20 18:01:54 +01:00
payload,
options,
2025-01-19 13:02:15 +01:00
})
}
2026-01-20 20:26:44 +01:00
pub fn base_acknowledgement_packet(&self) -> Self {
2025-01-23 23:24:04 +01:00
let base = self.base_response_packet();
let mut flags = self.header.types_and_flags.flags(0);
flags.set_flag(ACK);
2026-01-20 20:26:44 +01:00
let options = self
.options
2025-01-23 23:24:04 +01:00
.iter()
.filter(|o| matches!(o, FragmentId(_)))
2025-01-23 23:27:50 +01:00
.cloned()
2025-01-23 23:24:04 +01:00
.collect();
2026-01-20 20:26:44 +01:00
Self {
header: PRUDPV1Header {
2025-01-23 23:24:04 +01:00
types_and_flags: flags,
sequence_id: self.header.sequence_id,
substream_id: self.header.substream_id,
session_id: self.header.session_id,
2025-01-23 23:24:04 +01:00
..base.header
},
options,
..base
}
}
2025-01-20 18:01:54 +01:00
pub fn source_sockaddr(&self, socket_addr_v4: SocketAddrV4) -> PRUDPSockAddr {
PRUDPSockAddr {
2026-03-25 00:42:30 +01:00
regular_socket_addr: SocketAddr::V4(socket_addr_v4),
2025-01-20 18:01:54 +01:00
virtual_port: self.header.source_port,
}
}
2026-01-20 20:26:44 +01:00
fn generate_options_bytes(&self) -> Vec<u8> {
let mut vec = Vec::new();
2025-01-20 18:01:54 +01:00
2026-01-20 20:26:44 +01:00
for option in &self.options {
option
.write_to_stream(&mut vec)
.expect("vec should always automatically be able to extend");
}
vec
}
2026-01-20 20:26:44 +01:00
pub fn calculate_signature_value(
&self,
access_key: &str,
session_key: Option<[u8; 32]>,
connection_signature: Option<[u8; 16]>,
) -> [u8; 16] {
let access_key_bytes = access_key.as_bytes();
let access_key_sum: u32 = access_key_bytes.iter().map(|v| *v as u32).sum();
let access_key_sum_bytes: [u8; 4] = access_key_sum.to_le_bytes();
2025-01-21 20:38:48 +01:00
let header_data: [u8; 8] = bytemuck::bytes_of(&self.header)[0x6..].try_into().unwrap();
let option_bytes = self.generate_options_bytes();
let mut md5 = md5::Md5::default();
md5.update(access_key_bytes);
let key = md5.finalize();
let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck");
2026-01-20 20:26:44 +01:00
hmac.write(&header_data)
.expect("error during hmac calculation");
if let Some(session_key) = session_key {
2026-01-20 20:26:44 +01:00
hmac.write(&session_key)
.expect("error during hmac calculation");
}
2026-01-20 20:26:44 +01:00
hmac.write(&access_key_sum_bytes)
.expect("error during hmac calculation");
if let Some(connection_signature) = connection_signature {
2026-01-20 20:26:44 +01:00
hmac.write(&connection_signature)
.expect("error during hmac calculation");
}
2026-01-20 20:26:44 +01:00
hmac.write(&option_bytes)
.expect("error during hmac calculation");
2026-01-20 20:26:44 +01:00
hmac.write_all(&self.payload)
.expect("error during hmac calculation");
2026-01-20 20:26:44 +01:00
hmac.finalize().into_bytes()[0..16]
.try_into()
.expect("invalid hmac size")
}
2026-01-20 20:26:44 +01:00
pub fn calculate_and_assign_signature(
&mut self,
access_key: &str,
session_key: Option<[u8; 32]>,
connection_signature: Option<[u8; 16]>,
) {
self.packet_signature =
self.calculate_signature_value(access_key, session_key, connection_signature);
}
2026-01-20 20:26:44 +01:00
pub fn set_sizes(&mut self) {
self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum();
self.header.payload_size = self.payload.len() as u16;
}
2026-01-20 20:26:44 +01:00
pub fn base_response_packet(&self) -> Self {
Self {
header: PRUDPV1Header {
magic: [0xEA, 0xD0],
types_and_flags: TypesFlags(0),
destination_port: self.header.source_port,
source_port: self.header.destination_port,
payload_size: 0,
version: 1,
packet_specific_size: 0,
sequence_id: 0,
session_id: 0,
substream_id: 0,
},
packet_signature: [0; 16],
payload: Default::default(),
2026-01-20 20:26:44 +01:00
options: Default::default(),
}
}
2026-01-20 20:26:44 +01:00
pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> {
writer.write_all(bytemuck::bytes_of(&self.header))?;
writer.write_all(&self.packet_signature)?;
2026-01-20 20:26:44 +01:00
for option in &self.options {
option.write_to_stream(writer)?;
}
writer.write_all(&self.payload)?;
Ok(())
}
2025-01-19 13:02:15 +01:00
}
#[cfg(test)]
2025-01-20 18:01:54 +01:00
mod test {
2026-01-20 20:26:44 +01:00
use super::{OptionId, PRUDPV1Header, PacketOption, TypesFlags};
use rnex_core::prudp::{
types_flags::{
flags::{NEED_ACK, RELIABLE},
types::DATA,
},
virtual_port::VirtualPort,
};
2025-01-19 13:02:15 +01:00
#[test]
2025-01-20 18:01:54 +01:00
fn size_test() {
assert_eq!(size_of::<PRUDPV1Header>(), 14);
2025-01-19 13:02:15 +01:00
}
2025-01-21 20:33:08 +01:00
#[test]
2026-01-20 20:26:44 +01:00
fn test_options() {
let packet_types = [0, 1, 2, 3, 4];
2025-01-21 20:33:08 +01:00
2026-01-20 20:26:44 +01:00
for p_type in packet_types {
2025-01-21 20:33:08 +01:00
let option_id = OptionId::new(p_type).unwrap();
let buf = vec![0; option_id.option_type_size() as usize];
let opt = PacketOption::from(option_id, &buf).unwrap();
{
let mut write_buf = vec![];
opt.write_to_stream(&mut write_buf).unwrap();
assert_eq!(write_buf.len() as u8, opt.write_size())
}
2025-01-21 20:33:08 +01:00
}
}
2025-01-21 20:38:48 +01:00
#[test]
2026-01-20 20:26:44 +01:00
fn header_read() {
let header = PRUDPV1Header {
2025-01-21 20:38:48 +01:00
version: 0,
destination_port: VirtualPort(0),
substream_id: 0,
types_and_flags: TypesFlags(0),
session_id: 0,
packet_specific_size: 0,
payload_size: 0,
sequence_id: 0,
2026-01-20 20:26:44 +01:00
magic: [0xEA, 0xD0],
source_port: VirtualPort(0),
2025-01-21 20:38:48 +01:00
};
let bytes = bytemuck::bytes_of(&header);
let bytes = &bytes[0x6..];
let header_data: [u8; 8] = bytes.try_into().unwrap();
}
2025-02-12 18:46:29 +01:00
#[test]
2026-01-20 20:26:44 +01:00
fn test_types_flags() {
2025-02-12 18:46:29 +01:00
let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE);
assert_ne!((types.0 >> 4) & NEED_ACK, 0);
assert_ne!((types.0 >> 4) & RELIABLE, 0);
assert_ne!((types.0 & 0xFF) as u8 & DATA, 0);
}
2026-01-20 20:26:44 +01:00
}