支持tap网卡,优化tun网卡配置
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "win-tun-tap"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.17"
|
||||
winreg = "0.7"
|
||||
scopeguard = "1.1"
|
||||
libloading = "0.7"
|
||||
widestring = "0.4"
|
||||
once_cell = "1.8"
|
||||
itertools = "0.10.1"
|
||||
|
||||
[dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"errhandlingapi",
|
||||
"combaseapi",
|
||||
"ioapiset",
|
||||
"winioctl",
|
||||
"setupapi",
|
||||
"synchapi",
|
||||
"netioapi",
|
||||
"fileapi",
|
||||
"winbase",
|
||||
"winerror",
|
||||
"ipexport",
|
||||
"iphlpapi",
|
||||
"handleapi"
|
||||
]
|
||||
@@ -0,0 +1,534 @@
|
||||
// Many things will be used in the future
|
||||
#![allow(unused)]
|
||||
|
||||
//! Module holding safe wrappers over winapi functions
|
||||
|
||||
use winapi::shared::basetsd::*;
|
||||
use winapi::shared::guiddef::GUID;
|
||||
use winapi::shared::ifdef::*;
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::shared::netioapi::*;
|
||||
use winapi::shared::winerror::*;
|
||||
|
||||
use winapi::um::combaseapi::*;
|
||||
use winapi::um::errhandlingapi::*;
|
||||
use winapi::um::fileapi::*;
|
||||
use winapi::um::handleapi::*;
|
||||
use winapi::um::ioapiset::*;
|
||||
use winapi::um::setupapi::*;
|
||||
use winapi::um::synchapi::*;
|
||||
use winapi::um::winioctl::*;
|
||||
use winapi::um::winnt::*;
|
||||
use winapi::um::winreg::*;
|
||||
|
||||
use std::{io, mem, ptr};
|
||||
use std::error::Error;
|
||||
use winapi::um::minwinbase::OVERLAPPED_u;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
/// Custom type to handle variable size SP_DRVINFO_DETAIL_DATA_W
|
||||
pub struct SP_DRVINFO_DETAIL_DATA_W2 {
|
||||
pub cbSize: DWORD,
|
||||
pub InfDate: FILETIME,
|
||||
pub CompatIDsOffset: DWORD,
|
||||
pub CompatIDsLength: DWORD,
|
||||
pub Reserved: ULONG_PTR,
|
||||
pub SectionName: [WCHAR; 256],
|
||||
pub InfFileName: [WCHAR; 260],
|
||||
pub DrvDescription: [WCHAR; 256],
|
||||
pub HardwareID: [WCHAR; 512],
|
||||
}
|
||||
|
||||
pub fn string_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
|
||||
// GUID_STRING_CHARACTERS + 1
|
||||
let mut string = vec![0; 39];
|
||||
|
||||
match unsafe {
|
||||
StringFromGUID2(guid, string.as_mut_ptr(), string.len() as _)
|
||||
} {
|
||||
0 => Err(io::Error::new(io::ErrorKind::Other, "Insufficent buffer")),
|
||||
_ => Ok(string),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alias_to_luid(alias: &[WCHAR]) -> io::Result<NET_LUID> {
|
||||
let mut luid = unsafe { mem::zeroed() };
|
||||
|
||||
match unsafe { ConvertInterfaceAliasToLuid(alias.as_ptr(), &mut luid) } {
|
||||
0 => Ok(luid),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn luid_to_index(luid: &NET_LUID) -> io::Result<NET_IFINDEX> {
|
||||
let mut index = 0;
|
||||
|
||||
match unsafe { ConvertInterfaceLuidToIndex(luid, &mut index) } {
|
||||
0 => Ok(index),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn luid_to_guid(luid: &NET_LUID) -> io::Result<GUID> {
|
||||
let mut guid = unsafe { mem::zeroed() };
|
||||
|
||||
match unsafe { ConvertInterfaceLuidToGuid(luid, &mut guid) } {
|
||||
0 => Ok(guid),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn luid_to_alias(luid: &NET_LUID) -> io::Result<Vec<WCHAR>> {
|
||||
// IF_MAX_STRING_SIZE + 1
|
||||
let mut alias = vec![0; 257];
|
||||
|
||||
match unsafe {
|
||||
ConvertInterfaceLuidToAlias(luid, alias.as_mut_ptr(), alias.len())
|
||||
} {
|
||||
0 => {
|
||||
Ok(alias)
|
||||
}
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close_handle(handle: HANDLE) -> io::Result<()> {
|
||||
match unsafe { CloseHandle(handle) } {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_file(
|
||||
file_name: &[WCHAR],
|
||||
desired_access: DWORD,
|
||||
share_mode: DWORD,
|
||||
creation_disposition: DWORD,
|
||||
flags_and_attributes: DWORD,
|
||||
) -> io::Result<HANDLE> {
|
||||
match unsafe {
|
||||
CreateFileW(
|
||||
file_name.as_ptr(),
|
||||
desired_access,
|
||||
share_mode,
|
||||
ptr::null_mut(),
|
||||
creation_disposition,
|
||||
flags_and_attributes,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
handle => Ok(handle),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result<DWORD> {
|
||||
let mut ret = 0;
|
||||
//https://www.cnblogs.com/linyilong3/archive/2012/05/03/2480451.html
|
||||
unsafe {
|
||||
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
|
||||
Internal: 0,
|
||||
InternalHigh: 0,
|
||||
u: Default::default(),
|
||||
hEvent: ptr::null_mut(),
|
||||
};
|
||||
if 0 == ReadFile(
|
||||
handle,
|
||||
buffer.as_mut_ptr() as _,
|
||||
buffer.len() as _,
|
||||
&mut ret,
|
||||
&mut ip_overlapped, ) {
|
||||
let e = io::Error::last_os_error();
|
||||
if e.raw_os_error().unwrap_or(0) == 997 {
|
||||
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
|
||||
return Err(e);
|
||||
}
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result<DWORD> {
|
||||
let mut ret = 0;
|
||||
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
|
||||
Internal: 0,
|
||||
InternalHigh: 0,
|
||||
u: Default::default(),
|
||||
hEvent: ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
if 0 == WriteFile(
|
||||
handle,
|
||||
buffer.as_ptr() as _,
|
||||
buffer.len() as _,
|
||||
&mut ret,
|
||||
&mut ip_overlapped,
|
||||
) {
|
||||
let e = io::Error::last_os_error();
|
||||
if e.raw_os_error().unwrap_or(0) == 997 {
|
||||
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
|
||||
return Err(e);
|
||||
}
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_device_info_list(guid: &GUID) -> io::Result<HDEVINFO> {
|
||||
match unsafe { SetupDiCreateDeviceInfoList(guid, ptr::null_mut()) } {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
devinfo => Ok(devinfo),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_class_devs(guid: &GUID, flags: DWORD) -> io::Result<HDEVINFO> {
|
||||
match unsafe {
|
||||
SetupDiGetClassDevsW(guid, ptr::null(), ptr::null_mut(), flags)
|
||||
} {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
devinfo => Ok(devinfo),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy_device_info_list(devinfo: HDEVINFO) -> io::Result<()> {
|
||||
match unsafe { SetupDiDestroyDeviceInfoList(devinfo) } {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class_name_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
|
||||
let mut class_name = vec![0; 32];
|
||||
|
||||
match unsafe {
|
||||
SetupDiClassNameFromGuidW(
|
||||
guid,
|
||||
class_name.as_mut_ptr(),
|
||||
class_name.len() as _,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(class_name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_device_info(
|
||||
devinfo: HDEVINFO,
|
||||
device_name: &[WCHAR],
|
||||
guid: &GUID,
|
||||
device_description: &[WCHAR],
|
||||
creation_flags: DWORD,
|
||||
) -> io::Result<SP_DEVINFO_DATA> {
|
||||
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
|
||||
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiCreateDeviceInfoW(
|
||||
devinfo,
|
||||
device_name.as_ptr(),
|
||||
guid,
|
||||
device_description.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
creation_flags,
|
||||
&mut devinfo_data,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(devinfo_data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_selected_device(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetSelectedDevice(devinfo, devinfo_data as *const _ as _)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_device_registry_property(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
property: DWORD,
|
||||
value: &[WCHAR],
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetDeviceRegistryPropertyW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
property,
|
||||
value.as_ptr() as _,
|
||||
(value.len() * 2) as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_registry_property(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
property: DWORD,
|
||||
) -> io::Result<Vec<WCHAR>> {
|
||||
let mut value = vec![0; 32];
|
||||
|
||||
match unsafe {
|
||||
SetupDiGetDeviceRegistryPropertyW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
property,
|
||||
ptr::null_mut(),
|
||||
value.as_mut_ptr() as _,
|
||||
(value.len() * 2) as _,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_driver_info_list(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
driver_type: DWORD,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiBuildDriverInfoList(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
driver_type,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy_driver_info_list(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
driver_type: DWORD,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiDestroyDriverInfoList(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
driver_type,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_driver_info_detail(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
drvinfo_data: &SP_DRVINFO_DATA_W,
|
||||
) -> io::Result<SP_DRVINFO_DETAIL_DATA_W2> {
|
||||
let mut drvinfo_detail: SP_DRVINFO_DETAIL_DATA_W2 =
|
||||
unsafe { mem::zeroed() };
|
||||
drvinfo_detail.cbSize = mem::size_of::<SP_DRVINFO_DETAIL_DATA_W>() as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiGetDriverInfoDetailW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
drvinfo_data as *const _ as _,
|
||||
&mut drvinfo_detail as *mut _ as _,
|
||||
mem::size_of_val(&drvinfo_detail) as _,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(drvinfo_detail),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_selected_driver(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
drvinfo_data: &SP_DRVINFO_DATA_W,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetSelectedDriverW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
drvinfo_data as *const _ as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_class_install_params(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
params: &impl Copy,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetClassInstallParamsW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
params as *const _ as _,
|
||||
mem::size_of_val(params) as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_class_installer(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
install_function: DI_FUNCTION,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiCallClassInstaller(
|
||||
install_function,
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_dev_reg_key(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
scope: DWORD,
|
||||
hw_profile: DWORD,
|
||||
key_type: DWORD,
|
||||
sam_desired: REGSAM,
|
||||
) -> io::Result<HKEY> {
|
||||
const INVALID_KEY_VALUE: HKEY = INVALID_HANDLE_VALUE as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiOpenDevRegKey(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
scope,
|
||||
hw_profile,
|
||||
key_type,
|
||||
sam_desired,
|
||||
)
|
||||
} {
|
||||
INVALID_KEY_VALUE => Err(io::Error::last_os_error()),
|
||||
key => Ok(key),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_change_key_value(
|
||||
key: HKEY,
|
||||
watch_subtree: BOOL,
|
||||
notify_filter: DWORD,
|
||||
milliseconds: DWORD,
|
||||
) -> io::Result<()> {
|
||||
let event = match unsafe {
|
||||
CreateEventW(ptr::null_mut(), FALSE, FALSE, ptr::null())
|
||||
} {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
event => Ok(event),
|
||||
}?;
|
||||
|
||||
match unsafe {
|
||||
RegNotifyChangeKeyValue(key, watch_subtree, notify_filter, event, TRUE)
|
||||
} {
|
||||
0 => Ok(()),
|
||||
err => Err(io::Error::from_raw_os_error(err)),
|
||||
}?;
|
||||
|
||||
match unsafe { WaitForSingleObject(event, milliseconds) } {
|
||||
0 => Ok(()),
|
||||
0x102 => Err(io::Error::new(
|
||||
io::ErrorKind::TimedOut,
|
||||
"Registry timed out",
|
||||
)),
|
||||
_ => Err(io::Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enum_driver_info(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
driver_type: DWORD,
|
||||
member_index: DWORD,
|
||||
) -> Option<io::Result<SP_DRVINFO_DATA_W>> {
|
||||
let mut drvinfo_data: SP_DRVINFO_DATA_W = unsafe { mem::zeroed() };
|
||||
drvinfo_data.cbSize = mem::size_of_val(&drvinfo_data) as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiEnumDriverInfoW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
driver_type,
|
||||
member_index,
|
||||
&mut drvinfo_data,
|
||||
)
|
||||
} {
|
||||
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
|
||||
0 => Some(Err(io::Error::last_os_error())),
|
||||
_ => Some(Ok(drvinfo_data)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enum_device_info(
|
||||
devinfo: HDEVINFO,
|
||||
member_index: DWORD,
|
||||
) -> Option<io::Result<SP_DEVINFO_DATA>> {
|
||||
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
|
||||
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiEnumDeviceInfo(devinfo, member_index, &mut devinfo_data)
|
||||
} {
|
||||
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
|
||||
0 => Some(Err(io::Error::last_os_error())),
|
||||
_ => Some(Ok(devinfo_data)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_io_control(
|
||||
handle: HANDLE,
|
||||
io_control_code: DWORD,
|
||||
in_buffer: &impl Copy,
|
||||
out_buffer: &mut impl Copy,
|
||||
) -> io::Result<()> {
|
||||
let mut junk = 0;
|
||||
|
||||
match unsafe {
|
||||
DeviceIoControl(
|
||||
handle,
|
||||
io_control_code,
|
||||
in_buffer as *const _ as _,
|
||||
mem::size_of_val(in_buffer) as _,
|
||||
out_buffer as *mut _ as _,
|
||||
mem::size_of_val(out_buffer) as _,
|
||||
&mut junk,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#![cfg(windows)]
|
||||
|
||||
mod tap;
|
||||
mod tun;
|
||||
mod ffi;
|
||||
mod netsh;
|
||||
mod route;
|
||||
|
||||
use std::{io, net};
|
||||
pub use tap::TapDevice;
|
||||
pub use tun::*;
|
||||
|
||||
/// Encode a string as a utf16 buffer
|
||||
fn encode_utf16(string: &str) -> Vec<u16> {
|
||||
use std::iter::once;
|
||||
string.encode_utf16().chain(once(0)).collect()
|
||||
}
|
||||
|
||||
/// Decode a string from a utf16 buffer
|
||||
fn decode_utf16(string: &[u16]) -> String {
|
||||
let end = string.iter().position(|b| *b == 0).unwrap_or(string.len());
|
||||
String::from_utf16_lossy(&string[..end])
|
||||
}
|
||||
|
||||
pub trait IFace {
|
||||
fn shutdown(&self)->io::Result<()>;
|
||||
/// 获取接口索引
|
||||
fn get_index(&self) -> io::Result<u32>;
|
||||
/// 获取名称
|
||||
fn get_name(&self) -> io::Result<String>;
|
||||
/// 设置名称
|
||||
fn set_name(&self, new_name: &str) -> io::Result<()>;
|
||||
/// 设置ip
|
||||
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()>
|
||||
where IP: Into<net::Ipv4Addr>;
|
||||
/// 设置路由
|
||||
fn add_route<IP>(&self, dest: IP,
|
||||
netmask: IP,
|
||||
gateway: IP, ) -> io::Result<()>
|
||||
where IP: Into<net::Ipv4Addr>;
|
||||
/// 删除路由
|
||||
fn delete_route<IP>(&self, dest: IP,
|
||||
netmask: IP,
|
||||
gateway: IP, ) -> io::Result<()>
|
||||
where IP: Into<net::Ipv4Addr>;
|
||||
/// 设置最大传输单元
|
||||
fn set_mtu(&self, mtu: u16) -> io::Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
/// 设置网卡名称
|
||||
pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> {
|
||||
let cmd = format!(" netsh interface set interface name={:?} newname={:?}", old_name, new_name);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&cmd)
|
||||
.output()?;
|
||||
if !out.status.success() {
|
||||
log::warn!("修改网卡名称失败:cmd={:?},out={:?}",cmd,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "修改网卡名称失败"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// 设置网卡ip
|
||||
pub fn set_interface_ip(index: u32, address: &Ipv4Addr, netmask: &Ipv4Addr) -> io::Result<()> {
|
||||
let set_address = format!(
|
||||
"netsh interface ip set address {} static {:?} {:?} ",
|
||||
index, address, netmask,
|
||||
);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&set_address)
|
||||
.output()?;
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_address,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_interface_mtu(index: u32, mtu: u16) -> io::Result<()> {
|
||||
let set_mtu = format!(
|
||||
"netsh interface ipv4 set subinterface {} mtu={} store=persistent",
|
||||
index, mtu
|
||||
);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&set_mtu)
|
||||
.output()?;
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_mtu,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置mtu失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
/// 添加路由
|
||||
pub fn add_route(index: u32, dest: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr, ) -> io::Result<()> {
|
||||
let set_route = format!(
|
||||
"route add {:?} mask {:?} {:?} if {}",
|
||||
dest, netmask, gateway, index
|
||||
);
|
||||
// 执行添加路由命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&set_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_route,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 删除路由
|
||||
pub fn delete_route(index: u32, dest: Ipv4Addr,netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
if index == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
|
||||
}
|
||||
let delete_route = format!(
|
||||
"route delete {:?} mask {:?} {:?} if {}",
|
||||
dest, netmask, gateway, index
|
||||
);
|
||||
// 删除路由
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(delete_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("删除路由失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
use winapi::shared::ifdef::NET_LUID;
|
||||
use winapi::shared::minwindef::*;
|
||||
|
||||
use winapi::um::fileapi::*;
|
||||
use winapi::um::setupapi::*;
|
||||
use winapi::um::winnt::*;
|
||||
|
||||
use scopeguard::{guard, ScopeGuard};
|
||||
use winreg::RegKey;
|
||||
|
||||
use std::io;
|
||||
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
|
||||
|
||||
use crate::{decode_utf16, encode_utf16, ffi};
|
||||
|
||||
/// tap-windows hardware ID
|
||||
const HARDWARE_ID: &str = "tap0901";
|
||||
|
||||
winapi::DEFINE_GUID! {
|
||||
GUID_NETWORK_ADAPTER,
|
||||
0x4d36e972, 0xe325, 0x11ce,
|
||||
0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18
|
||||
}
|
||||
|
||||
/// Create a new interface and returns its NET_LUID
|
||||
pub fn create_interface() -> io::Result<NET_LUID> {
|
||||
let devinfo = ffi::create_device_info_list(&GUID_NETWORK_ADAPTER)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_device_info_list(devinfo);
|
||||
});
|
||||
|
||||
let class_name = ffi::class_name_from_guid(&GUID_NETWORK_ADAPTER)?;
|
||||
|
||||
let devinfo_data = ffi::create_device_info(
|
||||
devinfo,
|
||||
&class_name,
|
||||
&GUID_NETWORK_ADAPTER,
|
||||
&encode_utf16(""),
|
||||
DICD_GENERATE_ID,
|
||||
)?;
|
||||
|
||||
ffi::set_selected_device(devinfo, &devinfo_data)?;
|
||||
ffi::set_device_registry_property(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDRP_HARDWAREID,
|
||||
&encode_utf16(HARDWARE_ID),
|
||||
)?;
|
||||
|
||||
ffi::build_driver_info_list(devinfo, &devinfo_data, SPDIT_COMPATDRIVER)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_driver_info_list(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDIT_COMPATDRIVER,
|
||||
);
|
||||
});
|
||||
|
||||
let mut driver_version = 0;
|
||||
let mut member_index = 0;
|
||||
|
||||
while let Some(drvinfo_data) = ffi::enum_driver_info(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDIT_COMPATDRIVER,
|
||||
member_index,
|
||||
) {
|
||||
member_index += 1;
|
||||
|
||||
let drvinfo_data = match drvinfo_data {
|
||||
Ok(drvinfo_data) => drvinfo_data,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if drvinfo_data.DriverVersion <= driver_version {
|
||||
continue;
|
||||
}
|
||||
|
||||
let drvinfo_detail = match ffi::get_driver_info_detail(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
&drvinfo_data,
|
||||
) {
|
||||
Ok(drvinfo_detail) => drvinfo_detail,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let is_compatible = drvinfo_detail
|
||||
.HardwareID
|
||||
.split(|b| *b == 0)
|
||||
.map(|id| decode_utf16(id))
|
||||
.any(|id| id.eq_ignore_ascii_case(HARDWARE_ID));
|
||||
|
||||
if !is_compatible {
|
||||
continue;
|
||||
}
|
||||
|
||||
match ffi::set_selected_driver(devinfo, &devinfo_data, &drvinfo_data) {
|
||||
Ok(_) => (),
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
driver_version = drvinfo_data.DriverVersion;
|
||||
}
|
||||
|
||||
if driver_version == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "No driver found"));
|
||||
}
|
||||
|
||||
let uninstaller = guard((), |_| {
|
||||
let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
|
||||
});
|
||||
|
||||
ffi::call_class_installer(devinfo, &devinfo_data, DIF_REGISTERDEVICE)?;
|
||||
|
||||
let _ = ffi::call_class_installer(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DIF_REGISTER_COINSTALLERS,
|
||||
);
|
||||
let _ = ffi::call_class_installer(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DIF_INSTALLINTERFACES,
|
||||
);
|
||||
|
||||
ffi::call_class_installer(devinfo, &devinfo_data, DIF_INSTALLDEVICE)?;
|
||||
|
||||
let key = ffi::open_dev_reg_key(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DRV,
|
||||
KEY_QUERY_VALUE | KEY_NOTIFY,
|
||||
)?;
|
||||
|
||||
let key = RegKey::predef(key);
|
||||
|
||||
while let Err(_) = key.get_value::<DWORD, &str>("*IfType") {
|
||||
ffi::notify_change_key_value(
|
||||
key.raw_handle(),
|
||||
TRUE,
|
||||
REG_NOTIFY_CHANGE_NAME,
|
||||
2000,
|
||||
)?;
|
||||
}
|
||||
|
||||
while let Err(_) = key.get_value::<DWORD, &str>("NetLuidIndex") {
|
||||
ffi::notify_change_key_value(
|
||||
key.raw_handle(),
|
||||
TRUE,
|
||||
REG_NOTIFY_CHANGE_NAME,
|
||||
2000,
|
||||
)?;
|
||||
}
|
||||
|
||||
let if_type: DWORD = key.get_value("*IfType")?;
|
||||
let luid_index: DWORD = key.get_value("NetLuidIndex")?;
|
||||
|
||||
// Defuse the uninstaller
|
||||
ScopeGuard::into_inner(uninstaller);
|
||||
|
||||
let mut luid = NET_LUID { Value: 0 };
|
||||
|
||||
luid.set_IfType(if_type as _);
|
||||
luid.set_NetLuidIndex(luid_index as _);
|
||||
|
||||
Ok(luid)
|
||||
}
|
||||
|
||||
/// Check if the given interface exists and is a valid tap-windows device
|
||||
pub fn check_interface(luid: &NET_LUID) -> io::Result<()> {
|
||||
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_device_info_list(devinfo);
|
||||
});
|
||||
|
||||
let mut member_index = 0;
|
||||
|
||||
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
|
||||
{
|
||||
member_index += 1;
|
||||
|
||||
let devinfo_data = match devinfo_data {
|
||||
Ok(devinfo_data) => devinfo_data,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let hardware_id = match ffi::get_device_registry_property(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDRP_HARDWAREID,
|
||||
) {
|
||||
Ok(hardware_id) => hardware_id,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let key = match ffi::open_dev_reg_key(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DRV,
|
||||
KEY_QUERY_VALUE | KEY_NOTIFY,
|
||||
) {
|
||||
Ok(key) => RegKey::predef(key),
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let if_type: DWORD = match key.get_value("*IfType") {
|
||||
Ok(if_type) => if_type,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
|
||||
Ok(luid_index) => luid_index,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let mut luid2 = NET_LUID { Value: 0 };
|
||||
|
||||
luid2.set_IfType(if_type as _);
|
||||
luid2.set_NetLuidIndex(luid_index as _);
|
||||
|
||||
if luid.Value != luid2.Value {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found it!
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
|
||||
}
|
||||
|
||||
/// Deletes an existing interface
|
||||
pub fn delete_interface(luid: &NET_LUID) -> io::Result<()> {
|
||||
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_device_info_list(devinfo);
|
||||
});
|
||||
|
||||
let mut member_index = 0;
|
||||
|
||||
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
|
||||
{
|
||||
member_index += 1;
|
||||
|
||||
let devinfo_data = match devinfo_data {
|
||||
Ok(devinfo_data) => devinfo_data,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let hardware_id = match ffi::get_device_registry_property(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDRP_HARDWAREID,
|
||||
) {
|
||||
Ok(hardware_id) => hardware_id,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let key = match ffi::open_dev_reg_key(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DRV,
|
||||
KEY_QUERY_VALUE | KEY_NOTIFY,
|
||||
) {
|
||||
Ok(key) => RegKey::predef(key),
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let if_type: DWORD = match key.get_value("*IfType") {
|
||||
Ok(if_type) => if_type,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
|
||||
Ok(luid_index) => luid_index,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let mut luid2 = NET_LUID { Value: 0 };
|
||||
|
||||
luid2.set_IfType(if_type as _);
|
||||
luid2.set_NetLuidIndex(luid_index as _);
|
||||
|
||||
if luid.Value != luid2.Value {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found it!
|
||||
return ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
|
||||
}
|
||||
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
|
||||
}
|
||||
|
||||
/// Open an handle to an interface
|
||||
pub fn open_interface(luid: &NET_LUID) -> io::Result<HANDLE> {
|
||||
let guid = ffi::luid_to_guid(luid)
|
||||
.and_then(|guid| ffi::string_from_guid(&guid))?;
|
||||
|
||||
let path = format!(r"\\.\Global\{}.tap", &decode_utf16(&guid));
|
||||
|
||||
ffi::create_file(
|
||||
&encode_utf16(&path),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_SYSTEM,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
use std::{io, net, time};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use winapi::shared::ifdef::NET_LUID;
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::um::winioctl::*;
|
||||
use winapi::um::winnt::HANDLE;
|
||||
|
||||
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
|
||||
|
||||
mod iface;
|
||||
|
||||
pub struct TapDevice {
|
||||
luid: NET_LUID,
|
||||
handle: HANDLE,
|
||||
|
||||
}
|
||||
unsafe impl Send for TapDevice{}
|
||||
unsafe impl Sync for TapDevice{}
|
||||
|
||||
impl TapDevice {
|
||||
/// Retieve the mac of the interface
|
||||
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)
|
||||
}
|
||||
|
||||
/// Retrieve the version of the driver
|
||||
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)
|
||||
}
|
||||
|
||||
/// Retieve the mtu of the interface
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
/// Set the status of the interface, true for connected,
|
||||
/// false for disconnected.
|
||||
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 TapDevice {
|
||||
pub fn create() -> io::Result<Self> {
|
||||
let luid = iface::create_interface()?;
|
||||
// Even after retrieving the luid, we might need to wait
|
||||
let start = time::Instant::now();
|
||||
let handle = loop {
|
||||
// If we surpassed 2 seconds just return
|
||||
let now = time::Instant::now();
|
||||
if now - start > time::Duration::from_secs(3) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for TapDevice {
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
self.set_status(false)
|
||||
}
|
||||
|
||||
fn get_index(&self) -> io::Result<u32> {
|
||||
ffi::luid_to_index(&self.luid).map(|index| index as u32)
|
||||
}
|
||||
|
||||
fn get_name(&self) -> io::Result<String> {
|
||||
ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
|
||||
}
|
||||
|
||||
fn set_name(&self, new_name: &str) -> io::Result<()> {
|
||||
let name = self.get_name()?;
|
||||
netsh::set_interface_name(&name, new_name)
|
||||
}
|
||||
|
||||
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
let index = self.get_index()?;
|
||||
netsh::set_interface_ip(index, &address.into(), &mask.into())
|
||||
}
|
||||
|
||||
fn add_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
let index = self.get_index()?;
|
||||
route::add_route(index, dest.into(), netmask.into(), gateway.into())
|
||||
}
|
||||
|
||||
fn delete_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
let index = self.get_index()?;
|
||||
route::delete_route(index, dest.into(), netmask.into(), gateway.into())
|
||||
}
|
||||
|
||||
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
|
||||
let index = self.get_index()?;
|
||||
netsh::set_interface_mtu(index, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TapDevice {
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
ffi::read_file(self.handle, buf).map(|res| res as _)
|
||||
}
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
ffi::write_file(self.handle, buf).map(|res| res as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TapDevice {
|
||||
fn drop(&mut self) {
|
||||
let _ = ffi::close_handle(self.handle);
|
||||
let _ = iface::delete_interface(&self.luid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
use log::*;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use widestring::U16CStr;
|
||||
use crate::tun::wintun_raw;
|
||||
|
||||
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
|
||||
pub fn set_logger(win_tun: &wintun_raw::wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
|
||||
unsafe { win_tun.WintunSetLogger(f) };
|
||||
}
|
||||
|
||||
pub fn reset_logger(win_tun: &wintun_raw::wintun) {
|
||||
set_logger(win_tun, None);
|
||||
}
|
||||
|
||||
static SET_LOGGER: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// The logger that is active by default. Logs messages to the log crate
|
||||
///
|
||||
/// # Safety
|
||||
/// `message` must be a valid pointer that points to an aligned null terminated UTF-16 string
|
||||
pub unsafe extern "C" fn default_logger(
|
||||
level: wintun_raw::WINTUN_LOGGER_LEVEL,
|
||||
_timestamp: wintun_raw::DWORD64,
|
||||
message: *const wintun_raw::WCHAR,
|
||||
) {
|
||||
//Cant wait for RFC 2585
|
||||
#[allow(unused_unsafe)]
|
||||
//Wintun will always give us a valid UTF16 null termineted string
|
||||
let msg = unsafe { U16CStr::from_ptr_str(message) };
|
||||
let utf8_msg = msg.to_string_lossy();
|
||||
match level {
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO => info!("WinTun: {}", utf8_msg),
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN => warn!("WinTun: {}", utf8_msg),
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR => error!("WinTun: {}", utf8_msg),
|
||||
_ => error!("WinTun: {} (with invalid log level {})", utf8_msg, level),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_default_logger_if_unset(win_tun: &wintun_raw::wintun) {
|
||||
if SET_LOGGER
|
||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
set_logger(win_tun, Some(default_logger));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use winapi::um::{handleapi, synchapi, winbase, winnt};
|
||||
|
||||
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
|
||||
mod wintun_raw;
|
||||
mod log;
|
||||
pub mod packet;
|
||||
|
||||
/// The maximum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
|
||||
|
||||
/// The minimum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
|
||||
|
||||
/// Maximum pool name length including zero terminator
|
||||
pub const MAX_POOL: usize = 256;
|
||||
|
||||
|
||||
pub struct TunDevice {
|
||||
/// The session handle given to us by WintunStartSession
|
||||
pub(crate) session: wintun_raw::WINTUN_SESSION_HANDLE,
|
||||
|
||||
/// Shared dll for required wintun driver functions
|
||||
pub(crate) win_tun: wintun_raw::wintun,
|
||||
|
||||
/// Windows event handle that is signaled by the wintun driver when data becomes available to
|
||||
/// read
|
||||
pub(crate) read_event: winnt::HANDLE,
|
||||
|
||||
/// Windows event handle that is signaled when [`TunSession::shutdown`] is called force blocking
|
||||
/// readers to exit
|
||||
pub(crate) shutdown_event: winnt::HANDLE,
|
||||
|
||||
/// The adapter that owns this session
|
||||
pub(crate) adapter: wintun_raw::WINTUN_ADAPTER_HANDLE,
|
||||
|
||||
}
|
||||
|
||||
unsafe impl Send for TunDevice {}
|
||||
|
||||
unsafe impl Sync for TunDevice {}
|
||||
winapi::DEFINE_GUID! {
|
||||
GUID_NETWORK_ADAPTER,
|
||||
0x4d36e972, 0xe325, 0x11ce,
|
||||
0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18
|
||||
}
|
||||
impl TunDevice {
|
||||
pub unsafe fn create<L>(library: L, pool: &str, name: &str) -> io::Result<Self>
|
||||
where L: Into<libloading::Library>, {
|
||||
let win_tun = match wintun_raw::wintun::from_library(library) {
|
||||
Ok(win_tun) => { win_tun }
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("library error {:?} ", e)));
|
||||
}
|
||||
};
|
||||
let pool_utf16 = encode_utf16(pool);
|
||||
if pool_utf16.len() > MAX_POOL {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("长度大于{}:{:?}", MAX_POOL, pool)));
|
||||
}
|
||||
let name_utf16 = encode_utf16(name);
|
||||
if name_utf16.len() > MAX_POOL {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("长度大于{}:{:?}", MAX_POOL, pool)));
|
||||
}
|
||||
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
|
||||
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
|
||||
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(GUID_NETWORK_ADAPTER) };
|
||||
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
|
||||
|
||||
log::set_default_logger_if_unset(&win_tun);
|
||||
|
||||
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
|
||||
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
|
||||
//applies for all Wintun* functions below
|
||||
let adapter = win_tun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr);
|
||||
if adapter.is_null() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Failed to crate adapter"));
|
||||
}
|
||||
Self::init(win_tun, adapter)
|
||||
}
|
||||
pub unsafe fn init(win_tun: wintun_raw::wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> io::Result<Self> {
|
||||
// 开启session
|
||||
let session = win_tun.WintunStartSession(adapter, 128 * 1024);
|
||||
if session.is_null() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "WintunStartSession failed"));
|
||||
}
|
||||
//SAFETY: We follow the contract required by CreateEventA. See MSDN
|
||||
//(the pointers are allowed to be null, and 0 is okay for the others)
|
||||
let shutdown_event = synchapi::CreateEventA(std::ptr::null_mut(),
|
||||
0, 0, std::ptr::null_mut());
|
||||
let read_event = win_tun.WintunGetReadWaitEvent(session) as winnt::HANDLE;
|
||||
|
||||
Ok(TunDevice {
|
||||
session,
|
||||
win_tun,
|
||||
read_event,
|
||||
shutdown_event,
|
||||
adapter,
|
||||
})
|
||||
}
|
||||
pub unsafe fn open<L>(library: L, name: &str) -> io::Result<Self>
|
||||
where L: Into<libloading::Library>, {
|
||||
let win_tun = match wintun_raw::wintun::from_library(library) {
|
||||
Ok(win_tun) => win_tun,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("library error {:?} ", e)));
|
||||
}
|
||||
};
|
||||
log::set_default_logger_if_unset(&win_tun);
|
||||
let name_utf16 = encode_utf16(name);
|
||||
let adapter = win_tun.WintunOpenAdapter(name_utf16.as_ptr());
|
||||
if adapter.is_null() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Failed to open adapter"));
|
||||
}
|
||||
Self::init(win_tun, adapter)
|
||||
}
|
||||
pub fn delete(self) -> io::Result<()> {
|
||||
drop(self);
|
||||
Ok(())
|
||||
}
|
||||
pub fn version(&self) -> io::Result<Version> {
|
||||
let version = unsafe { self.win_tun.WintunGetRunningDriverVersion() };
|
||||
if version == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "WintunGetRunningDriverVersion"));
|
||||
} else {
|
||||
Ok(Version {
|
||||
major: ((version >> 16) & 0xFF) as u16,
|
||||
minor: (version & 0xFF) as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Version {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
}
|
||||
|
||||
impl TunDevice {
|
||||
fn get_adapter_luid(&self) -> u64 {
|
||||
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
|
||||
unsafe { self.win_tun.WintunGetAdapterLUID(self.adapter, &mut luid as *mut wintun_raw::NET_LUID) };
|
||||
unsafe { std::mem::transmute(luid) }
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for TunDevice {
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
let _ = unsafe { synchapi::SetEvent(self.shutdown_event) };
|
||||
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_index(&self) -> io::Result<u32> {
|
||||
let luid = self.get_adapter_luid();
|
||||
ffi::luid_to_index(&unsafe { std::mem::transmute(luid) }).map(|index| index as u32)
|
||||
}
|
||||
|
||||
fn get_name(&self) -> io::Result<String> {
|
||||
let luid = self.get_adapter_luid();
|
||||
ffi::luid_to_alias(&unsafe { std::mem::transmute(luid) }).map(|name| {
|
||||
decode_utf16(&name)
|
||||
})
|
||||
}
|
||||
|
||||
fn set_name(&self, new_name: &str) -> io::Result<()> {
|
||||
let name = self.get_name()?;
|
||||
netsh::set_interface_name(&name, new_name)
|
||||
}
|
||||
|
||||
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
netsh::set_interface_ip(self.get_index()?, &address.into(), &mask.into())
|
||||
}
|
||||
|
||||
fn add_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
route::add_route(self.get_index()?, dest.into(), netmask.into(), gateway.into())
|
||||
}
|
||||
|
||||
fn delete_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
route::delete_route(self.get_index()?, dest.into(), netmask.into(), gateway.into())
|
||||
}
|
||||
|
||||
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
|
||||
netsh::set_interface_mtu(self.get_index()?, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
impl TunDevice {
|
||||
pub fn try_receive(&self) -> io::Result<Option<packet::TunPacket>> {
|
||||
let mut size = 0u32;
|
||||
|
||||
let bytes_ptr = unsafe {
|
||||
self.win_tun
|
||||
.WintunReceivePacket(self.session, &mut size as *mut u32)
|
||||
};
|
||||
|
||||
debug_assert!(size <= u16::MAX as u32);
|
||||
if bytes_ptr.is_null() {
|
||||
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
|
||||
let last_error = unsafe { winapi::um::errhandlingapi::GetLastError() };
|
||||
if last_error == winapi::shared::winerror::ERROR_NO_MORE_ITEMS {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "try_receive failed"))
|
||||
}
|
||||
} else {
|
||||
Ok(Some(packet::TunPacket {
|
||||
kind: packet::Kind::ReceivePacket,
|
||||
size: size as usize,
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes_ptr,
|
||||
tun_device: Some(&self),
|
||||
}))
|
||||
}
|
||||
}
|
||||
pub fn receive_blocking(&self) -> io::Result<packet::TunPacket> {
|
||||
loop {
|
||||
//Try 5 times to receive without blocking so we don't have to issue a syscall to wait
|
||||
//for the event if packets are being received at a rapid rate
|
||||
for _ in 0..5 {
|
||||
match self.try_receive()? {
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
Some(packet) => {
|
||||
return Ok(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Wait on both the read handle and the shutdown handle so that we stop when requested
|
||||
let handles = [self.read_event, self.shutdown_event];
|
||||
let result = unsafe {
|
||||
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
|
||||
//pointer to valid, aligned, stack memory
|
||||
synchapi::WaitForMultipleObjects(
|
||||
2,
|
||||
&handles as *const winnt::HANDLE,
|
||||
0,
|
||||
winbase::INFINITE,
|
||||
)
|
||||
};
|
||||
match result {
|
||||
winbase::WAIT_FAILED => return Err(io::Error::new(io::ErrorKind::Other, "WAIT_FAILED")),
|
||||
_ => {
|
||||
if result == winbase::WAIT_OBJECT_0 {
|
||||
//We have data!
|
||||
continue;
|
||||
} else if result == winbase::WAIT_OBJECT_0 + 1 {
|
||||
//Shutdown event triggered
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Shutdown event triggered"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TunDevice {
|
||||
pub fn allocate_send_packet(&self, size: u16) -> io::Result<packet::TunPacket> {
|
||||
let bytes_ptr = unsafe {
|
||||
self.win_tun.WintunAllocateSendPacket(self.session, size as u32)
|
||||
};
|
||||
if bytes_ptr.is_null() {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "allocate_send_packet failed"))
|
||||
} else {
|
||||
Ok(packet::TunPacket {
|
||||
kind: packet::Kind::SendPacketPending,
|
||||
size: size as usize,
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes_ptr,
|
||||
tun_device: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn send_packet(&self, mut packet: packet::TunPacket) {
|
||||
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
|
||||
|
||||
unsafe {
|
||||
self.win_tun
|
||||
.WintunSendPacket(self.session, packet.bytes_ptr)
|
||||
};
|
||||
//Mark the packet at sent
|
||||
packet.kind = packet::Kind::SendPacketSent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Drop for TunDevice {
|
||||
fn drop(&mut self) {
|
||||
//Close adapter on drop
|
||||
//This is why we need an Arc of wintun
|
||||
unsafe {
|
||||
self.win_tun.WintunCloseAdapter(self.adapter);
|
||||
self.win_tun.WintunDeleteDriver()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
use crate::TunDevice;
|
||||
|
||||
pub(crate) enum Kind {
|
||||
SendPacketPending,
|
||||
//Send packet type, but not sent yet
|
||||
SendPacketSent,
|
||||
//Send packet type - sent
|
||||
ReceivePacket,
|
||||
}
|
||||
|
||||
/// Represents a wintun packet
|
||||
pub struct TunPacket<'a> {
|
||||
pub(crate) kind: Kind,
|
||||
pub(crate) size:usize,
|
||||
pub(crate) bytes_ptr: *const u8,
|
||||
|
||||
//Share ownership of session to prevent the session from being dropped before packets that
|
||||
//belong to it
|
||||
pub(crate) tun_device: Option<&'a TunDevice>,
|
||||
}
|
||||
|
||||
impl <'a>TunPacket<'a> {
|
||||
/// Returns the bytes this packet holds as &mut.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.bytes_ptr as *mut u8, self.size) }
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the bytes this packet holds.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
unsafe { std::slice::from_raw_parts(self.bytes_ptr,self.size) }
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a>Drop for TunPacket<'a> {
|
||||
fn drop(&mut self) {
|
||||
match self.kind {
|
||||
Kind::ReceivePacket => {
|
||||
unsafe {
|
||||
//SAFETY:
|
||||
//
|
||||
// 1. We share ownership of the session therefore it hasn't been dropped yet
|
||||
// 2. Bytes is valid because each packet holds exclusive access to a region of the
|
||||
// ring buffer that the wintun session owns. We return that region of
|
||||
// memory back to wintun here
|
||||
let tun_device = self.tun_device.unwrap();
|
||||
tun_device.win_tun
|
||||
.WintunReleaseReceivePacket(tun_device.session, self.bytes_ptr)
|
||||
};
|
||||
}
|
||||
Kind::SendPacketPending => {
|
||||
//If someone allocates a packet with session.allocate_send_packet() and then it is
|
||||
//dropped without being sent, this will hold up the send queue because wintun expects
|
||||
//that every allocated packet is sent
|
||||
panic!("Packet was never sent!");
|
||||
}
|
||||
Kind::SendPacketSent => {
|
||||
//Nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
/* automatically generated by rust-bindgen 0.59.1 */
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct __BindgenBitfieldUnit<Storage> {
|
||||
storage: Storage,
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage> {
|
||||
#[inline]
|
||||
pub const fn new(storage: Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage>
|
||||
where
|
||||
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn get_bit(&self, index: usize) -> bool {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = self.storage.as_ref()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
byte & mask == mask
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = &mut self.storage.as_mut()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
if val {
|
||||
*byte |= mask;
|
||||
} else {
|
||||
*byte &= !mask;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
let mut val = 0;
|
||||
for i in 0..(bit_width as usize) {
|
||||
if self.get_bit(i + bit_offset) {
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
val |= 1 << index;
|
||||
}
|
||||
}
|
||||
val
|
||||
}
|
||||
#[inline]
|
||||
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
for i in 0..(bit_width as usize) {
|
||||
let mask = 1 << i;
|
||||
let val_bit_is_set = val & mask == mask;
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type wchar_t = ::std::os::raw::c_ushort;
|
||||
pub type DWORD = ::std::os::raw::c_ulong;
|
||||
pub type BOOL = ::std::os::raw::c_int;
|
||||
pub type BYTE = ::std::os::raw::c_uchar;
|
||||
pub type ULONG64 = ::std::os::raw::c_ulonglong;
|
||||
pub type DWORD64 = ::std::os::raw::c_ulonglong;
|
||||
pub type WCHAR = wchar_t;
|
||||
pub type LPCWSTR = *const WCHAR;
|
||||
pub type HANDLE = *mut ::std::os::raw::c_void;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _GUID {
|
||||
pub Data1: ::std::os::raw::c_ulong,
|
||||
pub Data2: ::std::os::raw::c_ushort,
|
||||
pub Data3: ::std::os::raw::c_ushort,
|
||||
pub Data4: [::std::os::raw::c_uchar; 8usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__GUID() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_GUID>(),
|
||||
16usize,
|
||||
concat!("Size of: ", stringify!(_GUID))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_GUID>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(_GUID))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data1 as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data1)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data2 as *const _ as usize },
|
||||
4usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data2)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data3 as *const _ as usize },
|
||||
6usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data3)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data4 as *const _ as usize },
|
||||
8usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data4)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type GUID = _GUID;
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union _NET_LUID_LH {
|
||||
pub Value: ULONG64,
|
||||
pub Info: _NET_LUID_LH__bindgen_ty_1,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[repr(align(8))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _NET_LUID_LH__bindgen_ty_1 {
|
||||
pub _bitfield_align_1: [u32; 0],
|
||||
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__NET_LUID_LH__bindgen_ty_1() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_NET_LUID_LH__bindgen_ty_1>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(_NET_LUID_LH__bindgen_ty_1))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_NET_LUID_LH__bindgen_ty_1>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(_NET_LUID_LH__bindgen_ty_1))
|
||||
);
|
||||
}
|
||||
impl _NET_LUID_LH__bindgen_ty_1 {
|
||||
#[inline]
|
||||
pub fn Reserved(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_Reserved(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(0usize, 24u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn NetLuidIndex(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 24u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_NetLuidIndex(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(24usize, 24u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn IfType(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 16u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_IfType(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(48usize, 16u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn new_bitfield_1(
|
||||
Reserved: ULONG64,
|
||||
NetLuidIndex: ULONG64,
|
||||
IfType: ULONG64,
|
||||
) -> __BindgenBitfieldUnit<[u8; 8usize]> {
|
||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default();
|
||||
__bindgen_bitfield_unit.set(0usize, 24u8, {
|
||||
let Reserved: u64 = unsafe { ::std::mem::transmute(Reserved) };
|
||||
Reserved as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(24usize, 24u8, {
|
||||
let NetLuidIndex: u64 = unsafe { ::std::mem::transmute(NetLuidIndex) };
|
||||
NetLuidIndex as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(48usize, 16u8, {
|
||||
let IfType: u64 = unsafe { ::std::mem::transmute(IfType) };
|
||||
IfType as u64
|
||||
});
|
||||
__bindgen_bitfield_unit
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__NET_LUID_LH() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_NET_LUID_LH>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(_NET_LUID_LH))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_NET_LUID_LH>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(_NET_LUID_LH))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Value as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_NET_LUID_LH),
|
||||
"::",
|
||||
stringify!(Value)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Info as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_NET_LUID_LH),
|
||||
"::",
|
||||
stringify!(Info)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type NET_LUID_LH = _NET_LUID_LH;
|
||||
pub type NET_LUID = NET_LUID_LH;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _WINTUN_ADAPTER {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[doc = " A handle representing Wintun adapter"]
|
||||
pub type WINTUN_ADAPTER_HANDLE = *mut _WINTUN_ADAPTER;
|
||||
#[doc = "< Informational"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO: WINTUN_LOGGER_LEVEL = 0;
|
||||
#[doc = "< Warning"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN: WINTUN_LOGGER_LEVEL = 1;
|
||||
#[doc = "< Error"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR: WINTUN_LOGGER_LEVEL = 2;
|
||||
#[doc = " Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK."]
|
||||
pub type WINTUN_LOGGER_LEVEL = ::std::os::raw::c_int;
|
||||
#[doc = " Called by internal logger to report diagnostic messages"]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Level Message level."]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC."]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Message Message text."]
|
||||
pub type WINTUN_LOGGER_CALLBACK = ::std::option::Option<
|
||||
unsafe extern "C" fn(Level: WINTUN_LOGGER_LEVEL, Timestamp: DWORD64, Message: LPCWSTR),
|
||||
>;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _TUN_SESSION {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[doc = " A handle representing Wintun session"]
|
||||
pub type WINTUN_SESSION_HANDLE = *mut _TUN_SESSION;
|
||||
extern crate libloading;
|
||||
pub struct wintun {
|
||||
__library: ::libloading::Library,
|
||||
pub WintunCreateAdapter: unsafe extern "C" fn(
|
||||
arg1: LPCWSTR,
|
||||
arg2: LPCWSTR,
|
||||
arg3: *const GUID,
|
||||
) -> WINTUN_ADAPTER_HANDLE,
|
||||
pub WintunCloseAdapter: unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE),
|
||||
pub WintunOpenAdapter: unsafe extern "C" fn(arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE,
|
||||
pub WintunGetAdapterLUID:
|
||||
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: *mut NET_LUID),
|
||||
pub WintunGetRunningDriverVersion: unsafe extern "C" fn() -> DWORD,
|
||||
pub WintunDeleteDriver: unsafe extern "C" fn() -> BOOL,
|
||||
pub WintunSetLogger: unsafe extern "C" fn(arg1: WINTUN_LOGGER_CALLBACK),
|
||||
pub WintunStartSession:
|
||||
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: DWORD) -> WINTUN_SESSION_HANDLE,
|
||||
pub WintunEndSession: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE),
|
||||
pub WintunGetReadWaitEvent: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE) -> HANDLE,
|
||||
pub WintunReceivePacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *mut DWORD) -> *mut BYTE,
|
||||
pub WintunReleaseReceivePacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
|
||||
pub WintunAllocateSendPacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: DWORD) -> *mut BYTE,
|
||||
pub WintunSendPacket: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
|
||||
}
|
||||
impl wintun {
|
||||
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
|
||||
where
|
||||
P: AsRef<::std::ffi::OsStr>,
|
||||
{
|
||||
let library = ::libloading::Library::new(path)?;
|
||||
Self::from_library(library)
|
||||
}
|
||||
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
|
||||
where
|
||||
L: Into<::libloading::Library>,
|
||||
{
|
||||
let __library = library.into();
|
||||
let WintunCreateAdapter = __library.get(b"WintunCreateAdapter\0").map(|sym| *sym)?;
|
||||
let WintunCloseAdapter = __library.get(b"WintunCloseAdapter\0").map(|sym| *sym)?;
|
||||
let WintunOpenAdapter = __library.get(b"WintunOpenAdapter\0").map(|sym| *sym)?;
|
||||
let WintunGetAdapterLUID = __library.get(b"WintunGetAdapterLUID\0").map(|sym| *sym)?;
|
||||
let WintunGetRunningDriverVersion = __library
|
||||
.get(b"WintunGetRunningDriverVersion\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunDeleteDriver = __library.get(b"WintunDeleteDriver\0").map(|sym| *sym)?;
|
||||
let WintunSetLogger = __library.get(b"WintunSetLogger\0").map(|sym| *sym)?;
|
||||
let WintunStartSession = __library.get(b"WintunStartSession\0").map(|sym| *sym)?;
|
||||
let WintunEndSession = __library.get(b"WintunEndSession\0").map(|sym| *sym)?;
|
||||
let WintunGetReadWaitEvent = __library.get(b"WintunGetReadWaitEvent\0").map(|sym| *sym)?;
|
||||
let WintunReceivePacket = __library.get(b"WintunReceivePacket\0").map(|sym| *sym)?;
|
||||
let WintunReleaseReceivePacket = __library
|
||||
.get(b"WintunReleaseReceivePacket\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunAllocateSendPacket = __library
|
||||
.get(b"WintunAllocateSendPacket\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunSendPacket = __library.get(b"WintunSendPacket\0").map(|sym| *sym)?;
|
||||
Ok(wintun {
|
||||
__library,
|
||||
WintunCreateAdapter,
|
||||
WintunCloseAdapter,
|
||||
WintunOpenAdapter,
|
||||
WintunGetAdapterLUID,
|
||||
WintunGetRunningDriverVersion,
|
||||
WintunDeleteDriver,
|
||||
WintunSetLogger,
|
||||
WintunStartSession,
|
||||
WintunEndSession,
|
||||
WintunGetReadWaitEvent,
|
||||
WintunReceivePacket,
|
||||
WintunReleaseReceivePacket,
|
||||
WintunAllocateSendPacket,
|
||||
WintunSendPacket,
|
||||
})
|
||||
}
|
||||
pub unsafe fn WintunCreateAdapter(
|
||||
&self,
|
||||
arg1: LPCWSTR,
|
||||
arg2: LPCWSTR,
|
||||
arg3: *const GUID,
|
||||
) -> WINTUN_ADAPTER_HANDLE {
|
||||
(self.WintunCreateAdapter)(arg1, arg2, arg3)
|
||||
}
|
||||
pub unsafe fn WintunCloseAdapter(&self, arg1: WINTUN_ADAPTER_HANDLE) -> () {
|
||||
(self.WintunCloseAdapter)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunOpenAdapter(&self, arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE {
|
||||
(self.WintunOpenAdapter)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunGetAdapterLUID(
|
||||
&self,
|
||||
arg1: WINTUN_ADAPTER_HANDLE,
|
||||
arg2: *mut NET_LUID,
|
||||
) -> () {
|
||||
(self.WintunGetAdapterLUID)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunGetRunningDriverVersion(&self) -> DWORD {
|
||||
(self.WintunGetRunningDriverVersion)()
|
||||
}
|
||||
pub unsafe fn WintunDeleteDriver(&self) -> BOOL {
|
||||
(self.WintunDeleteDriver)()
|
||||
}
|
||||
pub unsafe fn WintunSetLogger(&self, arg1: WINTUN_LOGGER_CALLBACK) -> () {
|
||||
(self.WintunSetLogger)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunStartSession(
|
||||
&self,
|
||||
arg1: WINTUN_ADAPTER_HANDLE,
|
||||
arg2: DWORD,
|
||||
) -> WINTUN_SESSION_HANDLE {
|
||||
(self.WintunStartSession)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunEndSession(&self, arg1: WINTUN_SESSION_HANDLE) -> () {
|
||||
(self.WintunEndSession)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunGetReadWaitEvent(&self, arg1: WINTUN_SESSION_HANDLE) -> HANDLE {
|
||||
(self.WintunGetReadWaitEvent)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunReceivePacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: *mut DWORD,
|
||||
) -> *mut BYTE {
|
||||
(self.WintunReceivePacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunReleaseReceivePacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: *const BYTE,
|
||||
) -> () {
|
||||
(self.WintunReleaseReceivePacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunAllocateSendPacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: DWORD,
|
||||
) -> *mut BYTE {
|
||||
(self.WintunAllocateSendPacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunSendPacket(&self, arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE) -> () {
|
||||
(self.WintunSendPacket)(arg1, arg2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user