#![allow(non_snake_case)]
use std::ptr::NonNull;
use std::{io, mem, ptr};
use windows_sys::core::{GUID, PCWSTR};
use windows_sys::Win32::Foundation::{BOOL, HANDLE};
use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH;
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
use super::{WintunAdapter, WintunLoggerCallback, WintunPacket, WintunSession};
const WINTUN_LIB: *const u8 = b"wintun.dll\0".as_ptr();
pub struct Wintun {
api: WintunApi,
}
struct WintunApi {
WintunCreateAdapter: unsafe extern "C" fn(
name: PCWSTR,
tunnel_type: PCWSTR,
requested_guid: GUID,
) -> *mut WintunAdapter,
WintunOpenAdapter: unsafe extern "C" fn(name: PCWSTR) -> *mut WintunAdapter,
WintunCloseAdapter: unsafe extern "C" fn(adapter: *mut WintunAdapter),
WintunDeleteDriver: unsafe extern "C" fn() -> BOOL,
WintunGetAdapterLUID: unsafe extern "C" fn(adapter: *mut WintunAdapter, luid: *mut NET_LUID_LH),
WintunGetRunningDriverVersion: unsafe extern "C" fn() -> u32,
WintunSetLogger: unsafe extern "C" fn(log_callback: WintunLoggerCallback),
WintunStartSession:
unsafe extern "C" fn(adapter: *mut WintunAdapter, capacity: u32) -> *mut WintunSession,
WintunEndSession: unsafe extern "C" fn(session: *mut WintunSession),
WintunGetReadWaitEvent: unsafe extern "C" fn(session: *mut WintunSession) -> HANDLE,
WintunReceivePacket:
unsafe extern "C" fn(session: *mut WintunSession, packet_size: *mut u32) -> *mut u8,
WintunReleaseReceivePacket: unsafe extern "C" fn(session: *mut WintunSession, packet: *mut u8),
WintunAllocateSendPacket:
unsafe extern "C" fn(session: *mut WintunSession, packet_size: u32) -> *mut u8,
WintunSendPacket: unsafe extern "C" fn(session: *mut WintunSession, packet: *mut u8),
}
impl Wintun {
unsafe fn resolve_func(
lib: *mut libc::c_void,
func_name: &[u8],
) -> io::Result<*const libc::c_void> {
GetProcAddress(lib, func_name.as_ptr())
.ok_or(io::Error::last_os_error())
.map(|f| f as *const libc::c_void)
}
unsafe fn resolve_api(lib: *mut libc::c_void) -> io::Result<WintunApi> {
Ok(WintunApi {
WintunCreateAdapter: mem::transmute(Self::resolve_func(lib, b"WintunCreateAdapter\0")?),
WintunOpenAdapter: mem::transmute(Self::resolve_func(lib, b"WintunOpenAdapter\0")?),
WintunCloseAdapter: mem::transmute(Self::resolve_func(lib, b"WintunCloseAdapter\0")?),
WintunDeleteDriver: mem::transmute(Self::resolve_func(lib, b"WintunDeleteAdapter\0")?),
WintunGetAdapterLUID: mem::transmute(Self::resolve_func(
lib,
b"WintunGetAdapterLUID\0",
)?),
WintunGetRunningDriverVersion: mem::transmute(Self::resolve_func(
lib,
b"WintunGetRunningDriverVersion\0",
)?),
WintunSetLogger: mem::transmute(Self::resolve_func(lib, b"WintunSetLogger\0")?),
WintunStartSession: mem::transmute(Self::resolve_func(lib, b"WintunStartSession\0")?),
WintunEndSession: mem::transmute(Self::resolve_func(lib, b"WintunEndSession\0")?),
WintunGetReadWaitEvent: mem::transmute(Self::resolve_func(
lib,
b"WintunGetReadWaitEvent\0",
)?),
WintunReceivePacket: mem::transmute(Self::resolve_func(lib, b"WintunReceivePacket\0")?),
WintunReleaseReceivePacket: mem::transmute(Self::resolve_func(
lib,
b"WintunReleaseReceivePacket\0",
)?),
WintunAllocateSendPacket: mem::transmute(Self::resolve_func(
lib,
b"WintunAllocateSendPacket\0",
)?),
WintunSendPacket: mem::transmute(Self::resolve_func(lib, b"WintunSendPacket\0")?),
})
}
pub fn new() -> io::Result<Self> {
unsafe {
let lib = LoadLibraryA(WINTUN_LIB);
if lib.is_null() {
return Err(io::Error::last_os_error());
}
let api =
Self::resolve_api(lib).map_err(|e| io::Error::new(io::ErrorKind::NotFound, e))?;
Ok(Self { api })
}
}
pub fn driver_version(&self) -> io::Result<u32> {
let version = unsafe { (self.api.WintunGetRunningDriverVersion)() };
match version {
0 => Err(io::Error::last_os_error()),
v => Ok(v),
}
}
pub fn create_adapter(
&self,
name: &[u16],
tunnel_type: &[u16],
requested_guid: GUID,
) -> io::Result<NonNull<WintunAdapter>> {
if name.last() != Some(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"adapter name not null-terminated",
));
}
if tunnel_type.last() != Some(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"tunnel type not null-terminated",
));
}
let handle = unsafe {
(self.api.WintunCreateAdapter)(name.as_ptr(), tunnel_type.as_ptr(), requested_guid)
};
NonNull::new(handle).ok_or(io::Error::last_os_error())
}
pub fn open_adapter(&self, name: &[u16]) -> io::Result<NonNull<WintunAdapter>> {
if name.last() != Some(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"adapter name not null-terminated",
));
}
let handle = unsafe { (self.api.WintunOpenAdapter)(name.as_ptr()) };
NonNull::new(handle).ok_or(io::Error::last_os_error())
}
pub unsafe fn close_adapter(&self, adapter: *mut WintunAdapter) {
(self.api.WintunCloseAdapter)(adapter)
}
pub fn delete_driver(&self) -> io::Result<()> {
if unsafe { (self.api.WintunDeleteDriver)() } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
#[allow(unused)]
pub fn get_adapter_luid(&self, adapter: &mut WintunAdapter) -> NET_LUID_LH {
let mut luid = NET_LUID_LH { Value: 0 };
unsafe {
(self.api.WintunGetAdapterLUID)(adapter, ptr::addr_of_mut!(luid));
}
luid
}
pub unsafe fn set_logger(&self, log_callback: WintunLoggerCallback) {
(self.api.WintunSetLogger)(log_callback);
}
pub fn start_session(
&self,
adapter: &mut WintunAdapter,
capacity: u32,
) -> io::Result<NonNull<WintunSession>> {
let session = unsafe { (self.api.WintunStartSession)(adapter, capacity) };
NonNull::new(session).ok_or(io::Error::last_os_error())
}
pub unsafe fn end_session(&self, session: *mut WintunSession) {
(self.api.WintunEndSession)(session)
}
pub fn read_event_handle(&self, session: *mut WintunSession) -> HANDLE {
unsafe { (self.api.WintunGetReadWaitEvent)(session) }
}
pub fn recv_packet(
&self,
session: *mut WintunSession,
packet_size: &mut u32,
) -> io::Result<WintunPacket> {
let pkt = unsafe { (self.api.WintunReceivePacket)(session, packet_size) };
NonNull::new(pkt).ok_or(io::Error::last_os_error())
}
pub fn free_packet(&self, session: *mut WintunSession, packet: WintunPacket) {
unsafe {
(self.api.WintunReleaseReceivePacket)(session, packet.as_ptr());
}
}
pub fn allocate_packet(
&self,
session: *mut WintunSession,
packet_size: u32,
) -> io::Result<WintunPacket> {
let pkt = unsafe { (self.api.WintunAllocateSendPacket)(session, packet_size) };
NonNull::new(pkt).ok_or(io::Error::last_os_error())
}
pub fn send_packet(&self, session: *mut WintunSession, packet: WintunPacket) {
unsafe { (self.api.WintunSendPacket)(session, packet.as_ptr()) }
}
}