This commit is contained in:
cool-mist
2026-07-11 09:53:24 +05:30
parent 4b52bdce76
commit d1ff5c0b10
4 changed files with 88 additions and 51 deletions
+4 -2
View File
@@ -12,8 +12,9 @@ pub enum WError {
InvalidConfigDir(String), InvalidConfigDir(String),
ConfigDirInitError(String), ConfigDirInitError(String),
ConfigFileFormatError(String), ConfigFileFormatError(String),
UdpSocketBindError(String), UdpSocketBindError(std::io::Error),
InvalidCommandString(String), InvalidCommandString(String),
FfiFailed(String),
DaemonSocketBindError(std::io::Error), DaemonSocketBindError(std::io::Error),
DaemonConnectionReadError(std::io::Error), DaemonConnectionReadError(std::io::Error),
@@ -32,8 +33,9 @@ impl Display for WError {
WError::InvalidConfigDir(s) => format!("{}", s), WError::InvalidConfigDir(s) => format!("{}", s),
WError::ConfigDirInitError(s) => format!("{}", s), WError::ConfigDirInitError(s) => format!("{}", s),
WError::ConfigFileFormatError(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::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::DaemonCommandError(s) => format!("Failed to execute command {}", s),
WError::DaemonCommandExecutionError(output) => format!( WError::DaemonCommandExecutionError(output) => format!(
+2 -1
View File
@@ -1,7 +1,8 @@
mod arp;
mod config; mod config;
mod daemon; mod daemon;
mod error; mod error;
mod ifaddrs; mod netif;
mod mac; mod mac;
mod wolz; mod wolz;
+51 -11
View File
@@ -3,6 +3,9 @@ use std::ffi::{CStr, c_uchar};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::os::raw::{c_char, c_int, c_uint, c_ushort, c_void}; 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)] #[repr(C)]
struct IfAddrs { struct IfAddrs {
ifa_next: *mut IfAddrs, ifa_next: *mut IfAddrs,
@@ -57,12 +60,12 @@ struct LinkAddress {
mac: [u8; 6], mac: [u8; 6],
} }
pub struct NetworkInterface { struct NetworkInterface {
pub name: String, name: String,
pub ip: Ipv4Addr, ip: Ipv4Addr,
pub _netmask: Ipv4Addr, _netmask: Ipv4Addr,
pub broadcast: Ipv4Addr, broadcast: Ipv4Addr,
pub _mac: [u8; 6], _mac: [u8; 6],
} }
impl NetworkInterface { impl NetworkInterface {
@@ -77,15 +80,52 @@ impl NetworkInterface {
} }
} }
pub fn get_network_interfaces() -> Vec<NetworkInterface> { pub struct LocalNetwork {
pub source_ip_hint: Ipv4Addr,
pub subnet_broadcast: Ipv4Addr,
}
pub fn get_local_networks() -> WResult<Vec<LocalNetwork>> {
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<Vec<LocalNetwork>> {
todo!()
}
fn get_network_interfaces() -> WResult<Vec<NetworkInterface>> {
let mut network_addresses = HashMap::new(); let mut network_addresses = HashMap::new();
let mut link_addresses = HashMap::new(); let mut link_addresses = HashMap::new();
let mut addrs: *mut IfAddrs = std::ptr::null_mut(); let mut addrs: *mut IfAddrs = std::ptr::null_mut();
unsafe { unsafe {
if getifaddrs(&mut addrs) != 0 { let stat = getifaddrs(&mut addrs);
eprintln!("getifaddrs failed"); if stat != 0 {
return Vec::new(); return Err(WError::FfiFailed(format!(
"Failed to call libc:getifaddrs, returned {}",
stat
)));
} }
let mut cur = addrs; let mut cur = addrs;
@@ -133,7 +173,7 @@ pub fn get_network_interfaces() -> Vec<NetworkInterface> {
}; };
} }
network_interfaces Ok(network_interfaces)
} }
fn calculate_broadcast_address(ip: Ipv4Addr, netmask: Ipv4Addr) -> Ipv4Addr { fn calculate_broadcast_address(ip: Ipv4Addr, netmask: Ipv4Addr) -> Ipv4Addr {
+31 -37
View File
@@ -1,7 +1,11 @@
use std::net::UdpSocket; use std::net::UdpSocket;
use crate::{ use crate::{
config::Config, daemon, error::{WError, WResult}, ifaddrs::{self}, arp,
config::Config,
error::{WError, WResult},
mac::Mac,
netif::{self, LocalNetwork},
}; };
pub struct Wolz { pub struct Wolz {
@@ -38,55 +42,45 @@ impl Wolz {
let mut magic_bytes = [0u8; 102]; let mut magic_bytes = [0u8; 102];
mac.copy_magic_bytes(&mut magic_bytes); mac.copy_magic_bytes(&mut magic_bytes);
let interfaces = ifaddrs::get_network_interfaces(); let networks = netif::get_local_networks()?;
let mut sent_addresses = Vec::new(); if networks.len() == 0 {
for i in &interfaces { println!("Refusing to send WoL packets. Are you connected to any network?");
if sent_addresses.contains(&i.broadcast) { return Ok(());
continue;
} }
// man systemd.net-naming-scheme for n in &networks {
// Only broadcast to local network try_send_magic_bytes(&magic_bytes, &mac, n);
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
);
}
}
}
}
if sent_addresses.len() == 0 {
println!("Did not send to any address, are you connected to local network?");
} }
Ok(()) Ok(())
} }
pub fn shut_down(&self, mac_or_nick: &str) -> WResult<()> { 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!() todo!()
} }
} }
fn try_send_magic_bytes(magic_bytes: &[u8; 102], send_addr: &str, broadcast_addr: &str) -> WResult<()> { fn try_send_magic_bytes(magic_bytes: &[u8; 102], mac: &Mac, local_network: &LocalNetwork) {
let socket = UdpSocket::bind(&send_addr) let send_addr = format!("{}:0", local_network.source_ip_hint);
.map_err(|e| WError::UdpSocketBindError(format!("couldn't bind to socket because {}", e)))?; let broadcast_addr = format!("{}:9", local_network.subnet_broadcast);
socket match send_magic_bytes(&magic_bytes, &send_addr, &broadcast_addr) {
.set_broadcast(true) Ok(()) => {
.map_err(|e| WError::UdpSocketBindError(format!("couldn't set broadcast because {}", e)))?; 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 socket
.send_to(magic_bytes, &broadcast_addr) .send_to(magic_bytes, &broadcast_addr)
.map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?; .map_err(WError::UdpSocketBindError)?;
Ok(()) Ok(())
} }