use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct ProxyOverrides { pub proxy_url: Option, pub cert_hash: Option>, pub any_server: bool, } #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct ServerStatus { #[serde(default)] pub success: bool, pub version: Option<(u32, u32, u32)>, pub users: Option, pub max_users: Option, pub bandwidth: Option, } #[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)] pub struct ServerEntry { pub name: String, pub address: String, pub port: u16, pub username: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub password: Option, } /// Mumble UDP ping protocol. /// /// Send a 12-byte packet: 4 zero bytes + 8-byte identifier. /// Receive a 24-byte response: 4 bytes version + 8 bytes identifier echo /// + 4 bytes current_users + 4 bytes max_users + 4 bytes bandwidth. #[cfg(feature = "networking")] pub async fn ping_server(address: &str, port: u16) -> color_eyre::Result { use color_eyre::eyre::{bail, eyre}; use std::net::ToSocketAddrs; use std::time::Duration; use tokio::net::UdpSocket; let dest = format!("{}:{}", address, port) .to_socket_addrs()? .next() .ok_or_else(|| eyre!("could not resolve address"))?; let bind_addr = if dest.is_ipv6() { "[::]:0" } else { "0.0.0.0:0" }; let socket = UdpSocket::bind(bind_addr).await?; socket.connect(dest).await?; let request_id: u64 = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() .as_nanos() as u64; let mut buf = [0u8; 12]; buf[4..12].copy_from_slice(&request_id.to_be_bytes()); socket.send(&buf).await?; let mut response = [0u8; 24]; let timeout = tokio::time::timeout(Duration::from_secs(2), socket.recv(&mut response)).await; match timeout { Ok(Ok(len)) if len >= 24 => { let version_major = response[0] as u32; let version_minor = response[1] as u32; let version_patch = response[2] as u32; let users = u32::from_be_bytes([response[12], response[13], response[14], response[15]]); let max_users = u32::from_be_bytes([response[16], response[17], response[18], response[19]]); let bandwidth = u32::from_be_bytes([response[20], response[21], response[22], response[23]]); Ok(ServerStatus { success: true, version: Some((version_major, version_minor, version_patch)), users: Some(users), max_users: Some(max_users), bandwidth: Some(bandwidth), }) } Ok(Ok(_)) => bail!("ping response too short"), Ok(Err(e)) => Err(e.into()), Err(_) => bail!("ping timed out"), } }