1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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)
}
|