diff --git a/desktop-client/src-tauri/src/tunnel_manager.rs b/desktop-client/src-tauri/src/tunnel_manager.rs index 90ca97b..8dde6af 100644 --- a/desktop-client/src-tauri/src/tunnel_manager.rs +++ b/desktop-client/src-tauri/src/tunnel_manager.rs @@ -61,13 +61,6 @@ pub fn is_active(app: &AppHandle, profile_path: &Path) -> Result { } pub fn metrics(app: &AppHandle, profile_path: &Path) -> Result { - #[cfg(target_os = "windows")] - { - if let Ok(metrics) = direct_windows_metrics(profile_path) { - return Ok(metrics); - } - } - let backend = bundled_backend(app)?; let mut command = Command::new(backend); command.arg("metrics").arg("--profile").arg(profile_path); @@ -93,157 +86,6 @@ pub struct TunnelMetrics { pub tx_bytes: u64, } -#[cfg(target_os = "windows")] -fn direct_windows_metrics(profile_path: &Path) -> Result { - let tunnel_name = profile_path - .file_stem() - .and_then(|value| value.to_str()) - .ok_or_else(|| "invalid profile filename".to_string())?; - let service_name = format!("WireGuardTunnel${}", tunnel_name); - - let mut status_cmd = Command::new("sc"); - status_cmd.arg("query").arg(&service_name).creation_flags(CREATE_NO_WINDOW); - let status_output = status_cmd - .output() - .map_err(|err| format!("Unable to query tunnel status: {err}"))?; - - if !status_output.status.success() { - return Ok(TunnelMetrics { - active: false, - rx_bytes: 0, - tx_bytes: 0, - }); - } - - let status_stdout = String::from_utf8_lossy(&status_output.stdout); - if !status_stdout.contains("RUNNING") { - return Ok(TunnelMetrics { - active: false, - rx_bytes: 0, - tx_bytes: 0, - }); - } - - let wg = find_windows_wg()?; - let mut wg_cmd = Command::new(wg); - wg_cmd - .arg("show") - .arg(tunnel_name) - .arg("dump") - .creation_flags(CREATE_NO_WINDOW); - let wg_output = wg_cmd - .output() - .map_err(|err| format!("Unable to query WireGuard counters: {err}"))?; - - if !wg_output.status.success() { - return read_windows_metrics_from_show(tunnel_name); - } - - let stdout = String::from_utf8_lossy(&wg_output.stdout); - let mut rx_bytes = 0_u64; - let mut tx_bytes = 0_u64; - - for line in stdout.lines().skip(1) { - let fields: Vec<&str> = line.split('\t').collect(); - if fields.len() < 7 { - continue; - } - rx_bytes = rx_bytes.saturating_add(fields[5].parse::().unwrap_or(0)); - tx_bytes = tx_bytes.saturating_add(fields[6].parse::().unwrap_or(0)); - } - - Ok(TunnelMetrics { - active: true, - rx_bytes, - tx_bytes, - }) -} - -#[cfg(target_os = "windows")] -fn read_windows_metrics_from_show(tunnel_name: &str) -> Result { - let wg = find_windows_wg()?; - let mut command = Command::new(wg); - command - .arg("show") - .arg(tunnel_name) - .creation_flags(CREATE_NO_WINDOW); - let output = command - .output() - .map_err(|err| format!("Unable to query WireGuard transfer text: {err}"))?; - - if !output.status.success() { - return Ok(TunnelMetrics { - active: true, - rx_bytes: 0, - tx_bytes: 0, - }); - } - - let stdout = String::from_utf8_lossy(&output.stdout); - let mut rx_bytes = 0_u64; - let mut tx_bytes = 0_u64; - - for line in stdout.lines() { - let trimmed = line.trim(); - if let Some(rest) = trimmed.strip_prefix("transfer:") { - let parts: Vec<&str> = rest.split(',').collect(); - if let Some(received) = parts.first() { - rx_bytes = parse_human_wireguard_bytes(received); - } - if let Some(sent) = parts.get(1) { - tx_bytes = parse_human_wireguard_bytes(sent); - } - } - } - - Ok(TunnelMetrics { - active: true, - rx_bytes, - tx_bytes, - }) -} - -#[cfg(target_os = "windows")] -fn parse_human_wireguard_bytes(value: &str) -> u64 { - let cleaned = value - .replace("received", "") - .replace("sent", "") - .trim() - .to_string(); - let mut parts = cleaned.split_whitespace(); - let amount = parts - .next() - .map(|raw| raw.replace(',', ".")) - .and_then(|raw| raw.parse::().ok()) - .unwrap_or(0.0); - let unit = parts.next().unwrap_or("B"); - - let multiplier = match unit { - "B" => 1.0, - "KiB" => 1024.0, - "MiB" => 1024.0 * 1024.0, - "GiB" => 1024.0 * 1024.0 * 1024.0, - "TiB" => 1024.0 * 1024.0 * 1024.0 * 1024.0, - _ => 1.0, - }; - - (amount * multiplier) as u64 -} - -#[cfg(target_os = "windows")] -fn find_windows_wg() -> Result { - let candidates = [ - PathBuf::from("wg"), - PathBuf::from(r"C:\Program Files\WireGuard\wg.exe"), - PathBuf::from(r"C:\Program Files (x86)\WireGuard\wg.exe"), - ]; - - candidates - .into_iter() - .find(|path| path.exists()) - .ok_or_else(|| "required Windows WireGuard CLI is not available".to_string()) -} - fn bundled_backend(app: &AppHandle) -> Result { let resource_dir = app .path() diff --git a/desktop-client/tunnel-helper/src/main.rs b/desktop-client/tunnel-helper/src/main.rs index 99baff2..d180f0a 100644 --- a/desktop-client/tunnel-helper/src/main.rs +++ b/desktop-client/tunnel-helper/src/main.rs @@ -735,15 +735,15 @@ fn read_transfer_totals(profile: &Path) -> Result<(u64, u64), String> { #[cfg(target_os = "windows")] fn find_wg_cli() -> Result { let candidates = [ - PathBuf::from("wg"), PathBuf::from(r"C:\Program Files\WireGuard\wg.exe"), PathBuf::from(r"C:\Program Files (x86)\WireGuard\wg.exe"), ]; - candidates - .into_iter() - .find(|path| path.exists()) - .ok_or_else(|| "required Windows WireGuard CLI is not available".to_string()) + if let Some(path) = candidates.into_iter().find(|path| path.exists()) { + return Ok(path); + } + + Ok(PathBuf::from("wg.exe")) } #[cfg(target_os = "macos")]