diff --git a/system-configuration/Cargo.toml b/system-configuration/Cargo.toml index 1b4df69..c34971c 100644 --- a/system-configuration/Cargo.toml +++ b/system-configuration/Cargo.toml @@ -12,6 +12,6 @@ edition = "2021" rust-version = "1.64.0" [dependencies] -core-foundation = "0.9" +core-foundation = "0.10" system-configuration-sys = { path = "../system-configuration-sys", version = "0.6" } bitflags = "2" diff --git a/system-configuration/src/dynamic_store.rs b/system-configuration/src/dynamic_store.rs index ee08881..78cc4fa 100644 --- a/system-configuration/src/dynamic_store.rs +++ b/system-configuration/src/dynamic_store.rs @@ -103,7 +103,7 @@ impl SCDynamicStoreBuilder { /// Create the dynamic store session. pub fn build(mut self) -> Option { - let store_options = self.create_store_options(); + let store_options = self.create_store_options()?; if let Some(callback_context) = self.callback_context.take() { SCDynamicStore::create( &self.name, @@ -116,11 +116,12 @@ impl SCDynamicStoreBuilder { } } - fn create_store_options(&self) -> CFDictionary { - let key = unsafe { CFString::wrap_under_create_rule(kSCDynamicStoreUseSessionKeys) }; + fn create_store_options(&self) -> Option { + let key = + unsafe { CFString::try_wrap_under_create_rule(kSCDynamicStoreUseSessionKeys) }?; let value = CFBoolean::from(self.session_keys); let typed_dict = CFDictionary::from_CFType_pairs(&[(key, value)]); - unsafe { CFDictionary::wrap_under_get_rule(typed_dict.as_concrete_TypeRef()) } + unsafe { CFDictionary::try_wrap_under_get_rule(typed_dict.as_concrete_TypeRef()) } } fn create_context( diff --git a/system-configuration/src/network_configuration.rs b/system-configuration/src/network_configuration.rs index 88f4483..6d43f95 100644 --- a/system-configuration/src/network_configuration.rs +++ b/system-configuration/src/network_configuration.rs @@ -150,8 +150,10 @@ impl SCNetworkInterfaceType { use system_configuration_sys::network_configuration::*; let id_is_equal_to = |const_str| -> bool { - let const_str = unsafe { CFString::wrap_under_get_rule(const_str) }; - &const_str == type_id + match unsafe { CFString::try_wrap_under_get_rule(const_str) } { + Some(const_str) => &const_str == type_id, + None => false, + } }; unsafe { if id_is_equal_to(kSCNetworkInterfaceType6to4) { @@ -200,8 +202,8 @@ impl SCNetworkInterfaceType { /// See [`SCNetworkInterfaceCopyAll`] for more details. /// /// [`SCNetworkInterfaceCopyAll`]: https://developer.apple.com/documentation/systemconfiguration/1517090-scnetworkinterfacecopyall?language=objc -pub fn get_interfaces() -> CFArray { - unsafe { CFArray::::wrap_under_create_rule(SCNetworkInterfaceCopyAll()) } +pub fn get_interfaces() -> Option> { + unsafe { CFArray::::try_wrap_under_create_rule(SCNetworkInterfaceCopyAll()) } } core_foundation::declare_TCFType!( @@ -277,9 +279,9 @@ core_foundation::impl_TCFType!(SCNetworkSet, SCNetworkSetRef, SCNetworkSetGetTyp impl SCNetworkSet { /// Constructs a new set of network services from the preferences. - pub fn new(prefs: &SCPreferences) -> Self { + pub fn new(prefs: &SCPreferences) -> Option { let ptr = unsafe { SCNetworkSetCopyCurrent(prefs.to_void()) }; - unsafe { SCNetworkSet::wrap_under_create_rule(ptr) } + unsafe { SCNetworkSet::try_wrap_under_create_rule(ptr) } } /// Returns an list of network service identifiers, ordered by their priority. @@ -312,12 +314,12 @@ mod test { #[test] fn test_get_all_interfaces() { - let _ = get_interfaces(); + let _ = get_interfaces().unwrap(); } #[test] fn test_get_type() { - for iface in get_interfaces().into_iter() { + for iface in get_interfaces().unwrap().into_iter() { if iface.interface_type().is_none() { panic!( "Interface {:?} ({:?}) has unrecognized type {:?}", @@ -331,9 +333,9 @@ mod test { #[test] fn test_service_order() { - let prefs = SCPreferences::default(&CFString::new("test")); + let prefs = SCPreferences::default(&CFString::new("test")).unwrap(); let services = SCNetworkService::get_services(&prefs); - let set = SCNetworkSet::new(&prefs); + let set = SCNetworkSet::new(&prefs).unwrap(); let service_order = set.service_order(); assert!(service_order.iter().all(|service_id| { diff --git a/system-configuration/src/network_reachability.rs b/system-configuration/src/network_reachability.rs index fa368b3..4921042 100644 --- a/system-configuration/src/network_reachability.rs +++ b/system-configuration/src/network_reachability.rs @@ -154,7 +154,10 @@ impl SCNetworkReachability { /// See [`SCNetworkReachabilityCreateWithAddressPair`] for details. /// /// [`SCNetworkReachabilityCreateWithAddressPair`]: https://developer.apple.com/documentation/systemconfiguration/1514908-scnetworkreachabilitycreatewitha?language=objc - pub fn from_addr_pair(local: SocketAddr, remote: SocketAddr) -> SCNetworkReachability { + pub fn from_addr_pair( + local: SocketAddr, + remote: SocketAddr, + ) -> Option { let ptr = unsafe { SCNetworkReachabilityCreateWithAddressPair( std::ptr::null(), @@ -163,7 +166,7 @@ impl SCNetworkReachability { ) }; - unsafe { Self::wrap_under_create_rule(ptr) } + unsafe { Self::try_wrap_under_create_rule(ptr) } } /// Construct a Reachability from either a hostname or a network node @@ -300,12 +303,30 @@ impl SCNetworkReachability { } } -impl From for SCNetworkReachability { - fn from(addr: SocketAddr) -> Self { +/// Failure to create a `SCNetworkReachability` from a network address. +#[derive(Debug)] +pub struct NetworkReachabilityCreateError(()); + +impl Display for NetworkReachabilityCreateError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Failed to create SCNetworkReachability from network address" + ) + } +} + +impl Error for NetworkReachabilityCreateError {} + +impl TryFrom for SCNetworkReachability { + type Error = NetworkReachabilityCreateError; + + fn try_from(addr: SocketAddr) -> Result { unsafe { let ptr = SCNetworkReachabilityCreateWithAddress(std::ptr::null(), &*to_c_sockaddr(addr)); - SCNetworkReachability::wrap_under_create_rule(ptr) + SCNetworkReachability::try_wrap_under_create_rule(ptr) + .ok_or(NetworkReachabilityCreateError(())) } } } @@ -410,7 +431,7 @@ mod test { ]; for addr in sockaddrs { - let mut reachability = SCNetworkReachability::from(addr); + let mut reachability = SCNetworkReachability::try_from(addr).unwrap(); assert!( !reachability.0.is_null(), "Failed to construct a SCNetworkReachability struct with {}", @@ -441,7 +462,7 @@ mod test { .map(|(a, b)| (a.parse().unwrap(), b.parse().unwrap())); for (local, remote) in pairs { - let mut reachability = SCNetworkReachability::from_addr_pair(local, remote); + let mut reachability = SCNetworkReachability::from_addr_pair(local, remote).unwrap(); assert!( !reachability.0.is_null(), "Failed to construct a SCNetworkReachability struct with address pair {} - {}", @@ -476,7 +497,7 @@ mod test { SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0) }; let reachability = - SCNetworkReachability::from_addr_pair(local_addr, remote_addr); + SCNetworkReachability::from_addr_pair(local_addr, remote_addr).unwrap(); let reachability_flags = reachability.reachability().unwrap(); // Verify that not established tcp connection path is reported as not reachable. assert!(!reachability_flags.contains(ReachabilityFlags::REACHABLE)); @@ -484,7 +505,7 @@ mod test { Ok(tcp) => { let local = tcp.local_addr().unwrap(); let remote = tcp.peer_addr().unwrap(); - let reachability = SCNetworkReachability::from_addr_pair(local, remote); + let reachability = SCNetworkReachability::from_addr_pair(local, remote).unwrap(); let reachability_flags = reachability.reachability().unwrap(); // Verify established tcp connection path is reported as reachable. assert!(reachability_flags.contains(ReachabilityFlags::REACHABLE)); @@ -539,7 +560,8 @@ mod test { let (tx, rx) = std::sync::mpsc::channel(); std::thread::spawn(move || { let mut reachability = - SCNetworkReachability::from("0.0.0.0:0".parse::().unwrap()); + SCNetworkReachability::try_from("0.0.0.0:0".parse::().unwrap()) + .unwrap(); reachability.set_callback(|_| {}).unwrap(); // SAFETY: We use the Apple provided run_loop_mode kCFRunLoopCommonModes unsafe { diff --git a/system-configuration/src/preferences.rs b/system-configuration/src/preferences.rs index 202b8cf..b071224 100644 --- a/system-configuration/src/preferences.rs +++ b/system-configuration/src/preferences.rs @@ -26,7 +26,7 @@ impl_TCFType!(SCPreferences, SCPreferencesRef, SCPreferencesGetTypeID); impl SCPreferences { /// Initiates access to the default system preferences using the default allocator. - pub fn default(calling_process_name: &CFString) -> Self { + pub fn default(calling_process_name: &CFString) -> Option { Self::new(None, calling_process_name, None) } @@ -35,7 +35,7 @@ impl SCPreferences { /// constructor. /// /// [`default`]: #method.default - pub fn group(calling_process_name: &CFString, prefs_id: &CFString) -> Self { + pub fn group(calling_process_name: &CFString, prefs_id: &CFString) -> Option { Self::new(None, calling_process_name, Some(prefs_id)) } @@ -51,7 +51,7 @@ impl SCPreferences { allocator: Option<&CFAllocator>, calling_process_name: &CFString, prefs_id: Option<&CFString>, - ) -> Self { + ) -> Option { let allocator_ref = match allocator { Some(allocator) => allocator.as_concrete_TypeRef(), None => ptr::null(), @@ -62,7 +62,7 @@ impl SCPreferences { }; unsafe { - SCPreferences::wrap_under_create_rule(SCPreferencesCreate( + SCPreferences::try_wrap_under_create_rule(SCPreferencesCreate( allocator_ref, calling_process_name.as_concrete_TypeRef(), prefs_id_ref, @@ -77,7 +77,7 @@ mod tests { #[test] fn retain_count() { - let preferences = SCPreferences::default(&CFString::new("test")); + let preferences = SCPreferences::default(&CFString::new("test")).unwrap(); assert_eq!(preferences.retain_count(), 1); } }