Files
wolz/src/wolz.rs
T
2026-07-11 21:10:24 +05:30

100 lines
2.8 KiB
Rust

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<Self> {
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(())
}