use std::net::IpAddr;
use chrono::prelude::{DateTime, Datelike, Local, Timelike};
use packed_struct::prelude::PackedStruct;
use crate::network::util::checksum;
#[derive(PackedStruct, Debug)]
#[packed_struct(bit_numbering = "msb0", endian = "lsb", size_bytes = "48")]
pub struct DiscoveryMessage {
#[packed_field(bytes = "8:11")]
gmt_offset: i32,
#[packed_field(bytes = "12:13")]
year: u16,
#[packed_field(bytes = "14")]
minute: u8,
#[packed_field(bytes = "15")]
hour: u8,
#[packed_field(bytes = "16")]
year_without_century: u8,
#[packed_field(bytes = "17")]
day_of_the_week: u8,
#[packed_field(bytes = "18")]
day_of_the_month: u8,
#[packed_field(bytes = "19")]
month: u8,
#[packed_field(bytes = "24:27")]
local_ip_reversed: [u8; 4],
#[packed_field(bytes = "28:29")]
local_port: u16,
#[packed_field(bytes = "32:33")]
checksum: u16,
#[packed_field(bytes = "38")]
magic_constant: u8,
}
#[derive(PackedStruct, Debug)]
#[packed_struct(bit_numbering = "msb0", endian = "lsb", size_bytes = "128")]
pub struct DiscoveryResponse {
#[packed_field(bytes = "52:53")]
pub model_code: u16,
#[packed_field(bytes = "58:63")]
pub mac: [u8; 6],
#[packed_field(bytes = "64:126")]
pub name: [u8; 62],
#[packed_field(bytes = "127")]
pub is_locked: bool,
}
impl DiscoveryMessage {
pub fn new(
addr: IpAddr,
port: u16,
time: Option<DateTime<Local>>,
) -> Result<DiscoveryMessage, String> {
let time = match time {
Some(t) => t,
None => Local::now(),
};
let selected_ip = match addr {
IpAddr::V4(ipv4) => ipv4,
_ => return Err("Could not construct DiscoveryMessage! IP address is not IPv4".into()),
};
let octets = selected_ip.octets();
let reversed_ip: [u8; 4] = [octets[3], octets[2], octets[1], octets[0]];
let mut msg = construct_message(reversed_ip, port, time)
.map_err(|e| format!("Could not construct DiscoveryMessage! {}", e))?;
msg.checksum = checksum(
&msg.pack()
.map_err(|e| format!("Could not pack DiscoveryMessage! {}", e))?,
);
return Ok(msg);
}
}
fn construct_message(
reversed_ip: [u8; 4],
port: u16,
time: DateTime<Local>,
) -> Result<DiscoveryMessage, String> {
Ok(DiscoveryMessage {
gmt_offset: time.offset().local_minus_utc(),
year: time
.year()
.try_into()
.map_err(|e| format!("Year is out of range. {}", e))?,
minute: time
.minute()
.try_into()
.map_err(|e| format!("Minutes are out of range. {}", e))?,
hour: time
.hour()
.try_into()
.map_err(|e| format!("Hour is out of range. {}", e))?,
year_without_century: (time.year() % 100) as u8,
day_of_the_week: time
.weekday()
.number_from_monday()
.try_into()
.map_err(|e| format!("Day of the week is out of range. {}", e))?,
day_of_the_month: time
.day()
.try_into()
.map_err(|e| format!("Day is out of range. {}", e))?,
month: time
.month()
.try_into()
.map_err(|e| format!("Month is out of range. {}", e))?,
local_ip_reversed: reversed_ip,
local_port: port,
checksum: 0,
magic_constant: 0x06,
})
}