aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorWill Dillon <william@housedillon.com>2025-11-06 00:03:54 +0000
committerWill Dillon <william@housedillon.com>2025-11-06 00:03:54 +0000
commit5b3d21e6a553b54d6c17a72e8ac6fa93608e232a (patch)
treeecd8a1f85b3474e0f3534ce874491216559cfd82 /src
parentInitial commit (diff)
downloadmeshcore-rs-5b3d21e6a553b54d6c17a72e8ac6fa93608e232a.tar.gz
meshcore-rs-5b3d21e6a553b54d6c17a72e8ac6fa93608e232a.zip
Packet header tests pass
Diffstat (limited to 'src')
-rw-r--r--src/packet.rs218
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