diff options
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/packet/mod.rs | 1 | ||||
| -rw-r--r-- | src/packet/packet.rs | 407 | ||||
| -rw-r--r-- | src/protocol/http.rs | 20 | ||||
| -rw-r--r-- | src/protocol/ipv4.rs | 2 | ||||
| -rw-r--r-- | src/protocol/ipv6.rs | 2 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 |
7 files changed, 436 insertions, 2 deletions
@@ -1 +1,2 @@ -pub mod protocol;
\ No newline at end of file +pub mod packet; +pub mod protocol; diff --git a/src/packet/mod.rs b/src/packet/mod.rs new file mode 100644 index 0000000..edb43db --- /dev/null +++ b/src/packet/mod.rs @@ -0,0 +1 @@ +pub mod packet; diff --git a/src/packet/packet.rs b/src/packet/packet.rs new file mode 100644 index 0000000..c199d8d --- /dev/null +++ b/src/packet/packet.rs @@ -0,0 +1,407 @@ +use crate::protocol::dns::DNS_MESSAGE; +use crate::protocol::ethernet::EtherType; +use crate::protocol::ethernet::EthernetFrame; +use crate::protocol::http::HTTP_MESSAGE; +use crate::protocol::ip::IPProtocol; +use crate::protocol::ipv4::IPv4Header; +use crate::protocol::ipv6::IPv6Header; +use crate::protocol::tcp::TcpHeader; +use crate::protocol::udp::UdpHeader; + +use core::fmt::Error; + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug)] +pub enum Encapsulation<'a> { + L2_ETH(EthernetFrame, &'a [u8]), + + L3_IP4(IPv4Header, &'a [u8]), + L3_IP6(IPv6Header, &'a [u8]), + + L4_TCP(TcpHeader, &'a [u8]), + L4_UDP(UdpHeader, &'a [u8]), + + L7_DNS(DNS_MESSAGE, &'a [u8]), + L7_HTTP(HTTP_MESSAGE, &'a [u8]), + + Unsupported(&'a [u8]), +} + +#[derive(Debug)] +pub struct Packet<'a> { + pub orig_data: &'a [u8], + pub orig_len: u32, + pub encapsulation: Vec<Encapsulation<'a>>, +} + +impl Packet<'_> { + pub fn new(data: &[u8], len: u32) -> Packet { + Packet { + orig_data: data, + orig_len: len, + encapsulation: vec![], + } + } + + pub fn handle(&mut self) -> Result<(), Error> { + return handle_l2(self, self.orig_data); + } +} + +fn handle_l2<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Error> { + let result = EthernetFrame::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L2_ETH(header, payload)); + return handle_l3(packet, payload, header.ether_type); + } + + println!("handle_l2: Incomplete data {:?}", input); + return Ok(()); +} + +fn handle_l3<'a>( + packet: &mut Packet<'a>, + input: &'a [u8], + next_proto: EtherType, +) -> Result<(), Error> { + match next_proto { + EtherType::IPv4 => { + let result = IPv4Header::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L3_IP4(header, payload)); + // TODO IPv4 Fragment + return handle_l4(packet, payload, header.protocol); + } + } + EtherType::IPv6 => { + let result = IPv6Header::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L3_IP6(header, payload)); + // TODO IPv6 Fragment + return handle_l4(packet, payload, header.next_header); + } + } + e => { + println!("handle_l3: Unsupported EtherType {:?}", e); + return Ok(()); + } + } + + println!("handle_l3: Incomplete data {:?}", input); + return Ok(()); +} + +fn handle_l4<'a>( + packet: &mut Packet<'a>, + input: &'a [u8], + next_proto: IPProtocol, +) -> Result<(), Error> { + match next_proto { + IPProtocol::UDP => { + let result = UdpHeader::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L4_UDP(header, payload)); + return handle_l7(packet, payload); + } + } + IPProtocol::TCP => { + let result = TcpHeader::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L4_TCP(header, payload)); + // TODO TCP Reassembly + return handle_l7(packet, payload); + } + } + e => { + println!("handle_l4: Unsupported IPProtocol {:?}", e); + return Ok(()); + } + } + + println!("handle_l4: Incomplete data {:?}", input); + return Ok(()); +} + +fn handle_l7<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Error> { + let result = DNS_MESSAGE::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L7_DNS(header, payload)); + return Ok(()); + } + + let result = HTTP_MESSAGE::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + packet + .encapsulation + .push(Encapsulation::L7_HTTP(header, payload)); + return Ok(()); + } + + println!("handle_l7: Incomplete data {:?}", input); + return Ok(()); +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::Packet; + + #[test] + fn test_packet_handle1() { + /* + * Frame 49: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface en0, id 0 + * Section number: 1 + * Interface id: 0 (en0) + * Interface name: en0 + * Interface description: Wi-Fi + * Encapsulation type: Ethernet (1) + * Arrival Time: Aug 2, 2023 11:36:35.739393000 CST + * [Time shift for this packet: 0.000000000 seconds] + * Epoch Time: 1690947395.739393000 seconds + * [Time delta from previous captured frame: 0.496904000 seconds] + * [Time delta from previous displayed frame: 0.000000000 seconds] + * [Time since reference or first frame: 7.146758000 seconds] + * Frame Number: 49 + * Frame Length: 74 bytes (592 bits) + * Capture Length: 74 bytes (592 bits) + * [Frame is marked: False] + * [Frame is ignored: False] + * [Protocols in frame: eth:ethertype:ip:udp:dns] + * [Coloring Rule Name: UDP] + * [Coloring Rule String: udp] + * Ethernet II, Src: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea), Dst: NewH3CTe_96:38:0e (48:73:97:96:38:0e) + * Destination: NewH3CTe_96:38:0e (48:73:97:96:38:0e) + * Address: NewH3CTe_96:38:0e (48:73:97:96:38:0e) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Source: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) + * Address: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 192.168.38.63, Dst: 192.168.44.40 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * 0000 00.. = Differentiated Services Codepoint: Default (0) + * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * Total Length: 60 + * Identification: 0x040c (1036) + * 000. .... = Flags: 0x0 + * 0... .... = Reserved bit: Not set + * .0.. .... = Don't fragment: Not set + * ..0. .... = More fragments: Not set + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: UDP (17) + * Header Checksum: 0xa2ed [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0xa2ed] + * Source Address: 192.168.38.63 + * Destination Address: 192.168.44.40 + * User Datagram Protocol, Src Port: 55298, Dst Port: 53 + * Source Port: 55298 + * Destination Port: 53 + * Length: 40 + * Checksum: 0xd3e5 [correct] + * [Calculated Checksum: 0xd3e5] + * [Checksum Status: Good] + * [Stream index: 13] + * [Timestamps] + * [Time since first frame: 0.000000000 seconds] + * [Time since previous frame: 0.000000000 seconds] + * UDP payload (32 bytes) + * Domain Name System (query) + * Transaction ID: 0xfc60 + * Flags: 0x0100 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 0 + * Queries + * deb.debian.org: type A, class IN + * Name: deb.debian.org + * [Name Length: 14] + * [Label Count: 3] + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * [Response In: 51] + */ + let bytes = [ + 0x48, 0x73, 0x97, 0x96, 0x38, 0x0e, 0x3c, 0xa6, 0xf6, 0x0a, 0xc5, 0xea, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x3c, 0x04, 0x0c, 0x00, 0x00, 0x40, 0x11, 0xa2, 0xed, 0xc0, 0xa8, + 0x26, 0x3f, 0xc0, 0xa8, 0x2c, 0x28, 0xd8, 0x02, 0x00, 0x35, 0x00, 0x28, 0xd3, 0xe5, + 0xfc, 0x60, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x64, + 0x65, 0x62, 0x06, 0x64, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x03, 0x6f, 0x72, 0x67, 0x00, + 0x00, 0x01, 0x00, 0x01, /* DNS END */ + ]; + + let mut packet = Packet::new(&bytes, bytes.len() as u32); + let action = packet.handle(); + println!("{:?}, {:?}", packet, action); + // println!("{:#?}, {:?}", packet, action); + // dbg!(packet); + + // assert_eq!(0, 1); + } + + #[test] + fn test_packet_handle2() { + /* + * Frame 217: 131 bytes on wire (1048 bits), 131 bytes captured (1048 bits) on interface en0, id 0 + * Section number: 1 + * Interface id: 0 (en0) + * Interface name: en0 + * Interface description: Wi-Fi + * Encapsulation type: Ethernet (1) + * Arrival Time: Aug 2, 2023 11:49:21.582237000 CST + * [Time shift for this packet: 0.000000000 seconds] + * Epoch Time: 1690948161.582237000 seconds + * [Time delta from previous captured frame: 0.000042000 seconds] + * [Time delta from previous displayed frame: 0.000000000 seconds] + * [Time since reference or first frame: 4.905717000 seconds] + * Frame Number: 217 + * Frame Length: 131 bytes (1048 bits) + * Capture Length: 131 bytes (1048 bits) + * [Frame is marked: False] + * [Frame is ignored: False] + * [Protocols in frame: eth:ethertype:ip:tcp:http] + * [Coloring Rule Name: HTTP] + * [Coloring Rule String: http || tcp.port == 80 || http2] + * Ethernet II, Src: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea), Dst: NewH3CTe_96:38:0e (48:73:97:96:38:0e) + * Destination: NewH3CTe_96:38:0e (48:73:97:96:38:0e) + * Address: NewH3CTe_96:38:0e (48:73:97:96:38:0e) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Source: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) + * Address: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 192.168.38.63, Dst: 182.61.200.6 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * 0000 00.. = Differentiated Services Codepoint: Default (0) + * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * Total Length: 117 + * Identification: 0x0000 (0) + * 010. .... = Flags: 0x2, Don't fragment + * 0... .... = Reserved bit: Not set + * .1.. .... = Don't fragment: Set + * ..0. .... = More fragments: Not set + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: TCP (6) + * Header Checksum: 0xd557 [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0xd557] + * Source Address: 192.168.38.63 + * Destination Address: 182.61.200.6 + * Transmission Control Protocol, Src Port: 57016, Dst Port: 80, Seq: 1, Ack: 1, Len: 77 + * Source Port: 57016 + * Destination Port: 80 + * [Stream index: 9] + * [Conversation completeness: Complete, WITH_DATA (31)] + * [TCP Segment Len: 77] + * Sequence Number: 1 (relative sequence number) + * Sequence Number (raw): 1965697618 + * [Next Sequence Number: 78 (relative sequence number)] + * Acknowledgment Number: 1 (relative ack number) + * Acknowledgment number (raw): 4259318185 + * 0101 .... = Header Length: 20 bytes (5) + * Flags: 0x018 (PSH, ACK) + * 000. .... .... = Reserved: Not set + * ...0 .... .... = Accurate ECN: Not set + * .... 0... .... = Congestion Window Reduced: Not set + * .... .0.. .... = ECN-Echo: Not set + * .... ..0. .... = Urgent: Not set + * .... ...1 .... = Acknowledgment: Set + * .... .... 1... = Push: Set + * .... .... .0.. = Reset: Not set + * .... .... ..0. = Syn: Not set + * .... .... ...0 = Fin: Not set + * [TCP Flags: ·······AP···] + * Window: 4096 + * [Calculated window size: 262144] + * [Window size scaling factor: 64] + * Checksum: 0x7f51 [correct] + * [Checksum Status: Good] + * [Calculated Checksum: 0x7f51] + * Urgent Pointer: 0 + * [Timestamps] + * [Time since first frame in this TCP stream: 0.010626000 seconds] + * [Time since previous frame in this TCP stream: 0.000042000 seconds] + * [SEQ/ACK analysis] + * [iRTT: 0.010584000 seconds] + * [Bytes in flight: 77] + * [Bytes sent since last PSH flag: 77] + * TCP payload (77 bytes) + * Hypertext Transfer Protocol + * GET / HTTP/1.1\r\n + * [Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n] + * [GET / HTTP/1.1\r\n] + * [Severity level: Chat] + * [Group: Sequence] + * Request Method: GET + * Request URI: / + * Request Version: HTTP/1.1 + * Host: www.baidu.com\r\n + * User-Agent: curl/7.64.1\r\n + */ + // Accept: */*\r\n + /* \r\n + * [Full request URI: http://www.baidu.com/] + * [HTTP request 1/1] + * [Response in frame: 220] + */ + let bytes = [ + 0x48, 0x73, 0x97, 0x96, 0x38, 0x0e, 0x3c, 0xa6, 0xf6, 0x0a, 0xc5, 0xea, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x75, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06, 0xd5, 0x57, 0xc0, 0xa8, + 0x26, 0x3f, 0xb6, 0x3d, 0xc8, 0x06, 0xde, 0xb8, 0x00, 0x50, 0x75, 0x2a, 0x2a, 0x52, + 0xfd, 0xe0, 0x09, 0xa9, 0x50, 0x18, 0x10, 0x00, 0x7f, 0x51, 0x00, 0x00, 0x47, 0x45, + 0x54, 0x20, 0x2f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, + 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x62, 0x61, 0x69, 0x64, + 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x36, 0x34, + 0x2e, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, + 0x2a, 0x0d, 0x0a, 0x0d, 0x0a, /* HTTP END */ + ]; + + let mut packet = Packet::new(&bytes, bytes.len() as u32); + let action = packet.handle(); + println!("{:?}, {:?}", packet, action); + // println!("{:#?}, {:?}", packet, action); + // dbg!(packet); + + // assert_eq!(0, 1); + } +} diff --git a/src/protocol/http.rs b/src/protocol/http.rs new file mode 100644 index 0000000..de6476f --- /dev/null +++ b/src/protocol/http.rs @@ -0,0 +1,20 @@ +use nom::IResult; + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HTTP_MESSAGE { + // TODO +} + +impl HTTP_MESSAGE { + pub fn new() -> HTTP_MESSAGE { + HTTP_MESSAGE { + // TODO + } + } + pub fn decode(input: &[u8]) -> IResult<&[u8], HTTP_MESSAGE> { + let message = HTTP_MESSAGE::new(); + // TODO + Ok((input, message)) + } +} diff --git a/src/protocol/ipv4.rs b/src/protocol/ipv4.rs index 3aaa182..4f07926 100644 --- a/src/protocol/ipv4.rs +++ b/src/protocol/ipv4.rs @@ -83,6 +83,8 @@ impl IPv4Header { let (input, source_address) = address_v4_decode(input)?; let (input, dest_address) = address_v4_decode(input)?; + // TODO IPv4 Options Decode + Ok(( input, IPv4Header { diff --git a/src/protocol/ipv6.rs b/src/protocol/ipv6.rs index 283172b..f21c4da 100644 --- a/src/protocol/ipv6.rs +++ b/src/protocol/ipv6.rs @@ -82,6 +82,8 @@ impl IPv6Header { let (input, source_address) = address_v6_decode(input)?; let (input, dest_address) = address_v6_decode(input)?; + // TODO IPv6 Ext Header Decode + Ok(( input, IPv6Header { diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 3427b8b..31046cb 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -4,4 +4,5 @@ pub mod ipv4; pub mod ipv6; pub mod udp; pub mod tcp; -pub mod dns;
\ No newline at end of file +pub mod dns; +pub mod http;
\ No newline at end of file |
