diff --git a/.gitignore b/.gitignore index ea8c4bf..869df07 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ab9d94d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#1C3310", + "titleBar.activeBackground": "#274816", + "titleBar.activeForeground": "#F6FBF4" + } +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 39f9e6f..8e3f4a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = { version = "1.0.75", features = ["backtrace"] } +if-addrs = "0.10.1" \ No newline at end of file diff --git a/__pycache__/discover.cpython-311.pyc b/__pycache__/discover.cpython-311.pyc index 30dcaf9..f32412d 100644 Binary files a/__pycache__/discover.cpython-311.pyc and b/__pycache__/discover.cpython-311.pyc differ diff --git a/discover.py b/discover.py index ddf3db8..df604fb 100644 --- a/discover.py +++ b/discover.py @@ -64,6 +64,11 @@ def discover(discover_type=None, ip_address=None): data = data[8:-16] else: continue + + print(data[20:26]) + print(data[20:26].hex()) + print(bytearray.fromhex(data[20:26].hex())) + device_id = int.from_bytes(bytearray.fromhex(data[20:26].hex()), "little") if device_id in found_devices: continue diff --git a/src/discover.rs b/src/discover.rs new file mode 100644 index 0000000..cc56c65 --- /dev/null +++ b/src/discover.rs @@ -0,0 +1,130 @@ +use std::{ + collections::HashMap, + net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, + num::ParseIntError, + time::Duration, +}; + +use anyhow::Result; + +pub 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 DEVICE_INFO_MSG: &'static [u8] = &[ + 0x5a, 0x5a, 0x15, 0x00, 0x00, 0x38, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x33, + 0x05, 0x13, 0x06, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x8d, 0x9b, 0xf9, 0xa0, + 0x30, 0x1a, 0xe3, 0xb7, 0xe4, 0x2d, 0x53, 0x49, 0x47, 0x62, 0xbe, + ]; + + 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] == Self::hex("5a5a")? { + 2 + } else if bytes[..2] == Self::hex("8370")? { + if bytes[8..10] == Self::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) { + devices.insert(device_id, (addr, bytes.to_vec(), protocol)); + } + } + } + } + + println!("{devices:#?}"); + + Ok(Self {}) + } +} + +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 + } + + fn hex(s: &str) -> Result, ParseIntError> { + (0..s.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) + .collect() + } +} + +#[cfg(test)] +mod test { + use anyhow::Result; + + use super::Startup; + + #[test] + fn discover() -> Result<()> { + Startup::discover()?; + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..28ff0e0 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +mod discover; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}