libFenrir/src/connection/packet.rs
Luca Fulchir faaf8762c7
Test (de)serialization of DirSync::Resp
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
2023-06-09 21:58:33 +02:00

173 lines
4.8 KiB
Rust

//
//! Raw packet handling, encryption, decryption, parsing
use crate::enc::{
sym::{HeadLen, TagLen},
Random,
};
/// Fenrir Connection id
/// 0 is special as it represents the handshake
/// Connection IDs are to be considered u64 little endian
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ConnectionID {
/// Connection id 0 represent the handshake
Handshake,
/// Non-zero id can represent any connection
ID(::core::num::NonZeroU64),
}
impl ConnectionID {
/// Set the conenction id to handshake
pub fn new_handshake() -> Self {
Self::Handshake
}
/// New id from u64. PLZ NON ZERO
pub(crate) fn new_u64(raw: u64) -> Self {
#[allow(unsafe_code)]
unsafe {
ConnectionID::ID(::core::num::NonZeroU64::new_unchecked(raw))
}
}
pub(crate) fn as_u64(&self) -> u64 {
match self {
ConnectionID::Handshake => 0,
ConnectionID::ID(id) => id.get(),
}
}
/// New random service ID
pub fn new_rand(rand: &Random) -> Self {
let mut raw = [0; 8];
let mut num = 0;
while num == 0 {
rand.fill(&mut raw);
num = u64::from_le_bytes(raw);
}
#[allow(unsafe_code)]
unsafe {
ConnectionID::ID(::core::num::NonZeroU64::new_unchecked(num))
}
}
/// Quick check to know if this is an handshake
pub fn is_handshake(&self) -> bool {
*self == ConnectionID::Handshake
}
/// length if the connection ID in bytes
pub const fn len() -> usize {
8
}
/// write the ID to a buffer
pub fn serialize(&self, out: &mut [u8]) {
match self {
ConnectionID::Handshake => out[..8].copy_from_slice(&[0; 8]),
ConnectionID::ID(id) => {
out[..8].copy_from_slice(&id.get().to_le_bytes())
}
}
}
}
impl From<u64> for ConnectionID {
fn from(raw: u64) -> Self {
if raw == 0 {
ConnectionID::Handshake
} else {
#[allow(unsafe_code)]
unsafe {
ConnectionID::ID(::core::num::NonZeroU64::new_unchecked(raw))
}
}
}
}
impl From<[u8; 8]> for ConnectionID {
fn from(raw: [u8; 8]) -> Self {
let raw_u64 = u64::from_le_bytes(raw);
raw_u64.into()
}
}
/// Enumerate the possible data in a fenrir packet
#[derive(Debug, Clone)]
pub enum PacketData {
/// A parsed handshake packet
Handshake(super::Handshake),
/// Raw packet. we only have the connection ID and packet length
Raw(usize),
}
impl PacketData {
/// total length of the data in bytes
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
match self {
PacketData::Handshake(h) => h.len(head_len, tag_len),
PacketData::Raw(len) => *len,
}
}
/// serialize data into bytes
/// NOTE: assumes that there is exactly asa much buffer as needed
pub fn serialize(
&self,
head_len: HeadLen,
tag_len: TagLen,
out: &mut [u8],
) {
assert!(
self.len(head_len, tag_len) == out.len(),
"PacketData: wrong buffer length"
);
match self {
PacketData::Handshake(h) => h.serialize(head_len, tag_len, out),
PacketData::Raw(_) => {
::tracing::error!("Tried to serialize a raw PacketData!");
}
}
}
}
const MIN_PACKET_BYTES: usize = 16;
/// Fenrir packet structure
#[derive(Debug, Clone)]
pub struct Packet {
/// Id of the Fenrir connection.
pub id: ConnectionID,
/// actual data inside the packet
pub data: PacketData,
}
impl Packet {
/// New recevied packet, yet unparsed
pub fn deserialize_id(raw: &[u8]) -> Result<Self, ()> {
// TODO: proper min_packet length. 16 is too conservative.
if raw.len() < MIN_PACKET_BYTES {
return Err(());
}
let raw_id: [u8; 8] = (raw[..8]).try_into().expect("unreachable");
Ok(Packet {
id: raw_id.into(),
data: PacketData::Raw(raw.len()),
})
}
/// get the total length of the packet
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
ConnectionID::len() + self.data.len(head_len, tag_len)
}
/// serialize packet into buffer
/// NOTE: assumes that there is exactly asa much buffer as needed
pub fn serialize(
&self,
head_len: HeadLen,
tag_len: TagLen,
out: &mut [u8],
) {
assert!(
out.len() > ConnectionID::len(),
"Packet: not enough buffer to serialize"
);
self.id.serialize(&mut out[0..ConnectionID::len()]);
self.data
.serialize(head_len, tag_len, &mut out[ConnectionID::len()..]);
}
}