use crate::packet_content::PeerToPeerCipher; use bytes::{Buf, Bytes}; use chrono::{DateTime, Utc}; #[derive(PartialEq, Debug, Clone)] pub struct Request { pub cipher: PeerToPeerCipher, pub cleartext: Option, } #[derive(PartialEq, Debug, Clone)] pub struct ClearRequest { pub timestamp: DateTime, pub request_type: RequestType, pub request_data: Bytes, } impl From for ClearRequest { fn from(value: Bytes) -> Self { let mut bytes = value; let mut clear_request = ClearRequest { timestamp: DateTime::from_timestamp(0, 0).unwrap(), request_type: RequestType::Invalid, request_data: Bytes::new(), }; // Just check for the whole fixed-size part at once if bytes.len() < 5 { return clear_request; } if let Some(timestamp) = DateTime::from_timestamp(bytes.get_u32() as i64, 0) { clear_request.timestamp = timestamp; } clear_request.request_type = RequestType::from(bytes.get_u8()); clear_request.request_data = bytes; clear_request } } #[derive(PartialEq, Debug, Clone)] pub enum RequestType { Stats, Keepalive, Telemetry, MinMaxAvg, ACL, Invalid, Neighbors, } impl From for RequestType { fn from(value: u8) -> Self { match value { 0x01 => RequestType::Stats, 0x02 => RequestType::Keepalive, 0x03 => RequestType::Telemetry, 0x04 => RequestType::MinMaxAvg, 0x05 => RequestType::ACL, 0x06 => RequestType::Neighbors, _ => RequestType::Invalid, } } } impl core::fmt::Display for RequestType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { RequestType::Stats => f.write_str("STATS"), RequestType::Keepalive => f.write_str("KEEP ALIVE"), RequestType::Telemetry => f.write_str("TELEMETRY"), RequestType::MinMaxAvg => f.write_str("MIN/MAX/AVG"), RequestType::ACL => f.write_str("ACL"), RequestType::Neighbors => f.write_str("NEIGHBORS"), RequestType::Invalid => f.write_str("INVALID"), } } } impl From for Request { fn from(value: Bytes) -> Self { Request { cipher: PeerToPeerCipher::from(value), cleartext: None, } } } impl core::fmt::Display for Request { 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, self.cipher.mac ))?; if let Some(cleartext) = &self.cleartext { f.write_fmt(format_args!("at: {} ", cleartext.timestamp))?; f.write_fmt(format_args!("{}", &cleartext.request_type)) } else { f.write_str("ENCRYPTED") } } } #[cfg(test)] mod tests { use std::str::FromStr; use crate::{ packet::*, packet_content::{PacketContent, PeerToPeerCipher}, request::{ClearRequest, Request, RequestType}, std_identity::KeystoreInput, }; use bytes::Bytes; use chrono::DateTime; use hex::decode; use tinyvec::ArrayVec; #[test] fn request_type() { assert_eq!(RequestType::from(0x01), RequestType::Stats); assert_eq!(RequestType::from(0x02), RequestType::Keepalive); assert_eq!(RequestType::from(0x03), RequestType::Telemetry); assert_eq!(RequestType::from(0x04), RequestType::MinMaxAvg); assert_eq!(RequestType::from(0x05), RequestType::ACL); assert_eq!(RequestType::from(0x06), RequestType::Neighbors); assert_eq!(RequestType::from(0x07), RequestType::Invalid); assert_eq!(RequestType::from(0xFF), RequestType::Invalid); assert_eq!(format!("{}", RequestType::Stats), "STATS"); assert_eq!(format!("{}", RequestType::Keepalive), "KEEP ALIVE"); assert_eq!(format!("{}", RequestType::Telemetry), "TELEMETRY"); assert_eq!(format!("{}", RequestType::MinMaxAvg), "MIN/MAX/AVG"); assert_eq!(format!("{}", RequestType::ACL), "ACL"); assert_eq!(format!("{}", RequestType::Neighbors), "NEIGHBORS"); assert_eq!(format!("{}", RequestType::Invalid), "INVALID"); } #[test] fn request() { let sample = "020012341d87ccaac89563cbb39d2333b725e407a1a6"; let lhs_packet = Packet { route_type: RouteType::Direct, version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], raw_content: Bytes::copy_from_slice( &decode("12341d87ccaac89563cbb39d2333b725e407a1a6").unwrap(), ), content: PacketContent::Request(Request { cipher: PeerToPeerCipher { destination: 0x12, source: 0x34, mac: 0x1d87, ciphertext: Bytes::copy_from_slice( &decode("ccaac89563cbb39d2333b725e407a1a6").unwrap(), ), cleartext: Some(Bytes::copy_from_slice( b"\x9d\xd9\x16\xd2\x01\0\0\0\0\x9d0\x96\xbb\0\0\0", )), }, cleartext: Some(ClearRequest { timestamp: DateTime::from_timestamp_secs(3524712861).unwrap(), request_type: crate::request::RequestType::Stats, request_data: Bytes::copy_from_slice(b"\0\0\0\0\x9d0\x96\xbb\0\0\0"), }), }), incomplete: false, }; let mut rhs_packet = Packet::from_str(sample).unwrap(); let file_contents = include_str!("../test_identities_file.toml"); let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap(); let keystore = keystore_in.compile(); assert_eq!( format!("{}", rhs_packet.content), " REQUEST | (34) -> (12) MAC: 1d87 ENCRYPTED" ); rhs_packet.try_decrypt(&keystore); // assert_eq!(format!("{}", rhs_packet.content) == " REQUEST | (34) -> (12) MAC: 1d87 at: 2081-09-10 06:54:21 UTC STATS"); // assert_eq!(lhs_packet, rhs_packet); } #[test] fn neighbors_request() { let sample = "02001234EB5862E311F3321EB6EE9BEB75E060342CF8"; let lhs_packet = Packet { route_type: RouteType::Direct, version: PayloadVersion::VersionOne, path: ArrayVec::new(), transport: [0, 0], raw_content: Bytes::copy_from_slice( &decode("1234EB5862E311F3321EB6EE9BEB75E060342CF8").unwrap(), ), content: PacketContent::Request(Request { cipher: PeerToPeerCipher { destination: 0x12, source: 0x34, mac: 0xEB58, ciphertext: Bytes::copy_from_slice( &decode("62E311F3321EB6EE9BEB75E060342CF8").unwrap(), ), cleartext: Some(Bytes::copy_from_slice( b"\x9d\xd9\x16\xd2\x01\0\0\0\0\x9d0\x96\xbb\0\0\0", )), }, cleartext: Some(ClearRequest { timestamp: DateTime::from_timestamp_secs(3524712861).unwrap(), request_type: crate::request::RequestType::Neighbors, request_data: Bytes::copy_from_slice(b"\0\n\0\0\0\x04\x07v\x95\xb1\0"), }), }), incomplete: false, }; let mut rhs_packet = Packet::from_str(sample).unwrap(); let file_contents = include_str!("../test_identities_file.toml"); let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap(); let keystore = keystore_in.compile(); // assert!(format!("{}", rhs_packet.content) == " REQUEST | (34) -> (12) MAC: 1d87 ENCRYPTED"); rhs_packet.try_decrypt(&keystore); // assert!(format!("{}", rhs_packet.content) == " REQUEST | (34) -> (12) MAC: 1d87 at: 2081-09-10 06:54:21 UTC STATS"); // assert_eq!(lhs_packet, rhs_packet); } }