Explorar o código

Proper calculation of TCP header length

The presence of TCP options will only increase
the header length to muliples of 4. The added
padding consists of zeros.

This also fixes an emit panic when the Window
Scale and MSS options result in a length of 27
which then gets floored to 24 when applied to
the header.

Closes: #251
Approved by: whitequark
Kai Lüke %!s(int64=6) %!d(string=hai) anos
pai
achega
4a8242a1fe
Modificáronse 1 ficheiros con 16 adicións e 1 borrados
  1. 16 1
      src/wire/tcp.rs

+ 16 - 1
src/wire/tcp.rs

@@ -576,7 +576,10 @@ impl<'a> TcpOption<'a> {
         match self {
             &TcpOption::EndOfList => {
                 length    = 1;
-                buffer[0] = field::OPT_END;
+                // There may be padding space which also should be initialized.
+                for p in buffer.iter_mut() {
+                    *p = field::OPT_END;
+                }
             }
             &TcpOption::NoOperation => {
                 length    = 1;
@@ -725,6 +728,7 @@ impl<'a> Repr<'a> {
     /// Return the length of a header that will be emitted from this high-level representation.
     ///
     /// This should be used for buffer space calculations.
+    /// The TCP header length is a multiple of 4.
     pub fn header_len(&self) -> usize {
         let mut length = field::URGENT.end;
         if self.max_seg_size.is_some() {
@@ -733,6 +737,9 @@ impl<'a> Repr<'a> {
         if self.window_scale.is_some() {
             length += 3
         }
+        if length % 4 != 0 {
+            length += 4 - length % 4;
+        }
         length
     }
 
@@ -1023,6 +1030,14 @@ mod test {
         assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
     }
 
+    #[test]
+    #[cfg(feature = "proto-ipv4")]
+    fn test_header_len_multiple_of_4() {
+        let mut repr = packet_repr();
+        repr.window_scale = Some(0); // This TCP Option needs 3 bytes.
+        assert_eq!(repr.header_len() % 4, 0); // Should e.g. be 28 instead of 27.
+    }
+
     macro_rules! assert_option_parses {
         ($opt:expr, $data:expr) => ({
             assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt)));