1#![deny(missing_docs)]
2#![forbid(unsafe_code)]
3#![doc = include_str!("../README.md")]
4#![doc(issue_tracker_base_url = "https://github.com/Finomnis/wol-relay/issues")]
5
6use std::{
7 io,
8 net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket},
9};
10
11use wol::MacAddr6;
12
13mod recursion_prevention;
14mod wol_message;
15
16pub struct WolReceiverConfig {
18 addr: SocketAddr,
19}
20
21impl WolReceiverConfig {
22 pub fn new() -> Self {
24 Self {
25 addr: SocketAddr::from((Ipv4Addr::UNSPECIFIED, 9)),
26 }
27 }
28
29 pub fn with_ip(mut self, ip: IpAddr) -> Self {
38 self.addr.set_ip(ip);
39 self
40 }
41
42 pub fn with_port(mut self, port: u16) -> Self {
51 self.addr.set_port(port);
52 self
53 }
54
55 pub fn bind(self) -> io::Result<WolReceiver> {
61 Ok(WolReceiver {
62 socket: UdpSocket::bind(self.addr)?,
63 })
64 }
65}
66
67impl Default for WolReceiverConfig {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73pub struct WolReceiver {
81 socket: UdpSocket,
82}
83
84impl WolReceiver {
85 pub fn relay_to(&mut self, host: &str, port: u16) -> io::Result<()> {
93 for target_mac in self {
94 let target_mac = target_mac?;
95 log::info!("Relaying WoL packet for '{target_mac}'");
96
97 let target_addrs = match (host, port).to_socket_addrs() {
98 Ok(addr) => addr,
99 Err(e) => {
100 log::error!("Unable to resolve '{host}:{port}': {e}");
101 continue;
102 }
103 };
104
105 for target_addr in target_addrs {
106 log::debug!("Sending WoL packet for '{target_mac}' to '{target_addr}'");
107 if let Err(e) = wol::send_magic_packet(target_mac, None, target_addr) {
108 log::error!("Failed to send WoL packet to '{target_addr}': {e}");
109 }
110 }
111 }
112
113 Ok(())
114 }
115
116 pub fn local_addr(&self) -> io::Result<SocketAddr> {
120 self.socket.local_addr()
121 }
122}
123
124impl Iterator for WolReceiver {
125 type Item = io::Result<MacAddr6>;
126
127 fn next(&mut self) -> Option<Self::Item> {
128 loop {
129 let mut buf = vec![0u8; wol_message::WOL_MAX_SIZE];
130 match self.socket.recv_from(&mut buf) {
131 Ok((size, addr)) => {
132 if !recursion_prevention::is_our_ip(addr) {
133 if let Some(buf) = buf.get(..size) {
134 if let Some(target_mac) = wol_message::parse_wol_message(buf) {
135 log::debug!(
136 "Received WoL from {} to wake {}",
137 addr.ip(),
138 target_mac
139 );
140 return Some(Ok(target_mac));
141 } else {
142 log::debug!("Received non-WoL message: {buf:x?}");
143 }
144 }
145 } else {
146 log::debug!("Detected recursion, skipping packet ...");
147 }
148 }
149 Err(e) => {
150 return Some(Err(e));
151 }
152 }
153 }
154 }
155}