summaryrefslogtreecommitdiffhomepage
path: root/src/lib.rs
blob: fe086c1a872768d128e244083b5217bcd739842a (plain)
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
//! Provides safe access to the `getifaddrs` libc function.

#![cfg(target_os = "linux")]

extern crate libc;

use std::io;
use std::marker::PhantomData;
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,
    marker: PhantomData<libc::ifaddrs>,
}

struct IfaddrIter<'a>(*const libc::ifaddrs, PhantomData<&'a libc::ifaddrs>);

impl IfaddrHandle {
    fn new() -> io::Result<IfaddrHandle> {
        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, marker: PhantomData}),
            _ => Err(io::Error::last_os_error()),
        }
    }

    fn iter(&self) -> IfaddrIter {
        IfaddrIter(self.addrs, PhantomData)
    }
}

impl Drop for IfaddrHandle {
    fn drop(&mut self) {
        unsafe { libc::freeifaddrs(self.addrs) }
    }
}

impl<'a> Iterator for IfaddrIter<'a> {
    type Item = &'a libc::ifaddrs;

    fn next(&mut self) -> Option<Self::Item> {
        match self.0 as usize {
            0 => None,
            _ => {
                let addr = unsafe { &*self.0 };
                self.0 = 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() -> io::Result<Vec<IfAddr>> {
    IfaddrHandle::new().and_then(|addrs| {
        let mut result = Vec::new();

        for ifaddr in addrs.iter() {
            let index = match unsafe { libc::if_nametoindex(ifaddr.ifa_name) } {
                0 => return Err(io::Error::last_os_error()),
                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);
    }
}