From 939d7f79f231307adf38df534a9f8b00bccd107a Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Sat, 6 Jul 2024 22:01:31 -0700 Subject: [PATCH] test(pcap): handle pcaps with tcp fragmentation --- tests/pcap/data/tcp_fragmentation.pcap | Bin 0 -> 3778 bytes tests/pcap/src/handshake_message.rs | 42 +++++++++++++++++-------- 2 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 tests/pcap/data/tcp_fragmentation.pcap diff --git a/tests/pcap/data/tcp_fragmentation.pcap b/tests/pcap/data/tcp_fragmentation.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b87d966877aed77f322748f820c101e41e9e6af4 GIT binary patch literal 3778 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 LJEZZ`kNo`~`$8mk literal 0 HcmV?d00001 diff --git a/tests/pcap/src/handshake_message.rs b/tests/pcap/src/handshake_message.rs index 76bf520aac0..f35ad5f9c26 100644 --- a/tests/pcap/src/handshake_message.rs +++ b/tests/pcap/src/handshake_message.rs @@ -65,10 +65,14 @@ impl HandshakeMessage { // 'fragment_ids' here is a list of the packets containing // TLS records which contain fragments of this handshake message. // - // The "tls.handshake.fragment" data from tshark actually includes the - // full hex of each fragment, not just the id of each packet. But rtshark - // uses the `show` tag instead of the `value` tag, and the `show` tag is - // just the packet id. There's currently no way to access the `value` tag. + // Note: `all_metadata` uses the tshark "value" field. For Self::FRAGMENT, + // the "value" field is the id of the packet containing the fragment. + // The "raw_value" field is the fragment payload. However, the "raw_value" + // field does not contain the TLS record header, only the TLS handshake + // message. TLS record headers could be useful for later replaying the + // handshake, so we continue to use "value" and manually reassemble the + // TLS handshake messages from the fragment packets. + // See https://github.com/CrabeDeFrance/rtshark/pull/17 let fragment_ids = packet.all_metadata(Self::FRAGMENT); let fragment_ids = if fragment_ids.is_empty() { vec![packet.id()] @@ -86,8 +90,10 @@ impl HandshakeMessage { let fragment = tcp_packets .get(&fragment_id) .context("Missing handshake message fragment record")?; + // TCP payloads can also be fragmented, so check for a reassembled payload first. let tcp_payload = fragment - .metadata(Builder::TCP_PAYLOAD) + .metadata(Builder::TCP_REASSEMBLED) + .or_else(|| fragment.metadata(Builder::TCP_PAYLOAD)) .context("Missing handshake message tcp payload")?; // The TCP payload is a hex string. However, rtshark formats hex strings // like "AB:CD:EF:12" instead of "ABCDEF12". @@ -129,14 +135,8 @@ pub struct Builder { } impl Builder { - // "tcp.payload" is actually insufficient. It's uncommon, - // but a TLS record can be split across multiple TCP segments. - // Then we would also need to check for "tcp.reassembled.data". - // However, rtshark currently doesn't support that field: - // https://github.com/CrabeDeFrance/rtshark/pull/16 - // - // For now, parsing will fail if TLS records are reassembled. const TCP_PAYLOAD: &'static str = "tcp.payload"; + const TCP_REASSEMBLED: &'static str = "tcp.reassembled.data"; const MESSAGE_TYPE: &'static str = "tls.handshake.type"; @@ -173,6 +173,7 @@ impl Builder { let tcp_capture = capture .display_filter(Self::TCP_PAYLOAD) .metadata_whitelist(Self::TCP_PAYLOAD) + .metadata_whitelist(Self::TCP_REASSEMBLED) .metadata_whitelist(Packet::PACKET_ID) .spawn()?; @@ -199,7 +200,7 @@ mod tests { use super::*; #[test] - fn fragmentation() -> Result<()> { + fn tls_fragmentation() -> Result<()> { let mut builder = Builder::default(); builder.set_capture_file("data/fragmented_ch.pcap"); let messages = builder.build()?; @@ -213,6 +214,21 @@ mod tests { Ok(()) } + #[test] + fn tcp_fragmentation() -> Result<()> { + let mut builder = Builder::default(); + builder.set_capture_file("data/tcp_fragmentation.pcap"); + let messages = builder.build()?; + + let first = messages.first().unwrap(); + assert_eq!(first.records.len(), 1); + let bytes = first.bytes(); + // Correct length read from wireshark + assert_eq!(bytes.len(), 271); + + Ok(()) + } + #[test] fn multiple_handshakes() -> Result<()> { let mut builder = Builder::default();