summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authordhivael <dhivael.git@eno.space>2017-05-28 01:18:57 +0200
committerdhivael <dhivael.git@eno.space>2017-05-28 03:31:43 +0200
commit428ec39cfbbd6ef7e35148d02e3d64e436d46ef5 (patch)
tree59d710776fcdf3efbe55c1f6a983e1c9363d524f
initial commit
-rw-r--r--.gitignore4
-rw-r--r--Cargo.toml8
-rw-r--r--src/lib.rs158
3 files changed, 170 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c378081
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+target/
+**/*.rs.bk
+Cargo.lock
+*.sw?
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..f3fc836
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "getifaddrs"
+version = "0.1.0"
+authors = ["dhivael"]
+
+[dependencies]
+libc = "0.2.23"
+errno = "0.2.3"
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..7b702f4
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,158 @@
+//! Provides safe access to the `getifaddrs` libc function.
+
+extern crate errno;
+extern crate libc;
+
+use std::mem;
+use std::net::*;
+use std::os::raw::*;
+use std::ptr;
+
+/// A single configured address.
+///
+/// Mostly analogous to `struct ifaddrs` from libc, with some field omitted or renamed.
+pub struct IfAddr {
+ /// Name of the interface the address is configured on. The name need not be valid UTF-8 and is
+ /// thus handled as pure binary data.
+ pub name: Vec<u8>,
+ /// Interface flags.
+ pub flags: u32,
+ /// Configured address. May be `None` if no address is configured or the configured address is
+ /// not IPv4 or IPv6.
+ pub address: Option<IpAddr>,
+ /// Configured netmask. May be `None` if no address is configured or the configured address is
+ /// not IPv4 or IPv6.
+ pub netmask: Option<IpAddr>,
+ /// Configured broadcast address or destination address (depending on flags). May be `None` if
+ /// no address is configured or the configured address is not IPv4 or IPv6. Corresponds to
+ /// `ifa_broadaddr` and `ifa_dstaddr` in `struct ifaddrs`.
+ pub broadcast_or_dest: Option<IpAddr>,
+ /// Index of the interface as determined by `if_nametoindex`.
+ pub index: u32,
+}
+
+struct IfaddrHandle {
+ addrs: *mut libc::ifaddrs,
+ cur: Option<*const libc::ifaddrs>,
+}
+
+impl IfaddrHandle {
+ fn new() -> Result<IfaddrHandle, errno::Errno> {
+ let mut addrs: *mut libc::ifaddrs = unsafe { mem::uninitialized() };
+ match unsafe { libc::getifaddrs(&mut addrs as *mut *mut libc::ifaddrs) } {
+ 0 => Ok(IfaddrHandle{addrs: addrs, cur: Some(addrs)}),
+ _ => Err(errno::errno()),
+ }
+ }
+}
+
+impl Drop for IfaddrHandle {
+ fn drop(&mut self) {
+ unsafe { libc::freeifaddrs(self.addrs) }
+ }
+}
+
+impl<'a> Iterator for &'a mut IfaddrHandle {
+ type Item = &'a libc::ifaddrs;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.cur {
+ None => None,
+ Some(addr) => {
+ let addr = unsafe { &*addr };
+ match addr.ifa_next as usize {
+ 0 => self.cur = None,
+ _ => self.cur = Some(unsafe { &*addr.ifa_next }),
+ }
+ Some(addr)
+ }
+ }
+ }
+}
+
+fn get_v4(addr: &libc::sockaddr_in) -> IpAddr {
+ let raw = u32::from_be(addr.sin_addr.s_addr);
+ IpAddr::V4(Ipv4Addr::new((raw >> 24) as u8, (raw >> 16) as u8, (raw >> 8) as u8, raw as u8))
+}
+
+fn get_v6(addr: &libc::sockaddr_in6) -> IpAddr {
+ let addr = &addr.sin6_addr.s6_addr;
+ IpAddr::V6(
+ Ipv6Addr::new(
+ (addr[0] as u16) << 8 | (addr[1] as u16), (addr[2] as u16) << 8 | (addr[3] as u16),
+ (addr[4] as u16) << 8 | (addr[5] as u16), (addr[6] as u16) << 8 | (addr[7] as u16),
+ (addr[8] as u16) << 8 | (addr[9] as u16), (addr[10] as u16) << 8 | (addr[11] as u16),
+ (addr[12] as u16) << 8 | (addr[13] as u16), (addr[14] as u16) << 8 | (addr[15] as u16)))
+}
+
+fn get_addr(addr: *const libc::sockaddr) -> Option<IpAddr> {
+ match addr as usize {
+ 0 => None,
+ _ => {
+ let addr = unsafe { &*addr };
+ match addr.sa_family as c_int {
+ libc::AF_INET => Some(get_v4(unsafe { mem::transmute(addr) })),
+ libc::AF_INET6 => Some(get_v6(unsafe { mem::transmute(addr) })),
+ _ => None,
+ }
+ }
+ }
+}
+
+fn copy_name(name: *const c_char) -> Vec<u8> {
+ unsafe {
+ let len = libc::strlen(name);
+ let mut result = Vec::with_capacity(len);
+ result.set_len(len);
+ ptr::copy(name, result.as_mut_ptr() as *mut c_char, len);
+ result
+ }
+}
+
+/// Lists available interfaces, their IP addresses and coresponding interface indexes.
+///
+/// The interface indexes are valid is `scope_id` values for IPv6 addresses.
+///
+/// # Examples
+///
+/// ```
+/// let lo = getifaddrs::getifaddrs().unwrap().into_iter().filter(|i| { i.index == 1 });
+/// ```
+pub fn getifaddrs() -> Result<Vec<IfAddr>, errno::Errno> {
+ IfaddrHandle::new().and_then(|mut addrs| {
+ let mut result = Vec::new();
+
+ for ifaddr in &mut addrs {
+ let index = match unsafe { libc::if_nametoindex(ifaddr.ifa_name) } {
+ 0 => return Err(errno::errno()),
+ i => i,
+ };
+
+ result.push(IfAddr{
+ flags: ifaddr.ifa_flags,
+ name: copy_name(ifaddr.ifa_name),
+ address: get_addr(ifaddr.ifa_addr),
+ netmask: get_addr(ifaddr.ifa_netmask),
+ broadcast_or_dest: get_addr(ifaddr.ifa_ifu),
+ index: index,
+ });
+ }
+
+ Ok(result)
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use std::net;
+
+ #[test]
+ fn lo_exists() {
+ let addrs = ::getifaddrs().unwrap();
+
+ assert!(addrs.into_iter().filter(|i| {
+ i.index == 1 &&
+ i.address.unwrap_or(net::IpAddr::V4(net::Ipv4Addr::new(0, 0, 0, 0))).is_loopback()
+ }).count() > 0);
+ }
+}