1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
//! `getaddrs` provides a safe interface for the system's network interface data. //! //! The `InterfaceAddrs` struct provides access to the system's network //! interface data. You can either iterate over it or consume it and convert //! it into an `InterfaceMap` (a `HashMap<String, Vec<InterfaceAddr>>`) for //! more convenient access by interface name. //! //! # Examples //! //! You can access the basic information of the system in a few lines. //! The following program prints all the known addresses. //! //! ``` //! use getaddrs::InterfaceAddrs; //! //! let addrs = InterfaceAddrs::query_system() //! .expect("System has no network interfaces."); //! //! for addr in addrs { //! println!("{}: {:?}", addr.name, addr.address); //! } //! ``` //! //! The `InterfaceFlags` struct provides access to info about the //! state of an interface. This program prints the addresses of only //! interfaces which are up. //! //! ``` //! use getaddrs::{InterfaceAddrs, if_flags}; //! //! let addrs = InterfaceAddrs::query_system() //! .expect("System has no network interfaces."); //! //! for addr in addrs { //! if addr.flags.contains(if_flags::IFF_UP) { //! println!("{}: {:?}", addr.name, addr.address); //! } //! } //! ``` //! //! You can convert the `InterfaceAddrs` struct into a `HashMap` easily. //! `InterfaceMap` is an alias for `HashMap<String, Vec<InterfaceAddr>>` for //! easier reference. //! //! ``` //! use getaddrs::{InterfaceAddrs, InterfaceAddr, InterfaceMap}; //! use std::collections::HashMap; //! //! let interfaces: InterfaceMap = //! InterfaceAddrs::query_system() //! .expect("System has no network interfaces.") //! .into(); // Convert to a hash map //! //! // Print all the addresses of the loopback interface //! if let Some(addrs) = interfaces.get("lo") { //! println!("Loopback addresses:"); //! for addr in addrs { //! println!("\t{:?}", addr); //! } //! } //! //! ``` //! extern crate libc; #[macro_use] extern crate bitflags; use std::net::{IpAddr}; use std::ptr::null_mut; use std::ffi::CStr; use std::collections::HashMap; pub mod if_flags; use self::if_flags::InterfaceFlags; mod sockaddr; use sockaddr::sockaddr_to_ipaddr; pub type InterfaceMap = HashMap<String, Vec<InterfaceAddr>>; /// Represents a handle into the operating system's knowledge about network /// interfaces present on the system. Allows the user to iterate over /// interface configurations. pub struct InterfaceAddrs { inner: *mut libc::ifaddrs, current: Option<&'static libc::ifaddrs>, } impl InterfaceAddrs { /// Produce an `InterfaceAddrs` from the system's information. Returns `None` /// if there are no interfaces to be inspected. pub fn query_system() -> Option<Self> { let mut p = null_mut(); // UNSAFETY: Calling libc FFI function, which allocates memory and // fills it with info about interfaces. unsafe { libc::getifaddrs(&mut p); } // UNSAFETY: *mut -> &'static mut. This is known to be either in valid memory // or null based on the guarantees of getifaddrs() return unsafe{ p.as_ref() } .and_then(|r| Some(Self { inner: p, current: Some(r) })); } } impl From<InterfaceAddrs> for HashMap<String, Vec<InterfaceAddr>> { /// Collect an `InterfaceAddrs` into a `HashMap<String, InterfaceAddr>`. fn from(ia: InterfaceAddrs) -> HashMap<String, Vec<InterfaceAddr>> { let mut m = HashMap::new(); for i in ia { if !m.contains_key(&i.name) { m.insert(i.name.clone(), Vec::new()); } // Unwrap here because contains is checked above m.get_mut(&i.name).unwrap().push(i); } m } } impl Drop for InterfaceAddrs { fn drop(&mut self) { // UNSAFETY: Calling libc FFI function which frees previously allocated // memory. unsafe { // Ask the libc to drop free the memory it allocated when the struct // was created. libc::freeifaddrs(self.inner as *mut libc::ifaddrs); } } } /// Represents the configuration and state of a network interface. /// Interfaces are uniquely identified by name, and each interface is likely /// to be referred to multiple times, e.g. one for IPv4 and one for IPv6. #[derive(Debug, Clone)] pub struct InterfaceAddr { /// The name of the interface pub name: String, /// The address assigned to the interface for this protocol. /// A value of `None` means the libc reported a type of IP address that /// `std::net` doesn't understand. pub address: Option<IpAddr>, /// The netmasks assigned to the interface for this protocol. /// A value of `None` means the libc reported a type of IP address that /// `std::net` doesn't understand. pub netmask: Option<IpAddr>, /// The ifu assigned to the interface for this protocol. /// A value of `None` means the libc reported a type of IP address that /// `std::net` doesn't understand. pub ifu: InterfaceIfu, /// Flags regarding the interface's behaviour and state pub flags: InterfaceFlags, } /// Represents the ifu of an interface: either its broadcast address or /// point-to-point destination address. #[derive(Debug, Clone)] pub enum InterfaceIfu { BroadcastAddr(Option<IpAddr>), DestinationAddr(Option<IpAddr>), Neither, } impl Iterator for InterfaceAddrs { type Item = InterfaceAddr; fn next(&mut self) -> Option<InterfaceAddr> { // If the current ifaddrs is None, there are no more ifaddrs to inspect if self.current.is_none() { return None; } // Workaround for the borrow checker being overzealous // (without ptr_temp, p would technically "still be in use" when the // loop ends, meaning we couldn't advance to the next struct) let ptr_temp = self.current.clone(); let p = ptr_temp.as_ref().unwrap(); // Get a pointer to the interface's name let name_ptr = p.ifa_name; // Check that name_ptr isn't null. if name_ptr.is_null() { panic!("getifaddrs() gave an ifaddrs struct with a null ifa_name"); } // UNSAFETY: Constructing CStr from pointer. If this pointer is // null it's a libc bug; it's checked above. let name = unsafe { CStr::from_ptr(name_ptr) .to_string_lossy() .into_owned()}; // Interpret the flags field into a typed version of those flags let flags = InterfaceFlags::from_bits_truncate(p.ifa_flags); // Get std::net::IpAddr representations of the address and netmask // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let address = unsafe { sockaddr_to_ipaddr(p.ifa_addr) }; // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let netmask = unsafe { sockaddr_to_ipaddr(p.ifa_netmask) }; // Figure out which ifu type is needed and create it let ifu = if flags.contains(if_flags::IFF_POINTOPOINT) { // Point to point destination address // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; InterfaceIfu::DestinationAddr(ifu_addr) } else if flags.contains(if_flags::IFF_BROADCAST) { // Broadcast address // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; InterfaceIfu::BroadcastAddr(ifu_addr) } else { InterfaceIfu::Neither }; // Move along the list to the next ifaddrs struct // UNSAFETY: *mut -> Option<&'static mut>. // This is known to be in valid memory or null. self.current = unsafe { p.ifa_next.as_ref() }; Some(InterfaceAddr { name: name, address: address, netmask: netmask, ifu: ifu, flags: flags, }) } } #[cfg(test)] mod tests { #[test] fn tests_get_if_addrs() { let ifs = super::InterfaceAddrs::query_system().unwrap(); for i in ifs { println!("{}:", i.name); println!("\tADDR {:?}, MASK {:?}, IFU {:?}\n\t{:?}", i.address, i.netmask, i.ifu, i.flags); } } }