#![allow(non_snake_case)]
use std::ptr::NonNull;
use std::{io, ptr};
use windows_sys::core::{GUID, PCWSTR};
use windows_sys::Win32::Foundation::{BOOL, HANDLE};
use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH;
use super::{WintunAdapter, WintunLoggerCallback, WintunPacket, WintunSession};
#[link(name = "wintun", kind = "raw-dylib")]
extern "C" {
fn WintunCreateAdapter(
name: PCWSTR,
tunnel_type: PCWSTR,
requested_guid: GUID,
) -> *mut WintunAdapter;
fn WintunOpenAdapter(name: PCWSTR) -> *mut WintunAdapter;
fn WintunCloseAdapter(adapter: *mut WintunAdapter);
fn WintunDeleteDriver() -> BOOL;
fn WintunGetAdapterLUID(adapter: *mut WintunAdapter, luid: *mut NET_LUID_LH);
fn WintunGetRunningDriverVersion() -> u32;
fn WintunSetLogger(log_callback: WintunLoggerCallback);
fn WintunStartSession(adapter: *mut WintunAdapter, capacity: u32) -> *mut WintunSession;
fn WintunEndSession(session: *mut WintunSession);
fn WintunGetReadWaitEvent(session: *mut WintunSession) -> HANDLE;
fn WintunReceivePacket(session: *mut WintunSession, packet_size: *mut u32) -> *mut u8;
fn WintunReleaseReceivePacket(session: *mut WintunSession, packet: *mut u8);
fn WintunAllocateSendPacket(session: *mut WintunSession, packet_size: u32) -> *mut u8;
fn WintunSendPacket(session: *mut WintunSession, packet: *mut u8);
}
pub struct Wintun;
impl Wintun {
pub fn new() -> io::Result<Self> {
Ok(Self)
}
pub fn driver_version(&self) -> io::Result<u32> {
let version = unsafe { 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 { 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 { WintunOpenAdapter(name.as_ptr()) };
NonNull::new(handle).ok_or(io::Error::last_os_error())
}
pub unsafe fn close_adapter(&self, adapter: *mut WintunAdapter) {
WintunCloseAdapter(adapter)
}
pub fn delete_driver(&self) -> io::Result<()> {
if unsafe { WintunDeleteDriver() } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn get_adapter_luid(&self, adapter: &mut WintunAdapter) -> NET_LUID_LH {
let mut luid = NET_LUID_LH { Value: 0 };
unsafe {
WintunGetAdapterLUID(adapter, ptr::addr_of_mut!(luid));
}
luid
}
pub unsafe fn set_logger(&self, log_callback: WintunLoggerCallback) {
WintunSetLogger(log_callback);
}
pub fn start_session(
&self,
adapter: &mut WintunAdapter,
capacity: u32,
) -> io::Result<NonNull<WintunSession>> {
let session = unsafe { WintunStartSession(adapter, capacity) };
NonNull::new(session).ok_or(io::Error::last_os_error())
}
pub unsafe fn end_session(&self, session: *mut WintunSession) {
WintunEndSession(session)
}
pub fn read_event_handle(&self, session: *mut WintunSession) -> HANDLE {
unsafe { WintunGetReadWaitEvent(session) }
}
pub fn recv_packet(
&self,
session: *mut WintunSession,
packet_size: &mut u32,
) -> io::Result<WintunPacket> {
let pkt = unsafe { 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 {
WintunReleaseReceivePacket(session, packet.as_ptr());
}
}
pub fn allocate_packet(
&self,
session: *mut WintunSession,
packet_size: u32,
) -> io::Result<WintunPacket> {
let pkt = unsafe { 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 { WintunSendPacket(session, packet.as_ptr()) }
}
}