[WIP] A simple wake-on-lan service
1use std::{fmt::Display, net::UdpSocket, num::ParseIntError, str::FromStr};
2
3use thiserror::Error;
4
5#[derive(Clone, Debug)]
6pub struct MacAddress([u8; 6]);
7
8#[derive(Error, Debug)]
9pub enum MacAddressParseError {
10 #[error("Integer error: {}", .0)]
11 ParseInt(#[from] ParseIntError),
12 #[error("Mac address to short")]
13 TooShort,
14}
15
16impl MacAddress {
17 pub fn packet(&self) -> [u8; 102] {
18 let mut magic = [0xffu8; 17 * 6];
19 let mut head: *mut u8 = &mut magic[0];
20 let mac: *const u8 = &self.0[0];
21 unsafe {
22 for _ in 0..16 {
23 head = head.offset(6);
24 head.copy_from_nonoverlapping(mac, 6);
25 }
26 };
27
28 magic
29 }
30
31 pub async fn wake(&self) -> Result<(), std::io::Error> {
32 let packet = self.packet();
33 let socket = UdpSocket::bind("0.0.0.0:0")?;
34 socket.set_broadcast(true)?;
35 socket.send_to(&packet, "255.255.255.255:9")?;
36
37 Ok(())
38 }
39}
40
41impl Display for MacAddress {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(
44 f,
45 "{:0<2X}:{:0<2X}:{:0<2X}:{:0<2X}:{:0<2X}:{:0<2X}",
46 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
47 )
48 }
49}
50
51impl From<MacAddress> for [u8; 6] {
52 fn from(value: MacAddress) -> Self {
53 value.0
54 }
55}
56
57impl FromStr for MacAddress {
58 type Err = MacAddressParseError;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 let address = s.split(":");
62 let address = address
63 .map(|x| u8::from_str_radix(x, 16))
64 .collect::<Result<Vec<_>, ParseIntError>>()?;
65 if address.len() != 6 {
66 return Err(MacAddressParseError::TooShort);
67 }
68 Ok(MacAddress([
69 address[0], address[1], address[2], address[3], address[4], address[5],
70 ]))
71 }
72}