#![cfg(windows)]
fn encode_utf16(string: &str) -> Vec<u16> {
use std::iter::once;
string.encode_utf16().chain(once(0)).collect()
}
fn decode_utf16(string: &[u16]) -> String {
let end = string.iter().position(|b| *b == 0).unwrap_or(string.len());
String::from_utf16_lossy(&string[..end])
}
mod ffi;
mod iface;
mod netsh;
use std::{io, net, time};
use winapi::shared::ifdef::NET_LUID;
use winapi::um::winioctl::*;
use winapi::um::winnt::HANDLE;
pub struct Device {
luid: NET_LUID,
handle: HANDLE,
}
impl Device {
pub fn create() -> io::Result<Self> {
let luid = iface::create_interface()?;
let start = time::Instant::now();
let handle = loop {
let now = time::Instant::now();
if now - start > time::Duration::from_secs(2) {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"Interface timed out",
));
}
match iface::open_interface(&luid) {
Err(_) => {
std::thread::yield_now();
continue;
}
Ok(handle) => break handle,
};
};
Ok(Self { luid, handle })
}
pub fn open(name: &str) -> io::Result<Self> {
let name = encode_utf16(name);
let luid = ffi::alias_to_luid(&name)?;
iface::check_interface(&luid)?;
let handle = iface::open_interface(&luid)?;
Ok(Self { luid, handle })
}
pub fn delete(self) -> io::Result<()> {
iface::delete_interface(&self.luid)?;
Ok(())
}
pub fn up(&self) -> io::Result<()> {
self.set_status(true)
}
pub fn down(&self) -> io::Result<()> {
self.set_status(false)
}
pub fn get_mac(&self) -> io::Result<[u8; 6]> {
let mut mac = [0; 6];
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut mac,
)
.map(|_| mac)
}
pub fn get_version(&self) -> io::Result<[u32; 3]> {
let mut version = [0; 3];
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut version,
)
.map(|_| version)
}
pub fn get_mtu(&self) -> io::Result<u32> {
let mut mtu = 0;
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut mtu,
)
.map(|_| mtu)
}
pub fn get_name(&self) -> io::Result<String> {
ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
}
pub fn set_name(&self, newname: &str) -> io::Result<()> {
let name = self.get_name()?;
netsh::set_interface_name(&name, newname)
}
pub fn set_ip<A, B>(&self, address: A, mask: B) -> io::Result<()>
where
A: Into<net::Ipv4Addr>,
B: Into<net::Ipv4Addr>,
{
let name = self.get_name()?;
let address = address.into().to_string();
let mask = mask.into().to_string();
netsh::set_interface_ip(&name, &address, &mask)
}
pub fn set_status(&self, status: bool) -> io::Result<()> {
let status: u32 = if status { 1 } else { 0 };
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS),
&status,
&mut (),
)
}
}
impl io::Read for Device {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
ffi::read_file(self.handle, buf).map(|res| res as _)
}
}
impl io::Write for Device {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
ffi::write_file(self.handle, buf).map(|res| res as _)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for Device {
fn drop(&mut self) {
let _ = ffi::close_handle(self.handle);
}
}