aboutsummaryrefslogtreecommitdiffstats
path: root/src/response.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/response.rs')
-rw-r--r--src/response.rs219
1 files changed, 190 insertions, 29 deletions
diff --git a/src/response.rs b/src/response.rs
index a8ad867..f68e881 100644
--- a/src/response.rs
+++ b/src/response.rs
@@ -1,13 +1,9 @@
-use std::fmt::Display;
-use bytes::Bytes;
+use bytes::{Buf, Bytes};
use chrono::{DateTime, Utc};
-use hex::encode;
-use structdiff::{Difference, StructDiff};
use crate::packet_content::PeerToPeerCipher;
-#[derive(PartialEq, Debug, Clone, Difference)]
-#[difference(expose)]
+#[derive(PartialEq, Debug, Clone)]
pub struct Response {
pub(crate) cipher: PeerToPeerCipher,
pub cleartext: Option<ClearResponse>
@@ -22,41 +18,77 @@ impl From<Bytes> for Response {
}
}
-#[derive(PartialEq, Debug, Clone, Difference)]
-#[difference(expose)]
+#[derive(PartialEq, Debug, Clone)]
pub struct ClearResponse {
pub timestamp: DateTime<Utc>,
- pub response: u8,
- pub keepalive_interval: u8,
- pub is_admin: bool,
- pub permissions: u8,
- pub nonce: u32,
- pub fw_version: u8
+ pub content: ResponseContent,
+}
+
+#[derive(PartialEq, Debug, Clone)]
+pub enum ResponseContent {
+ Stats(RepeaterStats),
+ Telemetry(Telemetry),
+ Invalid,
+}
+
+#[repr(C)]
+#[derive(PartialEq, Debug, Clone)]
+pub struct RepeaterStats {
+ pub batt_milli_volts: u16,
+ pub curr_tx_queue_len: u16,
+ pub noise_floor: i16,
+ pub last_rssi: i16,
+ pub n_packets_recv: u32,
+ pub n_packets_sent: u32,
+ pub total_air_time_secs: u32,
+ pub total_up_time_secs: u32,
+ pub n_sent_flood: u32,
+ pub n_sent_direct: u32,
+ pub n_recv_flood: u32,
+ pub n_recv_direct: u32,
+ pub err_events: u16,
+ pub last_snr: i16,
+ pub n_direct_dups: u16,
+ pub n_flood_dups: u16,
+ pub total_rx_air_time_secs: u32,
+}
+
+#[derive(PartialEq, Debug, Clone)]
+pub struct Telemetry {
+
}
impl From<Bytes> for ClearResponse {
fn from(value: Bytes) -> Self {
- let mut bytes = value;
+ let mut bytes = value.clone();
let mut clear_response = ClearResponse {
timestamp: DateTime::from_timestamp(0, 0).unwrap(),
- response: 0,
- keepalive_interval: 0,
- is_admin: false,
- permissions: 0,
- nonce: 0,
- fw_version: 0,
+ content: ResponseContent::Invalid
};
- println!("{} bytes: {}", bytes.len(), encode(&bytes));
+ if bytes.len() < 4 { return clear_response }
+ if let Some(timestamp) = DateTime::from_timestamp(bytes.get_u32_le() as i64, 0) {
+ clear_response.timestamp = timestamp;
+ }
+
+ // Unfortunately, there's no way to know for sure what kind of response this is,
+ // so we have to make some educated guesses. In particular, it seems like all
+ // telemetry responses start with voltage on channel 1. So, if the first two bytes
+ // are exactly 0x0174 then we'll assume it's a telemetry packet.
+ // let maybe_cayenne = bytes.clone().get_u16();
+ // if maybe_cayenne == 0x0174 {
+ // println!("CayenneLPP: {} bytes: {}", bytes.len(), encode(&bytes));
+ // } else {
+ // println!("{} bytes: {}", value.len(), encode(&value));
+ // }
-
clear_response
}
}
-impl Display for Response {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl core::fmt::Display for Response {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("({:2x?}) -> ({:2x?}) MAC: {:4x?} ",
self.cipher.source,
self.cipher.destination,
@@ -74,10 +106,10 @@ impl Display for Response {
#[cfg(test)]
mod tests {
use std::str::FromStr;
-
use bytes::Bytes;
use hex::decode;
- use crate::{identity::KeystoreInput, packet::*, packet_content::{PacketContent, PeerToPeerCipher}, response::Response};
+ use tinyvec::ArrayVec;
+ use crate::{std_identity::KeystoreInput, packet::*, packet_content::{PacketContent, PeerToPeerCipher}, response::Response};
#[test]
fn response_encrypted() {
@@ -86,7 +118,7 @@ mod tests {
let lhs_packet = Packet {
route_type: RouteType::Direct,
version: PayloadVersion::VersionOne,
- path: vec![],
+ path: ArrayVec::new(),
transport: [0, 0],
raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
content: PacketContent::Response(Response {
@@ -114,7 +146,7 @@ mod tests {
let lhs_packet = Packet {
route_type: RouteType::Direct,
version: PayloadVersion::VersionOne,
- path: vec![],
+ path: ArrayVec::new(),
transport: [0, 0],
raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
content: PacketContent::Response(Response {
@@ -140,4 +172,133 @@ mod tests {
// assert_eq!(lhs_packet, rhs_packet);
// assert_eq!(format!("{}", lhs_packet), " Direct | v1 | | [] | | RESPONSE | (12) -> (34) MAC: b900 ENCRYPTED")
}
+
+ #[test]
+ fn stats_response() {
+ let sample = "06003412CF45D4856713A44CA5411C327DD96575CF632F626C5B17046BECC220B9E476362B9AC510637F3CC68160E263F8D9181A03965B989D6DE1AA646C1E2EEF4CF13E6F92";
+
+ let file_contents = include_str!("../test_identities_file.toml");
+ let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap();
+ let keystore = keystore_in.compile();
+
+ let lhs_packet = Packet {
+ route_type: RouteType::Direct,
+ version: PayloadVersion::VersionOne,
+ path: ArrayVec::new(),
+ transport: [0, 0],
+ raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ content: PacketContent::Response(Response {
+ cipher: PeerToPeerCipher {
+ destination: 0x34,
+ source: 0x12,
+ mac: 0xb900,
+ ciphertext: Bytes::copy_from_slice(&decode("0cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ cleartext: Some(Bytes::copy_from_slice(&decode("c1cc1b69c8100000a7fff7ff1c000000160000000400000065060000050000001100000005000000170000000000300000000200040000000000000000000000").unwrap()))
+ },
+ cleartext: None}),
+ incomplete: false,
+ };
+
+ let mut rhs_packet = Packet::from_str(sample).unwrap();
+ rhs_packet.try_decrypt(&keystore);
+ println!("{:#?}", rhs_packet);
+ // assert_eq!(lhs_packet, rhs_packet);
+ }
+
+ #[test]
+ fn acl_response() {
+ let sample = "06003412DE04889E2387AC81CC38108EF3E575B4B61FFA6AB9573DBAF589891F79D6C20FA33B6CE5CD672EE982F6DAE79DD8BC83F648";
+
+ let file_contents = include_str!("../test_identities_file.toml");
+ let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap();
+ let keystore = keystore_in.compile();
+
+ let lhs_packet = Packet {
+ route_type: RouteType::Direct,
+ version: PayloadVersion::VersionOne,
+ path: ArrayVec::new(),
+ transport: [0, 0],
+ raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ content: PacketContent::Response(Response {
+ cipher: PeerToPeerCipher {
+ destination: 0x34,
+ source: 0x12,
+ mac: 0xb900,
+ ciphertext: Bytes::copy_from_slice(&decode("0cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ cleartext: Some(Bytes::copy_from_slice(&decode("efcc1b6987449a393ea60355ebee904f160398fe6b93288b0312349bdc1f760334569df1f96603000000000000000000").unwrap()))
+ },
+ cleartext: None}),
+ incomplete: false,
+ };
+
+ let mut rhs_packet = Packet::from_str(sample).unwrap();
+ rhs_packet.try_decrypt(&keystore);
+ println!("{:#?}", rhs_packet);
+ // assert_eq!(lhs_packet, rhs_packet);
+ }
+
+ #[test]
+ fn unknown_response() {
+ let sample = "06003412879A1045F0290639ED9B6D4BB0B51C14E13D";
+
+ let file_contents = include_str!("../test_identities_file.toml");
+ let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap();
+ let keystore = keystore_in.compile();
+
+ let lhs_packet = Packet {
+ route_type: RouteType::Direct,
+ version: PayloadVersion::VersionOne,
+ path: ArrayVec::new(),
+ transport: [0, 0],
+ raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ content: PacketContent::Response(Response {
+ cipher: PeerToPeerCipher {
+ destination: 0x34,
+ source: 0x12,
+ mac: 0xb900,
+ ciphertext: Bytes::copy_from_slice(&decode("0cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ cleartext: Some(Bytes::copy_from_slice(&decode("f6cc1b69000000000000000000000000").unwrap()))
+ },
+ cleartext: None}),
+ incomplete: false,
+ };
+
+ let mut rhs_packet = Packet::from_str(sample).unwrap();
+ rhs_packet.try_decrypt(&keystore);
+ println!("{:#?}", rhs_packet);
+ // assert_eq!(lhs_packet, rhs_packet);
+ }
+
+
+ #[test]
+ fn telemetry_response() {
+ let sample = "06003412187C3AE03E52D347B957D634221DB5E86815";
+
+ let file_contents = include_str!("../test_identities_file.toml");
+ let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap();
+ let keystore = keystore_in.compile();
+
+ let lhs_packet = Packet {
+ route_type: RouteType::Direct,
+ version: PayloadVersion::VersionOne,
+ path: ArrayVec::new(),
+ transport: [0, 0],
+ raw_content: Bytes::copy_from_slice(&decode("3412b9000cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ content: PacketContent::Response(Response {
+ cipher: PeerToPeerCipher {
+ destination: 0x34,
+ source: 0x12,
+ mac: 0xb900,
+ ciphertext: Bytes::copy_from_slice(&decode("0cf1641739f7e4d49bff88bf5695b304111b15277de3a6031b9af3a2b6371cf75615ffefe7dcc0bbea2856c4e798a72d5b989d6de1aa646c1e2eef4cf13e6f92").unwrap()),
+ cleartext: Some(Bytes::copy_from_slice(&decode("fdcc1b69017401ad0000000000000000").unwrap()))
+ },
+ cleartext: None}),
+ incomplete: false,
+ };
+
+ let mut rhs_packet = Packet::from_str(sample).unwrap();
+ rhs_packet.try_decrypt(&keystore);
+ println!("{:#?}", rhs_packet);
+ // assert_eq!(lhs_packet, rhs_packet);
+ }
} \ No newline at end of file