use core::fmt; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, ops::Deref, str::FromStr, }; use scurvy::{ Argument, Scurvy, formula::{Formula, U16Formula}, }; fn main() { let args = vec![ Argument::new("mac").arg("addr"), Argument::new("host").arg("ip"), Argument::new("port").arg("port"), ]; let scurvy = Scurvy::make(args); if scurvy.should_print_help() { print_help() } else if scurvy.should_print_version() { print_version() } let mac: Mac = scurvy.parse_req("mac", Formula::new("[opt] is not a valid MAC address")); let port = scurvy.parse_or("port", U16Formula::new(), 9); let host: IpAddr = scurvy.parse_or( "ip", Formula::new("[opt] is not a valid IP address"), IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), ); let mut packet = vec![255; 6]; packet.extend(mac.repeat(16)); let socket = UdpSocket::bind("0.0.0.0:0").unwrap(); socket.set_broadcast(true).unwrap(); let addr = SocketAddr::new(host, port); socket.send_to(&packet, addr).unwrap(); println!("Sent magic packet!\nUDP destination {host}\nUDP port {port}\nTo MAC {mac}") } #[derive(Debug)] struct Mac { bytes: [u8; 6], } impl FromStr for Mac { type Err = String; fn from_str(s: &str) -> Result { let mut bytes = [0u8; 6]; for (idx, split) in s.split(':').enumerate() { let Ok(octet) = u8::from_str_radix(split, 16) else { return Err(format!("'{split}' is not a valid byte")); }; if idx < 6 { bytes[idx] = octet; } else { return Err(format!("too many octets in MAC address")); } } return Ok(Self { bytes }); } } impl Deref for Mac { type Target = [u8; 6]; fn deref(&self) -> &Self::Target { &self.bytes } } impl fmt::Display for Mac { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let b = self.bytes; write!( f, "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", b[0], b[1], b[2], b[3], b[4], b[5] ) } } const NAME: &str = env!("CARGO_PKG_NAME"); const VERSION: &str = env!("CARGO_PKG_VERSION"); const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); fn print_help() -> ! { println!("usage: {NAME} [arguments...]\n"); println!("ARGUMENTS"); println!(" mac="); println!(" Required."); println!(" MAC address of the system you want to wake\n"); println!(" host="); println!(" IP address to send the UDP packet containing the magic to."); println!(" this argument defaults to 255.255.255.255 which is the broadcast address"); println!(" of \"this network\""); println!(" [Default 255.255.255.255]\n"); println!(" port="); println!(" Port the UDP packet is sent to."); println!(" [Default 9]\n"); println!(" help= | -h | --help"); println!(" print this message and exit\n"); println!(" version= | -V | --version"); println!(" print the version and authors and exit"); std::process::exit(0) } fn print_version() -> ! { println!("{NAME} version {VERSION}"); println!("written by {AUTHORS}"); std::process::exit(0) }