100 lines
2.8 KiB
Rust
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(())
|
|
}
|