use arrayvec::ArrayVec; use crate::err::{ValueTooBigError, ValueType}; use super::super::*; /// Deprecated use [`TcpHeader::MIN_LEN`] instead. #[deprecated(since = "0.14.0", note = "Use `TcpHeader::MIN_LEN` instead")] pub const TCP_MINIMUM_HEADER_SIZE: usize = 5 * 4; /// Deprecated use [`TcpHeader::MIN_DATA_OFFSET`] instead. #[deprecated(since = "0.14.0", note = "Use `TcpHeader::MIN_DATA_OFFSET` instead")] pub const TCP_MINIMUM_DATA_OFFSET: u8 = 5; /// Deprecated use [`TcpHeader::MAX_DATA_OFFSET`] instead. #[deprecated(since = "0.14.0", note = "Use `TcpHeader::MAX_DATA_OFFSET` instead")] pub const TCP_MAXIMUM_DATA_OFFSET: u8 = 0xf; /// TCP header according to rfc 793. /// /// Field descriptions copied from RFC 793 page 15++ #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct TcpHeader { /// The source port number. pub source_port: u16, /// The destination port number. pub destination_port: u16, /// The sequence number of the first data octet in this segment (except when SYN is present). /// /// If SYN is present the sequence number is the initial sequence number (ISN) /// and the first data octet is ISN+1. /// [copied from RFC 793, page 16] pub sequence_number: u32, /// If the ACK control bit is set this field contains the value of the /// next sequence number the sender of the segment is expecting to /// receive. /// /// Once a connection is established this is always sent. pub acknowledgment_number: u32, /// ECN-nonce - concealment protection (experimental: see RFC 3540) pub ns: bool, /// No more data from sender pub fin: bool, /// Synchronize sequence numbers pub syn: bool, /// Reset the connection pub rst: bool, /// Push Function pub psh: bool, /// Acknowledgment field significant pub ack: bool, /// Urgent Pointer field significant pub urg: bool, /// ECN-Echo (RFC 3168) pub ece: bool, /// Congestion Window Reduced (CWR) flag /// /// This flag is set by the sending host to indicate that it received a TCP segment with the ECE flag set and had responded in congestion control mechanism (added to header by RFC 3168). pub cwr: bool, /// The number of data octets beginning with the one indicated in the /// acknowledgment field which the sender of this segment is willing to /// accept. pub window_size: u16, /// Checksum (16 bit one's complement) of the pseudo ip header, this tcp header and the payload. pub checksum: u16, /// This field communicates the current value of the urgent pointer as a /// positive offset from the sequence number in this segment. /// /// The urgent pointer points to the sequence number of the octet following /// the urgent data. This field is only be interpreted in segments with /// the URG control bit set. pub urgent_pointer: u16, /// Options in the TCP header. pub options: TcpOptions, } impl TcpHeader { /// Minimum length of a TCP header in bytes/octets. pub const MIN_LEN: usize = 5 * 4; /// Maximum length of a TCP header in bytes/octets. /// /// The length is obtained by multiplying the maximum value /// that "data offset" can take (it is a 4 bit number so the max /// is 0b1111) and multiplying it by 4 as it describes the offset /// to the data in 4-bytes words. pub const MAX_LEN: usize = 0b1111 * 4; /// The minimum data offset size (size of the tcp header itself). pub const MIN_DATA_OFFSET: u8 = 5; /// The maximum allowed value for the data offset (it is a 4 bit value). pub const MAX_DATA_OFFSET: u8 = 0xf; /// Creates a TcpHeader with the given values and the rest initialized with default values. 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, 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: Default::default(), } } /// The number of 32 bit words in the TCP Header & TCP header options. /// /// This indicates where the data begins relative to the start of an /// TCP header in multiples of 4 bytes. This number is /// present in the `data_offset` field of the header and defines /// the length of the tcp options present. /// /// # Example /// /// ``` /// use etherparse::{TcpHeader, TcpOptions}; /// /// { /// let header = TcpHeader{ /// options: TcpOptions::try_from_slice(&[]).unwrap(), /// .. Default::default() /// }; /// // in case there are no options the minimum size of the tcp /// // is returned. /// assert_eq!(5, header.data_offset()); /// } /// { /// let header = TcpHeader{ /// options: TcpOptions::try_from_slice(&[1,2,3,4,5,6,7,8]).unwrap(), /// .. Default::default() /// }; /// // otherwise the base TCP header size plus the number of 4 byte /// // words in the options is returned /// assert_eq!(5 + 2, header.data_offset()); /// } /// ``` #[inline] pub fn data_offset(&self) -> u8 { self.options.data_offset() } /// Returns the length of the header including the options. #[inline] pub fn header_len(&self) -> usize { 20 + self.options.len() } /// Returns the length of the header including the options. #[inline] pub fn header_len_u16(&self) -> u16 { 20 + u16::from(self.options.len_u8()) } /// Returns the options size in bytes based on the currently set data_offset. Returns None if the data_offset is smaller then the minimum size or bigger then the maximum supported size. #[inline] #[deprecated(since = "0.14.0", note = "Please use `options.len()` instead")] pub fn options_len(&self) -> usize { self.options.len() } /// Returns a slice containing the options of the header (size is determined via the data_offset field. #[inline] #[deprecated(since = "0.14.0", note = "Please use `options.as_slice()` instead")] pub fn options(&self) -> &[u8] { self.options.as_slice() } /// Sets the options (overwrites the current options) or returns /// an error when there is not enough space. pub fn set_options( &mut self, elements: &[TcpOptionElement], ) -> Result<(), TcpOptionWriteError> { self.options = TcpOptions::try_from_elements(elements)?; Ok(()) } /// Sets the options to the data given. pub fn set_options_raw(&mut self, data: &[u8]) -> Result<(), TcpOptionWriteError> { self.options = TcpOptions::try_from_slice(data)?; Ok(()) } /// Returns an iterator that allows to iterate through all /// known TCP header options. #[inline] pub fn options_iterator(&self) -> TcpOptionsIterator { self.options.elements_iter() } /// Renamed to `TcpHeader::from_slice` #[deprecated(since = "0.10.1", note = "Use TcpHeader::from_slice instead.")] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), err::tcp::HeaderSliceError> { TcpHeader::from_slice(slice) } /// Reads a tcp header from a slice #[inline] pub fn from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), err::tcp::HeaderSliceError> { let h = TcpHeaderSlice::from_slice(slice)?; Ok((h.to_header(), &slice[h.slice().len()..])) } /// Read a tcp header from the current position #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read( reader: &mut T, ) -> Result { use err::tcp::{HeaderError::*, HeaderReadError::*}; let raw = { let mut raw: [u8; 20] = [0; 20]; reader.read_exact(&mut raw).map_err(Io)?; raw }; let source_port = u16::from_be_bytes([raw[0], raw[1]]); let destination_port = u16::from_be_bytes([raw[2], raw[3]]); let sequence_number = u32::from_be_bytes([raw[4], raw[5], raw[6], raw[7]]); let acknowledgment_number = u32::from_be_bytes([raw[8], raw[9], raw[10], raw[11]]); let (data_offset, ns) = { let value = raw[12]; ((value & 0xf0) >> 4, 0 != value & 1) }; let flags = raw[13]; 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: u16::from_be_bytes([raw[14], raw[15]]), checksum: u16::from_be_bytes([raw[16], raw[17]]), urgent_pointer: u16::from_be_bytes([raw[18], raw[19]]), options: { if data_offset < TcpHeader::MIN_DATA_OFFSET { return Err(Content(DataOffsetTooSmall { data_offset })); } else { let mut options = TcpOptions { len: (data_offset - TcpHeader::MIN_DATA_OFFSET) << 2, buf: [0; 40], }; // convert to bytes minus the tcp header size itself if options.len > 0 { reader .read_exact(&mut options.buf[..options.len.into()]) .map_err(Io)?; } options } }, }) } /// Write the tcp header to a stream (does NOT calculate the checksum). #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { //check that the data offset is within range let src_be = self.source_port.to_be_bytes(); let dst_be = self.destination_port.to_be_bytes(); let seq_be = self.sequence_number.to_be_bytes(); let ack_be = self.acknowledgment_number.to_be_bytes(); let window_be = self.window_size.to_be_bytes(); let checksum_be = self.checksum.to_be_bytes(); let urg_ptr_be = self.urgent_pointer.to_be_bytes(); let data_offset = self.data_offset(); debug_assert!(TcpHeader::MIN_DATA_OFFSET <= data_offset); debug_assert!(data_offset <= TcpHeader::MAX_DATA_OFFSET); writer.write_all(&[ src_be[0], src_be[1], dst_be[0], dst_be[1], seq_be[0], seq_be[1], seq_be[2], seq_be[3], ack_be[0], ack_be[1], ack_be[2], ack_be[3], { let value = (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 }, window_be[0], window_be[1], checksum_be[0], checksum_be[1], urg_ptr_be[0], urg_ptr_be[1], ])?; // write options if the data_offset is large enough let options = self.options.as_slice(); if false == options.is_empty() { writer.write_all(options)?; } Ok(()) } /// Returns the serialized header. pub fn to_bytes(&self) -> ArrayVec { //check that the data offset is within range let src_be = self.source_port.to_be_bytes(); let dst_be = self.destination_port.to_be_bytes(); let seq_be = self.sequence_number.to_be_bytes(); let ack_be = self.acknowledgment_number.to_be_bytes(); let window_be = self.window_size.to_be_bytes(); let checksum_be = self.checksum.to_be_bytes(); let urg_ptr_be = self.urgent_pointer.to_be_bytes(); let mut result = ArrayVec::new(); // write base header data result.extend([ src_be[0], src_be[1], dst_be[0], dst_be[1], seq_be[0], seq_be[1], seq_be[2], seq_be[3], ack_be[0], ack_be[1], ack_be[2], ack_be[3], { 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 }, window_be[0], window_be[1], checksum_be[0], checksum_be[1], urg_ptr_be[0], urg_ptr_be[1], ]); // add the options result.extend(self.options.buf); // SAFETY: Safe as the header len can not exceed the maximum length // of the header. unsafe { result.set_len(self.header_len()); } result } /// Calculates the upd header checksum based on a ipv4 header and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv4( &self, ip_header: &Ipv4Header, payload: &[u8], ) -> Result> { self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload) } /// Calculates the checksum for the current header in ipv4 mode and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv4_raw( &self, source_ip: [u8; 4], destination_ip: [u8; 4], payload: &[u8], ) -> Result> { // check that the total length fits into the tcp length field let max_payload = usize::from(u16::MAX) - self.header_len(); if max_payload < payload.len() { return Err(ValueTooBigError { actual: payload.len(), max_allowed: max_payload, value_type: ValueType::TcpPayloadLengthIpv4, }); } // calculate the checksum let tcp_len = self.header_len_u16() + (payload.len() as u16); Ok(self.calc_checksum_post_ip( checksum::Sum16BitWords::new() .add_4bytes(source_ip) .add_4bytes(destination_ip) .add_2bytes([0, ip_number::TCP.0]) .add_2bytes(tcp_len.to_be_bytes()), payload, )) } /// Calculates the upd header checksum based on a ipv6 header and returns the result. This does NOT set the checksum.. pub fn calc_checksum_ipv6( &self, ip_header: &Ipv6Header, payload: &[u8], ) -> Result> { self.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, payload) } /// Calculates the checksum for the current header in ipv6 mode and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv6_raw( &self, source: [u8; 16], destination: [u8; 16], payload: &[u8], ) -> Result> { // check that the total length fits into the tcp length field let max_payload = (u32::MAX as usize) - self.header_len(); if max_payload < payload.len() { return Err(ValueTooBigError { actual: payload.len(), max_allowed: max_payload, value_type: ValueType::TcpPayloadLengthIpv6, }); } let tcp_len = u32::from(self.header_len_u16()) + (payload.len() as u32); Ok(self.calc_checksum_post_ip( checksum::Sum16BitWords::new() .add_16bytes(source) .add_16bytes(destination) .add_4bytes(tcp_len.to_be_bytes()) .add_2bytes([0, ip_number::TCP.0]), payload, )) } ///This method takes the sum of the pseudo ip header and calculates the rest of the checksum. fn calc_checksum_post_ip( &self, ip_pseudo_header_sum: checksum::Sum16BitWords, payload: &[u8], ) -> u16 { ip_pseudo_header_sum .add_2bytes(self.source_port.to_be_bytes()) .add_2bytes(self.destination_port.to_be_bytes()) .add_4bytes(self.sequence_number.to_be_bytes()) .add_4bytes(self.acknowledgment_number.to_be_bytes()) .add_2bytes([ { 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 }, ]) .add_2bytes(self.window_size.to_be_bytes()) .add_2bytes(self.urgent_pointer.to_be_bytes()) .add_slice(self.options.as_slice()) .add_slice(payload) .ones_complement() .to_be() } } impl Default for TcpHeader { fn default() -> TcpHeader { TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, 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: TcpOptions { len: 0, buf: [0u8; 40], }, } } } #[cfg(test)] mod test { use crate::{ err::{ tcp::{HeaderError::*, HeaderSliceError::*}, ValueTooBigError, ValueType, }, tcp_option::*, test_gens::*, TcpOptionElement::*, *, }; use alloc::{format, vec::Vec}; use proptest::prelude::*; use std::io::Cursor; #[test] fn default() { let default: TcpHeader = Default::default(); assert_eq!(0, default.source_port); assert_eq!(0, default.destination_port); assert_eq!(0, default.sequence_number); assert_eq!(0, default.acknowledgment_number); assert_eq!(5, default.data_offset()); assert_eq!(false, default.ns); assert_eq!(false, default.fin); assert_eq!(false, default.syn); assert_eq!(false, default.rst); assert_eq!(false, default.psh); assert_eq!(false, default.ack); assert_eq!(false, default.ece); assert_eq!(false, default.urg); assert_eq!(false, default.cwr); assert_eq!(0, default.window_size); assert_eq!(0, default.checksum); assert_eq!(0, default.urgent_pointer); assert_eq!(0, default.options.as_slice().len()); } proptest! { #[test] fn debug(header in tcp_any()) { // normal debug printing assert_eq!( format!( "TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {}, options: {:?} }}", header.source_port, header.destination_port, header.sequence_number, header.acknowledgment_number, header.ns, header.fin, header.syn, header.rst, header.psh, header.ack, header.urg, header.ece, header.cwr, header.window_size, header.checksum, header.urgent_pointer, header.options_iterator() ), format!("{:?}", header) ); // multi line debug printing { let mut header = header.clone(); // lets exclude options for now, as I am not quiet sure // how to introduce additional indentation and the options // part is already checked by the previous test header.set_options(&[]).unwrap(); assert_eq!( format!( "TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {}, options: {:?}, }}", header.source_port, header.destination_port, header.sequence_number, header.acknowledgment_number, header.ns, header.fin, header.syn, header.rst, header.psh, header.ack, header.urg, header.ece, header.cwr, header.window_size, header.checksum, header.urgent_pointer, header.options_iterator() ), format!("{:#?}", header) ); } } } #[test] fn eq() { let options = [ TcpOptionElement::Timestamp(0x00102030, 0x01112131), //10 TcpOptionElement::SelectiveAcknowledgement( (0x02122232, 0x03132333), [None, None, None], ), //20 TcpOptionElement::Timestamp(0x04142434, 0x05152535), //30 TcpOptionElement::Timestamp(0x06162636, 0x07172737), //40 ]; let base: TcpHeader = { let mut base: TcpHeader = Default::default(); base.source_port = 1; base.destination_port = 2; base.sequence_number = 3; base.acknowledgment_number = 4; base.window_size = 6; base.checksum = 7; base.urgent_pointer = 8; base.set_options(&options[..]).unwrap(); base }; //equal { let other = base.clone(); assert_eq!(other, base); } //change every field anc check for neq //source_port { let mut other = base.clone(); other.source_port = 10; assert_ne!(other, base); } //destination_port { let mut other = base.clone(); other.destination_port = 10; assert_ne!(other, base); } //sequence_number { let mut other = base.clone(); other.sequence_number = 10; assert_ne!(other, base); } //acknowledgment_number { let mut other = base.clone(); other.acknowledgment_number = 10; assert_ne!(other, base); } //data_offset { let mut other = base.clone(); other .set_options(&[TcpOptionElement::MaximumSegmentSize(16)]) .unwrap(); assert_ne!(other, base); } //ns { let mut other = base.clone(); other.ns = true; assert_ne!(other, base); } //fin { let mut other = base.clone(); other.fin = true; assert_ne!(other, base); } //syn { let mut other = base.clone(); other.syn = true; assert_ne!(other, base); } //rst { let mut other = base.clone(); other.rst = true; assert_ne!(other, base); } //psh { let mut other = base.clone(); other.psh = true; assert_ne!(other, base); } //ack { let mut other = base.clone(); other.ack = true; assert_ne!(other, base); } //ece { let mut other = base.clone(); other.ece = true; assert_ne!(other, base); } //urg { let mut other = base.clone(); other.urg = true; assert_ne!(other, base); } //cwr { let mut other = base.clone(); other.cwr = true; assert_ne!(other, base); } //window_size { let mut other = base.clone(); other.window_size = 10; assert_ne!(other, base); } //checksum { let mut other = base.clone(); other.checksum = 10; assert_ne!(other, base); } //urgent_pointer { let mut other = base.clone(); other.urgent_pointer = 10; assert_ne!(other, base); } //options (first element different) { let mut other = base.clone(); other .set_options(&{ let mut other_options = options.clone(); other_options[0] = TcpOptionElement::Timestamp(0x00102039, 0x01112131); other_options }) .unwrap(); assert_ne!(other, base); } //options (last element) { let mut other = base.clone(); other.set_options(&options).unwrap(); let mut other2 = base.clone(); other2 .set_options(&{ let mut options2 = options.clone(); options2[3] = TcpOptionElement::Timestamp(0x06162636, 0x97172737); options2 }) .unwrap(); assert_ne!(other, other2); } //options (check only relevant data is compared) { let mut other = base.clone(); other.set_options(&options).unwrap(); let mut other2 = base.clone(); other2 .set_options(&{ let mut options2 = options.clone(); options2[3] = TcpOptionElement::Timestamp(0x06162636, 0x97172737); options2 }) .unwrap(); // reset the data let new_options = [TcpOptionElement::Timestamp(0x00102030, 0x01112131)]; other.set_options(&new_options).unwrap(); other2.set_options(&new_options).unwrap(); assert_eq!(other, other2); } } proptest! { #[test] fn hash(header in tcp_any()) { use std::collections::hash_map::DefaultHasher; use core::hash::{Hash, Hasher}; let a = { let mut hasher = DefaultHasher::new(); header.hash(&mut hasher); hasher.finish() }; let b = { let mut hasher = DefaultHasher::new(); header.hash(&mut hasher); hasher.finish() }; assert_eq!(a, b); } } proptest! { #[test] fn new( source_port in any::(), destination_port in any::(), sequence_number in any::(), window_size in any::() ) { let header = TcpHeader::new( source_port, destination_port, sequence_number, window_size ); assert_eq!(header.source_port, source_port); assert_eq!(header.destination_port, destination_port); assert_eq!(header.sequence_number, sequence_number); assert_eq!(header.acknowledgment_number, 0); assert_eq!(header.ns, false); assert_eq!(header.fin, false); assert_eq!(header.syn, false); assert_eq!(header.rst, false); assert_eq!(header.psh, false); assert_eq!(header.ack, false); assert_eq!(header.urg, false); assert_eq!(header.ece, false); assert_eq!(header.cwr, false); assert_eq!(header.window_size, window_size); assert_eq!(header.checksum, 0); assert_eq!(header.urgent_pointer, 0); assert_eq!(header.options.as_slice(), &[]); } } proptest! { #[test] fn data_offset(header in tcp_any()) { assert_eq!(header.options.len()/4 + 5, header.data_offset().into()); } } proptest! { #[test] fn header_len(header in tcp_any()) { assert_eq!( header.header_len(), (20 + header.options.len()) ); } } proptest! { #[test] fn header_len_u16(header in tcp_any()) { assert_eq!( header.header_len_u16(), (20 + header.options.len()) as u16 ); } } proptest! { #[test] #[allow(deprecated)] fn options_len(header in tcp_any()) { assert_eq!( header.options_len(), header.to_bytes().len() - 20 ); } } proptest! { #[test] #[allow(deprecated)] fn options(header in tcp_any()) { assert_eq!( header.options(), &header.to_bytes()[20..] ); } } proptest! { #[test] #[rustfmt::skip] fn set_options( header in tcp_any(), arg_u8 in any::(), arg_u16 in any::(), ack_args in proptest::collection::vec(any::(), 4*2), arg0_u32 in any::(), arg1_u32 in any::() ) { use crate::TcpOptionElement::*; // maximum segment size { let mut header = header.clone(); header.set_options( &[Noop, Noop, MaximumSegmentSize(arg_u16), Noop] ).unwrap(); assert_eq!( header.options.as_slice(), &{ let arg_be = arg_u16.to_be_bytes(); [ KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4, arg_be[0], arg_be[1], KIND_NOOP, KIND_END ] } ); } // window scale { let mut header = header.clone(); header.set_options( &[Noop, Noop, WindowScale(arg_u8), Noop] ).unwrap(); assert_eq!( header.options.as_slice(), &[ KIND_NOOP, KIND_NOOP, KIND_WINDOW_SCALE, 3, arg_u8, KIND_NOOP, KIND_END, 0 ] ); } // selective ack permitted { let mut header = header.clone(); header.set_options( &[Noop, Noop, SelectiveAcknowledgementPermitted, Noop] ).unwrap(); assert_eq!( header.options.as_slice(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK_PERMITTED, 2, KIND_NOOP, KIND_END, 0, 0 ] ); } // selective ack { let args_be : Vec<[u8;4]> = ack_args.iter().map(|v| v.to_be_bytes()).collect(); //1 { let mut header = header.clone(); header.set_options( &[Noop, Noop, SelectiveAcknowledgement((ack_args[0], ack_args[1]), [None, None, None]), Noop] ).unwrap(); assert_eq!( header.options.as_slice(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 10, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], KIND_NOOP, KIND_END, 0, 0 ] ); } //2 { let mut header = header.clone(); header.set_options( &[ Noop, Noop, SelectiveAcknowledgement( (ack_args[0], ack_args[1]), [Some((ack_args[2], ack_args[3])), None, None] ), Noop ] ).unwrap(); assert_eq!( header.options.as_slice(), [ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 18, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3], args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3], KIND_NOOP, KIND_END, 0, 0 ] ); } //3 { let mut header = header.clone(); header.set_options( &[ Noop, Noop, SelectiveAcknowledgement( (ack_args[0], ack_args[1]), [ Some((ack_args[2], ack_args[3])), Some((ack_args[4], ack_args[5])), None ] ), Noop ] ).unwrap(); assert_eq!( header.options.as_slice(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 26, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3], args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3], args_be[4][0], args_be[4][1], args_be[4][2], args_be[4][3], args_be[5][0], args_be[5][1], args_be[5][2], args_be[5][3], KIND_NOOP, KIND_END, 0, 0 ] ); } //4 { let mut header = header.clone(); header.set_options( &[ Noop, Noop, SelectiveAcknowledgement( (ack_args[0], ack_args[1]), [ Some((ack_args[2], ack_args[3])), Some((ack_args[4], ack_args[5])), Some((ack_args[6], ack_args[7])) ] ), Noop ] ).unwrap(); assert_eq!( header.options.as_slice(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 34, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3], args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3], args_be[4][0], args_be[4][1], args_be[4][2], args_be[4][3], args_be[5][0], args_be[5][1], args_be[5][2], args_be[5][3], args_be[6][0], args_be[6][1], args_be[6][2], args_be[6][3], args_be[7][0], args_be[7][1], args_be[7][2], args_be[7][3], KIND_NOOP, KIND_END, 0, 0 ] ); } } // timestamp { let mut header = header.clone(); header.set_options( &[Noop, Noop, Timestamp(arg0_u32, arg1_u32), Noop] ).unwrap(); assert_eq!( header.options.as_slice(), &{ let arg0_be = arg0_u32.to_be_bytes(); let arg1_be = arg1_u32.to_be_bytes(); [ KIND_NOOP, KIND_NOOP, KIND_TIMESTAMP, 10, arg0_be[0], arg0_be[1], arg0_be[2], arg0_be[3], arg1_be[0], arg1_be[1], arg1_be[2], arg1_be[3], KIND_NOOP, KIND_END, 0, 0 ] } ); } // check for padding { let mut header = header.clone(); header.set_options(&[ MaximumSegmentSize(1400), // 4 SelectiveAcknowledgementPermitted, // 2 Timestamp(2661445915, 0), // 10 Noop, // 1 WindowScale(7), // 3 ]).unwrap(); // total 20 // + header 20 = 40 byte assert_eq!(40, header.header_len()); } // not enough memory error { let mut header = header.clone(); assert_eq!( Err(TcpOptionWriteError::NotEnoughSpace(41)), header.set_options(&[ MaximumSegmentSize(1), //4 WindowScale(2), //+3 = 7 SelectiveAcknowledgementPermitted, //+2 = 9 SelectiveAcknowledgement((3, 4), [Some((5, 6)), None, None]), // + 18 = 27 Timestamp(5, 6), // + 10 = 37 Noop, Noop, Noop, Noop // + 4 ]) ); //test with all fields filled of the selective ack assert_eq!( Err(TcpOptionWriteError::NotEnoughSpace(41)), header.set_options(&[ Noop, // 1 SelectiveAcknowledgement((3, 4), [Some((5, 6)), Some((5, 6)), Some((5, 6))]), // + 34 = 35 MaximumSegmentSize(1), // + 4 = 39 Noop, Noop // + 2 = 41 ]) ); //test with all fields filled of the selective ack assert_eq!( Err(TcpOptionWriteError::NotEnoughSpace(41)), header.set_options(&[ Noop, // 1 SelectiveAcknowledgement((3, 4), [None, None, None]), // + 10 = 11 Timestamp(1, 2), // + 10 = 21 Timestamp(1, 2), // + 10 = 31 MaximumSegmentSize(1), // + 4 = 35 Noop, Noop, Noop, Noop, Noop, Noop // + 6 = 41 ]) ); } } } proptest! { #[test] fn set_options_raw(header in tcp_any()) { let base: TcpHeader = Default::default(); let dummy = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, ]; //ok size -> expect output based on options size for i in 0..40 { let mut header = header.clone(); //set the options header.set_options_raw(&dummy[..i]).unwrap(); //determine the expected options length let mut options_length = i / 4; if i % 4 != 0 { options_length += 1; } options_length = options_length * 4; //expecetd data let mut expected_options = [0; 40]; expected_options[..i].copy_from_slice(&dummy[..i]); assert_eq!(options_length, header.options.len()); assert_eq!( (options_length / 4) as u8 + TcpHeader::MIN_DATA_OFFSET, header.data_offset() ); assert_eq!(&expected_options[..options_length], header.options.as_slice()); } //too big -> expect error let mut header = base.clone(); use crate::TcpOptionWriteError::*; assert_eq!( Err(NotEnoughSpace(dummy.len())), header.set_options_raw(&dummy[..]) ); } } #[test] fn options_iterator() { let options = [ TcpOptionElement::Timestamp(0x00102030, 0x01112131), //10 TcpOptionElement::SelectiveAcknowledgement( (0x02122232, 0x03132333), [None, None, None], ), //20 TcpOptionElement::Timestamp(0x04142434, 0x05152535), //30 TcpOptionElement::Timestamp(0x06162636, 0x07172737), //40 ]; let base: TcpHeader = { let mut base: TcpHeader = Default::default(); base.set_options(&options[..]).unwrap(); base }; assert_eq!( &options[..], &base .options_iterator() .map(|x| x.unwrap()) .collect::>()[..] ); } proptest! { #[test] #[allow(deprecated)] fn read_from_slice(header in tcp_any()) { // ok case { let bytes = { let mut bytes = header.to_bytes(); bytes.try_extend_from_slice( &([0u8;TcpHeader::MAX_LEN])[..bytes.remaining_capacity()] ).unwrap(); bytes }; let (actual_header, actual_rest) = TcpHeader::read_from_slice(&bytes[..]).unwrap(); assert_eq!(actual_header, header); assert_eq!(actual_rest, &bytes[header.header_len() as usize..]); } // data offset error for data_offset in 0..TcpHeader::MIN_DATA_OFFSET { let bytes = { let mut bytes = header.to_bytes(); bytes[12] = (bytes[12] & 0xf) | ((data_offset << 4) & 0xf0); bytes }; assert_eq!( TcpHeader::read_from_slice(&bytes[..]), Err(Content(DataOffsetTooSmall{ data_offset })) ); } // length error { let bytes = header.to_bytes(); for len in 0..(header.header_len() as usize) { assert_eq!( TcpHeader::read_from_slice(&bytes[..len]) .unwrap_err(), Len(err::LenError { required_len: if len < TcpHeader::MIN_LEN { TcpHeader::MIN_LEN } else { header.header_len() as usize }, len: len, len_source: LenSource::Slice, layer: err::Layer::TcpHeader, layer_start_offset: 0, }) ); } } } } proptest! { #[test] fn from_slice(header in tcp_any()) { // ok case { let bytes = { let mut bytes = header.to_bytes(); bytes.try_extend_from_slice( &([0u8;TcpHeader::MAX_LEN])[..bytes.remaining_capacity()] ).unwrap(); bytes }; let (actual_header, actual_rest) = TcpHeader::from_slice(&bytes[..]).unwrap(); assert_eq!(actual_header, header); assert_eq!(actual_rest, &bytes[header.header_len() as usize..]); } // data offset error for data_offset in 0..TcpHeader::MIN_DATA_OFFSET { let bytes = { let mut bytes = header.to_bytes(); bytes[12] = (bytes[12] & 0b1111) | ((data_offset << 4) & 0b1111_0000); bytes }; assert_eq!( TcpHeader::from_slice(&bytes[..]), Err(Content(DataOffsetTooSmall{ data_offset })) ); } // length error { let bytes = header.to_bytes(); for len in 0..(header.header_len() as usize) { assert_eq!( TcpHeader::from_slice(&bytes[..len]) .unwrap_err(), Len(err::LenError { required_len: if len < TcpHeader::MIN_LEN { TcpHeader::MIN_LEN } else { header.header_len() as usize }, len: len, len_source: LenSource::Slice, layer: err::Layer::TcpHeader, layer_start_offset: 0, }) ); } } } } proptest! { #[test] fn read(header in tcp_any()) { // ok case { let bytes = header.to_bytes(); let mut cursor = Cursor::new(&bytes[..]); let actual = TcpHeader::read(&mut cursor).unwrap(); assert_eq!(header.header_len() as u64, cursor.position()); assert_eq!(header, actual); } // data offset error for data_offset in 0..TcpHeader::MIN_DATA_OFFSET { let bytes = { let mut bytes = header.to_bytes(); bytes[12] = (bytes[12] & 0xf) | ((data_offset << 4) & 0xf0); bytes }; assert_eq!( TcpHeader::read(&mut Cursor::new(&bytes[..])) .unwrap_err() .content_error() .unwrap(), DataOffsetTooSmall{ data_offset } ); } // length error { let bytes = header.to_bytes(); for len in 0..(header.header_len() as usize) { let mut cursor = Cursor::new(&bytes[..len]); let err = TcpHeader::read(&mut cursor).unwrap_err(); assert!(err.io_error().is_some()); } } } } proptest! { #[test] fn write(header in tcp_any()) { // ok { let mut bytes = [0u8;TcpHeader::MAX_LEN]; let len = { let mut cursor = Cursor::new(&mut bytes[..]); header.write(&mut cursor).unwrap(); cursor.position() as usize }; assert_eq!(header.header_len() as usize, len); assert_eq!( header, TcpHeader::from_slice(&bytes[..len]).unwrap().0 ); } // length error for len in 0..header.header_len() { let mut bytes = [0u8;TcpHeader::MAX_LEN]; let mut cursor = Cursor::new(&mut bytes[..len as usize]); let result = header.write(&mut cursor); assert!(result.is_err()); } } } proptest! { #[test] fn to_bytes(header in tcp_any()) { let bytes = header.to_bytes(); let actual = TcpHeader::from_slice(&bytes).unwrap().0; assert_eq!(actual, header); } } #[test] fn calc_checksum_ipv4() { use crate::TcpOptionElement::*; // checksum == 0xf (no carries) (aka sum == 0xffff) { let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8]; //write the tcp header let tcp = TcpHeader::new(0, 0, 40905, 0); let ip_header = Ipv4Header::new( //payload length tcp.header_len_u16() + (tcp_payload.len() as u16), //time to live 0, ip_number::TCP, //source ip address [0; 4], //destination ip address [0; 4], ) .unwrap(); assert_eq!(Ok(0x0), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); assert_eq!( Ok(0x0), tcp.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, &tcp_payload) ); } //a header with options { let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8]; let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)]) .unwrap(); let ip_header = Ipv4Header::new( //payload length tcp.header_len_u16() + (tcp_payload.len() as u16), //time to live 20, //contained protocol is udp ip_number::TCP, //source ip address [192, 168, 1, 42], //destination ip address [192, 168, 1, 1], ) .unwrap(); //check checksum assert_eq!(Ok(0xdeeb), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); } //a header with an uneven number of options { let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)]) .unwrap(); let ip_header = Ipv4Header::new( //payload length tcp.header_len_u16() + (tcp_payload.len() as u16), //time to live 20, //contained protocol is udp ip_number::TCP, //source ip address [192, 168, 1, 42], //destination ip address [192, 168, 1, 1], ) .unwrap(); //check checksum assert_eq!(Ok(0xd5ea), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); } // value error { // write the udp header let tcp: TcpHeader = Default::default(); let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1; let mut tcp_payload = Vec::with_capacity(len); tcp_payload.resize(len, 0); let ip_header = Ipv4Header::new(0, 0, ip_number::TCP, [0; 4], [0; 4]).unwrap(); assert_eq!( Err(ValueTooBigError { actual: len, max_allowed: usize::from(core::u16::MAX) - usize::from(tcp.header_len()), value_type: ValueType::TcpPayloadLengthIpv4, }), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload) ); } } #[test] fn calc_checksum_ipv4_raw() { // checksum == 0xf (no carries) (aka sum == 0xffff) { let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8]; //write the tcp header let tcp = TcpHeader::new(0, 0, 40905, 0); assert_eq!( Ok(0x0), tcp.calc_checksum_ipv4_raw([0; 4], [0; 4], &tcp_payload) ); } // a header with options { let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8]; let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)]) .unwrap(); // check checksum assert_eq!( Ok(0xdeeb), tcp.calc_checksum_ipv4_raw([192, 168, 1, 42], [192, 168, 1, 1], &tcp_payload) ); } // a header with an uneven number of options { let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)]) .unwrap(); // check checksum assert_eq!( Ok(0xd5ea), tcp.calc_checksum_ipv4_raw([192, 168, 1, 42], [192, 168, 1, 1], &tcp_payload) ); } // value error { // write the udp header let tcp: TcpHeader = Default::default(); let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1; let mut tcp_payload = Vec::with_capacity(len); tcp_payload.resize(len, 0); assert_eq!( Err(ValueTooBigError { actual: len, max_allowed: usize::from(core::u16::MAX) - usize::from(tcp.header_len()), value_type: ValueType::TcpPayloadLengthIpv4, }), tcp.calc_checksum_ipv4_raw([0; 4], [0; 4], &tcp_payload) ); } } #[test] fn calc_checksum_ipv6() { // ok case { let tcp_payload = [51, 52, 53, 54, 55, 56, 57, 58]; // setup tcp header let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; use crate::TcpOptionElement::*; tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)]) .unwrap(); let ip_header = Ipv6Header { traffic_class: 1, flow_label: 0x81806.try_into().unwrap(), payload_length: tcp_payload.len() as u16 + tcp.header_len_u16(), next_header: ip_number::TCP, hop_limit: 40, source: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], destination: [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, ], }; // check checksum assert_eq!(Ok(0x786e), tcp.calc_checksum_ipv6(&ip_header, &tcp_payload)); } // error #[cfg(target_pointer_width = "64")] { //write the udp header let tcp: TcpHeader = Default::default(); let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1; //lets create a slice of that size that points to zero //(as most systems can not allocate blocks of the size of u32::MAX) let tcp_payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fulfilled // which can lead to crashes in release mode. use core::ptr::NonNull; core::slice::from_raw_parts(NonNull::::dangling().as_ptr(), len) }; let ip_header = Ipv6Header { traffic_class: 1, flow_label: 0x81806.try_into().unwrap(), payload_length: 0, //lets assume jumbograms behavior (set to 0, as bigger then u16) next_header: ip_number::TCP, hop_limit: 40, source: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], destination: [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, ], }; assert_eq!( Err(ValueTooBigError { actual: len, max_allowed: core::u32::MAX as usize - usize::from(tcp.header_len()), value_type: ValueType::TcpPayloadLengthIpv6, }), tcp.calc_checksum_ipv6(&ip_header, &tcp_payload) ); } } #[test] fn calc_checksum_ipv6_raw() { // ok case { let tcp_payload = [51, 52, 53, 54, 55, 56, 57, 58]; //write the tcp header let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; use crate::TcpOptionElement::*; tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)]) .unwrap(); // check checksum assert_eq!( Ok(0x786e), tcp.calc_checksum_ipv6_raw( [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,], &tcp_payload ) ); } // error #[cfg(target_pointer_width = "64")] { //write the udp header let tcp: TcpHeader = Default::default(); let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1; //lets create a slice of that size that points to zero //(as most systems can not allocate blocks of the size of u32::MAX) let tcp_payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fulfilled // which can lead to crashes in release mode. use core::ptr::NonNull; core::slice::from_raw_parts(NonNull::::dangling().as_ptr(), len) }; assert_eq!( Err(ValueTooBigError { actual: len, max_allowed: core::u32::MAX as usize - usize::from(tcp.header_len()), value_type: ValueType::TcpPayloadLengthIpv6, }), tcp.calc_checksum_ipv6_raw( [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,], &tcp_payload ) ); } } }