moblink_rust/
utils.rs

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}