87 lines
2.9 KiB
Rust
87 lines
2.9 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
|
pub struct ProxyOverrides {
|
|
pub proxy_url: Option<String>,
|
|
pub cert_hash: Option<Vec<u8>>,
|
|
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<u32>,
|
|
pub max_users: Option<u32>,
|
|
pub bandwidth: Option<u32>,
|
|
}
|
|
|
|
#[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<String>,
|
|
}
|
|
|
|
/// 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<ServerStatus> {
|
|
use color_eyre::eyre::{bail, eyre};
|
|
use std::time::Duration;
|
|
use tokio::net::{lookup_host, UdpSocket};
|
|
|
|
let dest = lookup_host(format!("{}:{}", address, port))
|
|
.await?
|
|
.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"),
|
|
}
|
|
}
|