system_configuration/
network_configuration.rs1use core_foundation::{
5 array::CFArray,
6 base::{TCFType, ToVoid},
7 string::CFString,
8};
9use system_configuration_sys::network_configuration::{
10 SCNetworkInterfaceCopyAll, SCNetworkInterfaceGetBSDName, SCNetworkInterfaceGetInterfaceType,
11 SCNetworkInterfaceGetLocalizedDisplayName, SCNetworkInterfaceGetTypeID, SCNetworkInterfaceRef,
12 SCNetworkServiceCopyAll, SCNetworkServiceGetEnabled, SCNetworkServiceGetInterface,
13 SCNetworkServiceGetServiceID, SCNetworkServiceGetTypeID, SCNetworkServiceRef,
14 SCNetworkSetCopyCurrent, SCNetworkSetGetServiceOrder, SCNetworkSetGetTypeID, SCNetworkSetRef,
15};
16
17use crate::preferences::SCPreferences;
18
19core_foundation::declare_TCFType!(
20 SCNetworkInterface,
27 SCNetworkInterfaceRef
28);
29core_foundation::impl_TCFType!(
30 SCNetworkInterface,
31 SCNetworkInterfaceRef,
32 SCNetworkInterfaceGetTypeID
33);
34
35impl SCNetworkInterface {
37 pub fn interface_type(&self) -> Option<SCNetworkInterfaceType> {
43 SCNetworkInterfaceType::from_cfstring(&self.interface_type_string()?)
44 }
45
46 pub fn interface_type_string(&self) -> Option<CFString> {
52 unsafe {
53 let ptr = SCNetworkInterfaceGetInterfaceType(self.0);
54 if ptr.is_null() {
55 None
56 } else {
57 Some(CFString::wrap_under_get_rule(ptr))
58 }
59 }
60 }
61
62 pub fn bsd_name(&self) -> Option<CFString> {
68 unsafe {
69 let ptr = SCNetworkInterfaceGetBSDName(self.0);
70 if ptr.is_null() {
71 None
72 } else {
73 Some(CFString::wrap_under_get_rule(ptr))
74 }
75 }
76 }
77
78 pub fn display_name(&self) -> Option<CFString> {
84 unsafe {
85 let ptr = SCNetworkInterfaceGetLocalizedDisplayName(self.0);
86 if ptr.is_null() {
87 None
88 } else {
89 Some(CFString::wrap_under_get_rule(ptr))
90 }
91 }
92 }
93}
94
95#[derive(Debug)]
101pub enum SCNetworkInterfaceType {
102 SixToFour,
104 Bluetooth,
106 Bridge,
108 Bond,
110 Ethernet,
112 FireWire,
114 IEEE80211,
116 IPSec,
118 IrDA,
120 L2TP,
122 Modem,
124 PPP,
126 PPTP,
130 Serial,
132 VLAN,
134 WWAN,
136 IPv4,
138}
139
140static BRIDGE_INTERFACE_TYPE_ID: &str = "Bridge";
142
143static IRDA_INTERFACE_TYPE_ID: &str = "IrDA";
145
146impl SCNetworkInterfaceType {
147 pub fn from_cfstring(type_id: &CFString) -> Option<Self> {
150 use system_configuration_sys::network_configuration::*;
151
152 let id_is_equal_to = |const_str| -> bool {
153 let const_str = unsafe { CFString::wrap_under_get_rule(const_str) };
154 &const_str == type_id
155 };
156 unsafe {
157 if id_is_equal_to(kSCNetworkInterfaceType6to4) {
158 Some(SCNetworkInterfaceType::SixToFour)
159 } else if id_is_equal_to(kSCNetworkInterfaceTypeBluetooth) {
160 Some(SCNetworkInterfaceType::Bluetooth)
161 } else if type_id == &BRIDGE_INTERFACE_TYPE_ID {
162 Some(SCNetworkInterfaceType::Bridge)
163 } else if id_is_equal_to(kSCNetworkInterfaceTypeBond) {
164 Some(SCNetworkInterfaceType::Bond)
165 } else if id_is_equal_to(kSCNetworkInterfaceTypeEthernet) {
166 Some(SCNetworkInterfaceType::Ethernet)
167 } else if id_is_equal_to(kSCNetworkInterfaceTypeFireWire) {
168 Some(SCNetworkInterfaceType::FireWire)
169 } else if id_is_equal_to(kSCNetworkInterfaceTypeIEEE80211) {
170 Some(SCNetworkInterfaceType::IEEE80211)
171 } else if id_is_equal_to(kSCNetworkInterfaceTypeIPSec) {
172 Some(SCNetworkInterfaceType::IPSec)
173 } else if type_id == &IRDA_INTERFACE_TYPE_ID {
174 Some(SCNetworkInterfaceType::IrDA)
175 } else if id_is_equal_to(kSCNetworkInterfaceTypeL2TP) {
176 Some(SCNetworkInterfaceType::L2TP)
177 } else if id_is_equal_to(kSCNetworkInterfaceTypeModem) {
178 Some(SCNetworkInterfaceType::Modem)
179 } else if id_is_equal_to(kSCNetworkInterfaceTypePPP) {
180 Some(SCNetworkInterfaceType::PPP)
181 } else if id_is_equal_to(kSCNetworkInterfaceTypePPTP) {
182 Some(SCNetworkInterfaceType::PPTP)
183 } else if id_is_equal_to(kSCNetworkInterfaceTypeSerial) {
184 Some(SCNetworkInterfaceType::Serial)
185 } else if id_is_equal_to(kSCNetworkInterfaceTypeVLAN) {
186 Some(SCNetworkInterfaceType::VLAN)
187 } else if id_is_equal_to(kSCNetworkInterfaceTypeWWAN) {
188 Some(SCNetworkInterfaceType::WWAN)
189 } else if id_is_equal_to(kSCNetworkInterfaceTypeIPv4) {
190 Some(SCNetworkInterfaceType::IPv4)
191 } else {
192 None
193 }
194 }
195 }
196}
197
198pub fn get_interfaces() -> CFArray<SCNetworkInterface> {
204 unsafe { CFArray::<SCNetworkInterface>::wrap_under_create_rule(SCNetworkInterfaceCopyAll()) }
205}
206
207core_foundation::declare_TCFType!(
208 SCNetworkService,
215 SCNetworkServiceRef
216);
217
218core_foundation::impl_TCFType!(
219 SCNetworkService,
220 SCNetworkServiceRef,
221 SCNetworkServiceGetTypeID
222);
223
224impl SCNetworkService {
225 pub fn get_services(prefs: &SCPreferences) -> CFArray<Self> {
227 unsafe {
228 let array_ptr = SCNetworkServiceCopyAll(prefs.to_void());
229 if array_ptr.is_null() {
230 return create_empty_array();
231 }
232 CFArray::<Self>::wrap_under_create_rule(array_ptr)
233 }
234 }
235
236 pub fn enabled(&self) -> bool {
238 unsafe { SCNetworkServiceGetEnabled(self.0) == 0 }
239 }
240
241 pub fn network_interface(&self) -> Option<SCNetworkInterface> {
243 unsafe {
244 let ptr = SCNetworkServiceGetInterface(self.0);
245 if ptr.is_null() {
246 None
247 } else {
248 Some(SCNetworkInterface::wrap_under_get_rule(ptr))
249 }
250 }
251 }
252
253 pub fn id(&self) -> Option<CFString> {
255 unsafe {
256 let ptr = SCNetworkServiceGetServiceID(self.0);
257 if ptr.is_null() {
258 None
259 } else {
260 Some(CFString::wrap_under_get_rule(ptr))
261 }
262 }
263 }
264}
265
266core_foundation::declare_TCFType!(
267 SCNetworkSet,
273 SCNetworkSetRef
274);
275
276core_foundation::impl_TCFType!(SCNetworkSet, SCNetworkSetRef, SCNetworkSetGetTypeID);
277
278impl SCNetworkSet {
279 pub fn new(prefs: &SCPreferences) -> Self {
281 let ptr = unsafe { SCNetworkSetCopyCurrent(prefs.to_void()) };
282 unsafe { SCNetworkSet::wrap_under_create_rule(ptr) }
283 }
284
285 pub fn service_order(&self) -> CFArray<CFString> {
287 unsafe {
288 let array_ptr = SCNetworkSetGetServiceOrder(self.0);
289 if array_ptr.is_null() {
290 return create_empty_array();
291 }
292 CFArray::<CFString>::wrap_under_get_rule(array_ptr)
293 }
294 }
295}
296
297fn create_empty_array<T>() -> CFArray<T> {
298 use std::ptr::null;
299 unsafe {
300 CFArray::wrap_under_create_rule(core_foundation::array::CFArrayCreate(
301 null() as *const _,
302 null() as *const _,
303 0,
304 null() as *const _,
305 ))
306 }
307}
308
309#[cfg(test)]
310mod test {
311 use super::*;
312
313 #[test]
314 fn test_get_all_interfaces() {
315 let _ = get_interfaces();
316 }
317
318 #[test]
319 fn test_get_type() {
320 for iface in get_interfaces().into_iter() {
321 if iface.interface_type().is_none() {
322 panic!(
323 "Interface {:?} ({:?}) has unrecognized type {:?}",
324 iface.display_name(),
325 iface.bsd_name(),
326 iface.interface_type_string()
327 )
328 }
329 }
330 }
331
332 #[test]
333 fn test_service_order() {
334 let prefs = SCPreferences::default(&CFString::new("test"));
335 let services = SCNetworkService::get_services(&prefs);
336 let set = SCNetworkSet::new(&prefs);
337 let service_order = set.service_order();
338
339 assert!(service_order.iter().all(|service_id| {
340 services
341 .iter()
342 .any(|service| service.id().as_ref() == Some(&*service_id))
343 }))
344 }
345
346 #[test]
347 fn test_empty_array() {
348 let empty = create_empty_array::<CFString>();
349 let values = empty.get_all_values();
350 assert!(values.is_empty())
351 }
352}