1use std::collections::HashSet;
2use std::net::Ipv4Addr;
3use std::time::Duration;
4
5use log::{error, info, warn};
6use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig};
7use rand::distr::{Alphanumeric, SampleString};
8use tokio::net::lookup_host;
9use tokio::process::Command;
10
11pub const MDNS_SERVICE_TYPE: &str = "_moblink._tcp.local.";
12
13pub type AnyError = Box<dyn std::error::Error + Send + Sync>;
14
15pub fn random_string() -> String {
16 Alphanumeric.sample_string(&mut rand::rng(), 64)
17}
18
19pub async fn execute_command(executable: &str, args: &[&str]) {
20 let command = format_command(executable, args);
21 match Command::new(executable).args(args).status().await {
22 Ok(status) => {
23 if status.success() {
24 info!("Command '{}' succeeded!", command);
25 } else {
26 warn!("Command '{}' failed with status {}", command, status);
27 }
28 }
29 Err(error) => {
30 error!("Command '{}' failed with error: {}", command, error);
31 }
32 }
33}
34
35pub fn format_command(executable: &str, args: &[&str]) -> String {
36 format!("{} {}", executable, args.join(" "))
37}
38
39pub async fn resolve_host(address: &str) -> Result<String, AnyError> {
40 for _ in 0..50 {
41 match lookup_host(format!("{}:9999", address)).await {
42 Ok(mut addresses) => {
43 if let Some(address) = addresses.next() {
44 return Ok(address.ip().to_string());
45 } else {
46 warn!("No address found for {}", address);
47 }
48 }
49 Err(error) => {
50 warn!("DNS lookup for '{}' failed with error {}", address, error);
51 }
52 }
53 tokio::time::sleep(Duration::from_secs(2)).await;
54 }
55 Err("DNS lookup failed after timeout".into())
56}
57
58pub fn get_first_ipv4_address(interface: &NetworkInterface) -> Option<Ipv4Addr> {
59 for address in &interface.addr {
60 if let Addr::V4(address) = address {
61 return Some(address.ip);
62 }
63 }
64 None
65}
66
67pub fn any_address_belongs_to_this_machine(addresses: &HashSet<&Ipv4Addr>) -> bool {
68 let Ok(interfaces) = NetworkInterface::show() else {
69 return true;
70 };
71 for interface in interfaces {
72 for addr in interface.addr {
73 if let Addr::V4(addr) = addr {
74 if addresses.contains(&addr.ip) {
75 return true;
76 }
77 }
78 }
79 }
80 false
81}