From 513923725ec4b4f5c91b73efc55ce7902a335181 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 20 Dec 2016 12:52:33 +0000 Subject: [PATCH] Implement TCP representation parsing and emission. --- src/wire/icmpv4.rs | 5 +- src/wire/ipv4.rs | 11 ++-- src/wire/mod.rs | 2 + src/wire/tcp.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++- src/wire/udp.rs | 5 +- 5 files changed, 145 insertions(+), 8 deletions(-) diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 48a61d3..9d45a45 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -462,9 +462,10 @@ mod test { #[test] fn test_echo_emit() { - let mut bytes = vec![0; 12]; + let repr = echo_packet_repr(); + let mut bytes = vec![0; repr.buffer_len()]; let mut packet = Packet::new(&mut bytes).unwrap(); - echo_packet_repr().emit(&mut packet); + repr.emit(&mut packet); assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 14f9f87..6288716 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -555,7 +555,10 @@ mod test { 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, - 0x00, 0x00, 0x00, 0x00]; + 0xaa, 0x00, 0x00, 0xff]; + + static REPR_PAYLOAD_BYTES: [u8; 4] = + [0xaa, 0x00, 0x00, 0xff]; fn packet_repr() -> Repr { Repr { @@ -574,9 +577,11 @@ mod test { #[test] fn test_emit() { - let mut bytes = vec![0; 24]; + let repr = packet_repr(); + let mut bytes = vec![0; repr.buffer_len() + REPR_PAYLOAD_BYTES.len()]; let mut packet = Packet::new(&mut bytes).unwrap(); - packet_repr().emit(&mut packet, 4); + repr.emit(&mut packet, REPR_PAYLOAD_BYTES.len()); + packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES); assert_eq!(&packet.into_inner()[..], &REPR_PACKET_BYTES[..]); } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 5e2a2b8..7395b80 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -119,3 +119,5 @@ pub use self::udp::Packet as UdpPacket; pub use self::udp::Repr as UdpRepr; pub use self::tcp::Packet as TcpPacket; +pub use self::tcp::Repr as TcpRepr; +pub use self::tcp::Control as TcpControl; diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 18f95bf..57caab5 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -239,7 +239,9 @@ impl + AsMut<[u8]>> Packet { #[inline(always)] pub fn clear_flags(&mut self) { let data = self.buffer.as_mut(); - NetworkEndian::write_u16(&mut data[field::FLAGS], 0) + let raw = NetworkEndian::read_u16(&data[field::FLAGS]); + let raw = raw & !0x0fff; + NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } /// Set the FIN flag. @@ -382,6 +384,96 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { } } +/// A high-level representation of a Transmission Control Protocol packet. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Repr<'a> { + src_port: u16, + dst_port: u16, + seq_number: u32, + ack_number: Option, + window_len: u16, + control: Control, + payload: &'a [u8] +} + +/// The control flags of a Transmission Control Protocol packet. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Control { + None, + Syn, + Fin, + Rst +} + +impl<'a> Repr<'a> { + /// Parse a Transmission Control Protocol packet and return a high-level representation. + pub fn parse(packet: &Packet<&'a T>, + src_addr: &InternetAddress, + dst_addr: &InternetAddress) -> Result, Error> + where T: AsRef<[u8]> { + // Source and destination ports must be present. + if packet.src_port() == 0 { return Err(Error::Malformed) } + if packet.dst_port() == 0 { return Err(Error::Malformed) } + // Valid checksum is expected... + if !packet.verify_checksum(src_addr, dst_addr) { return Err(Error::Checksum) } + + let control = + match (packet.syn(), packet.fin(), packet.rst()) { + (false, false, false) => Control::None, + (true, false, false) => Control::Syn, + (false, true, false) => Control::Fin, + (false, false, true ) => Control::Rst, + _ => return Err(Error::Malformed) + }; + let ack_number = + match packet.ack() { + true => Some(packet.ack_number()), + false => None + }; + // The PSH flag is ignored. + // The URG flag and the urgent field is ignored. This behavior is standards-compliant, + // however, most deployed systems (e.g. Linux) are *not* standards-compliant, and would + // cut the byte at the urgent pointer from the stream. + + Ok(Repr { + src_port: packet.src_port(), + dst_port: packet.dst_port(), + seq_number: packet.seq_number(), + ack_number: ack_number, + window_len: packet.window_len(), + control: control, + payload: packet.payload() + }) + } + + /// Return the length of a packet that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + field::URGENT.end + self.payload.len() + } + + /// Emit a high-level representation into a Transmission Control Protocol packet. + pub fn emit(&self, packet: &mut Packet<&mut T>, + src_addr: &InternetAddress, + dst_addr: &InternetAddress) + where T: AsRef<[u8]> + AsMut<[u8]> { + packet.set_src_port(self.src_port); + packet.set_dst_port(self.dst_port); + packet.set_seq_number(self.seq_number); + packet.set_ack_number(self.ack_number.unwrap_or(0)); + packet.set_window_len(self.window_len); + packet.set_header_len(20); + packet.clear_flags(); + match self.control { + Control::None => (), + Control::Syn => packet.set_syn(true), + Control::Fin => packet.set_fin(true), + Control::Rst => packet.set_rst(true) + } + packet.payload_mut().copy_from_slice(self.payload); + packet.fill_checksum(src_addr, dst_addr) + } +} + #[cfg(test)] mod test { use wire::Ipv4Address; @@ -444,4 +536,40 @@ mod test { packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into()); assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); } + + static SYN_PACKET_BYTES: [u8; 24] = + [0xbf, 0x00, 0x00, 0x50, + 0x01, 0x23, 0x45, 0x67, + 0x00, 0x00, 0x00, 0x00, + 0x50, 0x02, 0x01, 0x23, + 0x7a, 0x8d, 0x00, 0x00, + 0xaa, 0x00, 0x00, 0xff]; + + fn packet_repr() -> Repr<'static> { + Repr { + src_port: 48896, + dst_port: 80, + seq_number: 0x01234567, + ack_number: None, + window_len: 0x0123, + control: Control::Syn, + payload: &PAYLOAD_BYTES + } + } + + #[test] + fn test_parse() { + let packet = Packet::new(&SYN_PACKET_BYTES[..]).unwrap(); + let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into()).unwrap(); + assert_eq!(repr, packet_repr()); + } + + #[test] + fn test_emit() { + let repr = packet_repr(); + let mut bytes = vec![0; repr.buffer_len()]; + let mut packet = Packet::new(&mut bytes).unwrap(); + repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into()); + assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]); + } } diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 8880fb4..ba9314b 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -298,9 +298,10 @@ mod test { #[test] fn test_emit() { - let mut bytes = vec![0; 12]; + let repr = packet_repr(); + let mut bytes = vec![0; repr.buffer_len()]; let mut packet = Packet::new(&mut bytes).unwrap(); - packet_repr().emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into()); + repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into()); assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); } }