use colored::Colorize;
use dusa_collection_utils::core::types::stringy::Stringy;
use dusa_collection_utils::core::version::Version;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::net::IpAddr;
use crate::protocol::flags::{ConnectionParams, MsgType};
use crate::protocol::status::ProtocolStatus;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RecordMeta {
pub session_id: [u8; 16],
pub seq_no: u32,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProtocolHeader {
pub version: u16,
pub flags: u8,
pub payload_length: u64,
pub msg_type: u8,
pub reserved: u8,
pub status: u8,
pub origin_address: [u8; 4],
pub encryption_key: [u8; 32],
}
impl fmt::Display for ProtocolHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let version: Version = Version::decode(self.version);
let origin_addr: Stringy = match self.get_origin_ip() == IpAddr::V4([0, 0, 0, 0].into()) {
true => Stringy::from("Internal"),
false => Stringy::from(self.get_origin_ip().to_string()),
};
write!(
f,
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
format!("Library Version: {}", version).bold().green(),
format!(
"Flags: {:#010b} ({})",
self.flags,
self.flags()
)
.bold()
.blue(),
format!(
"Type: ({:?})",
self.msg_type(),
)
.bold()
.blue(),
format!(
"Payload Key: {}",
if self.encryption_key == [0u8; 32] {
"No Key Set".to_string()
} else {
format!("{}", hex::encode(self.encryption_key))
}
)
.bold()
.purple(),
format!("Payload Length: {}", self.payload_length)
.bold()
.purple(),
format!(
"Status: {:#010b} ({})",
self.status,
self.status()
)
.bold()
.red(),
format!("Origin Address: {}", origin_addr).bold().cyan(),
)
}
}
impl ProtocolHeader {
pub fn get_origin_ip(&self) -> std::net::Ipv4Addr {
std::net::Ipv4Addr::from(self.origin_address)
}
pub fn meta(&self) -> RecordMeta {
let mut sid = [0u8; 16];
sid.copy_from_slice(&self.encryption_key[0..16]);
let mut seq = [0u8; 4];
seq.copy_from_slice(&self.encryption_key[16..20]);
RecordMeta {
session_id: sid,
seq_no: u32::from_be_bytes(seq),
}
}
pub fn set_meta(&mut self, m: &RecordMeta) {
self.encryption_key[0..16].copy_from_slice(&m.session_id);
self.encryption_key[16..20].copy_from_slice(&m.seq_no.to_be_bytes());
self.encryption_key[20..32].fill(0);
}
pub fn msg_type(&self) -> MsgType {
MsgType::from(self.msg_type)
}
pub fn set_msg_type(&mut self, t: MsgType) {
self.msg_type = t.bits();
}
pub fn flags(&self) -> ConnectionParams {
ConnectionParams::from_bits_truncate(self.flags)
}
pub fn set_flags(&mut self, params: ConnectionParams) {
self.flags = params.bits();
}
pub fn status(&self) -> ProtocolStatus {
ProtocolStatus::from_bits_truncate(self.status)
}
pub fn set_status(&mut self, status: ProtocolStatus) {
self.status = status.bits();
}
}
const HEADER_VERSION_LEN: usize = 2; const HEADER_FLAGS_LEN: usize = 1; const HEADER_PAYLOAD_LENGTH_LEN: usize = 8; const HEADER_MSG_TYPE_LEN: usize = 1; const HEADER_RESERVED_LEN: usize = 1; const HEADER_STATUS_LEN: usize = 1; const HEADER_ORIGIN_ADDRESS_LEN: usize = 4; const HEADER_ENCRYPTION_KEY_LEN: usize = 32;
pub const HEADER_LENGTH: usize = HEADER_VERSION_LEN
+ HEADER_FLAGS_LEN
+ HEADER_PAYLOAD_LENGTH_LEN
+ HEADER_MSG_TYPE_LEN
+ HEADER_RESERVED_LEN
+ HEADER_STATUS_LEN
+ HEADER_ORIGIN_ADDRESS_LEN
+ HEADER_ENCRYPTION_KEY_LEN;
pub const EOL: &[u8] = b"-EOL-";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_meta_roundtrip() {
let meta = RecordMeta {
session_id: [0xAA; 16],
seq_no: 42,
};
let mut header = ProtocolHeader {
version: 2,
flags: 0,
payload_length: 0,
msg_type: 0,
reserved: 0,
status: 0,
origin_address: [0; 4],
encryption_key: [0u8; 32],
};
header.set_meta(&meta);
let parsed = header.meta();
assert_eq!(parsed, meta);
}
#[test]
fn msg_type_and_reserved_are_independent() {
let mut header = ProtocolHeader {
version: 2,
flags: 0,
payload_length: 0,
msg_type: 0,
reserved: 0,
status: 0,
origin_address: [0; 4],
encryption_key: [0u8; 32],
};
header.set_msg_type(MsgType::Data);
header.reserved = ConnectionParams::COMPRESSED.bits();
assert_eq!(header.msg_type(), MsgType::Data);
assert_eq!(header.reserved, ConnectionParams::COMPRESSED.bits());
}
#[test]
fn flags_and_status_accessors_roundtrip() {
let mut header = ProtocolHeader {
version: 2,
flags: 0,
payload_length: 0,
msg_type: 0,
reserved: 0,
status: 0,
origin_address: [0; 4],
encryption_key: [0u8; 32],
};
header.set_flags(ConnectionParams::ENCRYPTED | ConnectionParams::COMPRESSED);
header.set_status(ProtocolStatus::OK);
assert_eq!(
header.flags(),
ConnectionParams::ENCRYPTED | ConnectionParams::COMPRESSED
);
assert_eq!(header.status(), ProtocolStatus::OK);
}
}