From d1ff5c0b10bbe1ca48b51be7510a80e1eb6920fb Mon Sep 17 00:00:00 2001 From: cool-mist Date: Sat, 11 Jul 2026 09:53:24 +0530 Subject: [PATCH] wip --- src/error.rs | 6 ++-- src/main.rs | 3 +- src/{ifaddrs.rs => netif.rs} | 62 ++++++++++++++++++++++++++------ src/wolz.rs | 68 ++++++++++++++++-------------------- 4 files changed, 88 insertions(+), 51 deletions(-) rename src/{ifaddrs.rs => netif.rs} (72%) diff --git a/src/error.rs b/src/error.rs index 8b25d2e..d97666b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,8 +12,9 @@ pub enum WError { InvalidConfigDir(String), ConfigDirInitError(String), ConfigFileFormatError(String), - UdpSocketBindError(String), + UdpSocketBindError(std::io::Error), InvalidCommandString(String), + FfiFailed(String), DaemonSocketBindError(std::io::Error), DaemonConnectionReadError(std::io::Error), @@ -32,8 +33,9 @@ impl Display for WError { WError::InvalidConfigDir(s) => format!("{}", s), WError::ConfigDirInitError(s) => format!("{}", s), WError::ConfigFileFormatError(s) => format!("{}", s), - WError::UdpSocketBindError(s) => format!("{}", s), + WError::UdpSocketBindError(e) => format!("Couldn't bind and send udp packet because {}", e), WError::InvalidCommandString(s) => format!("Command string '{}' is unknown", s), + WError::FfiFailed(s) => format!("FFI invocation failed {}", s), WError::DaemonCommandError(s) => format!("Failed to execute command {}", s), WError::DaemonCommandExecutionError(output) => format!( diff --git a/src/main.rs b/src/main.rs index 3393453..fc900ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ +mod arp; mod config; mod daemon; mod error; -mod ifaddrs; +mod netif; mod mac; mod wolz; diff --git a/src/ifaddrs.rs b/src/netif.rs similarity index 72% rename from src/ifaddrs.rs rename to src/netif.rs index f9b6fb5..23454f1 100644 --- a/src/ifaddrs.rs +++ b/src/netif.rs @@ -3,6 +3,9 @@ use std::ffi::{CStr, c_uchar}; use std::net::Ipv4Addr; use std::os::raw::{c_char, c_int, c_uint, c_ushort, c_void}; +use crate::error::{WError, WResult}; +use crate::mac::Mac; + #[repr(C)] struct IfAddrs { ifa_next: *mut IfAddrs, @@ -57,12 +60,12 @@ struct LinkAddress { mac: [u8; 6], } -pub struct NetworkInterface { - pub name: String, - pub ip: Ipv4Addr, - pub _netmask: Ipv4Addr, - pub broadcast: Ipv4Addr, - pub _mac: [u8; 6], +struct NetworkInterface { + name: String, + ip: Ipv4Addr, + _netmask: Ipv4Addr, + broadcast: Ipv4Addr, + _mac: [u8; 6], } impl NetworkInterface { @@ -77,15 +80,52 @@ impl NetworkInterface { } } -pub fn get_network_interfaces() -> Vec { +pub struct LocalNetwork { + pub source_ip_hint: Ipv4Addr, + pub subnet_broadcast: Ipv4Addr, +} + +pub fn get_local_networks() -> WResult> { + let mut networks = Vec::new(); + let interfaces = get_network_interfaces()?; + let mut unique_broadcast_addresses = Vec::new(); + for i in &interfaces { + if unique_broadcast_addresses.contains(&i.broadcast) { + continue; + } + + // man systemd.net-naming-scheme + // Only broadcast to local network + let iname = &i.name; + if iname.starts_with("en") || iname.starts_with("wl") || iname.starts_with("eth") { + unique_broadcast_addresses.push(i.broadcast); + let local_network = LocalNetwork { + source_ip_hint: i.ip, + subnet_broadcast: i.broadcast, + }; + networks.push(local_network); + } + } + + Ok(networks) +} + +pub(crate) fn find_local_networks_with_mac(mac: Mac) -> WResult> { + todo!() +} + +fn get_network_interfaces() -> WResult> { let mut network_addresses = HashMap::new(); let mut link_addresses = HashMap::new(); let mut addrs: *mut IfAddrs = std::ptr::null_mut(); unsafe { - if getifaddrs(&mut addrs) != 0 { - eprintln!("getifaddrs failed"); - return Vec::new(); + let stat = getifaddrs(&mut addrs); + if stat != 0 { + return Err(WError::FfiFailed(format!( + "Failed to call libc:getifaddrs, returned {}", + stat + ))); } let mut cur = addrs; @@ -133,7 +173,7 @@ pub fn get_network_interfaces() -> Vec { }; } - network_interfaces + Ok(network_interfaces) } fn calculate_broadcast_address(ip: Ipv4Addr, netmask: Ipv4Addr) -> Ipv4Addr { diff --git a/src/wolz.rs b/src/wolz.rs index 80d5da5..9d1193e 100644 --- a/src/wolz.rs +++ b/src/wolz.rs @@ -1,7 +1,11 @@ use std::net::UdpSocket; use crate::{ - config::Config, daemon, error::{WError, WResult}, ifaddrs::{self}, + arp, + config::Config, + error::{WError, WResult}, + mac::Mac, + netif::{self, LocalNetwork}, }; pub struct Wolz { @@ -38,55 +42,45 @@ impl Wolz { let mut magic_bytes = [0u8; 102]; mac.copy_magic_bytes(&mut magic_bytes); - let interfaces = ifaddrs::get_network_interfaces(); - let mut sent_addresses = Vec::new(); - for i in &interfaces { - if sent_addresses.contains(&i.broadcast) { - continue; - } - - // man systemd.net-naming-scheme - // Only broadcast to local network - let iname = &i.name; - if iname.starts_with("en") || iname.starts_with("wl") || iname.starts_with("eth") { - let send_addr = format!("{}:0", i.ip); - let broadcast_addr = format!("{}:9", i.broadcast); - match try_send_magic_bytes(&magic_bytes, &send_addr, &broadcast_addr) { - Ok(()) => { - println!("Sent magic pkt {} -> {} for Mac {}", send_addr, broadcast_addr, mac); - sent_addresses.push(i.broadcast); - } - Err(e) => { - println!( - "Unable to send magic pkt {} -> {} for Mac {}, because {}", - send_addr, broadcast_addr, mac, e - ); - } - } - } + let networks = netif::get_local_networks()?; + if networks.len() == 0 { + println!("Refusing to send WoL packets. Are you connected to any network?"); + return Ok(()); } - if sent_addresses.len() == 0 { - println!("Did not send to any address, are you connected to local network?"); + for n in &networks { + try_send_magic_bytes(&magic_bytes, &mac, n); } Ok(()) } pub fn shut_down(&self, mac_or_nick: &str) -> WResult<()> { + let mac = self.config.get_mac(mac_or_nick)?; + let ip_from_arp = netif::find_local_networks_with_mac(mac)?; todo!() } } -fn try_send_magic_bytes(magic_bytes: &[u8; 102], send_addr: &str, broadcast_addr: &str) -> WResult<()> { - let socket = UdpSocket::bind(&send_addr) - .map_err(|e| WError::UdpSocketBindError(format!("couldn't bind to socket because {}", e)))?; - socket - .set_broadcast(true) - .map_err(|e| WError::UdpSocketBindError(format!("couldn't set broadcast because {}", e)))?; +fn try_send_magic_bytes(magic_bytes: &[u8; 102], mac: &Mac, local_network: &LocalNetwork) { + let send_addr = format!("{}:0", local_network.source_ip_hint); + let broadcast_addr = format!("{}:9", local_network.subnet_broadcast); + match send_magic_bytes(&magic_bytes, &send_addr, &broadcast_addr) { + Ok(()) => { + println!("Sent magic pkt {} -> {} for Mac {}", send_addr, broadcast_addr, mac); + } + Err(e) => println!( + "Unable to send magic pkt {} -> {} for Mac {}, because {}", + send_addr, broadcast_addr, mac, e + ), + } +} + +fn send_magic_bytes(magic_bytes: &[u8; 102], send_addr: &str, broadcast_addr: &str) -> WResult<()> { + let socket = UdpSocket::bind(&send_addr).map_err(WError::UdpSocketBindError)?; + socket.set_broadcast(true).map_err(WError::UdpSocketBindError)?; socket .send_to(magic_bytes, &broadcast_addr) - .map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?; - + .map_err(WError::UdpSocketBindError)?; Ok(()) }