net_parser_rs/layer4/
tcp.rs

1use crate::Error;
2use byteorder::{BigEndian as BE, WriteBytesExt};
3use log::*;
4use nom::{ErrorKind as NomErrorKind, *};
5use std::mem::size_of;
6use std::io::{Cursor, Write};
7
8const MINIMUM_HEADER_BYTES: usize = 20; //5 32bit words
9const MAXIMUM_HEADER_BYTES: usize = 60; //15 32bit words
10
11#[derive(Clone, Copy, Debug)]
12pub struct HeaderLengthAndFlags {
13    pub inner: u16,
14    pub header_length: usize,
15    pub flags: u16,
16}
17
18#[derive(Clone, Copy, Debug)]
19pub struct Tcp<'a> {
20    pub src_port: u16,
21    pub dst_port: u16,
22    pub sequence_number: u32,
23    pub acknowledgement_number: u32,
24    pub header_length_and_flags: HeaderLengthAndFlags,
25    pub window: u16,
26    pub check: u16,
27    pub urgent: u16,
28    pub options: &'a [u8],
29    pub payload: &'a [u8],
30}
31
32impl<'a> Tcp<'a> {
33    pub fn as_bytes(&self) -> Vec<u8> {
34        let inner = Vec::with_capacity(
35            size_of::<u16>() * 6
36            + size_of::<u32>() * 2
37            + self.options.len()
38            + self.payload.len()
39        );
40        let mut writer = Cursor::new(inner);
41        writer.write_u16::<BE>(self.src_port).unwrap();
42        writer.write_u16::<BE>(self.dst_port).unwrap();
43        writer.write_u32::<BE>(self.sequence_number).unwrap();
44        writer.write_u32::<BE>(self.acknowledgement_number).unwrap();
45        writer.write_u16::<BE>(self.header_length_and_flags.inner).unwrap();
46        writer.write_u16::<BE>(self.window).unwrap();
47        writer.write_u16::<BE>(self.check).unwrap();
48        writer.write_u16::<BE>(self.urgent).unwrap();
49        writer.write(self.options).unwrap();
50        writer.write(self.payload).unwrap();
51        writer.into_inner()
52    }
53
54    pub fn extract_length(value: u16) -> usize {
55        let words = value >> 12;
56        (words * 4) as usize
57    }
58
59    pub fn parse<'b>(input: &'b [u8]) -> Result<(&'b [u8], Tcp<'b>), Error> {
60        trace!("Available={}", input.len());
61
62        do_parse!(
63            input,
64            src_port: be_u16
65                >> dst_port: be_u16
66                >> sequence_number: be_u32
67                >> acknowledgement_number: be_u32
68                >> header_length_and_flags: map_res!(be_u16, |v| {
69                    let hl = Tcp::extract_length(v);
70                    trace!("Header Length={}", hl);
71                    if hl >= MINIMUM_HEADER_BYTES && hl <= MAXIMUM_HEADER_BYTES {
72                        let flags = v & 0x01FF; //take lower 9 bits
73                        let h = HeaderLengthAndFlags {
74                            inner: v,
75                            header_length: hl,
76                            flags: flags,
77                        };
78                        Ok(h)
79                    } else {
80                        Err(error_position!(input, NomErrorKind::CondReduce::<u32>))
81                    }
82                })
83                >> window: be_u16
84                >> check: be_u16
85                >> urgent: be_u16
86                >> options: take!(header_length_and_flags.header_length - MINIMUM_HEADER_BYTES)
87                >> payload: rest
88                >> (Tcp {
89                    src_port: src_port,
90                    dst_port: dst_port,
91                    sequence_number: sequence_number,
92                    acknowledgement_number: acknowledgement_number,
93                    header_length_and_flags: header_length_and_flags,
94                    window: window,
95                    check: check,
96                    urgent: urgent,
97                    options: options,
98                    payload: payload.into()
99                })
100        ).map_err(Error::from)
101    }
102}
103
104#[cfg(test)]
105pub mod tests {
106    use hex_slice::AsHex;
107
108    use super::*;
109
110    pub const RAW_DATA: &'static [u8] = &[
111        0xC6u8, 0xB7u8, //src port, 50871
112        0x00u8, 0x50u8, //dst port, 80
113        0x00u8, 0x00u8, 0x00u8, 0x01u8, //sequence number, 1
114        0x00u8, 0x00u8, 0x00u8, 0x02u8, //acknowledgement number, 2
115        0x50u8, 0x00u8, //header and flags, 0
116        0x00u8, 0x00u8, //window
117        0x00u8, 0x00u8, //check
118        0x00u8, 0x00u8, //urgent
119        //no options
120        //payload
121        0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8,
122        0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8,
123        0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0xfcu8, 0xfdu8, 0xfeu8,
124        0xffu8, //payload, 8 words
125    ];
126
127    #[test]
128    fn convert_length() {
129        assert_eq!(Tcp::extract_length(0x0000u16), 0); //0 words, 0 bytes
130        assert_eq!(Tcp::extract_length(0x3000u16), 12); //3 words, 12 bytes
131    }
132
133    #[test]
134    fn parse_tcp() {
135        let _ = env_logger::try_init();
136
137        let (rem, l4) = Tcp::parse(RAW_DATA).expect("Unable to parse");
138
139        assert!(rem.is_empty());
140
141        assert_eq!(l4.dst_port, 80);
142        assert_eq!(l4.src_port, 50871);
143        assert_eq!(
144            l4.payload,
145            [
146                0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8,
147                0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8,
148                0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0xfcu8, 0xfdu8,
149                0xfeu8, 0xffu8
150            ],
151            "Payload Mismatch: {:x}",
152            l4.payload.as_hex()
153        );
154
155        assert_eq!(l4.as_bytes().as_slice(), RAW_DATA);
156    }
157}