diff options
| author | Will Dillon <william@housedillon.com> | 2025-11-06 00:03:54 +0000 |
|---|---|---|
| committer | Will Dillon <william@housedillon.com> | 2025-11-06 00:03:54 +0000 |
| commit | 5b3d21e6a553b54d6c17a72e8ac6fa93608e232a (patch) | |
| tree | ecd8a1f85b3474e0f3534ce874491216559cfd82 /src | |
| parent | Initial commit (diff) | |
| download | meshcore-rs-5b3d21e6a553b54d6c17a72e8ac6fa93608e232a.tar.gz meshcore-rs-5b3d21e6a553b54d6c17a72e8ac6fa93608e232a.zip | |
Packet header tests pass
Diffstat (limited to 'src')
| -rw-r--r-- | src/packet.rs | 218 |
1 files changed, 190 insertions, 28 deletions
diff --git a/src/packet.rs b/src/packet.rs index d44aa66..ecc13c6 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -2,10 +2,11 @@ use std::str::FromStr; use hex::decode; use tokio_util::bytes::{Buf, Bytes}; - +use structdiff::{Difference, StructDiff}; use crate::{crypto::SharedSecret, packet}; -#[derive(PartialEq, Debug)] +#[difference(expose)] +#[derive(PartialEq, Debug, Clone, Difference)] struct Packet { route_type: RouteType, version: PayloadVersion, @@ -16,7 +17,7 @@ struct Packet { incomplete: bool } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] enum RouteType { TransportFlood, Flood, @@ -37,7 +38,7 @@ impl From<u8> for RouteType { } } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] enum PayloadVersion { VersionOne, VersionTwo, @@ -61,7 +62,7 @@ impl From<u8> for PayloadVersion { } } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] enum PacketContent { Request(Request), Response(Response), @@ -79,41 +80,131 @@ enum PacketContent { Invalid, } -#[derive(PartialEq, Debug)] +impl PacketContent { + fn new(header: u8, bytes: Bytes) -> PacketContent { + // Specialize based on the Payload Type from the header + match (header & 0x3C) >> 2 { + 0x00 => PacketContent::Request( Request::from(bytes)), + 0x01 => PacketContent::Response( Response::from(bytes)), + 0x02 => PacketContent::Text( Text::from(bytes)), + 0x03 => PacketContent::Ack( Ack::from(bytes)), + 0x04 => PacketContent::Advert( Advert::from(bytes)), + 0x05 => PacketContent::GroupText(GroupText::from(bytes)), + 0x06 => PacketContent::GroupData(GroupData::from(bytes)), + 0x07 => PacketContent::AnonReq( AnonReq::from(bytes)), + 0x08 => PacketContent::Path( Path::from(bytes)), + 0x09 => PacketContent::Trace( Trace::from(bytes)), + 0x0A => PacketContent::Multipart(MultiPart::from(bytes)), + 0x0F => PacketContent::Raw( Raw { bytes }), + + _ => PacketContent::Invalid + } + } +} + +#[derive(PartialEq, Debug, Clone)] struct Request {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Request { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct Response {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Response { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct Text {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Text { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct Ack {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Ack { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct Advert {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Advert { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct GroupText {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for GroupText { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct GroupData {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for GroupData { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct AnonReq {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for AnonReq { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct Path {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Path { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct Trace {} -#[derive(PartialEq, Debug)] +impl From<Bytes> for Trace { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] struct MultiPart {} -#[derive(PartialEq, Debug)] -struct Raw {} +impl From<Bytes> for MultiPart { + fn from(value: Bytes) -> Self { + todo!() + } +} + +#[derive(PartialEq, Debug, Clone)] +struct Raw { + bytes: Bytes +} impl FromStr for Packet { type Err = hex::FromHexError; @@ -125,17 +216,17 @@ impl FromStr for Packet { } impl From<Bytes> for Packet { - fn from(bytes: Bytes) -> Self { - let mut bytes = bytes; + fn from(bytes_in: Bytes) -> Self { + let mut bytes = bytes_in; // This is the packet we'll build as we begin to parse let mut packet = Packet::default(); - if bytes.len() == 0 { + if bytes.len() < 2 { return packet } - let header = bytes.split_to(0).get_u8(); + let header = bytes.get_u8(); // Parse the header byte packet.route_type = RouteType::from(header); @@ -147,7 +238,9 @@ impl From<Bytes> for Packet { // The packet isn't long enough to contain the transport if bytes.len() < 4 { return packet; } - [bytes.split_to(2).get_u16(), bytes.split_to(2).get_u16()] + let t1 = bytes.get_u16(); + let t2 = bytes.get_u16(); + [t1, t2] }, _ => { [0, 0] @@ -156,7 +249,7 @@ impl From<Bytes> for Packet { // Get the route if bytes.len() == 0 { return packet; } - let path_length = bytes.split_to(1).get_u8() as usize; + let path_length = bytes.get_u8() as usize; packet.path = match packet.version { PayloadVersion::VersionOne => { @@ -177,7 +270,7 @@ impl From<Bytes> for Packet { let route: Vec<u16> = bytes .split_to(path_length * 2) .chunks(2) - .map(|c| Bytes::copy_from_slice(c).get_u16()) + .map(|c| Bytes::copy_from_slice(c).get_u16_le()) .collect(); route @@ -187,8 +280,10 @@ impl From<Bytes> for Packet { } }; - // Get the rest of the payload and subscript the parsing to the other structs - packet.raw_content = bytes; + // Get the rest of the payload and pass the parsing to the other structs + packet.raw_content = bytes.clone(); + + packet.content = PacketContent::new(header, bytes); // Mark the packet as complete and valid packet.incomplete = false; @@ -247,6 +342,15 @@ mod tests { assert!(PayloadVersion::VersionFour == PayloadVersion::from(0xFF)); } + fn print_compare(lhs: Packet, rhs: Packet) -> String { + let mut output_string = format!("Differences between packets: \n"); + + for diff in lhs.diff(&rhs) { + output_string.push_str(&format!("{:#?}", diff)); + } + output_string + } + #[test] fn packet() { // Check the hex decode errors @@ -254,6 +358,64 @@ mod tests { assert!(Err(hex::FromHexError::OddLength) == Packet::from_str("0")); // Check errors related to packet length issues - assert!(Packet::default() == Packet::from_str("").unwrap()) + assert!(Packet::default() == Packet::from_str("").unwrap()); + let rhs_packet = Packet::from_str("01").unwrap(); + assert!(Packet::default() == rhs_packet, "{}", print_compare(Packet::default(), rhs_packet)); + + // Packet not long enough to contain the provided path + let mut lhs_packet = Packet::default(); + lhs_packet.route_type = RouteType::Flood; + lhs_packet.version = PayloadVersion::VersionOne; + let rhs_packet = Packet::from_str("0101").unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + + // Packet not long enough for transport + let mut lhs_packet = Packet::default(); + lhs_packet.route_type = RouteType::TransportDirect; + lhs_packet.version = PayloadVersion::VersionOne; + let rhs_packet = Packet::from_str("0301").unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + + // Packet not long enough for version 2 path + let mut lhs_packet = Packet::default(); + lhs_packet.route_type = RouteType::Direct; + lhs_packet.version = PayloadVersion::VersionTwo; + let rhs_packet = Packet::from_str("420102").unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + + // Ensure packet remainder is captured. This + // will test version 1 plus transport + let lhs_packet = Packet { + route_type: RouteType::TransportDirect, + version: PayloadVersion::VersionOne, + path: [0x06, 0x07, 0x08, 0x09, 0x0A].to_vec(), + transport: [0x0102, 0x0304], + raw_content: Bytes::copy_from_slice(&[0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19]), + content: PacketContent::Invalid, + incomplete: false, + }; + let rhs_packet = Packet::from_str("030102030405060708090A10111213141516171819").unwrap(); + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); + } + + #[test] + fn advert() { + // Real sample packet from the air + let sample = "110c015f7e9b60661da0f2671512460728508c17ef336412a223144d3a623215162682045c44fef7241af0161923d3af0769d2d976b687a506dd5325ef526bf3eb52ae687277fcbde9969a5b0087e0eb0f7c1760a50c6a88bec13cc30a2a9b681d713166515e3bbc2bc27f20c0e4d7b67e08910c29dc02c468b8f8484f574c"; + + let lhs_packet = Packet { + route_type: RouteType::Flood, + version: PayloadVersion::VersionOne, + path: vec![0x01, 0x5f, 0x7e, 0x9b, 0x60, 0x66, 0x1d, 0xa0, 0xf2, 0x67, 0x15, 0x12], + transport: [0x00, 0x00], + raw_content: Bytes::copy_from_slice(&decode("460728508c17ef336412a223144d3a623215162682045c44fef7241af0161923d3af0769d2d976b687a506dd5325ef526bf3eb52ae687277fcbde9969a5b0087e0eb0f7c1760a50c6a88bec13cc30a2a9b681d713166515e3bbc2bc27f20c0e4d7b67e08910c29dc02c468b8f8484f574c").unwrap()), + content: packet::PacketContent::Advert(Advert { + + }), + incomplete: false, + }; + let rhs_packet = Packet::from_str(sample).unwrap(); + + assert!(lhs_packet == rhs_packet, "{}", print_compare(lhs_packet, rhs_packet)); } }
\ No newline at end of file |
