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
use std::ffi::{OsString, OsStr, CString};
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixDatagram;
use std::fs::File;
use std::io::Read;
use std::mem::MaybeUninit;

use libc::ioctl;

mod ffi;
mod error;

#[derive(Debug)]
pub struct Interface {
    name: OsString,
}

impl Interface {
    pub fn find_interface<S: AsRef<str>>(name: S) -> Result<Option<Interface>, error::InterfaceListError> {
        let ifs = interfaces()?;

        Ok(ifs.into_iter().find(|i| i.name == name.as_ref()))
    }

    pub fn get_connected_essid(&self) -> Result<CString, error::EssidFetchError> {
        let socket = UnixDatagram::unbound()?;
        let socketfd = socket.as_raw_fd();

        let mut wreq: ffi::iwreq = unsafe { MaybeUninit::zeroed().assume_init() };

        let mut name = [0u8; 16];

        name[0..self.name.len()].copy_from_slice(self.name.as_bytes());

        unsafe { wreq.ifr_ifrn.ifrn_name.copy_from_slice(std::mem::transmute(&name[..])) };

        wreq.u.essid.length = (ffi::IW_ESSID_MAX_SIZE + 1) as u16;

        let mut name: MaybeUninit<[u8; ffi::IW_ESSID_MAX_SIZE as usize + 1]> = MaybeUninit::uninit();

        wreq.u.essid.pointer = name.as_mut_ptr() as *mut _;

        let ret = unsafe { ioctl(socketfd, ffi::SIOCGIWESSID.into(), &mut wreq as *mut _) };
        if ret == -1 {
            return Err(std::io::Error::last_os_error())?;
        }

        Ok(CString::new(unsafe {&name.assume_init()[..wreq.u.essid.length as usize - 1]})?)
    }

    pub fn get_name(&self) -> &OsStr {
        &self.name
    }
}

pub fn interfaces() -> Result<Vec<Interface>, error::InterfaceListError> {

    let mut proc_wireless = File::open("/proc/net/wireless")?;

    let mut buf = vec![];

    proc_wireless.read_to_end(&mut buf)?;

    Ok(buf.split(|&b| b == b'\n').skip(2)
        .flat_map(|line| line.split(|&c| c == b':').next())
        .flat_map(|line| line.rsplit(|c| c.is_ascii_whitespace()).next())
        .filter(|line| line.len() != 0)
        .map(|n| OsStr::from_bytes(n))
        .map(|n| Interface { name: n.to_os_string() })
        .collect())
}