diff --git a/system-configuration-sys/src/lib.rs b/system-configuration-sys/src/lib.rs index a9ccd3a..00cd3c9 100644 --- a/system-configuration-sys/src/lib.rs +++ b/system-configuration-sys/src/lib.rs @@ -19,3 +19,11 @@ extern crate core_foundation_sys; pub mod dynamic_store; + +mod preferences; +mod network_configuration; + +pub use dynamic_store::*; +pub use preferences::*; +pub use network_configuration::*; + diff --git a/system-configuration-sys/src/network_configuration.rs b/system-configuration-sys/src/network_configuration.rs new file mode 100644 index 0000000..6dbb622 --- /dev/null +++ b/system-configuration-sys/src/network_configuration.rs @@ -0,0 +1,65 @@ +use core_foundation_sys::base::Boolean; +use core_foundation_sys::string::CFStringRef; +use core_foundation_sys::dictionary::CFDictionaryRef; +use core_foundation_sys::array::CFArrayRef; + +use SCPreferencesRef; + +use std::os::raw::{c_void, c_int}; + + +pub type __SCNetworkInterface = c_void; +pub type SCNetworkInterfaceRef = *const __SCNetworkInterface; +pub type SCBondInterfaceRef = SCNetworkInterfaceRef; +pub type SCVLANInterfaceRef = SCNetworkInterfaceRef; + +pub type __SCBondStatus = c_void; +pub type SCBondStatusRef = *const __SCBondStatus; + +pub type __SCNetworkProtocol = c_void; +pub type SCNetworkProtocolRef = *const __SCNetworkProtocol; + +pub type __SCNetworkService = c_void; +pub type SCNetworkServiceRef = *const __SCNetworkService; + +pub type __SCNetworkSet = c_void; +pub type SCNetworkSetRef = *const __SCNetworkSet; + + +#[link(name = "SystemConfiguration", kind = "framework")] +extern "C" { + pub fn SCNetworkServiceCopyAll(prefs: SCPreferencesRef) -> CFArrayRef; + pub fn SCNetworkServiceCopy(prefs: SCPreferencesRef, + serviceID: CFStringRef) -> SCNetworkServiceRef; + pub fn SCNetworkServiceGetEnabled(service: SCNetworkServiceRef) -> Boolean; + pub fn SCNetworkServiceGetInterface(service: SCNetworkServiceRef) -> SCNetworkInterfaceRef; + pub fn SCNetworkServiceGetName(service: SCNetworkServiceRef) -> CFStringRef; + pub fn SCNetworkServiceGetServiceID(service: SCNetworkServiceRef) -> CFStringRef; + pub fn SCNetworkSetGetServiceOrder(set: SCNetworkSetRef) -> CFArrayRef; + pub fn SCNetworkSetCopyServices(set: SCNetworkSetRef) -> CFArrayRef; + pub fn SCNetworkSetCopyCurrent(prefs:SCPreferencesRef) -> SCNetworkSetRef; + + pub fn SCNetworkInterfaceCopyAll() -> CFArrayRef; + pub fn SCNetworkInterfaceCopyMTU(interface: SCNetworkInterfaceRef, + mtu_cur: *mut c_int, + mtu_min: *mut c_int, + mtu_max: *mut c_int) -> Boolean; + pub fn SCNetworkInterfaceCopyMediaOptions(interface: SCNetworkInterfaceRef, + urrent: *mut CFDictionaryRef, + active: *mut CFDictionaryRef, + available: *mut CFArrayRef, + filter: Boolean) -> Boolean; + pub fn SCNetworkInterfaceGetBSDName(interface: SCNetworkInterfaceRef) -> CFStringRef; + pub fn SCNetworkInterfaceGetInterfaceType(interface: SCNetworkInterfaceRef) -> CFStringRef; + pub fn SCNetworkInterfaceGetHardwareAddressString(interface: SCNetworkInterfaceRef) -> CFStringRef; + + pub fn SCNetworkInterfaceGetConfiguration(interface: SCNetworkInterfaceRef) -> CFDictionaryRef; + pub fn SCNetworkInterfaceGetExtendedConfiguration(interface: SCNetworkInterfaceRef, + extendedType: CFStringRef) -> CFDictionaryRef; + + pub fn SCNetworkInterfaceSetConfiguration(interface: SCNetworkInterfaceRef, + config: CFDictionaryRef) -> Boolean; + pub fn SCNetworkInterfaceSetExtendedConfiguration(interface: SCNetworkInterfaceRef, + extendedType: CFStringRef, + config: CFDictionaryRef) -> Boolean; +} \ No newline at end of file diff --git a/system-configuration-sys/src/preferences.rs b/system-configuration-sys/src/preferences.rs new file mode 100644 index 0000000..58f2667 --- /dev/null +++ b/system-configuration-sys/src/preferences.rs @@ -0,0 +1,16 @@ +use core_foundation_sys::base::CFAllocatorRef; +use core_foundation_sys::string::CFStringRef; + +use std::os::raw::c_void; + + +pub type __SCPreferences = c_void; +pub type SCPreferencesRef = *const __SCPreferences; + + +#[link(name = "SystemConfiguration", kind = "framework")] +extern "C" { + pub fn SCPreferencesCreate(allocator: CFAllocatorRef, + name: CFStringRef, + prefsID: CFStringRef) -> SCPreferencesRef; +} \ No newline at end of file diff --git a/system-configuration/Cargo.toml b/system-configuration/Cargo.toml index ffe77b3..0cceaf8 100644 --- a/system-configuration/Cargo.toml +++ b/system-configuration/Cargo.toml @@ -11,5 +11,5 @@ readme = "../README.md" [dependencies] core-foundation = "0.5" - +core-foundation-sys = "0.5" system-configuration-sys = { path = "../system-configuration-sys", version = "0.1" } diff --git a/system-configuration/examples/dns.rs b/system-configuration/examples/dns.rs new file mode 100644 index 0000000..797e90c --- /dev/null +++ b/system-configuration/examples/dns.rs @@ -0,0 +1,23 @@ +extern crate system_configuration; + +use system_configuration::network_configuration::{ + // SCNetworkInterfaceMTU, SCNetworkServiceDNS, + SCNetworkGlobal, SCNetworkService, SCNetworkInterface +}; + + +fn main (){ + println!("{:?}", SCNetworkGlobal.service()); + + println!("{:?}", SCNetworkGlobal.interface()); + + println!("{:?}", SCNetworkGlobal.router()); + + for service in SCNetworkService::list_order() { + println!("{:?}\n\n", service); + } + + println!("{:?}", SCNetworkService::list()); + + println!("{:?}", SCNetworkInterface::list()); +} \ No newline at end of file diff --git a/system-configuration/src/lib.rs b/system-configuration/src/lib.rs index f0234d1..4c28fac 100644 --- a/system-configuration/src/lib.rs +++ b/system-configuration/src/lib.rs @@ -19,6 +19,8 @@ #[macro_use] extern crate core_foundation; +extern crate core_foundation_sys; extern crate system_configuration_sys; pub mod dynamic_store; +pub mod network_configuration; \ No newline at end of file diff --git a/system-configuration/src/network_configuration.rs b/system-configuration/src/network_configuration.rs new file mode 100644 index 0000000..88129a8 --- /dev/null +++ b/system-configuration/src/network_configuration.rs @@ -0,0 +1,437 @@ +use std::fmt; +use std::ptr; +use std::mem; +use std::net::IpAddr; + +use core_foundation_sys::base::kCFAllocatorDefault; + +use core_foundation::base::{CFType, TCFType}; +use core_foundation::string::{CFString, CFStringRef}; +use core_foundation::array::{CFArray}; +use core_foundation::dictionary::{CFDictionary}; + + +use system_configuration_sys::*; +use dynamic_store::{SCDynamicStoreBuilder}; + + +#[derive(Debug)] +pub struct SCNetworkInterfaceMTU { + cur: u32, + min: u32, + max: u32, +} + +impl SCNetworkInterfaceMTU { + pub fn cur(&self) -> u32 { + self.cur + } + + pub fn min(&self) -> u32 { + self.min + } + + pub fn max(&self) -> u32 { + self.max + } +} + +pub struct SCNetworkServiceDNS { + state_domain_name: Option, + setup_domain_name: Option, + state_server_addresses: Option>, + setup_server_addresses: Option>, +} + +impl SCNetworkServiceDNS { + pub fn new(domain_name: (Option, Option), + server_addresses: (Option>, Option>)) -> SCNetworkServiceDNS { + + SCNetworkServiceDNS { + state_domain_name: domain_name.0, + setup_domain_name: domain_name.1, + state_server_addresses: server_addresses.0, + setup_server_addresses: server_addresses.1, + } + } + + pub fn domain_name(&self) -> (Option, Option) { + (self.state_domain_name.clone(), self.setup_domain_name.clone()) + } + + pub fn server_addresses(&self) -> (Option>, Option>) { + (self.state_server_addresses.clone(), self.setup_server_addresses.clone()) + } +} + + +pub struct SCNetworkGlobal; + +impl SCNetworkGlobal { + pub fn service(&self) -> Option { + let store = SCDynamicStoreBuilder::new("ng_service").build(); + let key = CFString::from_static_string("State:/Network/Global/IPv4"); + + if let Some(value) = store.get(key.clone()) { + if let Some(dict) = value.downcast_into::() { + if let Some(val) = dict.find2(&CFString::from_static_string("PrimaryService")) { + let value = unsafe { CFType::wrap_under_get_rule(val) }; + if let Some(service_id) = value.downcast::() { + let service_id = service_id.to_string(); + + for _service in SCNetworkService::list(){ + if _service.id() == service_id { + return Some(_service); + } + } + } + } + } + } + return None; + } + + pub fn interface(&self) -> Option { + let store = SCDynamicStoreBuilder::new("ng_interface").build(); + let key = CFString::from_static_string("State:/Network/Global/IPv4"); + + if let Some(value) = store.get(key.clone()) { + if let Some(dict) = value.downcast_into::() { + if let Some(val) = dict.find2(&CFString::from_static_string("PrimaryInterface")) { + let value = unsafe { CFType::wrap_under_get_rule(val) }; + if let Some(ifname) = value.downcast::() { + for iface in SCNetworkInterface::list(){ + let bsd_name = iface.bsd_name(); + if bsd_name.is_some() && bsd_name.unwrap() == ifname.to_string() { + return Some(iface); + } + } + } + } + } + } + + return None; + } + + pub fn router(&self) -> Option { + let store = SCDynamicStoreBuilder::new("ng_interface").build(); + let key = CFString::from_static_string("State:/Network/Global/IPv4"); + + if let Some(value) = store.get(key) { + if let Some(dict) = value.downcast_into::() { + if let Some(val) = dict.find2(&CFString::from_static_string("Router")) { + let value = unsafe { CFType::wrap_under_get_rule(val) }; + if let Some(router_str) = value.downcast::() { + let router_str = router_str.to_string(); + match router_str.parse::() { + Ok(router_ip) => { + return Some(router_ip); + } + _ => { } + } + } + } + } + } + + return None; + } + + // pub fn netinfo(&self); + // pub fn proxies(&self) ; +} + + +pub struct SCNetworkService(pub SCNetworkServiceRef); + +impl SCNetworkService { + pub fn list() -> Vec { + let prefs = unsafe { + SCPreferencesCreate(kCFAllocatorDefault, + CFString::from_static_string("ns_list").as_concrete_TypeRef(), + ptr::null()) + }; + + let array: CFArray = unsafe { + CFArray::wrap_under_get_rule(SCNetworkServiceCopyAll(prefs)) + }; + + array.get_all_values() + .iter() + .map(|service_ptr| SCNetworkService( unsafe { mem::transmute(*service_ptr) } )) + .collect::>() + } + + pub fn list_order() -> Vec { + let prefs = unsafe { + SCPreferencesCreate(kCFAllocatorDefault, + CFString::from_static_string("ns_list_order").as_concrete_TypeRef(), + ptr::null()) + }; + + let netset = unsafe { SCNetworkSetCopyCurrent(prefs) }; + + let array: CFArray = unsafe { + CFArray::wrap_under_get_rule( SCNetworkSetGetServiceOrder(netset) ) + }; + + let mut services = Vec::new(); + + for id in array.get_all_values().iter() { + let id_ptr: CFStringRef = unsafe { mem::transmute(*id) }; + let service_ptr: SCNetworkServiceRef = unsafe { SCNetworkServiceCopy(prefs, id_ptr) }; + services.push(SCNetworkService(service_ptr)); + } + + services + } + + pub fn id(&self) -> String { + unsafe { CFString::wrap_under_get_rule( SCNetworkServiceGetServiceID( self.0 ) ) }.to_string() + } + + pub fn name(&self) -> String { + unsafe { CFString::wrap_under_get_rule( SCNetworkServiceGetName( self.0 ) ) }.to_string() + } + + pub fn enabled(&self) -> bool { + let ret = unsafe { SCNetworkServiceGetEnabled( self.0 ) }; + ret == 1 + } + + pub fn dns(&self) -> SCNetworkServiceDNS { + let store = SCDynamicStoreBuilder::new("ns_dns").build(); + + let mut state_domain_name: Option = None; + let mut state_server_addresses: Option> = None; + let mut setup_domain_name: Option = None; + let mut setup_server_addresses: Option> = None; + + if let Some(value) = store.get(CFString::new(&format!("State:/Network/Service/{}/DNS", self.id()))) { + if let Some(dict) = value.downcast_into::() { + if let Some(domain_name) = dict.find2(&CFString::from_static_string("DomainName")) { + let domain_name = unsafe { CFType::wrap_under_get_rule(domain_name) }; + if let Some(domain_name) = domain_name.downcast::() { + state_domain_name = Some(domain_name.to_string()); + } + } + + if let Some(addrs) = dict.find2(&CFString::from_static_string("ServerAddresses")) { + let addrs = unsafe { CFType::wrap_under_get_rule(addrs) }; + if let Some(addrs) = addrs.downcast::>() { + let mut temp = Vec::new(); + for addr in addrs.iter() { + if let Ok(ip_addr) = addr.to_string().parse::() { + temp.push(ip_addr); + } + } + + if temp.len() > 0 { + state_server_addresses = Some(temp); + } + } + } + } + } + + if let Some(value) = store.get(CFString::new(&format!("Setup:/Network/Service/{}/DNS", self.id()))) { + if let Some(dict) = value.downcast_into::() { + if let Some(domain_name) = dict.find2(&CFString::from_static_string("DomainName")) { + let domain_name = unsafe { CFType::wrap_under_get_rule(domain_name) }; + if let Some(domain_name) = domain_name.downcast::() { + setup_domain_name = Some(domain_name.to_string()); + } + } + + if let Some(addrs) = dict.find2(&CFString::from_static_string("ServerAddresses")) { + let addrs = unsafe { CFType::wrap_under_get_rule(addrs) }; + if let Some(addrs) = addrs.downcast::>() { + let mut temp = Vec::new(); + for addr in addrs.iter() { + if let Ok(ip_addr) = addr.to_string().parse::() { + temp.push(ip_addr); + } + } + + if temp.len() > 0 { + setup_server_addresses = Some(temp); + } + } + } + } + } + + SCNetworkServiceDNS { + state_domain_name: state_domain_name, + state_server_addresses: state_server_addresses, + setup_domain_name: setup_domain_name, + setup_server_addresses: setup_server_addresses, + } + } + + pub fn set_dns(&self, dns: SCNetworkServiceDNS) -> bool { + let store = SCDynamicStoreBuilder::new("ns_dns_set").build(); + + if dns.setup_server_addresses.is_some() { + let key = CFString::from_static_string("ServerAddresses"); + let addrs: Vec = dns.setup_server_addresses.unwrap() + .iter() + .map(|s| CFString::new(&format!("{}", s)) ) + .collect(); + let value = CFArray::from_CFTypes(&addrs); + let dictionary = CFDictionary::from_CFType_pairs(&[(key, value)]); + + let path = CFString::new(&format!("Setup:/Network/Service/{}/DNS", self.id())); + + if !store.set(path, dictionary) { + return false; + } + } + + if dns.setup_domain_name.is_some() { + let key = CFString::from_static_string("DomainName"); + let value = CFString::new(dns.setup_domain_name.unwrap().as_str()); + let dictionary = CFDictionary::from_CFType_pairs(&[(key, value)]); + + let path = CFString::new(&format!("Setup:/Network/Service/{}/DNS", self.id())); + + if !store.set(path, dictionary) { + // FIXME: should rollback ? + return false; + } + } + + return true; + } + + pub fn interface(&self) -> Option { + let interface_ptr = unsafe { SCNetworkServiceGetInterface( self.0 ) }; + if interface_ptr.is_null() { + None + } else { + Some(SCNetworkInterface( interface_ptr )) + } + } +} + +impl fmt::Display for SCNetworkService { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl fmt::Debug for SCNetworkService { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SCNetworkService{{ id: {:?}, name: {:?}, enabled: {}, interface: {:?} }}", + self.id(), + self.name(), + self.enabled(), + self.interface()) + } +} + + +pub struct SCNetworkInterface(pub SCNetworkInterfaceRef); + +impl SCNetworkInterface { + pub fn list() -> Vec { + let array: CFArray = unsafe { + CFArray::wrap_under_get_rule(SCNetworkInterfaceCopyAll()) + }; + + array.get_all_values() + .iter() + .map(|interface_ptr| SCNetworkInterface(unsafe { mem::transmute(*interface_ptr) }) ) + .collect::>() + } + + pub fn mtu(&self) -> Option { + let mut current = 0i32; + let mut min = 0i32; + let mut max = 0i32; + + let ret_code = unsafe { SCNetworkInterfaceCopyMTU(self.0, &mut current, &mut min, &mut max) }; + if ret_code == 0 { + None + } else { + Some(SCNetworkInterfaceMTU { + cur: current as u32, + min: min as u32, + max: max as u32, + }) + } + } + + pub fn bsd_name(&self) -> Option { + unsafe { + let str_ptr = SCNetworkInterfaceGetBSDName(self.0); + if str_ptr.is_null() { + None + } else { + Some(CFString::wrap_under_get_rule( str_ptr ).to_string()) + } + } + } + + pub fn name(&self) -> Option { + self.bsd_name() + } + + pub fn type_(&self) -> Option { + unsafe { + let str_ptr = SCNetworkInterfaceGetInterfaceType(self.0); + if str_ptr.is_null() { + None + } else { + Some(CFString::wrap_under_get_rule( str_ptr ).to_string()) + } + } + } + + pub fn hwaddr(&self) -> Option { + unsafe { + let str_ptr = SCNetworkInterfaceGetHardwareAddressString(self.0); + if str_ptr.is_null() { + None + } else { + Some(CFString::wrap_under_get_rule( str_ptr ).to_string()) + } + } + } + + pub fn config(&self) -> Option { + unsafe { + let config_ptr = SCNetworkInterfaceGetConfiguration(self.0); + if config_ptr.is_null() { + None + } else { + Some(CFDictionary::wrap_under_get_rule( config_ptr )) + } + } + } +} + +impl fmt::Display for SCNetworkInterface { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl fmt::Debug for SCNetworkInterface { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mtu = self.mtu(); + let mtu_fmt = if mtu.is_none() { + format!("None") + } else { + let mtu = mtu.unwrap(); + format!("{{cur: {}, min: {}, max: {} }}", mtu.cur(), mtu.min(), mtu.max()) + }; + + write!(f, "SCNetworkInterface{{ mtu: {}, bsd_name: {:?}, type: {:?}, hwaddr: {:?} }}", + mtu_fmt, + self.bsd_name(), + self.type_(), + self.hwaddr()) + } +} \ No newline at end of file