use super::super::*;
extern crate byteorder;
use self::byteorder::{ByteOrder, BigEndian, ReadBytesExt, WriteBytesExt};
use std::fmt::{Debug, Formatter};
pub const TCP_MINIMUM_HEADER_SIZE: usize = 5*4;
pub const TCP_MINIMUM_DATA_OFFSET: u8 = 5;
pub const TCP_MAXIMUM_DATA_OFFSET: u8 = 0xf;
#[derive(Clone)]
pub struct TcpHeader {
pub source_port: u16,
pub destination_port: u16,
pub sequence_number: u32,
pub acknowledgment_number: u32,
_data_offset: u8,
pub ns: bool,
pub fin: bool,
pub syn: bool,
pub rst: bool,
pub psh: bool,
pub ack: bool,
pub urg: bool,
pub ece: bool,
pub cwr: bool,
pub window_size: u16,
pub checksum: u16,
pub urgent_pointer: u16,
options_buffer: [u8;40]
}
impl TcpHeader {
pub fn new(source_port: u16, destination_port: u16, sequence_number: u32, window_size: u16) -> TcpHeader {
TcpHeader {
source_port,
destination_port,
sequence_number,
acknowledgment_number: 0,
_data_offset: TCP_MINIMUM_DATA_OFFSET,
ns: false,
fin: false,
syn: false,
rst: false,
psh: false,
ack: false,
ece: false,
urg: false,
cwr: false,
window_size,
checksum: 0,
urgent_pointer: 0,
options_buffer: [0;40]
}
}
pub fn data_offset(&self) -> u8 {
self._data_offset
}
pub fn header_len(&self) -> u16 {
u16::from(self._data_offset) * 4
}
pub fn options_len(&self) -> usize {
debug_assert!(TCP_MINIMUM_DATA_OFFSET <= self._data_offset);
debug_assert!(self._data_offset <= TCP_MAXIMUM_DATA_OFFSET);
(self._data_offset - TCP_MINIMUM_DATA_OFFSET) as usize * 4
}
pub fn options(&self) -> &[u8] {
&self.options_buffer[..self.options_len()]
}
pub fn set_options(&mut self, options: &[TcpOptionElement]) -> Result<(), TcpOptionWriteError> {
use crate::TcpOptionElement::*;
let required_length = options.iter().fold(0, |acc, ref x| {
acc + match x {
Nop => 1,
MaximumSegmentSize(_) => 4,
WindowScale(_) => 3,
SelectiveAcknowledgementPermitted => 2,
SelectiveAcknowledgement(_, rest) => {
rest.iter().fold(10, |acc2, ref y| {
match y {
None => acc2,
Some(_) => acc2 + 8
}
})
},
Timestamp(_, _) => 10,
}
});
if self.options_buffer.len() < required_length {
Err(TcpOptionWriteError::NotEnoughSpace(required_length))
} else {
self.options_buffer = [0;40];
self._data_offset = TCP_MINIMUM_DATA_OFFSET;
let mut i = 0;
for element in options {
match element {
Nop => {
self.options_buffer[i] = TCP_OPTION_ID_NOP;
i += 1;
},
MaximumSegmentSize(value) => {
self.options_buffer[i] = TCP_OPTION_ID_MAXIMUM_SEGMENT_SIZE;
i += 1;
self.options_buffer[i] = 4;
i += 1;
BigEndian::write_u16(&mut self.options_buffer[i..i + 2], *value);
i += 2;
},
WindowScale(value) => {
self.options_buffer[i] = TCP_OPTION_ID_WINDOW_SCALE;
i += 1;
self.options_buffer[i] = 3;
i += 1;
self.options_buffer[i] = *value;
i += 1;
},
SelectiveAcknowledgementPermitted => {
self.options_buffer[i] = TCP_OPTION_ID_SELECTIVE_ACK_PERMITTED;
i += 1;
self.options_buffer[i] = 2;
i += 1;
},
SelectiveAcknowledgement(first, rest) => {
self.options_buffer[i] = TCP_OPTION_ID_SELECTIVE_ACK;
i += 1;
self.options_buffer[i] = rest.iter().fold(10, |acc, ref y| {
match y {
None => acc,
Some(_) => acc + 8
}
});
i += 1;
BigEndian::write_u32(&mut self.options_buffer[i..i + 4], first.0);
i += 4;
BigEndian::write_u32(&mut self.options_buffer[i..i + 4], first.1);
i += 4;
for v in rest {
match v {
None => {},
Some((a,b)) => {
BigEndian::write_u32(&mut self.options_buffer[i..i + 4], *a);
i += 4;
BigEndian::write_u32(&mut self.options_buffer[i..i + 4], *b);
i += 4;
}
}
}
},
Timestamp(a, b) => {
self.options_buffer[i] = TCP_OPTION_ID_TIMESTAMP;
i += 1;
self.options_buffer[i] = 10;
i += 1;
BigEndian::write_u32(&mut self.options_buffer[i..i + 4], *a);
i += 4;
BigEndian::write_u32(&mut self.options_buffer[i..i + 4], *b);
i += 4;
}
}
}
if i > 0 {
self._data_offset = (i / 4) as u8 + TCP_MINIMUM_DATA_OFFSET;
if i % 4 != 0 {
self._data_offset += 1;
}
}
Ok(())
}
}
pub fn set_options_raw(&mut self, data: &[u8]) -> Result<(), TcpOptionWriteError> {
if self.options_buffer.len() < data.len() {
Err(TcpOptionWriteError::NotEnoughSpace(data.len()))
} else {
self.options_buffer = [0;40];
self.options_buffer[..data.len()].copy_from_slice(data);
self._data_offset = (data.len() / 4) as u8 + TCP_MINIMUM_DATA_OFFSET;
if data.len() % 4 != 0 {
self._data_offset += 1;
}
Ok(())
}
}
pub fn options_iterator(&self) -> TcpOptionsIterator {
TcpOptionsIterator {
options: &self.options_buffer[..self.options_len()]
}
}
pub fn read_from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), ReadError> {
let h = TcpHeaderSlice::from_slice(slice)?;
Ok((
h.to_header(),
&slice[h.slice().len()..]
))
}
pub fn read<T: io::Read + Sized>(reader: &mut T) -> Result<TcpHeader, ReadError> {
let source_port = reader.read_u16::<BigEndian>()?;
let destination_port = reader.read_u16::<BigEndian>()?;
let sequence_number = reader.read_u32::<BigEndian>()?;
let acknowledgment_number = reader.read_u32::<BigEndian>()?;
let (data_offset, ns) = {
let value = reader.read_u8()?;
((value & 0xf0) >> 4, 0 != value & 1)
};
let flags = reader.read_u8()?;
Ok(TcpHeader{
source_port,
destination_port,
sequence_number,
acknowledgment_number,
ns,
fin: 0 != flags & 1,
syn: 0 != flags & 2,
rst: 0 != flags & 4,
psh: 0 != flags & 8,
ack: 0 != flags & 16,
urg: 0 != flags & 32,
ece: 0 != flags & 64,
cwr: 0 != flags & 128,
window_size: reader.read_u16::<BigEndian>()?,
checksum: reader.read_u16::<BigEndian>()?,
urgent_pointer: reader.read_u16::<BigEndian>()?,
options_buffer: {
if data_offset < TCP_MINIMUM_DATA_OFFSET {
return Err(ReadError::TcpDataOffsetTooSmall(data_offset));
} else {
let mut buffer: [u8;40] = [0;40];
let len = ((data_offset - TCP_MINIMUM_DATA_OFFSET) as usize)*4;
if len > 0 {
reader.read_exact(&mut buffer[..len])?;
}
buffer
}
},
_data_offset: data_offset,
})
}
pub fn write<T: io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
debug_assert!(TCP_MINIMUM_DATA_OFFSET <= self._data_offset);
debug_assert!(self._data_offset <= TCP_MAXIMUM_DATA_OFFSET);
writer.write_u16::<BigEndian>(self.source_port)?;
writer.write_u16::<BigEndian>(self.destination_port)?;
writer.write_u32::<BigEndian>(self.sequence_number)?;
writer.write_u32::<BigEndian>(self.acknowledgment_number)?;
writer.write_u8({
let value = (self._data_offset << 4) & 0xF0;
if self.ns {
value | 1
} else {
value
}
})?;
writer.write_u8({
let mut value = 0;
if self.fin {
value |= 1;
}
if self.syn {
value |= 2;
}
if self.rst {
value |= 4;
}
if self.psh {
value |= 8;
}
if self.ack {
value |= 16;
}
if self.urg {
value |= 32;
}
if self.ece {
value |= 64;
}
if self.cwr {
value |= 128;
}
value
})?;
writer.write_u16::<BigEndian>(self.window_size)?;
writer.write_u16::<BigEndian>(self.checksum)?;
writer.write_u16::<BigEndian>(self.urgent_pointer)?;
if self._data_offset > TCP_MINIMUM_DATA_OFFSET {
let len = ((self._data_offset - TCP_MINIMUM_DATA_OFFSET) as usize)*4;
writer.write_all(&self.options_buffer[..len])?;
}
Ok(())
}
pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4Header, payload: &[u8]) -> Result<u16, ValueError> {
self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload)
}
pub fn calc_checksum_ipv4_raw(&self, source_ip: [u8;4], destination_ip: [u8;4], payload: &[u8]) -> Result<u16, ValueError> {
let tcp_length = (self._data_offset as usize)*4 + payload.len();
if (std::u16::MAX as usize) < tcp_length {
return Err(ValueError::TcpLengthTooLarge(tcp_length));
}
Ok(self.calc_checksum_post_ip(u64::from( BigEndian::read_u16(&source_ip[0..2]) ) +
u64::from( BigEndian::read_u16(&source_ip[2..4]) ) +
u64::from( BigEndian::read_u16(&destination_ip[0..2]) ) +
u64::from( BigEndian::read_u16(&destination_ip[2..4]) ) +
IpTrafficClass::Tcp as u64 +
tcp_length as u64,
payload))
}
pub fn calc_checksum_ipv6(&self, ip_header: &Ipv6Header, payload: &[u8]) -> Result<u16, ValueError> {
self.calc_checksum_ipv6_raw(&ip_header.source, &ip_header.destination, payload)
}
pub fn calc_checksum_ipv6_raw(&self, source: &[u8;16], destination: &[u8;16], payload: &[u8]) -> Result<u16, ValueError> {
let tcp_length = (self._data_offset as usize)*4 + payload.len();
if (std::u32::MAX as usize) < tcp_length {
return Err(ValueError::TcpLengthTooLarge(tcp_length));
}
fn calc_sum(value: &[u8;16]) -> u64 {
let mut result = 0;
for i in 0..8 {
let index = i*2;
result += u64::from( BigEndian::read_u16(&value[index..(index + 2)]) );
}
result
}
Ok(self.calc_checksum_post_ip(
calc_sum(source) +
calc_sum(destination) +
IpTrafficClass::Tcp as u64 +
{
let mut buffer: [u8;4] = Default::default();
BigEndian::write_u32(&mut buffer[..], tcp_length as u32);
u64::from( BigEndian::read_u16(&buffer[0..2]) ) +
u64::from( BigEndian::read_u16(&buffer[2..4]) )
},
payload))
}
fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: u64, payload: &[u8]) -> u16 {
fn calc_u32_checksum(value: u32) -> u64 {
let mut buffer: [u8;4] = [0;4];
BigEndian::write_u32(&mut buffer, value);
u64::from( BigEndian::read_u16(&buffer[..2]) ) +
u64::from( BigEndian::read_u16(&buffer[2..]) )
}
let mut sum =
ip_pseudo_header_sum +
u64::from( self.source_port ) +
u64::from( self.destination_port ) +
calc_u32_checksum(self.sequence_number) +
calc_u32_checksum(self.acknowledgment_number) +
u64::from( BigEndian::read_u16(&[
{
let value = (self._data_offset << 4) & 0xF0;
if self.ns {
value | 1
} else {
value
}
},
{
let mut value = 0;
if self.fin {
value |= 1;
}
if self.syn {
value |= 2;
}
if self.rst {
value |= 4;
}
if self.psh {
value |= 8;
}
if self.ack {
value |= 16;
}
if self.urg {
value |= 32;
}
if self.ece {
value |= 64;
}
if self.cwr {
value |= 128;
}
value
}
]) ) +
u64::from( self.window_size ) +
u64::from( self.urgent_pointer );
let options_len = self.options_len();
for i in RangeStep::new(0, options_len, 2) {
sum += u64::from( BigEndian::read_u16(&self.options_buffer[i..i + 2]) );
}
for i in RangeStep::new(0, payload.len()/2*2, 2) {
sum += u64::from( BigEndian::read_u16(&payload[i..i + 2]) );
}
if payload.len() % 2 == 1 {
sum += u64::from( BigEndian::read_u16(&[*payload.last().unwrap(), 0]) );
}
let carry_add = (sum & 0xffff) +
((sum >> 16) & 0xffff) +
((sum >> 32) & 0xffff) +
((sum >> 48) & 0xffff);
let result = ((carry_add & 0xffff) + (carry_add >> 16)) as u16;
!result
}
}
impl Default for TcpHeader {
fn default() -> TcpHeader {
TcpHeader {
source_port: 0,
destination_port: 0,
sequence_number: 0,
acknowledgment_number: 0,
_data_offset: 5,
ns: false, fin: false, syn: false, rst: false,
psh: false, ack: false, urg: false, ece: false,
cwr: false,
window_size: 0,
checksum: 0,
urgent_pointer: 0,
options_buffer: [0;40]
}
}
}
impl Debug for TcpHeader {
fn fmt(&self, fotmatter: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(fotmatter, "TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, data_offset: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {} }}",
self.source_port,
self.destination_port,
self.sequence_number,
self.acknowledgment_number,
self._data_offset,
self.ns,
self.fin,
self.syn,
self.rst,
self.psh,
self.ack,
self.urg,
self.ece,
self.cwr,
self.window_size,
self.checksum,
self.urgent_pointer)
}
}
impl std::cmp::PartialEq for TcpHeader {
fn eq(&self, other: &TcpHeader) -> bool {
self.source_port == other.source_port &&
self.destination_port == other.destination_port &&
self.sequence_number == other.sequence_number &&
self.acknowledgment_number == other.acknowledgment_number &&
self._data_offset == other._data_offset &&
self.ns == other.ns &&
self.fin == other.fin &&
self.syn == other.syn &&
self.rst == other.rst &&
self.psh == other.psh &&
self.ack == other.ack &&
self.urg == other.urg &&
self.ece == other.ece &&
self.cwr == other.cwr &&
self.window_size == other.window_size &&
self.checksum == other.checksum &&
self.urgent_pointer == other.urgent_pointer &&
self.options_buffer[..] == other.options_buffer[..]
}
}
impl std::cmp::Eq for TcpHeader {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TcpHeaderSlice<'a> {
slice: &'a [u8]
}
impl<'a> TcpHeaderSlice<'a> {
pub fn from_slice(slice: &'a[u8]) -> Result<TcpHeaderSlice<'a>, ReadError> {
use crate::ReadError::*;
if slice.len() < TCP_MINIMUM_HEADER_SIZE {
return Err(UnexpectedEndOfSlice(TCP_MINIMUM_HEADER_SIZE));
}
let data_offset = (slice[12] & 0xf0) >> 4;
let len = data_offset as usize * 4;
if data_offset < TCP_MINIMUM_DATA_OFFSET {
Err(ReadError::TcpDataOffsetTooSmall(data_offset))
} else if slice.len() < len {
Err(UnexpectedEndOfSlice(len))
} else {
Ok(TcpHeaderSlice::<'a>{
slice: &slice[..len],
})
}
}
pub fn slice(&self) -> &'a [u8] {
self.slice
}
pub fn source_port(&self) -> u16 {
BigEndian::read_u16(&self.slice[..2])
}
pub fn destination_port(&self) -> u16 {
BigEndian::read_u16(&self.slice[2..4])
}
pub fn sequence_number(&self) -> u32 {
BigEndian::read_u32(&self.slice[4..8])
}
pub fn acknowledgment_number(&self) -> u32 {
BigEndian::read_u32(&self.slice[8..12])
}
pub fn data_offset(&self) -> u8 {
(self.slice[12] & 0xf0) >> 4
}
pub fn ns(&self) -> bool {
0 != (self.slice[12] & 1)
}
pub fn fin(&self) -> bool {
0 != (self.slice[13] & 1)
}
pub fn syn(&self) -> bool {
0 != (self.slice[13] & 2)
}
pub fn rst(&self) -> bool {
0 != (self.slice[13] & 4)
}
pub fn psh(&self) -> bool {
0 != (self.slice[13] & 8)
}
pub fn ack(&self) -> bool {
0 != (self.slice[13] & 16)
}
pub fn urg(&self) -> bool {
0 != (self.slice[13] & 32)
}
pub fn ece(&self) -> bool {
0 != (self.slice[13] & 64)
}
pub fn cwr(&self) -> bool {
0 != (self.slice[13] & 128)
}
pub fn window_size(&self) -> u16 {
BigEndian::read_u16(&self.slice[14..16])
}
pub fn checksum(&self) -> u16 {
BigEndian::read_u16(&self.slice[16..18])
}
pub fn urgent_pointer(&self) -> u16 {
BigEndian::read_u16(&self.slice[18..20])
}
pub fn options(&self) -> &[u8] {
&self.slice[TCP_MINIMUM_HEADER_SIZE..self.data_offset() as usize*4]
}
pub fn options_iterator(&self) -> TcpOptionsIterator {
TcpOptionsIterator::from_slice(self.options())
}
pub fn to_header(&self) -> TcpHeader {
TcpHeader {
source_port: self.source_port(),
destination_port: self.destination_port(),
sequence_number: self.sequence_number(),
acknowledgment_number: self.acknowledgment_number(),
_data_offset: self.data_offset(),
ns: self.ns(),
fin: self.fin(),
syn: self.syn(),
rst: self.rst(),
psh: self.psh(),
ack: self.ack(),
ece: self.ece(),
urg: self.urg(),
cwr: self.cwr(),
window_size: self.window_size(),
checksum: self.checksum(),
urgent_pointer: self.urgent_pointer(),
options_buffer: {
let options = self.options();
let mut result: [u8;40] = [0;40];
if !options.is_empty() {
result[..options.len()].clone_from_slice(&options);
}
result
}
}
}
pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4HeaderSlice, payload: &[u8]) -> Result<u16, ValueError> {
self.calc_checksum_ipv4_raw(&ip_header.source(), &ip_header.destination(), payload)
}
pub fn calc_checksum_ipv4_raw(&self, source_ip: &[u8], destination_ip: &[u8], payload: &[u8]) -> Result<u16, ValueError> {
let tcp_length = self.slice.len() + payload.len();
if (std::u16::MAX as usize) < tcp_length {
return Err(ValueError::TcpLengthTooLarge(tcp_length));
}
Ok(self.calc_checksum_post_ip(u64::from( BigEndian::read_u16(&source_ip[0..2]) ) +
u64::from( BigEndian::read_u16(&source_ip[2..4]) ) +
u64::from( BigEndian::read_u16(&destination_ip[0..2]) ) +
u64::from( BigEndian::read_u16(&destination_ip[2..4]) ) +
IpTrafficClass::Tcp as u64 +
tcp_length as u64,
payload))
}
pub fn calc_checksum_ipv6(&self, ip_header: &Ipv6HeaderSlice, payload: &[u8]) -> Result<u16, ValueError> {
self.calc_checksum_ipv6_raw(&ip_header.source(), &ip_header.destination(), payload)
}
pub fn calc_checksum_ipv6_raw(&self, source: &[u8], destination: &[u8], payload: &[u8]) -> Result<u16, ValueError> {
let tcp_length = (self.data_offset() as usize)*4 + payload.len();
if (std::u32::MAX as usize) < tcp_length {
return Err(ValueError::TcpLengthTooLarge(tcp_length));
}
fn calc_addr_sum(value: &[u8]) -> u64 {
let mut result = 0;
for i in 0..8 {
let index = i*2;
result += u64::from( BigEndian::read_u16(&value[index..(index + 2)]) );
}
result
}
Ok(self.calc_checksum_post_ip(
calc_addr_sum(source) +
calc_addr_sum(destination) +
IpTrafficClass::Tcp as u64 +
{
let mut buffer: [u8;4] = Default::default();
BigEndian::write_u32(&mut buffer[..], tcp_length as u32);
u64::from( BigEndian::read_u16(&buffer[0..2]) ) +
u64::from( BigEndian::read_u16(&buffer[2..4]) )
},
payload))
}
fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: u64, payload: &[u8]) -> u16 {
let mut sum = ip_pseudo_header_sum;
for i in RangeStep::new(0, 16, 2) {
sum += u64::from( BigEndian::read_u16(&self.slice[i..i + 2]) );
}
for i in RangeStep::new(18, self.slice.len(), 2) {
sum += u64::from( BigEndian::read_u16(&self.slice[i..i + 2]) );
}
for i in RangeStep::new(0, payload.len()/2*2, 2) {
sum += u64::from( BigEndian::read_u16(&payload[i..i + 2]) );
}
if payload.len() % 2 == 1 {
sum += u64::from( BigEndian::read_u16(&[*payload.last().unwrap(), 0]) );
}
let carry_add = (sum & 0xffff) +
((sum >> 16) & 0xffff) +
((sum >> 32) & 0xffff) +
((sum >> 48) & 0xffff);
let result = ((carry_add & 0xffff) + (carry_add >> 16)) as u16;
!result
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TcpOptionElement {
Nop,
MaximumSegmentSize(u16),
WindowScale(u8),
SelectiveAcknowledgementPermitted,
SelectiveAcknowledgement((u32,u32), [Option<(u32,u32)>;3]),
Timestamp(u32, u32),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TcpOptionReadError {
UnexpectedEndOfSlice(u8),
UnexpectedSize{option_id: u8, size: u8 },
UnknownId(u8),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TcpOptionWriteError {
NotEnoughSpace(usize)
}
pub struct TcpOptionsIterator<'a> {
options: &'a [u8]
}
pub const TCP_OPTION_ID_END: u8 = 0;
pub const TCP_OPTION_ID_NOP: u8 = 1;
pub const TCP_OPTION_ID_MAXIMUM_SEGMENT_SIZE: u8 = 2;
pub const TCP_OPTION_ID_WINDOW_SCALE: u8 = 3;
pub const TCP_OPTION_ID_SELECTIVE_ACK_PERMITTED: u8 = 4;
pub const TCP_OPTION_ID_SELECTIVE_ACK: u8 = 5;
pub const TCP_OPTION_ID_TIMESTAMP: u8 = 8;
impl<'a> TcpOptionsIterator<'a> {
pub fn from_slice(options: &'a [u8]) -> TcpOptionsIterator<'a> {
TcpOptionsIterator{ options }
}
pub fn rest(&self) -> &'a [u8] {
self.options
}
}
impl<'a> Iterator for TcpOptionsIterator<'a> {
type Item = Result<TcpOptionElement, TcpOptionReadError>;
fn next(&mut self) -> Option<Self::Item> {
use crate::TcpOptionReadError::*;
use crate::TcpOptionElement::*;
let expect_specific_size = |expected_size: u8, slice: &[u8]| -> Result<(), TcpOptionReadError> {
let id = slice[0];
if slice.len() < expected_size as usize {
Err(UnexpectedEndOfSlice(id))
} else if slice[1] != expected_size {
Err(UnexpectedSize{
option_id: slice[0],
size: slice[1]
})
} else {
Ok(())
}
};
if self.options.is_empty() {
None
} else {
let result = match self.options[0] {
TCP_OPTION_ID_END => {
None
},
TCP_OPTION_ID_NOP => {
self.options = &self.options[1..];
Some(Ok(Nop))
},
TCP_OPTION_ID_MAXIMUM_SEGMENT_SIZE => {
match expect_specific_size(4, self.options) {
Err(value) => {
Some(Err(value))
},
_ => {
let value = BigEndian::read_u16(&self.options[2..4]);
self.options = &self.options[4..];
Some(Ok(MaximumSegmentSize(value)))
}
}
},
TCP_OPTION_ID_WINDOW_SCALE => {
match expect_specific_size(3, self.options) {
Err(value) => Some(Err(value)),
_ => {
let value = self.options[2];
self.options = &self.options[3..];
Some(Ok(WindowScale(value)))
}
}
},
TCP_OPTION_ID_SELECTIVE_ACK_PERMITTED => {
match expect_specific_size(2, self.options) {
Err(value) => Some(Err(value)),
_ => {
self.options = &self.options[2..];
Some(Ok(SelectiveAcknowledgementPermitted))
}
}
},
TCP_OPTION_ID_SELECTIVE_ACK => {
if self.options.len() < 2 {
Some(Err(UnexpectedEndOfSlice(self.options[0])))
} else {
let len = self.options[1];
if len != 10 && len != 18 && len != 26 && len != 34 {
Some(Err(UnexpectedSize{
option_id: self.options[0],
size: len
}))
} else if self.options.len() < (len as usize) {
Some(Err(UnexpectedEndOfSlice(self.options[0])))
} else {
let mut acks: [Option<(u32,u32)>;3] = [None;3];
let first = (BigEndian::read_u32(&self.options[2..2 + 4]),
BigEndian::read_u32(&self.options[2 + 4..2 + 8]));
for (i, item) in acks.iter_mut()
.enumerate()
.take(3)
{
let offset = 2 + 8 + (i*8);
if offset < (len as usize) {
*item = Some((
BigEndian::read_u32(&self.options[offset..offset + 4]),
BigEndian::read_u32(&self.options[offset + 4..offset + 8]))
);
}
}
self.options = &self.options[len as usize..];
Some(Ok(SelectiveAcknowledgement(first, acks)))
}
}
},
TCP_OPTION_ID_TIMESTAMP => {
match expect_specific_size(10, self.options) {
Err(value) => Some(Err(value)),
_ => {
let t = Timestamp(
BigEndian::read_u32(&self.options[2..6]),
BigEndian::read_u32(&self.options[6..10])
);
self.options = &self.options[10..];
Some(Ok(t))
}
}
},
_ => {
Some(Err(UnknownId(self.options[0])))
},
};
match result {
None | Some(Err(_)) => {
let len = self.options.len();
self.options = &self.options[len..len];
},
_ => {}
}
result
}
}
}