diff --git a/Cargo.lock b/Cargo.lock index 773742f..0ba2fc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,12 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + [[package]] name = "cc" version = "1.2.51" @@ -556,6 +562,7 @@ name = "roowho2" version = "0.1.0" dependencies = [ "anyhow", + "bytes", "chrono", "clap", "nix", @@ -753,9 +760,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "type-map" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 84f7492..adec9f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,11 @@ autolib = false [dependencies] anyhow = "1.0.100" +bytes = "1.11.0" chrono = { version = "0.4.42", features = ["serde"] } clap = { version = "4.5.53", features = ["derive"] } nix = { version = "0.30.1", features = ["hostname", "net"] } -tokio = { version = "1.49.0", features = ["net", "rt-multi-thread"] } +tokio = { version = "1.49.0", features = ["macros", "net", "rt-multi-thread"] } # onc-rpc = "0.3.2" # sd-notify = "0.4.5" # serde = { version = "1.0.228", features = ["derive"] } diff --git a/src/bin/roowhod.rs b/src/bin/roowhod.rs index 0b8f907..a23096f 100644 --- a/src/bin/roowhod.rs +++ b/src/bin/roowhod.rs @@ -1,10 +1,29 @@ -fn main() { - println!( - "{:#?}", - roowho2_lib::server::rwhod::generate_rwhod_status_update(), - ); - println!( - "{:#?}", - roowho2_lib::server::rwhod::determine_relevant_interfaces(), +use std::net::SocketAddrV4; + +use roowho2_lib::proto::Whod; + +const RWHOD_BROADCAST_PORT: u16 = 513; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let addr = SocketAddrV4::new( + std::net::Ipv4Addr::UNSPECIFIED, + RWHOD_BROADCAST_PORT, ); + let socket = tokio::net::UdpSocket::bind(addr).await?; + socket.set_broadcast(true)?; + + let mut buf = [0u8; Whod::SIZE]; + loop { + let (len, src) = socket.recv_from(&mut buf).await?; + if len == Whod::SIZE { + if let Ok(whod) = Whod::from_bytes(&buf) { + println!("Received whod packet from {}: {:?}", src, whod); + } else { + println!("Received malformed whod packet from {}", src); + } + } else { + println!("Received unexpected packet size {} from {}", len, src); + } + } } diff --git a/src/proto/rwhod_protocol.rs b/src/proto/rwhod_protocol.rs index 93bceba..16a6cfe 100644 --- a/src/proto/rwhod_protocol.rs +++ b/src/proto/rwhod_protocol.rs @@ -56,13 +56,88 @@ impl Whod { // NOTE: there was probably meant to be more packet types, but only status is defined. pub const WHODTYPE_STATUS: u8 = 1; - pub fn to_bytes(&self) -> [u8; std::mem::size_of::()] { + pub fn to_bytes(&self) -> [u8; Whod::SIZE] { unsafe { std::mem::transmute_copy(self) } } - // TODO: we should probably make a safer parser. - pub fn from_bytes(bytes: &[u8; std::mem::size_of::()]) -> Self { - unsafe { std::mem::transmute_copy(bytes) } + // TODO: use the bytes crate to make this simpler and safer + pub fn from_bytes(bytes: &[u8; Whod::SIZE]) -> anyhow::Result { + let mut i = 0; + + let wd_vers = bytes[i]; + i += 1; + + let wd_type = bytes[1]; + i += 1; + + i += 2; // skip wd_pad + + let wd_sendtime = i32::from_le_bytes(bytes[i..i + 4].try_into()?); + i += 4; + + let wd_recvtime = i32::from_le_bytes(bytes[i..i + 4].try_into()?); + i += 4; + + let mut wd_hostname = [0u8; Self::MAX_HOSTNAME_LEN]; + wd_hostname.copy_from_slice(&bytes[i..i + Self::MAX_HOSTNAME_LEN]); + i += Self::MAX_HOSTNAME_LEN; + + let wd_loadav = [ + i32::from_le_bytes(bytes[i..i + 4].try_into()?), + i32::from_le_bytes(bytes[i + 4..i + 8].try_into()?), + i32::from_le_bytes(bytes[i + 8..i + 12].try_into()?), + ]; + + i += 4 * 3; + + let wd_boottime = i32::from_le_bytes(bytes[i..i + 4].try_into()?); + i += 4; + + let mut wd_we = array::from_fn(|_| Whoent { + we_utmp: Outmp { + out_line: [0u8; Outmp::MAX_TTY_NAME_LEN], + out_name: [0u8; Outmp::MAX_USER_ID_LEN], + out_time: 0, + }, + we_idle: 0, + }); + + for entry in wd_we.iter_mut() { + let mut out_line = [0u8; Outmp::MAX_TTY_NAME_LEN]; + out_line.copy_from_slice(&bytes[i..i + Outmp::MAX_TTY_NAME_LEN]); + i += Outmp::MAX_TTY_NAME_LEN; + + let mut out_name = [0u8; Outmp::MAX_USER_ID_LEN]; + out_name.copy_from_slice(&bytes[i..i + Outmp::MAX_USER_ID_LEN]); + i += Outmp::MAX_USER_ID_LEN; + + let out_time = i32::from_le_bytes(bytes[i..i + 4].try_into()?); + i += 4; + + let we_idle = i32::from_le_bytes(bytes[i..i + 4].try_into()?); + i += 4; + + entry.we_utmp = Outmp { + out_line, + out_name, + out_time, + }; + entry.we_idle = we_idle; + } + + debug_assert!(i == Whod::SIZE); + + Ok(Whod { + wd_vers, + wd_type, + wd_pad: [0u8; 2], + wd_sendtime, + wd_recvtime, + wd_hostname, + wd_loadav, + wd_boottime, + wd_we, + }) } }