pwlp/pwlp/
server.rs

1use super::program::Program;
2use super::protocol::{Message, MessageType};
3use eui48::MacAddress;
4use serde::{Deserialize, Serialize, Serializer};
5use std::collections::HashMap;
6use std::net::{SocketAddr, UdpSocket};
7use std::sync::{Arc, Mutex};
8use std::time::Instant;
9
10#[derive(Serialize, Deserialize, Debug, Clone)]
11pub struct DeviceConfig {
12	program: Option<String>,
13	secret: Option<String>,
14}
15
16#[derive(Serialize, Debug, Clone)]
17pub struct DeviceStatus {
18	pub address: SocketAddr,
19	pub program: Option<Program>,
20
21	#[serde(skip)]
22	pub secret: String,
23
24	#[serde(skip)]
25	pub last_seen: Instant,
26}
27
28impl Serialize for Program {
29	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
30		serializer.serialize_bytes(&self.code)
31	}
32}
33
34pub struct ServerState {
35	pub config: HashMap<String, DeviceConfig>,
36	pub devices: HashMap<String, DeviceStatus>,
37	pub socket: UdpSocket,
38}
39
40pub struct Server {
41	state: Arc<Mutex<ServerState>>,
42	default_secret: String,
43	default_program: Program,
44}
45
46impl Server {
47	pub fn new(
48		devices: HashMap<String, DeviceConfig>,
49		default_secret: &str,
50		default_program: Program,
51		bind_address: &str,
52	) -> std::io::Result<Server> {
53		Ok(Server {
54			state: Arc::new(Mutex::new(ServerState {
55				config: devices,
56				devices: HashMap::new(),
57				socket: UdpSocket::bind(bind_address)?,
58			})),
59			default_secret: default_secret.to_string(),
60			default_program,
61		})
62	}
63
64	pub fn state(&mut self) -> Arc<Mutex<ServerState>> {
65		self.state.clone()
66	}
67
68	pub fn run(&mut self) -> std::io::Result<()> {
69		let socket = {
70			let m = self.state.lock().unwrap();
71			m.socket.try_clone()?
72		};
73
74		loop {
75			let mut buf = [0; 1500];
76			let (amt, source_address) = socket.recv_from(&mut buf)?;
77
78			match Message::peek_mac_address(&buf[0..amt]) {
79				Err(t) => log::error!("\tError reading MAC address: {:?}", t),
80				Ok(mac) => {
81					// Do we have a config for this mac?
82					let canonical_mac = mac.to_canonical();
83					let device_config: Option<DeviceConfig> = {
84						let m = self.state.lock().unwrap();
85						if m.config.contains_key(&canonical_mac) {
86							Some(m.config[&canonical_mac].clone())
87						} else {
88							None
89						}
90					};
91
92					// Find the secret to use to verify the message signature
93					let secret = match &device_config {
94						Some(d) => match &d.secret {
95							Some(s) => s.clone(),
96							None => self.default_secret.clone(),
97						},
98						None => self.default_secret.clone(),
99					};
100
101					// Decode message
102					match Message::from_buffer(&buf[0..amt], secret.as_bytes()) {
103						Err(t) => log::error!(
104							"{} error {:?} (size={}b source={} secret={:?})",
105							source_address,
106							t,
107							amt,
108							mac,
109							secret
110						),
111						Ok(msg) => {
112							let mac_identifier = mac.to_canonical();
113							log::info!(
114								"{} @ {}: {:?} t={}",
115								&mac_identifier,
116								&source_address,
117								msg.message_type,
118								msg.unix_time
119							);
120
121							// Update or create device status
122							{
123								let mut m = self.state.lock().unwrap();
124								let mut new_status = match m.devices.get(&mac_identifier) {
125									Some(status) => (*status).clone(),
126									None => DeviceStatus {
127										address: source_address,
128										program: None,
129										secret: secret.clone(),
130										last_seen: Instant::now(),
131									},
132								};
133								new_status.last_seen = Instant::now();
134
135								match msg.message_type {
136									MessageType::Ping => {
137										let pong = Message {
138											message_type: MessageType::Pong,
139											unix_time: msg.unix_time,
140											mac_address: MacAddress::nil(),
141											payload: None,
142										};
143
144										// Check deserialize
145										let secret_bytes = secret.as_bytes();
146										assert!(
147											Message::from_buffer(
148												&pong.signed(secret_bytes),
149												secret_bytes
150											)
151											.is_ok(),
152											"deserialize own message"
153										);
154
155										if let Err(t) = socket.send_to(
156											&pong.signed(secret.as_bytes()),
157											source_address,
158										) {
159											println!("Send pong failed: {:?}", t);
160										}
161
162										let device_program = if let Some(p) = new_status.program {
163											p
164										} else if let Some(config) = &device_config {
165											if let Some(path) = &config.program {
166												Program::from_file(&path)
167													.expect("error loading device-specific program")
168											} else {
169												self.default_program.clone()
170											}
171										} else {
172											self.default_program.clone()
173										};
174
175										let run = Message {
176											message_type: MessageType::Run,
177											unix_time: msg.unix_time,
178											mac_address: MacAddress::nil(),
179											payload: Some(device_program.clone().code),
180										};
181
182										new_status.program = Some(device_program);
183
184										if let Err(t) = socket
185											.send_to(&run.signed(secret.as_bytes()), source_address)
186										{
187											println!("Send pong failed: {:?}", t);
188										}
189									}
190									MessageType::Pong => {
191										// Ignore
192									}
193									_ => {}
194								}
195
196								m.devices.insert(mac_identifier, new_status);
197							}
198						}
199					}
200				}
201			}
202		}
203	}
204}