use rns_core::{constants, packet::PacketFlags};
pub(crate) struct PacketHeader<'a> {
pub flags: PacketFlags,
pub destination_hash: &'a [u8],
pub data: &'a [u8],
}
impl<'a> PacketHeader<'a> {
pub fn unpack(raw: &'a [u8]) -> Option<Self> {
if raw.len() < constants::HEADER_MINSIZE || raw[1] >= constants::PATHFINDER_M {
return None;
}
let flags = PacketFlags::unpack(raw[0]);
let destination_start = match flags.header_type {
constants::HEADER_1 => 2,
constants::HEADER_2 => 2 + constants::TRUNCATED_HASHLENGTH / 8,
_ => return None,
};
let destination_end = destination_start + constants::TRUNCATED_HASHLENGTH / 8;
if raw.len() <= destination_end + 1 {
return None;
}
Some(Self {
flags,
destination_hash: &raw[destination_start..destination_end],
data: &raw[destination_end + 1..],
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use rns_core::packet::RawPacket;
#[test]
fn header_matches_full_unpack_at_wire_boundaries() {
for flags in 0..=u8::MAX {
for hops in [
0,
1,
constants::PATHFINDER_M - 1,
constants::PATHFINDER_M,
255,
] {
let mut raw: Vec<u8> = (0..80).collect();
raw[0] = flags;
raw[1] = hops;
for len in (0..=40).chain([80]) {
let full = RawPacket::unpack(&raw[..len]);
let header = PacketHeader::unpack(&raw[..len]);
assert_eq!(
header.is_some(),
full.is_ok(),
"flags={flags} hops={hops} len={len}"
);
if let (Some(header), Ok(full)) = (header, full) {
assert_eq!(header.flags, full.flags);
assert_eq!(header.destination_hash, full.destination_hash);
assert_eq!(header.data, full.data());
}
}
}
}
}
}