use std::{ collections::HashMap, net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, str::from_utf8, time::Duration, }; use anyhow::Result; use crate::{ hex, security::{MsgType, Security}, }; #[derive(Debug, Clone)] pub struct DeviceInfo { id: u64, model: String, sn: String, protocol: u8, addr: SocketAddr, } pub struct Device { info: DeviceInfo, socket: UdpSocket, } impl Device { pub fn connect(info: DeviceInfo) -> Result { let socket = UdpSocket::bind("0.0.0.0:0")?; socket.set_write_timeout(Some(Duration::from_secs(10)))?; socket.set_read_timeout(Some(Duration::from_secs(10)))?; socket.connect(info.addr)?; let me = Self { info, socket }; if me.info.protocol == 3 { me.authenticate()?; } me.refresh_status()?; Ok(me) } fn authenticate(&self) -> Result<()> { let request = Security::encode_8370(MsgType::HANDSHAKE_REQUEST)?; Ok(()) } pub fn refresh_status(&self) -> Result<()> { // Ok(()) } } struct Startup; impl Startup { const BROADCAST_MSG: &'static [u8] = &[ 0x5a, 0x5a, 0x01, 0x11, 0x48, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x75, 0xbd, 0x6b, 0x3e, 0x4f, 0x8b, 0x76, 0x2e, 0x84, 0x9c, 0x6e, 0x57, 0x8d, 0x65, 0x90, 0x03, 0x6e, 0x9d, 0x43, 0x42, 0xa5, 0x0f, 0x1f, 0x56, 0x9e, 0xb8, 0xec, 0x91, 0x8e, 0x92, 0xe5, ]; const NUM_RETRIES: u8 = 5; pub fn discover() -> Result> { let socket = UdpSocket::bind("0.0.0.0:0")?; socket.set_broadcast(true)?; socket.set_read_timeout(Some(Duration::from_secs(2)))?; let mut devices = HashMap::new(); for addr in Self::broadcast_addresses() { for _ in 0..Self::NUM_RETRIES { socket.send_to(Self::BROADCAST_MSG, (addr, 6445))?; socket.send_to(Self::BROADCAST_MSG, (addr, 20086))?; let mut buffer = [0; 1024]; if let Ok((bytes_read, addr)) = socket.recv_from(&mut buffer) { let mut bytes = buffer[0..bytes_read].to_vec(); let protocol = if bytes[..2] == hex("5a5a")? { 2 } else if bytes[..2] == hex("8370")? { if bytes[8..10] == hex("5a5a")? { bytes = bytes[8..(bytes.len() - 16)].to_vec(); } 3 } else { continue; }; let mut tmp: Vec = bytes[20..26].to_vec(); tmp.push(0); tmp.push(0); let device_id = u64::from_le_bytes(tmp.try_into().unwrap()); if devices.contains_key(&device_id) { continue; } let len = bytes.len(); let encrypt_data = Security::decrypt(&mut bytes[40..(len - 16)]); let model = from_utf8(&encrypt_data[17..25])?; let sn = from_utf8(&encrypt_data[8..40])?; devices.insert( device_id, (addr, model.to_string(), sn.to_string(), protocol), ); } } } Ok(devices .into_iter() .map(|(id, (addr, model, sn, protocol))| DeviceInfo { id, model, sn, protocol, addr, }) .collect()) } } impl Startup { fn broadcast_addresses() -> Vec { use if_addrs::*; let mut addresses = Vec::new(); if let Some(interfaces) = get_if_addrs().ok() { for interface in interfaces { match interface.addr { IfAddr::V4(v4) => { let ip = v4.ip; let octets = ip.octets(); if ip.is_private() { addresses.push(IpAddr::V4(Ipv4Addr::new( octets[0], octets[1], octets[2], u8::MAX, ))) } } IfAddr::V6(_v6) => { // TODO // addresses.push(IpAddr::V6(v6.ip)); } } } } addresses } } #[cfg(test)] mod test { use anyhow::Result; use super::{Device, Startup}; #[test] fn discover() -> Result<()> { let devices = Startup::discover()?; println!("{devices:#?}"); Ok(()) } #[test] fn connect() -> Result<()> { for device_info in Startup::discover()? { let device = Device::connect(device_info)?; } Ok(()) } }