1use std::collections::HashMap;
2use std::net::Ipv4Addr;
3use std::path::PathBuf;
4use std::sync::{Arc, Weak};
5use std::time::Duration;
6
7use log::{error, info};
8use mdns_sd::{ServiceDaemon, ServiceEvent};
9use network_interface::{NetworkInterface, NetworkInterfaceConfig};
10use regex::Regex;
11use serde::{Deserialize, Serialize};
12use tokio::fs::File;
13use tokio::io::{AsyncReadExt, AsyncWriteExt};
14use tokio::sync::Mutex;
15use tokio::task::JoinHandle;
16use url::Url;
17use uuid::Uuid;
18
19use crate::MDNS_SERVICE_TYPE;
20use crate::relay::{GetStatusClosure, Relay, Status};
21use crate::utils::{any_address_belongs_to_this_machine, get_first_ipv4_address};
22
23#[derive(Serialize, Deserialize, Default)]
24struct DatabaseContent {
25 relay_ids: HashMap<String, Uuid>,
26}
27
28struct Database {
29 path: PathBuf,
30 content: DatabaseContent,
31}
32
33impl Database {
34 async fn new(path: PathBuf) -> Self {
35 let content = Self::load(&path).await;
36 Self { path, content }
37 }
38
39 async fn load(path: &PathBuf) -> DatabaseContent {
40 let mut content = "".to_string();
41 if let Ok(mut file) = File::open(path).await {
42 let mut buffer = vec![];
43 if file.read_to_end(&mut buffer).await.is_ok() {
44 content = String::from_utf8(buffer).unwrap_or_default();
45 }
46 }
47 serde_json::from_str(&content).unwrap_or_default()
48 }
49
50 async fn store(&self) {
51 let content = serde_json::to_string(&self.content).unwrap_or_default();
52 if let Ok(mut file) = File::create(&self.path).await {
53 file.write_all(content.as_bytes()).await.ok();
54 }
55 }
56
57 async fn get_relay_id(&mut self, name: &str) -> Uuid {
58 if !self.content.relay_ids.contains_key(name) {
59 self.content
60 .relay_ids
61 .insert(name.to_string(), Uuid::new_v4());
62 self.store().await;
63 }
64 *self.content.relay_ids.get(name).unwrap()
65 }
66}
67
68struct ServiceRelay {
69 interface_name: String,
70 interface_address: Ipv4Addr,
71 streamer_name: String,
72 streamer_url: String,
73 relay: Relay,
74}
75
76impl ServiceRelay {
77 async fn new(
78 interface_name: String,
79 interface_address: Ipv4Addr,
80 relay_name: String,
81 streamer: Streamer,
82 password: String,
83 get_status: Option<GetStatusClosure>,
84 database: Arc<Mutex<Database>>,
85 ) -> Self {
86 let relay = Relay::new();
87 relay.set_bind_address(interface_address.to_string()).await;
88 relay
89 .setup(
90 streamer.url.clone(),
91 password,
92 database.lock().await.get_relay_id(&interface_name).await,
93 relay_name,
94 |_| {},
95 get_status,
96 )
97 .await;
98 relay.start().await;
99 Self {
100 interface_name,
101 interface_address,
102 streamer_name: streamer.name,
103 streamer_url: streamer.url,
104 relay,
105 }
106 }
107}
108
109#[derive(Clone)]
110struct Streamer {
111 name: String,
112 url: String,
113}
114
115#[derive(Serialize)]
116struct RuntimeStreamerStatus {
117 name: String,
118 url: String,
119 host: String,
120}
121
122#[derive(Serialize)]
123struct RuntimeRelayStatus {
124 interface_name: String,
125 interface_address: String,
126 streamer_name: String,
127 streamer_url: String,
128 streamer_host: String,
129}
130
131#[derive(Serialize)]
132struct RuntimeStatus {
133 connected: bool,
134 manual_streamer: bool,
135 streamers: Vec<RuntimeStreamerStatus>,
136 relays: Vec<RuntimeRelayStatus>,
137}
138
139fn parse_interface_name_overrides(values: Vec<String>) -> HashMap<String, String> {
142 let mut overrides = HashMap::new();
143
144 for value in values {
145 let Some((interface, label)) = value.split_once('=') else {
146 continue;
147 };
148 let interface = interface.trim();
149 let label = label.trim();
150
151 if !interface.is_empty() && !label.is_empty() {
152 overrides.insert(interface.to_string(), label.to_string());
153 }
154 }
155
156 overrides
157}
158
159fn host_from_url(url: &str) -> String {
160 Url::parse(url)
161 .ok()
162 .and_then(|parsed| parsed.host_str().map(|host| host.to_string()))
163 .unwrap_or_default()
164}
165
166fn parse_manual_streamers(values: Vec<String>) -> Vec<Streamer> {
169 values
170 .into_iter()
171 .filter_map(|value| match Url::parse(&value) {
172 Ok(url) => Some(Streamer {
173 name: url
174 .host_str()
175 .map(|host| host.to_string())
176 .unwrap_or_else(|| value.clone()),
177 url: value,
178 }),
179 Err(error) => {
180 error!("Invalid manual streamer URL {}: {}", value, error);
181 None
182 }
183 })
184 .collect()
185}
186
187struct NetworkInterfaceFilter {
188 patterns_to_allow: Option<Regex>,
189 patterns_to_ignore: Option<Regex>,
190}
191
192impl NetworkInterfaceFilter {
193 fn new(patterns_to_allow: Vec<String>, patterns_to_ignore: Vec<String>) -> Self {
194 Self {
195 patterns_to_allow: Self::compile(patterns_to_allow),
196 patterns_to_ignore: Self::compile(patterns_to_ignore),
197 }
198 }
199
200 fn filter(&self, interfaces: &mut Vec<NetworkInterface>) {
201 if let Some(patterns_to_allow) = &self.patterns_to_allow {
202 interfaces.retain(|interface| patterns_to_allow.is_match(&interface.name));
203 }
204 if let Some(patterns_to_ignore) = &self.patterns_to_ignore {
205 interfaces.retain(|interface| !patterns_to_ignore.is_match(&interface.name));
206 }
207 }
208
209 fn compile(patterns: Vec<String>) -> Option<Regex> {
210 if !patterns.is_empty() {
211 let pattern = format!("^{}$", patterns.join("|"));
212 match Regex::new(&pattern) {
213 Ok(regex) => return Some(regex),
214 Err(error) => {
215 error!("Failed to compile regex {} with error: {}", pattern, error);
216 }
217 }
218 }
219 None
220 }
221}
222
223struct RelayServiceInner {
224 me: Weak<Mutex<Self>>,
225 password: String,
226 network_interface_filter: NetworkInterfaceFilter,
227 manual_streamers: Vec<Streamer>,
228 interface_name_overrides: HashMap<String, String>,
229 runtime_status_file: Option<PathBuf>,
230 get_status: Option<GetStatusClosure>,
231 status: Status,
232 relays: Vec<ServiceRelay>,
233 network_interfaces: Vec<NetworkInterface>,
234 streamers: Vec<Streamer>,
235 network_interface_monitor: Option<JoinHandle<()>>,
236 streamers_monitor: Option<JoinHandle<()>>,
237 get_status_updater: Option<JoinHandle<()>>,
238 database: Arc<Mutex<Database>>,
239}
240
241impl RelayServiceInner {
242 async fn new(
243 config: RelayServiceConfig,
244 get_status: Option<GetStatusClosure>,
245 ) -> Arc<Mutex<Self>> {
246 let database = Arc::new(Mutex::new(Database::new(config.database).await));
247 Arc::new_cyclic(|me| {
248 Mutex::new(Self {
249 me: me.clone(),
250 password: config.password,
251 network_interface_filter: NetworkInterfaceFilter::new(
252 config.network_interfaces_to_allow,
253 config.network_interfaces_to_ignore,
254 ),
255 manual_streamers: parse_manual_streamers(config.streamer_urls),
256 interface_name_overrides: parse_interface_name_overrides(
257 config.interface_name_overrides,
258 ),
259 runtime_status_file: config.runtime_status_file,
260 get_status,
261 status: Default::default(),
262 relays: Vec::new(),
263 network_interfaces: Vec::new(),
264 streamers: Vec::new(),
265 network_interface_monitor: None,
266 streamers_monitor: None,
267 get_status_updater: None,
268 database,
269 })
270 })
271 }
272
273 async fn start(&mut self) {
274 self.start_network_interfaces_monitor();
275 if self.manual_streamers.is_empty() {
276 self.start_streamers_monitor();
277 } else {
278 self.streamers = self.manual_streamers.clone();
279 self.updated().await;
280 info!(
281 "Using {} manually configured streamer URL(s)",
282 self.streamers.len()
283 );
284 }
285 self.start_get_status_updater();
286 self.write_runtime_status().await;
287 }
288
289 async fn stop(&mut self) {
290 if let Some(network_interface_monitor) = self.network_interface_monitor.take() {
291 network_interface_monitor.abort();
292 network_interface_monitor.await.ok();
293 }
294 if let Some(streamers_finder) = self.streamers_monitor.take() {
295 streamers_finder.abort();
296 streamers_finder.await.ok();
297 }
298 }
299
300 fn start_network_interfaces_monitor(&mut self) {
301 let relay_service = self.me.clone();
302 self.network_interface_monitor = Some(tokio::spawn(async move {
303 while let Ok(interfaces) = NetworkInterface::show() {
304 let Some(relay_service) = relay_service.upgrade() else {
305 break;
306 };
307 {
308 let mut relay_service = relay_service.lock().await;
309 relay_service.update_network_interfaces(interfaces);
310 relay_service.updated().await;
311 }
312 tokio::time::sleep(Duration::from_secs(3)).await;
313 }
314 }));
315 }
316
317 fn update_network_interfaces(&mut self, mut interfaces: Vec<NetworkInterface>) {
318 self.network_interface_filter.filter(&mut interfaces);
319 self.network_interfaces = interfaces;
320 }
321
322 fn start_streamers_monitor(&mut self) {
323 let relay_service = self.me.clone();
324 self.streamers_monitor = Some(tokio::spawn(async move {
325 loop {
326 let Ok(browser) = ServiceDaemon::new() else {
327 return;
328 };
329 let Ok(receiver) = browser.browse(MDNS_SERVICE_TYPE) else {
330 return;
331 };
332 while let Ok(event) = receiver.recv_async().await {
333 if let ServiceEvent::ServiceResolved(info) = event {
334 info!(
335 "mDNS-SD: Found streamer {} {:?} {}",
336 info.get_fullname(),
337 info.get_addresses(),
338 info.get_port()
339 );
340 let Some(name) = info.get_property_val_str("name") else {
341 continue;
342 };
343 let addresses = info.get_addresses_v4();
344 if any_address_belongs_to_this_machine(&addresses) {
345 continue;
346 }
347 let Some(address) = addresses.iter().next().cloned() else {
348 continue;
349 };
350 let Some(relay_service) = relay_service.upgrade() else {
351 break;
352 };
353 {
354 let mut relay_service = relay_service.lock().await;
355 relay_service.add_streamer(name.to_string(), address, info.get_port());
356 relay_service.updated().await;
357 }
358 }
359 }
360 }
361 }));
362 }
363
364 fn start_get_status_updater(&mut self) {
365 let relay_service = self.me.clone();
366 self.get_status_updater = Some(tokio::spawn(async move {
367 loop {
368 let Some(relay_service) = relay_service.upgrade() else {
369 break;
370 };
371 relay_service.lock().await.update_status().await;
372 tokio::time::sleep(Duration::from_secs(5)).await;
373 }
374 }));
375 }
376
377 async fn update_status(&mut self) {
378 self.status = if let Some(get_status) = &self.get_status {
379 get_status().await
380 } else {
381 Status::default()
382 }
383 }
384
385 fn add_streamer(&mut self, name: String, address: Ipv4Addr, port: u16) {
386 let url = format!("ws://{}:{}", address, port);
387 self.streamers.retain(|streamer| streamer.url != url);
388 self.streamers.push(Streamer { name, url });
389 }
390
391 async fn write_runtime_status(&self) {
394 let Some(path) = &self.runtime_status_file else {
395 return;
396 };
397
398 let status = RuntimeStatus {
399 connected: !self.relays.is_empty(),
400 manual_streamer: !self.manual_streamers.is_empty(),
401 streamers: self
402 .streamers
403 .iter()
404 .map(|streamer| RuntimeStreamerStatus {
405 name: streamer.name.clone(),
406 url: streamer.url.clone(),
407 host: host_from_url(&streamer.url),
408 })
409 .collect(),
410 relays: self
411 .relays
412 .iter()
413 .map(|relay| RuntimeRelayStatus {
414 interface_name: relay.interface_name.clone(),
415 interface_address: relay.interface_address.to_string(),
416 streamer_name: relay.streamer_name.clone(),
417 streamer_url: relay.streamer_url.clone(),
418 streamer_host: host_from_url(&relay.streamer_url),
419 })
420 .collect(),
421 };
422
423 let Ok(content) = serde_json::to_string(&status) else {
424 return;
425 };
426
427 if let Some(parent) = path.parent() {
428 tokio::fs::create_dir_all(parent).await.ok();
429 }
430
431 let mut temp_path = path.clone();
437 temp_path.as_mut_os_string().push(".tmp");
438 if let Err(error) = tokio::fs::write(&temp_path, content).await {
439 error!("Failed to write runtime status file: {}", error);
440 return;
441 }
442 if let Err(error) = tokio::fs::rename(&temp_path, path).await {
443 error!("Failed to replace runtime status file: {}", error);
444 tokio::fs::remove_file(&temp_path).await.ok();
445 }
446 }
447
448 async fn updated(&mut self) {
449 let old_number_of_relays = self.relays.len();
450 self.add_relays().await;
451 self.remove_relays().await;
452 let new_number_of_relays = self.relays.len();
453 if new_number_of_relays != old_number_of_relays {
454 info!("Number of relays: {}", new_number_of_relays);
455 }
456 self.write_runtime_status().await;
457 }
458
459 async fn add_relays(&mut self) {
460 for interface in &self.network_interfaces {
461 let Some(interface_address) = get_first_ipv4_address(interface) else {
462 continue;
463 };
464 if interface_address.is_loopback() {
465 continue;
466 }
467 for streamer in &self.streamers {
468 if self.relay_already_added(interface_address, &streamer.url) {
469 continue;
470 }
471 let relay_name = self
472 .interface_name_overrides
473 .get(&interface.name)
474 .cloned()
475 .unwrap_or_else(|| interface.name.clone());
476 info!(
477 "Adding relay called {} with interface address {} for streamer name {} and \
478 URL {}",
479 relay_name, interface_address, streamer.name, streamer.url
480 );
481 self.relays.push(
482 ServiceRelay::new(
483 interface.name.clone(),
484 interface_address,
485 relay_name,
486 streamer.clone(),
487 self.password.clone(),
488 self.create_get_status_closure(),
489 self.database.clone(),
490 )
491 .await,
492 );
493 }
494 }
495 }
496
497 pub fn create_get_status_closure(&self) -> Option<GetStatusClosure> {
498 let relay_service = self.me.clone();
499 Some(Box::new(move || {
500 let relay_service = relay_service.clone();
501 Box::pin(async move {
502 if let Some(relay_service) = relay_service.upgrade() {
503 relay_service.lock().await.status.clone()
504 } else {
505 Status::default()
506 }
507 })
508 }))
509 }
510
511 fn relay_already_added(&self, interface_address: Ipv4Addr, streamer_url: &str) -> bool {
512 self.relays.iter().any(|relay| {
513 relay.interface_address == interface_address && relay.streamer_url == streamer_url
514 })
515 }
516
517 async fn remove_relays(&mut self) {
518 let mut relays_to_keep: Vec<ServiceRelay> = Vec::new();
519 let mut relays_to_remove: Vec<ServiceRelay> = Vec::new();
520 for relay in self.relays.drain(..) {
521 if Self::should_keep_relay(&self.network_interfaces, relay.interface_address) {
522 relays_to_keep.push(relay);
523 } else {
524 relays_to_remove.push(relay);
525 }
526 }
527 self.relays = relays_to_keep;
528 for relay in relays_to_remove {
529 info!(
530 "Removing relay called {} with interface address {} for streamer name {} and URL \
531 {}",
532 relay.interface_name,
533 relay.interface_address,
534 relay.streamer_name,
535 relay.streamer_url
536 );
537 relay.relay.stop().await;
538 }
539 }
540
541 fn should_keep_relay(
542 network_interfaces: &[NetworkInterface],
543 interface_address: Ipv4Addr,
544 ) -> bool {
545 network_interfaces
546 .iter()
547 .any(|interface| get_first_ipv4_address(interface) == Some(interface_address))
548 }
549}
550
551pub struct RelayServiceConfig {
553 pub password: String,
554 pub network_interfaces_to_allow: Vec<String>,
556 pub network_interfaces_to_ignore: Vec<String>,
559 pub streamer_urls: Vec<String>,
561 pub interface_name_overrides: Vec<String>,
563 pub runtime_status_file: Option<PathBuf>,
565 pub database: PathBuf,
567}
568
569pub struct RelayService {
570 inner: Arc<Mutex<RelayServiceInner>>,
571}
572
573impl RelayService {
574 pub async fn new(config: RelayServiceConfig, get_status: Option<GetStatusClosure>) -> Self {
575 Self {
576 inner: RelayServiceInner::new(config, get_status).await,
577 }
578 }
579
580 pub async fn start(&self) {
581 self.inner.lock().await.start().await;
582 }
583
584 pub async fn stop(&self) {
585 self.inner.lock().await.stop().await;
586 }
587}