Skip to content

Commit 8df1cb1

Browse files
Chris Mazanecclaude
andcommitted
fix: resolve hostname and strip port in WireGuard peer detection
When a WireGuard endpoint uses a hostname with a custom port (e.g. myvpn.example.com:51820), scutil reports the RemoteAddress in that form. The previous code attempted to parse it directly as an IpAddr, which fails both because of the port suffix and because hostnames require DNS resolution. Strip the port suffix (handling host:port and [ipv6]:port), then resolve the hostname to an IPv4 address before passing it to is_valid_vpn_peer. Fixes #35 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent da86b2d commit 8df1cb1

1 file changed

Lines changed: 41 additions & 8 deletions

File tree

src/killswitch/network.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use crate::cli::verbosity::Verbosity;
99
use crate::killswitch::is_private_ip;
1010
use anyhow::{Context, Result, bail};
11-
use std::net::IpAddr;
11+
use std::net::{IpAddr, ToSocketAddrs};
1212
use std::process::Command;
1313

1414
// ============================================================================
@@ -228,18 +228,51 @@ fn detect_peer_from_scutil(verbose: Verbosity) -> Result<String> {
228228

229229
let detail = String::from_utf8_lossy(&show_output.stdout);
230230

231-
// Look for "RemoteAddress : <ip>"
231+
// Look for "RemoteAddress : <host>[:<port>]"
232232
for detail_line in detail.lines() {
233233
let trimmed = detail_line.trim();
234-
if let Some(ip) = trimmed.strip_prefix("RemoteAddress : ") {
235-
let ip = ip.trim();
236-
if is_valid_vpn_peer(ip) {
234+
if let Some(raw) = trimmed.strip_prefix("RemoteAddress : ") {
235+
let raw = raw.trim();
236+
// Strip optional port suffix (host:port or [ipv6]:port)
237+
let host = if raw.starts_with('[') {
238+
raw.trim_start_matches('[').split(']').next().unwrap_or(raw)
239+
} else {
240+
raw.splitn(2, ':').next().unwrap_or(raw)
241+
};
242+
243+
// Resolve hostname to IP if needed
244+
let resolved = if host.parse::<IpAddr>().is_ok() {
245+
host.to_string()
246+
} else {
247+
if verbose.is_debug() {
248+
eprintln!(" Resolving hostname: {host}");
249+
}
250+
match format!("{host}:0").to_socket_addrs() {
251+
Ok(mut addrs) => match addrs.find(|a| a.is_ipv4()) {
252+
Some(addr) => addr.ip().to_string(),
253+
None => {
254+
if verbose.is_debug() {
255+
eprintln!(" No IPv4 address for: {host}");
256+
}
257+
continue;
258+
}
259+
},
260+
Err(e) => {
261+
if verbose.is_debug() {
262+
eprintln!(" DNS resolution failed for {host}: {e}");
263+
}
264+
continue;
265+
}
266+
}
267+
};
268+
269+
if is_valid_vpn_peer(&resolved) {
237270
if verbose.is_verbose() {
238-
eprintln!(" Detected VPN peer via scutil: {ip}");
271+
eprintln!(" Detected VPN peer via scutil: {resolved}");
239272
}
240-
return Ok(ip.to_string());
273+
return Ok(resolved);
241274
} else if verbose.is_debug() {
242-
eprintln!(" Skipping non-public RemoteAddress: {ip}");
275+
eprintln!(" Skipping non-public RemoteAddress: {resolved}");
243276
}
244277
}
245278
}

0 commit comments

Comments
 (0)