use crate::protocol::{ProtocolError, decode_optional_coords};
use crate::transport::PacketBuffer;
use crate::tree::TreeCoordinate;
use std::ops::Range;
pub const FSP_VERSION: u8 = 0;
pub const FSP_PHASE_ESTABLISHED: u8 = 0x0;
pub const FSP_PHASE_MSG1: u8 = 0x1;
pub const FSP_PHASE_MSG2: u8 = 0x2;
pub const FSP_PHASE_MSG3: u8 = 0x3;
pub const FSP_COMMON_PREFIX_SIZE: usize = 4;
pub const FSP_HEADER_SIZE: usize = 12;
pub const FSP_INNER_HEADER_SIZE: usize = 6;
pub const FSP_PORT_HEADER_SIZE: usize = 4;
pub const FSP_PORT_IPV6_SHIM: u16 = 256;
pub const FSP_ENDPOINT_DATA_BULK_MAX_PACKETS: usize = 48;
const FSP_ENDPOINT_DATA_BULK_COUNT_SIZE: usize = 2;
pub(crate) const FSP_ENDPOINT_DATA_BULK_LEN_SIZE: usize = 2;
pub const FSP_FLAG_CP: u8 = 0x01;
#[allow(dead_code)]
pub const FSP_FLAG_K: u8 = 0x02;
pub const FSP_FLAG_U: u8 = 0x04;
pub const FSP_FLAG_DIRECT_TRANSPORT: u8 = 0x08;
#[allow(dead_code)]
pub const FSP_INNER_FLAG_SP: u8 = 0x01;
pub const fn fsp_endpoint_data_max_body_len() -> usize {
u16::MAX as usize - FSP_INNER_HEADER_SIZE
}
pub const fn fsp_endpoint_data_bulk_packet_wire_len(packet_len: usize) -> Option<usize> {
if packet_len > u16::MAX as usize {
return None;
}
Some(FSP_ENDPOINT_DATA_BULK_LEN_SIZE + packet_len)
}
pub const fn fsp_endpoint_data_bulk_base_wire_len() -> usize {
FSP_ENDPOINT_DATA_BULK_COUNT_SIZE
}
#[cfg(test)]
pub(crate) fn encode_fsp_endpoint_data_bulk_payload(mut packets: Vec<Vec<u8>>) -> Option<Vec<u8>> {
if packets.is_empty() || packets.len() > FSP_ENDPOINT_DATA_BULK_MAX_PACKETS {
return None;
}
let mut wire_len = fsp_endpoint_data_bulk_base_wire_len();
for packet in &packets {
wire_len = wire_len.checked_add(fsp_endpoint_data_bulk_packet_wire_len(packet.len())?)?;
}
if wire_len > fsp_endpoint_data_max_body_len() {
return None;
}
let mut encoded = Vec::with_capacity(wire_len);
encoded.extend_from_slice(&(packets.len() as u16).to_le_bytes());
for packet in &mut packets {
encoded.extend_from_slice(&(packet.len() as u16).to_le_bytes());
encoded.append(packet);
}
Some(encoded)
}
pub fn decode_fsp_endpoint_data_bulk_lengths(payload: &[u8]) -> Option<Vec<usize>> {
Some(
decode_fsp_endpoint_data_bulk_ranges(payload)?
.into_iter()
.map(|range| range.len())
.collect(),
)
}
pub fn decode_fsp_endpoint_data_bulk_ranges(payload: &[u8]) -> Option<Vec<Range<usize>>> {
if payload.len() < FSP_ENDPOINT_DATA_BULK_COUNT_SIZE {
return None;
}
let count = u16::from_le_bytes([payload[0], payload[1]]) as usize;
if count == 0 || count > FSP_ENDPOINT_DATA_BULK_MAX_PACKETS {
return None;
}
let mut lengths = Vec::with_capacity(count);
let mut offset = FSP_ENDPOINT_DATA_BULK_COUNT_SIZE;
for _ in 0..count {
let len_end = offset.checked_add(FSP_ENDPOINT_DATA_BULK_LEN_SIZE)?;
let len_bytes = payload.get(offset..len_end)?;
let packet_len = u16::from_le_bytes([len_bytes[0], len_bytes[1]]) as usize;
offset = len_end;
let packet_end = offset.checked_add(packet_len)?;
payload.get(offset..packet_end)?;
lengths.push(offset..packet_end);
offset = packet_end;
}
(offset == payload.len()).then_some(lengths)
}
pub fn split_fsp_endpoint_data_bulk_payload(
payload: PacketBuffer,
lengths: &[usize],
) -> Vec<PacketBuffer> {
let body = payload.as_slice();
let mut packets = Vec::with_capacity(lengths.len());
let mut offset = FSP_ENDPOINT_DATA_BULK_COUNT_SIZE;
for packet_len in lengths {
offset += FSP_ENDPOINT_DATA_BULK_LEN_SIZE;
let packet_end = offset + packet_len;
packets.push(body[offset..packet_end].to_vec().into());
offset = packet_end;
}
packets
}
#[derive(Clone, Debug)]
pub struct FspCommonPrefix {
#[cfg_attr(not(test), allow(dead_code))]
pub version: u8,
pub phase: u8,
pub flags: u8,
#[cfg_attr(not(test), allow(dead_code))]
pub payload_len: u16,
}
impl FspCommonPrefix {
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() < FSP_COMMON_PREFIX_SIZE {
return None;
}
let version = data[0] >> 4;
let phase = data[0] & 0x0F;
let flags = data[1];
let payload_len = u16::from_le_bytes([data[2], data[3]]);
Some(Self {
version,
phase,
flags,
payload_len,
})
}
pub fn is_unencrypted(&self) -> bool {
self.flags & FSP_FLAG_U != 0
}
pub fn has_coords(&self) -> bool {
self.flags & FSP_FLAG_CP != 0
}
fn ver_phase_byte(version: u8, phase: u8) -> u8 {
(version << 4) | (phase & 0x0F)
}
}
#[cfg(test)]
pub fn build_fsp_header(counter: u64, flags: u8, payload_len: u16) -> [u8; FSP_HEADER_SIZE] {
let mut header = [0u8; FSP_HEADER_SIZE];
header[0] = FspCommonPrefix::ver_phase_byte(FSP_VERSION, FSP_PHASE_ESTABLISHED);
header[1] = flags;
header[2..4].copy_from_slice(&payload_len.to_le_bytes());
header[4..12].copy_from_slice(&counter.to_le_bytes());
header
}
#[cfg_attr(not(test), allow(dead_code))]
pub fn build_fsp_handshake_prefix(phase: u8, payload_len: u16) -> [u8; FSP_COMMON_PREFIX_SIZE] {
let mut prefix = [0u8; FSP_COMMON_PREFIX_SIZE];
prefix[0] = FspCommonPrefix::ver_phase_byte(FSP_VERSION, phase);
prefix[1] = 0x00; prefix[2..4].copy_from_slice(&payload_len.to_le_bytes());
prefix
}
#[cfg_attr(not(test), allow(dead_code))]
pub fn build_fsp_error_prefix(payload_len: u16) -> [u8; FSP_COMMON_PREFIX_SIZE] {
let mut prefix = [0u8; FSP_COMMON_PREFIX_SIZE];
prefix[0] = FspCommonPrefix::ver_phase_byte(FSP_VERSION, FSP_PHASE_ESTABLISHED);
prefix[1] = FSP_FLAG_U;
prefix[2..4].copy_from_slice(&payload_len.to_le_bytes());
prefix
}
#[cfg(test)]
pub fn fsp_prepend_inner_header(
timestamp_ms: u32,
msg_type: u8,
inner_flags: u8,
payload: &[u8],
) -> Vec<u8> {
let mut buf = Vec::with_capacity(FSP_INNER_HEADER_SIZE + payload.len());
buf.extend_from_slice(×tamp_ms.to_le_bytes());
buf.push(msg_type);
buf.push(inner_flags);
buf.extend_from_slice(payload);
buf
}
pub fn fsp_strip_inner_header(plaintext: &[u8]) -> Option<(u32, u8, u8, &[u8])> {
if plaintext.len() < FSP_INNER_HEADER_SIZE {
return None;
}
let timestamp = u32::from_le_bytes([plaintext[0], plaintext[1], plaintext[2], plaintext[3]]);
let msg_type = plaintext[4];
let inner_flags = plaintext[5];
Some((
timestamp,
msg_type,
inner_flags,
&plaintext[FSP_INNER_HEADER_SIZE..],
))
}
pub fn parse_encrypted_coords(
data: &[u8],
) -> Result<(Option<TreeCoordinate>, Option<TreeCoordinate>, usize), ProtocolError> {
let (src_coords, src_consumed) = decode_optional_coords(data)?;
let (dest_coords, dest_consumed) = decode_optional_coords(&data[src_consumed..])?;
Ok((src_coords, dest_coords, src_consumed + dest_consumed))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wire_sizes() {
assert_eq!(FSP_COMMON_PREFIX_SIZE, 4);
assert_eq!(FSP_HEADER_SIZE, 12);
assert_eq!(FSP_INNER_HEADER_SIZE, 6);
}
#[test]
fn test_common_prefix_parse_established() {
let data = [0x00, 0x01, 0x40, 0x00]; let prefix = FspCommonPrefix::parse(&data).unwrap();
assert_eq!(prefix.version, 0);
assert_eq!(prefix.phase, FSP_PHASE_ESTABLISHED);
assert_eq!(prefix.flags, FSP_FLAG_CP);
assert_eq!(prefix.payload_len, 64);
assert!(prefix.has_coords());
assert!(!prefix.is_unencrypted());
}
#[test]
fn test_common_prefix_parse_handshake() {
let data = [0x01, 0x00, 0x50, 0x00]; let prefix = FspCommonPrefix::parse(&data).unwrap();
assert_eq!(prefix.version, 0);
assert_eq!(prefix.phase, FSP_PHASE_MSG1);
assert_eq!(prefix.flags, 0);
assert_eq!(prefix.payload_len, 80);
}
#[test]
fn test_common_prefix_parse_error_signal() {
let data = [0x00, FSP_FLAG_U, 0x22, 0x00]; let prefix = FspCommonPrefix::parse(&data).unwrap();
assert_eq!(prefix.phase, FSP_PHASE_ESTABLISHED);
assert!(prefix.is_unencrypted());
assert_eq!(prefix.payload_len, 34);
}
#[test]
fn test_common_prefix_too_short() {
assert!(FspCommonPrefix::parse(&[0, 0, 0]).is_none());
}
#[test]
fn test_build_fsp_header() {
let header = build_fsp_header(1000, FSP_FLAG_CP, 200);
assert_eq!(header[0], 0x00); assert_eq!(header[1], FSP_FLAG_CP);
assert_eq!(u16::from_le_bytes([header[2], header[3]]), 200);
assert_eq!(
u64::from_le_bytes([
header[4], header[5], header[6], header[7], header[8], header[9], header[10],
header[11],
]),
1000
);
}
#[test]
fn test_build_fsp_handshake_prefix_msg1() {
let prefix = build_fsp_handshake_prefix(FSP_PHASE_MSG1, 100);
assert_eq!(prefix[0], 0x01); assert_eq!(prefix[1], 0x00); assert_eq!(u16::from_le_bytes([prefix[2], prefix[3]]), 100);
let parsed = FspCommonPrefix::parse(&prefix).unwrap();
assert_eq!(parsed.phase, FSP_PHASE_MSG1);
}
#[test]
fn test_build_fsp_handshake_prefix_msg2() {
let prefix = build_fsp_handshake_prefix(FSP_PHASE_MSG2, 50);
assert_eq!(prefix[0], 0x02); assert_eq!(prefix[1], 0x00);
assert_eq!(u16::from_le_bytes([prefix[2], prefix[3]]), 50);
}
#[test]
fn test_build_fsp_handshake_prefix_msg3() {
let prefix = build_fsp_handshake_prefix(FSP_PHASE_MSG3, 73);
assert_eq!(prefix[0], 0x03); assert_eq!(prefix[1], 0x00); assert_eq!(u16::from_le_bytes([prefix[2], prefix[3]]), 73);
let parsed = FspCommonPrefix::parse(&prefix).unwrap();
assert_eq!(parsed.phase, FSP_PHASE_MSG3);
}
#[test]
fn test_build_fsp_error_prefix() {
let prefix = build_fsp_error_prefix(34);
assert_eq!(prefix[0], 0x00); assert_eq!(prefix[1], FSP_FLAG_U);
assert_eq!(u16::from_le_bytes([prefix[2], prefix[3]]), 34);
let parsed = FspCommonPrefix::parse(&prefix).unwrap();
assert!(parsed.is_unencrypted());
assert_eq!(parsed.phase, FSP_PHASE_ESTABLISHED);
}
#[test]
fn test_inner_header_prepend_strip() {
let timestamp: u32 = 12345;
let msg_type: u8 = 0x10;
let inner_flags: u8 = 0x01; let payload = vec![0xAA, 0xBB, 0xCC];
let with_header = fsp_prepend_inner_header(timestamp, msg_type, inner_flags, &payload);
assert_eq!(with_header.len(), FSP_INNER_HEADER_SIZE + 3);
let (ts, mt, flags, rest) = fsp_strip_inner_header(&with_header).unwrap();
assert_eq!(ts, 12345);
assert_eq!(mt, 0x10);
assert_eq!(flags, 0x01);
assert_eq!(rest, &payload[..]);
}
#[test]
fn test_inner_header_empty_payload() {
let with_header = fsp_prepend_inner_header(0, 0x13, 0, &[]);
assert_eq!(with_header.len(), FSP_INNER_HEADER_SIZE);
let (ts, mt, flags, rest) = fsp_strip_inner_header(&with_header).unwrap();
assert_eq!(ts, 0);
assert_eq!(mt, 0x13);
assert_eq!(flags, 0);
assert!(rest.is_empty());
}
#[test]
fn test_inner_header_too_short() {
assert!(fsp_strip_inner_header(&[0, 0, 0, 0, 0]).is_none()); assert!(fsp_strip_inner_header(&[]).is_none());
}
#[test]
fn endpoint_data_bulk_payload_roundtrips_packet_boundaries() {
let encoded = encode_fsp_endpoint_data_bulk_payload(vec![
b"first".to_vec(),
b"second-packet".to_vec(),
b"third".to_vec(),
])
.unwrap();
let lengths = decode_fsp_endpoint_data_bulk_lengths(&encoded).unwrap();
assert_eq!(lengths, vec![5, 13, 5]);
let packets = split_fsp_endpoint_data_bulk_payload(encoded.into(), &lengths);
assert_eq!(packets.len(), 3);
assert_eq!(packets[0].as_slice(), b"first");
assert_eq!(packets[1].as_slice(), b"second-packet");
assert_eq!(packets[2].as_slice(), b"third");
}
#[test]
fn endpoint_data_bulk_payload_rejects_malformed_records() {
assert!(encode_fsp_endpoint_data_bulk_payload(Vec::new()).is_none());
assert!(decode_fsp_endpoint_data_bulk_lengths(&[]).is_none());
assert!(decode_fsp_endpoint_data_bulk_lengths(&[0, 0]).is_none());
assert!(decode_fsp_endpoint_data_bulk_lengths(&[1, 0, 5, 0, b'a']).is_none());
let too_many = vec![Vec::new(); FSP_ENDPOINT_DATA_BULK_MAX_PACKETS + 1];
assert!(encode_fsp_endpoint_data_bulk_payload(too_many).is_none());
}
#[test]
fn endpoint_data_bulk_payload_preserves_vnet_sized_run() {
let mtu_sized_packets = vec![vec![0x42; 1342]; FSP_ENDPOINT_DATA_BULK_MAX_PACKETS];
let encoded =
encode_fsp_endpoint_data_bulk_payload(mtu_sized_packets).expect("48 MTU packets fit");
let lengths = decode_fsp_endpoint_data_bulk_lengths(&encoded).unwrap();
assert_eq!(lengths, vec![1342; FSP_ENDPOINT_DATA_BULK_MAX_PACKETS]);
let one_too_many = vec![vec![0x42; 1342]; FSP_ENDPOINT_DATA_BULK_MAX_PACKETS + 1];
assert!(encode_fsp_endpoint_data_bulk_payload(one_too_many).is_none());
}
#[test]
fn test_flag_bits_distinct() {
assert_eq!(FSP_FLAG_CP & FSP_FLAG_K, 0);
assert_eq!(FSP_FLAG_CP & FSP_FLAG_U, 0);
assert_eq!(FSP_FLAG_K & FSP_FLAG_U, 0);
}
#[test]
fn test_all_message_types_through_prefix() {
let prefix = FspCommonPrefix::parse(&[0x00, 0x00, 0x10, 0x00]).unwrap();
assert_eq!(prefix.phase, 0);
assert!(!prefix.is_unencrypted());
let prefix = FspCommonPrefix::parse(&[0x00, FSP_FLAG_U, 0x22, 0x00]).unwrap();
assert_eq!(prefix.phase, 0);
assert!(prefix.is_unencrypted());
let prefix = FspCommonPrefix::parse(&[0x01, 0x00, 0x50, 0x00]).unwrap();
assert_eq!(prefix.phase, 1);
let prefix = FspCommonPrefix::parse(&[0x02, 0x00, 0x21, 0x00]).unwrap();
assert_eq!(prefix.phase, 2);
let prefix = FspCommonPrefix::parse(&[0x03, 0x00, 0x49, 0x00]).unwrap();
assert_eq!(prefix.phase, 3);
}
}