diff --git a/src/killswitch/network.rs b/src/killswitch/network.rs index 5a98b17..28a55e6 100644 --- a/src/killswitch/network.rs +++ b/src/killswitch/network.rs @@ -8,7 +8,7 @@ use crate::cli::verbosity::Verbosity; use crate::killswitch::is_private_ip; use anyhow::{Context, Result, bail}; -use std::net::IpAddr; +use std::net::{IpAddr, ToSocketAddrs}; use std::process::Command; // ============================================================================ @@ -228,18 +228,51 @@ fn detect_peer_from_scutil(verbose: Verbosity) -> Result { let detail = String::from_utf8_lossy(&show_output.stdout); - // Look for "RemoteAddress : " + // Look for "RemoteAddress : [:]" for detail_line in detail.lines() { let trimmed = detail_line.trim(); - if let Some(ip) = trimmed.strip_prefix("RemoteAddress : ") { - let ip = ip.trim(); - if is_valid_vpn_peer(ip) { + if let Some(raw) = trimmed.strip_prefix("RemoteAddress : ") { + let raw = raw.trim(); + // Strip optional port suffix (host:port or [ipv6]:port) + let host = if raw.starts_with('[') { + raw.trim_start_matches('[').split(']').next().unwrap_or(raw) + } else { + raw.splitn(2, ':').next().unwrap_or(raw) + }; + + // Resolve hostname to IP if needed + let resolved = if host.parse::().is_ok() { + host.to_string() + } else { + if verbose.is_debug() { + eprintln!(" Resolving hostname: {host}"); + } + match format!("{host}:0").to_socket_addrs() { + Ok(mut addrs) => match addrs.find(|a| a.is_ipv4()) { + Some(addr) => addr.ip().to_string(), + None => { + if verbose.is_debug() { + eprintln!(" No IPv4 address for: {host}"); + } + continue; + } + }, + Err(e) => { + if verbose.is_debug() { + eprintln!(" DNS resolution failed for {host}: {e}"); + } + continue; + } + } + }; + + if is_valid_vpn_peer(&resolved) { if verbose.is_verbose() { - eprintln!(" Detected VPN peer via scutil: {ip}"); + eprintln!(" Detected VPN peer via scutil: {resolved}"); } - return Ok(ip.to_string()); + return Ok(resolved); } else if verbose.is_debug() { - eprintln!(" Skipping non-public RemoteAddress: {ip}"); + eprintln!(" Skipping non-public RemoteAddress: {resolved}"); } } }