use std::fmt::{self, Formatter};
use zerocopy::{BigEndian, FromBytes, Immutable, Ref, Unaligned, U32};
use crate::packet::PacketHeaderError;
const KIND_EOL: u8 = 0;
const KIND_NOP: u8 = 1;
const KIND_MSS: u8 = 2;
const KIND_WSCALE: u8 = 3;
const KIND_SACK_PERM: u8 = 4;
const KIND_SACK: u8 = 5;
const KIND_TIMESTAMP: u8 = 8;
#[derive(Debug)]
pub enum TcpOptionElement<'a> {
Eol, Nop, Mss(u16), WindowScale(u8), SackPermitted, Sack(&'a [SackBlock]), Timestamp(u32, u32), Unknown { kind: u8, data: &'a [u8] },
}
#[derive(FromBytes, Unaligned, Immutable, Debug)]
#[repr(C)]
pub struct SackBlock {
pub left_edge: U32<BigEndian>,
pub right_edge: U32<BigEndian>,
}
impl fmt::Display for TcpOptionElement<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TcpOptionElement::Eol => write!(f, "EOL"),
TcpOptionElement::Nop => write!(f, "NOP"),
TcpOptionElement::Mss(mss) => write!(f, "MSS({})", mss),
TcpOptionElement::WindowScale(shift) => write!(f, "WS({})", shift),
TcpOptionElement::SackPermitted => write!(f, "SACK_OK"),
TcpOptionElement::Sack(blocks) => {
write!(f, "SACK(")?;
for (i, block) in blocks.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, "{}-{}", block.left_edge.get(), block.right_edge.get())?;
}
write!(f, ")")
}
TcpOptionElement::Timestamp(ts_val, ts_ecr) => {
write!(f, "TS({},{})", ts_val, ts_ecr)
}
TcpOptionElement::Unknown { kind, .. } => write!(f, "UNK({})", kind),
}
}
}
pub struct TcpOptionsIter<'a> {
cursor: &'a [u8],
}
impl<'a> TcpOptionsIter<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { cursor: data }
}
}
impl<'a> Iterator for TcpOptionsIter<'a> {
type Item = Result<TcpOptionElement<'a>, PacketHeaderError>;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor.is_empty() {
return None;
}
let kind = self.cursor[0];
match kind {
KIND_EOL => {
self.cursor = &[]; return Some(Ok(TcpOptionElement::Eol));
}
KIND_NOP => {
self.cursor = &self.cursor[1..];
return Some(Ok(TcpOptionElement::Nop));
}
_ => {} }
if self.cursor.len() < 2 {
return Some(Err(PacketHeaderError::TooShort("TcpHeader"))); }
let len = self.cursor[1] as usize;
if len < 2 {
return Some(Err(PacketHeaderError::Invalid("TcpHeader")));
}
if self.cursor.len() < len {
return Some(Err(PacketHeaderError::TooShort("TcpHeader"))); }
let data = &self.cursor[2..len];
self.cursor = &self.cursor[len..];
let option_result = match kind {
KIND_MSS => {
if data.len() != 2 {
Ok(TcpOptionElement::Unknown { kind, data }) } else {
let mss = u16::from_be_bytes([data[0], data[1]]);
Ok(TcpOptionElement::Mss(mss))
}
}
KIND_WSCALE => {
if data.len() != 1 {
Ok(TcpOptionElement::Unknown { kind, data })
} else {
Ok(TcpOptionElement::WindowScale(data[0]))
}
}
KIND_SACK_PERM => Ok(TcpOptionElement::SackPermitted),
KIND_SACK => {
match Ref::<_, [SackBlock]>::from_bytes(data) {
Ok(slice) => Ok(TcpOptionElement::Sack(Ref::into_ref(slice))),
Err(_) => Err(PacketHeaderError::Invalid("TcpHeader: OPT_SACK")), }
}
KIND_TIMESTAMP => {
if data.len() != 8 {
Ok(TcpOptionElement::Unknown { kind, data })
} else {
let ts_val = u32::from_be_bytes(data[0..4].try_into().unwrap());
let ts_ecr = u32::from_be_bytes(data[4..8].try_into().unwrap());
Ok(TcpOptionElement::Timestamp(ts_val, ts_ecr))
}
}
_ => Ok(TcpOptionElement::Unknown { kind, data }),
};
Some(option_result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tcp_option_eol() {
let data = [KIND_EOL, 0x00, 0x00];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::Eol));
assert!(iter.next().is_none());
}
#[test]
fn test_tcp_option_nop() {
let data = [KIND_NOP, KIND_NOP, KIND_NOP];
let mut iter = TcpOptionsIter::new(&data);
for _ in 0..3 {
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::Nop));
}
assert!(iter.next().is_none());
}
#[test]
fn test_tcp_option_mss() {
let data = [KIND_MSS, 0x04, 0x05, 0xB4];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Mss(mss) => assert_eq!(mss, 1460),
_ => panic!("Expected MSS option"),
}
assert!(iter.next().is_none());
}
#[test]
fn test_tcp_option_mss_malformed() {
let data = [KIND_MSS, 0x03, 0x05];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Unknown { kind, data } => {
assert_eq!(kind, KIND_MSS);
assert_eq!(data.len(), 1); }
_ => panic!("Expected Unknown option for malformed MSS"),
}
}
#[test]
fn test_tcp_option_window_scale() {
let data = [KIND_WSCALE, 0x03, 0x07];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::WindowScale(shift) => assert_eq!(shift, 7),
_ => panic!("Expected WindowScale option"),
}
}
#[test]
fn test_tcp_option_sack_permitted() {
let data = [KIND_SACK_PERM, 0x02];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::SackPermitted));
}
#[test]
fn test_tcp_option_sack_single_block() {
let mut data = vec![KIND_SACK, 0x0A];
data.extend_from_slice(&1000u32.to_be_bytes()); data.extend_from_slice(&2000u32.to_be_bytes());
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Sack(blocks) => {
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0].left_edge.get(), 1000);
assert_eq!(blocks[0].right_edge.get(), 2000);
}
_ => panic!("Expected SACK option"),
}
}
#[test]
fn test_tcp_option_sack_multiple_blocks() {
let mut data = vec![KIND_SACK, 0x1A];
data.extend_from_slice(&1000u32.to_be_bytes());
data.extend_from_slice(&2000u32.to_be_bytes());
data.extend_from_slice(&3000u32.to_be_bytes());
data.extend_from_slice(&4000u32.to_be_bytes());
data.extend_from_slice(&5000u32.to_be_bytes());
data.extend_from_slice(&6000u32.to_be_bytes());
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Sack(blocks) => {
assert_eq!(blocks.len(), 3);
assert_eq!(blocks[0].left_edge.get(), 1000);
assert_eq!(blocks[0].right_edge.get(), 2000);
assert_eq!(blocks[1].left_edge.get(), 3000);
assert_eq!(blocks[1].right_edge.get(), 4000);
assert_eq!(blocks[2].left_edge.get(), 5000);
assert_eq!(blocks[2].right_edge.get(), 6000);
}
_ => panic!("Expected SACK option"),
}
}
#[test]
fn test_tcp_option_sack_malformed() {
let data = [KIND_SACK, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00];
let mut iter = TcpOptionsIter::new(&data);
let result = iter.next().unwrap();
assert!(result.is_err());
}
#[test]
fn test_tcp_option_timestamp() {
let mut data = vec![KIND_TIMESTAMP, 0x0A];
data.extend_from_slice(&12345678u32.to_be_bytes()); data.extend_from_slice(&87654321u32.to_be_bytes());
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Timestamp(ts_val, ts_ecr) => {
assert_eq!(ts_val, 12345678);
assert_eq!(ts_ecr, 87654321);
}
_ => panic!("Expected Timestamp option"),
}
}
#[test]
fn test_tcp_option_timestamp_malformed() {
let data = [KIND_TIMESTAMP, 0x06, 0x00, 0x00, 0x00, 0x00];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Unknown { kind, .. } => {
assert_eq!(kind, KIND_TIMESTAMP);
}
_ => panic!("Expected Unknown option for malformed Timestamp"),
}
}
#[test]
fn test_tcp_option_unknown() {
let data = [99, 0x05, 0xAA, 0xBB, 0xCC];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Unknown { kind, data } => {
assert_eq!(kind, 99);
assert_eq!(data, &[0xAA, 0xBB, 0xCC]);
}
_ => panic!("Expected Unknown option"),
}
}
#[test]
fn test_tcp_options_multiple_mixed() {
let mut data = Vec::new();
data.push(KIND_NOP);
data.extend_from_slice(&[KIND_MSS, 0x04, 0x05, 0xB4]);
data.push(KIND_NOP);
data.extend_from_slice(&[KIND_WSCALE, 0x03, 0x07]);
data.extend_from_slice(&[KIND_SACK_PERM, 0x02]);
data.push(KIND_TIMESTAMP);
data.push(0x0A);
data.extend_from_slice(&123u32.to_be_bytes());
data.extend_from_slice(&456u32.to_be_bytes());
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::Nop));
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Mss(mss) => assert_eq!(mss, 1460),
_ => panic!("Expected MSS"),
}
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::Nop));
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::WindowScale(shift) => assert_eq!(shift, 7),
_ => panic!("Expected WindowScale"),
}
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::SackPermitted));
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Timestamp(ts_val, ts_ecr) => {
assert_eq!(ts_val, 123);
assert_eq!(ts_ecr, 456);
}
_ => panic!("Expected Timestamp"),
}
assert!(iter.next().is_none());
}
#[test]
fn test_tcp_options_eol_stops_parsing() {
let data = [
KIND_NOP, KIND_MSS, 0x04, 0x05, 0xB4, KIND_EOL, KIND_NOP, KIND_NOP,
];
let mut iter = TcpOptionsIter::new(&data);
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::Nop));
let opt = iter.next().unwrap().unwrap();
match opt {
TcpOptionElement::Mss(mss) => assert_eq!(mss, 1460),
_ => panic!("Expected MSS"),
}
let opt = iter.next().unwrap().unwrap();
assert!(matches!(opt, TcpOptionElement::Eol));
assert!(iter.next().is_none());
}
#[test]
fn test_tcp_option_too_short_no_length() {
let data = [KIND_MSS]; let mut iter = TcpOptionsIter::new(&data);
let result = iter.next().unwrap();
assert!(result.is_err());
}
#[test]
fn test_tcp_option_length_too_small() {
let data = [KIND_MSS, 0x01]; let mut iter = TcpOptionsIter::new(&data);
let result = iter.next().unwrap();
assert!(result.is_err());
}
#[test]
fn test_tcp_option_length_exceeds_buffer() {
let data = [KIND_TIMESTAMP, 0x0A, 0x00, 0x00, 0x00];
let mut iter = TcpOptionsIter::new(&data);
let result = iter.next().unwrap();
assert!(result.is_err());
}
#[test]
fn test_tcp_options_empty_buffer() {
let data: [u8; 0] = [];
let mut iter = TcpOptionsIter::new(&data);
assert!(iter.next().is_none());
}
#[test]
fn test_tcp_option_real_world_syn() {
let mut data = Vec::new();
data.extend_from_slice(&[KIND_MSS, 0x04, 0x05, 0xB4]);
data.extend_from_slice(&[KIND_SACK_PERM, 0x02]);
data.push(KIND_TIMESTAMP);
data.push(0x0A);
data.extend_from_slice(&3845678901u32.to_be_bytes());
data.extend_from_slice(&0u32.to_be_bytes());
data.push(KIND_NOP);
data.extend_from_slice(&[KIND_WSCALE, 0x03, 0x07]);
let opts: Vec<_> = TcpOptionsIter::new(&data)
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(opts.len(), 5);
assert!(matches!(opts[0], TcpOptionElement::Mss(1460)));
assert!(matches!(opts[1], TcpOptionElement::SackPermitted));
assert!(matches!(
opts[2],
TcpOptionElement::Timestamp(3845678901, 0)
));
assert!(matches!(opts[3], TcpOptionElement::Nop));
assert!(matches!(opts[4], TcpOptionElement::WindowScale(7)));
}
}