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);
        }
    }
}