diff options
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | talpid-routing/src/unix/macos/interface.rs | 100 |
2 files changed, 55 insertions, 48 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fed86ec51..e3a0b0b2d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ Line wrap the file at 100 chars. Th - Fix node native module being unpacked to a temporary folder. - Fix BSOD caused by routing loop in wireguard-nt. +#### macOS +- Fix bug in parsing of network services from SCDynamicStore. + ## [2025.5] - 2025-03-26 This release is identical to 2025.5-beta1 diff --git a/talpid-routing/src/unix/macos/interface.rs b/talpid-routing/src/unix/macos/interface.rs index 16f085c969..f23870945f 100644 --- a/talpid-routing/src/unix/macos/interface.rs +++ b/talpid-routing/src/unix/macos/interface.rs @@ -228,26 +228,18 @@ impl PrimaryInterfaceMonitor { .get(key) .and_then(|v| v.downcast_into::<CFDictionary>())?; - let name = get_dict_elem_as_string( + let service_id = get_dict_elem_as_string( &global_dict, - schema_definition!(kSCDynamicStorePropNetPrimaryInterface), + schema_definition!(kSCDynamicStorePropNetPrimaryService), ) .or_else(|| { - log::debug!("Missing name for primary interface ({family})"); + log::debug!("Missing service ID for primary interface ({family})"); None })?; - let router_ip = get_service_router_ip(&global_dict, family).or_else(|| { - log::debug!("Missing router IP for primary interface ({name}, {family})"); - None - })?; - let first_ip = get_service_first_ip(&global_dict, family).or_else(|| { - log::debug!("Missing IP for primary interface ({name}, {family})"); + + self.get_network_service(&service_id, family).or_else(|| { + log::debug!("Invalid service ID for primary interface ({family})"); None - })?; - Some(NetworkServiceDetails { - name, - router_ip, - first_ip, }) } @@ -255,43 +247,45 @@ impl PrimaryInterfaceMonitor { SCNetworkSet::new(&self.prefs) .service_order() .iter() - .filter_map(|service_id| { - let service_id_s = service_id.to_string(); - let service_key = if family == Family::V4 { - format!("State:/Network/Service/{service_id_s}/IPv4") - } else { - format!("State:/Network/Service/{service_id_s}/IPv6") - }; - let service_dict = self - .store - .get(CFString::new(&service_key)) - .and_then(|v| v.downcast_into::<CFDictionary>())?; - - let name = get_dict_elem_as_string( - &service_dict, - schema_definition!(kSCPropInterfaceName), - ) - .or_else(|| { - log::debug!("Missing name for service {service_key} ({family})"); - None - })?; - let router_ip = get_service_router_ip(&service_dict, family).or_else(|| { - log::debug!("Missing router IP for {service_key} ({name}, {family})"); - None - })?; - let first_ip = get_service_first_ip(&service_dict, family).or_else(|| { - log::debug!("Missing IP for \"{service_key}\" ({name}, {family})"); - None - })?; - Some(NetworkServiceDetails { - name, - router_ip, - first_ip, - }) - }) + .filter_map(|service_id| self.get_network_service(&service_id.to_string(), family)) .collect::<Vec<_>>() } + /// Get details about a specific network interface. + /// + /// Will return `None` and log a message on any error. + fn get_network_service( + &self, + service_id: &str, + family: Family, + ) -> Option<NetworkServiceDetails> { + let service_key = network_service_key(service_id.to_string(), family); + let service_dict = self + .store + .get(CFString::new(&service_key)) + .and_then(|v| v.downcast_into::<CFDictionary>())?; + + let name = get_dict_elem_as_string(&service_dict, schema_definition!(kSCPropInterfaceName)) + .or_else(|| { + log::debug!("Missing name for service {service_key} ({family})"); + None + })?; + let router_ip = get_service_router_ip(&service_dict, family).or_else(|| { + log::debug!("Missing router IP for {service_key} ({name}, {family})"); + None + })?; + let first_ip = get_service_first_ip(&service_dict, family).or_else(|| { + log::debug!("Missing IP for \"{service_key}\" ({name}, {family})"); + None + })?; + + Some(NetworkServiceDetails { + name, + router_ip, + first_ip, + }) + } + pub fn debug(&self) { for family in [Family::V4, Family::V6] { log::debug!( @@ -306,6 +300,16 @@ impl PrimaryInterfaceMonitor { } } +/// Construct the string key for a network service from its ID. +fn network_service_key(service_id: String, family: Family) -> String { + let family = match family { + Family::V4 => "IPv4", + Family::V6 => "IPv6", + }; + + format!("State:/Network/Service/{service_id}/{family}") +} + /// Return a map from interface name to link addresses (AF_LINK) pub fn get_interface_link_addresses() -> io::Result<BTreeMap<String, SockaddrStorage>> { let mut gateway_link_addrs = BTreeMap::new(); |
