use core::convert::TryInto;
use crate::{util, Error, Result};
#[allow(non_snake_case)]
pub mod TcpFlag {
pub const FIN: u8 = 1;
pub const SYN: u8 = 2;
pub const RST: u8 = 4;
pub const PSH: u8 = 8;
pub const ACK: u8 = 16;
pub const URG: u8 = 32;
pub const ECN: u8 = 64;
pub const CWR: u8 = 128;
}
#[derive(Debug, Copy, Clone)]
pub struct TcpPdu<'a> {
buffer: &'a [u8],
}
#[derive(Debug, Copy, Clone)]
pub enum Tcp<'a> {
Raw(&'a [u8]),
}
impl<'a> TcpPdu<'a> {
pub fn new(buffer: &'a [u8]) -> Result<Self> {
let pdu = TcpPdu { buffer };
if buffer.len() < 20 || buffer.len() < pdu.computed_data_offset() {
return Err(Error::Truncated);
}
if pdu.computed_data_offset() > 20 {
let mut position = 20;
while position < pdu.computed_data_offset() {
if buffer.len() <= position {
return Err(Error::Truncated);
}
position += match buffer[position] {
0 | 1 => 1usize,
_ => {
if buffer.len() <= (position + 1) {
return Err(Error::Truncated);
}
if buffer[position + 1] < 2 {
return Err(Error::Malformed);
}
buffer[position + 1] as usize
}
};
}
if buffer.len() < position {
return Err(Error::Truncated);
}
if pdu.computed_data_offset() != position {
return Err(Error::Malformed);
}
}
Ok(pdu)
}
pub fn buffer(&'a self) -> &'a [u8] {
self.buffer
}
pub fn as_bytes(&'a self) -> &'a [u8] {
&self.buffer[0..self.computed_data_offset()]
}
pub fn inner(&'a self) -> Result<Tcp<'a>> {
Ok(Tcp::Raw(&self.buffer[self.computed_data_offset()..]))
}
pub fn source_port(&'a self) -> u16 {
u16::from_be_bytes(self.buffer[0..=1].try_into().unwrap())
}
pub fn destination_port(&'a self) -> u16 {
u16::from_be_bytes(self.buffer[2..=3].try_into().unwrap())
}
pub fn sequence_number(&'a self) -> u32 {
u32::from_be_bytes(self.buffer[4..=7].try_into().unwrap())
}
pub fn acknowledgement_number(&'a self) -> u32 {
u32::from_be_bytes(self.buffer[8..=11].try_into().unwrap())
}
pub fn data_offset(&'a self) -> u8 {
self.buffer[12] >> 4
}
pub fn computed_data_offset(&'a self) -> usize {
self.data_offset() as usize * 4
}
pub fn flags(&'a self) -> u8 {
self.buffer[13]
}
pub fn fin(&'a self) -> bool {
self.flags() & 0x1 != 0
}
pub fn syn(&'a self) -> bool {
self.flags() & 0x2 != 0
}
pub fn rst(&'a self) -> bool {
self.flags() & 0x4 != 0
}
pub fn psh(&'a self) -> bool {
self.flags() & 0x8 != 0
}
pub fn ack(&'a self) -> bool {
self.flags() & 0x10 != 0
}
pub fn urg(&'a self) -> bool {
self.flags() & 0x20 != 0
}
pub fn ecn(&'a self) -> bool {
self.flags() & 0x40 != 0
}
pub fn cwr(&'a self) -> bool {
self.flags() & 0x80 != 0
}
pub fn window_size(&'a self) -> u16 {
u16::from_be_bytes(self.buffer[14..=15].try_into().unwrap())
}
pub fn computed_window_size(&'a self, shift: u8) -> u32 {
(self.window_size() as u32) << (shift as usize % std::mem::size_of::<u32>()) as u32
}
pub fn checksum(&'a self) -> u16 {
u16::from_be_bytes(self.buffer[16..=17].try_into().unwrap())
}
pub fn computed_checksum(&'a self, ip: &crate::Ip) -> u16 {
match ip {
crate::Ip::Ipv4(ipv4) => util::checksum(&[
&ipv4.source_address().as_ref(),
&ipv4.destination_address().as_ref(),
&[0x00, ipv4.protocol()].as_ref(),
&(ipv4.total_length() as usize - ipv4.computed_ihl()).to_be_bytes().as_ref(),
&self.buffer[0..=15],
&self.buffer[18..],
]),
crate::Ip::Ipv6(ipv6) => util::checksum(&[
&ipv6.source_address().as_ref(),
&ipv6.destination_address().as_ref(),
&(ipv6.payload_length() as u32).to_be_bytes().as_ref(),
&[0x0, 0x0, 0x0, ipv6.computed_protocol()].as_ref(),
&self.buffer[0..=15],
&self.buffer[18..],
]),
}
}
pub fn urgent_pointer(&'a self) -> u16 {
u16::from_be_bytes(self.buffer[18..=19].try_into().unwrap())
}
pub fn options(&'a self) -> TcpOptionIterator<'a> {
TcpOptionIterator { buffer: self.buffer, pos: 20, data_offset: self.computed_data_offset() }
}
}
#[derive(Debug, Copy, Clone)]
pub enum TcpOption<'a> {
Raw { option: u8, data: &'a [u8] },
NoOp,
Mss { size: u16 },
WindowScale { shift: u8 },
SackPermitted,
Sack { blocks: [Option<(u32, u32)>; 4] },
Timestamp { val: u32, ecr: u32 },
}
#[derive(Debug, Copy, Clone)]
pub struct TcpOptionIterator<'a> {
buffer: &'a [u8],
pos: usize,
data_offset: usize,
}
impl<'a> Iterator for TcpOptionIterator<'a> {
type Item = TcpOption<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.data_offset {
let pos = self.pos;
let option = self.buffer[pos];
let len = match option {
0 | 1 => 1usize,
_ => self.buffer[pos + 1] as usize,
};
self.pos += len;
match option {
0 => None,
1 => Some(TcpOption::NoOp),
2 if len == 4 => Some(TcpOption::Mss {
size: u16::from_be_bytes(self.buffer[pos + 2..=pos + 3].try_into().unwrap()),
}),
3 if len == 3 => Some(TcpOption::WindowScale { shift: self.buffer[pos + 2] }),
4 => Some(TcpOption::SackPermitted),
5 if len == 10 => Some(TcpOption::Sack {
blocks: [
Some((
u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()),
)),
None,
None,
None,
],
}),
5 if len == 18 => Some(TcpOption::Sack {
blocks: [
Some((
u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()),
)),
Some((
u32::from_be_bytes(self.buffer[pos + 10..=pos + 13].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 14..=pos + 17].try_into().unwrap()),
)),
None,
None,
],
}),
5 if len == 26 => Some(TcpOption::Sack {
blocks: [
Some((
u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()),
)),
Some((
u32::from_be_bytes(self.buffer[pos + 10..=pos + 13].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 14..=pos + 17].try_into().unwrap()),
)),
Some((
u32::from_be_bytes(self.buffer[pos + 18..=pos + 21].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 22..=pos + 25].try_into().unwrap()),
)),
None,
],
}),
5 if len == 34 => Some(TcpOption::Sack {
blocks: [
Some((
u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()),
)),
Some((
u32::from_be_bytes(self.buffer[pos + 10..=pos + 13].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 14..=pos + 17].try_into().unwrap()),
)),
Some((
u32::from_be_bytes(self.buffer[pos + 18..=pos + 21].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 22..=pos + 25].try_into().unwrap()),
)),
Some((
u32::from_be_bytes(self.buffer[pos + 26..=pos + 29].try_into().unwrap()),
u32::from_be_bytes(self.buffer[pos + 30..=pos + 33].try_into().unwrap()),
)),
],
}),
8 if len == 10 => Some(TcpOption::Timestamp {
val: u32::from_be_bytes(self.buffer[pos + 2..=pos + 5].try_into().unwrap()),
ecr: u32::from_be_bytes(self.buffer[pos + 6..=pos + 9].try_into().unwrap()),
}),
_ => Some(TcpOption::Raw { option, data: &self.buffer[pos..(pos + len)] }),
}
} else {
None
}
}
}