use std::{ io::Write, net::{TcpStream, UdpSocket}, }; use crate::{ config::Config, error::{WError, WResult}, mac::Mac, netif::{self, LocalNetwork}, }; pub struct Wolz { config: Config, } pub struct WCommand { name: String, arg: String, } impl WCommand { pub fn create(name: String, arg: String) -> Self { Self { name, arg } } } impl Wolz { pub fn create() -> WResult { let config = Config::create()?; Ok(Self { config }) } pub fn execute(&self, command: &WCommand) -> WResult<()> { match command.name.as_str() { "up" => self.wake_up(&command.arg), "down" => self.shut_down(&command.arg), _ => Err(WError::InvalidCommandString(command.name.to_string())), } } fn wake_up(&self, mac_or_nick: &str) -> WResult<()> { let mac = self.config.get_mac(mac_or_nick)?; let mut magic_bytes = [0u8; 102]; mac.copy_magic_bytes(&mut magic_bytes); let networks = netif::get_local_networks()?; if networks.len() == 0 { println!("Refusing to send WoL packets. Are you connected to any network?"); return Ok(()); } for n in &networks { try_send_magic_bytes(&magic_bytes, &mac, n); } Ok(()) } // TODO: Fallback to full network scan if /proc/net/arp file lookup fails fn shut_down(&self, mac_or_nick: &str) -> WResult<()> { let mac = self.config.get_mac(mac_or_nick)?; let ip = netif::find_ip_with_mac(mac)?; println!("Sending shutdown signal to {}:21367", ip); let mut stream = TcpStream::connect(format!("{}:21367", ip)).map_err(WError::TcpSocketBindError)?; stream .write("shutdown".as_bytes()) .map_err(WError::TcpSocketWriteError)?; Ok(()) } } 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 WoL magic packet {} -> {} for mac {}", send_addr, broadcast_addr, mac ); } Err(e) => println!( "Unable to send WoL magic packet {} -> {} 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(WError::UdpSocketBindError)?; Ok(()) }