diff options
author | gennyble <gen@nyble.dev> | 2025-10-08 17:55:14 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2025-10-08 17:55:14 -0500 |
commit | 08eb194b5d6ce172c12886fb36b4c0302287e468 (patch) | |
tree | eece49b889e80349cd80ddb44fcdf501fa951b46 | |
download | wolff-08eb194b5d6ce172c12886fb36b4c0302287e468.tar.gz wolff-08eb194b5d6ce172c12886fb36b4c0302287e468.zip |
wolff 0.1.0
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .rustfmt.toml | 1 | ||||
-rw-r--r-- | Cargo.lock | 15 | ||||
-rw-r--r-- | Cargo.toml | 8 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | src/main.rs | 123 |
6 files changed, 151 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..417524c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,15 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "scurvy" +version = "0.2.1" +source = "git+https://git.dreamy.place/scurvy/?tag=0.2.1#d70b7250fcad2a757ac7e805d3e69dd4f6dcbf1f" + +[[package]] +name = "wolff" +version = "0.1.0" +dependencies = [ + "scurvy", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c3bd2f3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "wolff" +version = "0.1.0" +edition = "2024" +authors = ["Genevieve Raine <gen@nyble.dev>"] + +[dependencies] +scurvy = { git = "https://git.dreamy.place/scurvy/", tag = "0.2.1" } diff --git a/README.md b/README.md new file mode 100644 index 0000000..857f90b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +**wolff** + +send Wake-on-LAN packets. diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..65634a6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,123 @@ +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<Self, Self::Err> { + 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=<addr>"); + println!(" Required."); + println!(" MAC address of the system you want to wake\n"); + println!(" host=<ip>"); + 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=<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) +} |