解决win7不能启动的问题

This commit is contained in:
lubeilin
2023-04-23 15:54:15 +08:00
parent c08b9cefe9
commit 18df3c2c92
21 changed files with 2450 additions and 13 deletions
+345
View File
@@ -0,0 +1,345 @@
/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
/// the WintunAdapter* C functions.
///
/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
/// wintun functionality
use crate::error;
use crate::session;
use crate::util;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Wintun;
use std::ptr;
use std::sync::Arc;
use itertools::Itertools;
use log::*;
use once_cell::sync::OnceCell;
use rand::Rng;
use widestring::U16CStr;
use widestring::U16CString;
use winapi::{
shared::winerror,
um::{ipexport, iphlpapi, synchapi},
};
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
pub struct Adapter {
adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
wintun: Wintun,
guid: u128,
}
fn encode_utf16(string: &str, max_characters: usize) -> Result<U16CString, error::WintunError> {
let utf16 = U16CString::from_str(string)?;
if utf16.len() >= max_characters {
//max_characters is the maximum number of characters including the null terminator. And .len() measures the
//number of characters (excluding the null terminator). Therefore we can hold a string with
//max_characters - 1 because the null terminator sits in the last element. However a string
//of length max_characters needs max_characters + 1 to store the null terminator the >=
//check holds
Err(format!(
//TODO: Better error handling
"Length too large. Size: {}, Max: {}",
utf16.len(),
max_characters
)
.into())
} else {
Ok(utf16)
}
}
fn encode_pool_name(name: &str) -> Result<U16CString, error::WintunError> {
encode_utf16(name, crate::MAX_POOL)
}
fn encode_adapter_name(name: &str) -> Result<U16CString, error::WintunError> {
encode_utf16(name, crate::MAX_POOL)
}
fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> u64 {
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
unsafe { std::mem::transmute(luid) }
}
impl Adapter {
//TODO: Call get last error for error information on failure and improve error types
/// Creates a new wintun adapter inside the pool `pool` with name `name`
///
/// Optionally a GUID can be specified that will become the GUID of this adapter once created.
/// Adapters obtained via this function will be able to return their adapter index via
/// [`Adapter::get_adapter_index`]
pub fn create(
wintun: &Wintun,
pool: &str,
name: &str,
guid: Option<u128>,
) -> Result<Arc<Adapter>, error::WintunError> {
let pool_utf16 = encode_pool_name(pool)?;
let name_utf16 = encode_adapter_name(name)?;
let guid = match guid {
Some(guid) => guid,
None => {
// Use random bytes so that we can identify this adapter in get_adapter_index
let mut guid_bytes: [u8; 16] = [0u8; 16];
rand::thread_rng().fill(&mut guid_bytes);
u128::from_ne_bytes(guid_bytes)
}
};
//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) };
//TODO: The guid of the adapter once created might differ from the one provided because of
//the byte order of the segments of the GUID struct that are larger than a byte. Verify
//that this works as expected
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
crate::log::set_default_logger_if_unset(wintun);
//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 result = unsafe {
wintun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr)
};
if result.is_null() {
Err("Failed to crate adapter".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
guid,
}))
}
}
/// Attempts to open an existing wintun interface name `name`.
///
/// Adapters opened via this call will have an unknown GUID meaning [`Adapter::get_adapter_index`]
/// will always fail because knowing the adapter's GUID is required to determine its index.
/// Currently a workaround is to delete and re-create a new adapter every time one is needed so
/// that it gets created with a known GUID, allowing [`Adapter::get_adapter_index`] to works as
/// expected. There is likely a way to get the GUID of our adapter using the Windows Registry
/// or via the Win32 API, so PR's that solve this issue are always welcome!
pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, error::WintunError> {
let name_utf16 = encode_adapter_name(name)?;
crate::log::set_default_logger_if_unset(wintun);
let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
if result.is_null() {
Err("WintunOpenAdapter failed".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
// TODO: get GUID somehow
guid: 0,
}))
}
}
/// Delete an adapter, consuming it in the process
pub fn delete(self) -> Result<(), ()> {
//Dropping an adapter closes it
drop(self);
// Return a result here so that if later the API changes to be fallible, we can support it
// without making a breaking change
Ok(())
}
/// Initiates a new wintun session on the given adapter.
///
/// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
/// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MIN_RING_CAPACITY`].
pub fn start_session(
self: &Arc<Self>,
capacity: u32,
) -> Result<session::Session, error::WintunError> {
let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
if !range.contains(&capacity) {
return Err(Box::new(error::ApiError::CapacityOutOfRange(
error::OutOfRangeData {
range,
value: capacity,
},
)));
}
if !capacity.is_power_of_two() {
return Err(Box::new(error::ApiError::CapacityNotPowerOfTwo(capacity)));
}
let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
if result.is_null() {
Err("WintunStartSession failed".into())
} else {
Ok(session::Session {
session: UnsafeHandle(result),
wintun: self.wintun.clone(),
read_event: OnceCell::new(),
shutdown_event: unsafe {
//SAFETY: We follow the contract required by CreateEventA. See MSDN
//(the pointers are allowed to be null, and 0 is okay for the others)
UnsafeHandle(synchapi::CreateEventA(
std::ptr::null_mut(),
0,
0,
std::ptr::null_mut(),
))
},
adapter: Arc::clone(self),
})
}
}
/// Returns the Win32 LUID for this adapter
pub fn get_luid(&self) -> u64 {
get_adapter_luid(&self.wintun, self.adapter.0)
}
/// Returns the Win32 interface index of this adapter. Useful for specifying the interface
/// when executing `netsh interface ip` commands
pub fn get_adapter_index(&self) -> Result<u32, error::WintunError> {
let mut buf_len: u32 = 0;
//First figure out the size of the buffer needed to store the adapter info
//SAFETY: We are upholding the contract of GetInterfaceInfo. buf_len is a valid pointer to
//stack memory
let result =
unsafe { iphlpapi::GetInterfaceInfo(std::ptr::null_mut(), &mut buf_len as *mut u32) };
if result != winerror::NO_ERROR && result != winerror::ERROR_INSUFFICIENT_BUFFER {
let err_msg = util::get_error_message(result);
error!("Failed to get interface info: {}", err_msg);
//TODO: Better error types
return Err(format!("GetInterfaceInfo failed: {}", err_msg).into());
}
//Allocate a buffer of the requested size
//IP_INTERFACE_INFO must be aligned by at least 4 byte boundaries so use u32 as the
//underlying data storage type
let buf_elements = buf_len as usize / std::mem::size_of::<u32>() + 1;
//Round up incase integer division truncated a byte that filled a partial element
let mut buf: Vec<u32> = vec![0; buf_elements];
let buf_bytes = buf.len() * std::mem::size_of::<u32>();
assert!(buf_bytes >= buf_len as usize);
//SAFETY:
//
// 1. We are upholding the contract of GetInterfaceInfo.
// 2. `final_buf_len` is an aligned, valid pointer to stack memory
// 3. buf is a valid, non-null pointer to at least `buf_len` bytes of heap memory,
// aligned to at least 4 byte boundaries
//
//Get the info
let mut final_buf_len: u32 = buf_len;
let result = unsafe {
iphlpapi::GetInterfaceInfo(
buf.as_mut_ptr() as *mut ipexport::IP_INTERFACE_INFO,
&mut final_buf_len as *mut u32,
)
};
if result != winerror::NO_ERROR {
let err_msg = util::get_error_message(result);
//TODO: maybe over allocate the buffer in case the needed size changes between the two
//calls to GetInterfaceInfo if another adapter is added
error!(
"Failed to get interface info a second time: {}. Original len: {}, final len: {}",
err_msg, buf_len, final_buf_len
);
return Err(format!("GetInterfaceInfo failed a second time: {}", err_msg).into());
}
let info = buf.as_mut_ptr() as *const ipexport::IP_INTERFACE_INFO;
//SAFETY:
// info is a valid, non-null, at least 4 byte aligned pointer obtained from
// Vec::with_capacity that is readable for up to `buf_len` bytes which is guaranteed to be
// larger than on IP_INTERFACE_INFO struct as the kernel would never ask for less memory then
// what it will write. The largest type inside IP_INTERFACE_INFO is a u32 therefore
// a painter to IP_INTERFACE_INFO requires an alignment of at leant 4 bytes, which
// Vec<u32>::as_mut_ptr() provides
let adapter_base = unsafe { &*info };
let adapter_count = adapter_base.NumAdapters;
let first_adapter = &adapter_base.Adapter as *const ipexport::IP_ADAPTER_INDEX_MAP;
// SAFETY:
// 1. first_adapter is a valid, non null pointer, aligned to at least 4 byte boundaries
// obtained from moving a multiple of 4 offset into the buf given by Vec::with_capacity.
// 2. We gave GetInterfaceInfo a buffer of at least least `buf_len` bytes to work with and it
// succeeded in writing the adapter information within the bounds of that buffer, otherwise
// it would've failed. Because the operation succeeded, we know that reading n=NumAdapters
// IP_ADAPTER_INDEX_MAP structs stays within the bounds of buf's buffer
let interfaces =
unsafe { std::slice::from_raw_parts(first_adapter, adapter_count as usize) };
let mut tmp = Vec::new();
for interface in interfaces {
let name =
unsafe { U16CStr::from_ptr_str(&interface.Name as *const u16).to_string_lossy() };
//Nam is something like: \DEVICE\TCPIP_{29C47F55-C7BD-433A-8BF7-408DFD3B3390}
//where the GUID is the {29C4...90}, separated by dashes
let open = name.chars().position(|c| c == '{').ok_or(format!(
"Failed to find {{ character inside adapter name: {}",
name
))?;
let close = name.chars().position(|c| c == '}').ok_or(format!(
"Failed to find }} character inside adapter name: {}",
name
))?;
let digits: Vec<u8> = name[open..close]
.chars()
.filter(|c| c.is_digit(16))
.chunks(2)
.into_iter()
.filter_map(|mut chunk| {
//Filter out chunks that have < 2 digits
if let Some(a) = chunk.next() {
if let Some(b) = chunk.next() {
return Some((a, b));
}
}
None
})
.map(|digits| {
let chars: [u8; 2] = [digits.0 as u8, digits.1 as u8];
let s = std::str::from_utf8(&chars).unwrap();
u8::from_str_radix(s, 16).unwrap()
})
.collect();
//Our index is the adapter which has a guid in its name that matches ours
//For now we just check for a guid with the same hex bytes in any order
//TODO: byte swap GUID from name so that we can compare self.guid with the parsed GUID
//directly
let mut match_count = 0;
for byte in self.guid.to_ne_bytes() {
if digits.contains(&byte) {
match_count += 1;
}
}
tmp.push(format!("interfaces name={:?},digits={:?},index={:?}", name,digits, interface.Index));
if match_count == digits.len() {
return Ok(interface.Index);
}
}
log::info!("interfaces:{:?},guid={}",tmp,self.guid);
Err("Unable to find matching GUID".into())
}
}
impl Drop for Adapter {
fn drop(&mut self) {
//Close adapter on drop
//This is why we need an Arc of wintun
unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
self.adapter = UnsafeHandle(ptr::null_mut());
}
}
+36
View File
@@ -0,0 +1,36 @@
use std::fmt::Display;
pub type WintunError = Box<dyn std::error::Error>;
/// Error type used to convey that a value is outside of a range that it must fall inside
#[derive(Debug)]
pub struct OutOfRangeData<T> {
pub range: std::ops::RangeInclusive<T>,
pub value: T,
}
/// Error type returned when preconditions of this API are broken
#[derive(Debug)]
pub enum ApiError {
CapacityNotPowerOfTwo(u32),
CapacityOutOfRange(OutOfRangeData<u32>),
}
impl Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
ApiError::CapacityOutOfRange(data) => write!(
f,
"Capacity {} out of range. Must be within {}..={}",
data.value,
data.range.start(),
data.range.end()
),
ApiError::CapacityNotPowerOfTwo(cap) => {
write!(f, "Capacity {} is not a power of two", cap)
}
}
}
}
impl std::error::Error for ApiError {}
+174
View File
@@ -0,0 +1,174 @@
//! Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
//!
//! All features of the Wintun library are wrapped using pure rust types and functions to make
//! usage feel ergonomic.
//!
//! # Usage
//!
//! Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
//! using [`load`], [`load_from_path`] or [`load_from_library`].
//!
//! Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
//! adapter. Start a session with [`Adapter::start_session`].
//!
//! # Example
//! ```no_run
//! use std::sync::Arc;
//!
//! //Must be run as Administrator because we create network adapters
//! //Load the wintun dll file so that we can call the underlying C functions
//! //Unsafe because we are loading an arbitrary dll file
//! let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
//! .expect("Failed to load wintun dll");
//!
//! //Try to open an adapter with the name "Demo"
//! let adapter = match wintun::Adapter::open(&wintun, "Demo") {
//! Ok(a) => a,
//! Err(_) => {
//! //If loading failed (most likely it didn't exist), create a new one
//! wintun::Adapter::create(&wintun, "Example", "Demo", None)
//! .expect("Failed to create wintun adapter!")
//! }
//! };
//! //Specify the size of the ring buffer the wintun driver should use.
//! let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
//!
//! //Get a 20 byte packet from the ring buffer
//! let mut packet = session.allocate_send_packet(20).unwrap();
//! let bytes: &mut [u8] = packet.bytes_mut();
//! //Write IPV4 version and header length
//! bytes[0] = 0x40;
//!
//! //Finish writing IP header
//! bytes[9] = 0x69;
//! bytes[10] = 0x04;
//! bytes[11] = 0x20;
//! //...
//!
//! //Send the packet to wintun virtual adapter for processing by the system
//! session.send_packet(packet);
//!
//! //Stop any readers blocking for data on other threads
//! //Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
//! //session, blocking it from being dropped
//! session.shutdown();
//!
//! //the session is stopped on drop
//! //drop(session);
//!
//! //drop(adapter)
//! //And the adapter closes its resources when dropped
//! ```
//!
//! See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
//! file.
//!
//! # Features
//!
//! - `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
//! debugging packet issues because unsent packets that are dropped without being sent hold up
//! wintun's internal ring buffer.
//!
//! # TODO:
//! - Add async support
//! Requires hooking into a windows specific reactor and registering read interest on wintun's read
//! handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
//!
mod adapter;
mod error;
mod log;
mod packet;
mod session;
mod util;
//Generated by bingen
#[allow(
non_snake_case,
dead_code,
unused_variables,
non_camel_case_types,
deref_nullptr,
clippy::all
)]
mod wintun_raw;
pub use crate::adapter::Adapter;
pub use crate::error::{ApiError, OutOfRangeData, WintunError};
pub use crate::log::{default_logger, reset_logger, set_logger};
pub use crate::packet::Packet;
pub use crate::session::Session;
pub use crate::util::get_running_driver_version;
// TODO: Get bindgen to scrape these from the `wintun.h`
// We need to make sure these stay up to date
/// 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 type Wintun = Arc<wintun_raw::wintun>;
use std::sync::Arc;
/// Attempts to load the Wintun library from the current directory using the default name "wintun.dll".
///
/// Use [`load_from_path`] with an absolute path when more control is needed as to where wintun.dll is
///
///
/// # Safety
/// This function loads a dll file with the name wintun.dll using the default system search paths.
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
/// file which exports all of the required functions for these bindings to work is loaded. Because
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
/// Hoverer one can never be too cautious when loading a dll file.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
pub unsafe fn load() -> Result<Wintun, libloading::Error> {
load_from_path("wintun")
}
/// Attempts to load the Wintun library as a dynamic library from the given path.
///
///
/// # Safety
/// This function loads a dll file with the path provided.
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
/// file which exports all of the required functions for these bindings to work is loaded. Because
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
/// Hoverer one can never be too cautious when loading a dll file.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
pub unsafe fn load_from_path<P>(path: P) -> Result<Wintun, libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
{
check_version(wintun_raw::wintun::new(path)?)
}
/// Attempts to load the Wintun library from an existing [`libloading::Library`].
///
///
/// # Safety
/// This function loads the required WinTun functions using the provided library. Reading a symbol table
/// of a dynamic library and transmuting the function pointers inside to have the parameters and return
/// values expected by the functions documented at: <https://git.zx2c4.com/wintun/about/#reference>
/// is inherently unsafe.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading::Library::new`]
pub unsafe fn load_from_library<L>(library: L) -> Result<Wintun, libloading::Error>
where
L: Into<libloading::Library>,
{
check_version(wintun_raw::wintun::from_library(library)?)
}
fn check_version(lib: wintun_raw::wintun) -> Result<Wintun, libloading::Error> {
Ok(Arc::new(lib))
}
+48
View File
@@ -0,0 +1,48 @@
use crate::wintun_raw;
use crate::Wintun;
use log::*;
use widestring::U16CStr;
use std::sync::atomic::{AtomicBool, Ordering};
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
pub fn set_logger(wintun: &Wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
unsafe { wintun.WintunSetLogger(f) };
}
pub fn reset_logger(wintun: &Wintun) {
set_logger(wintun, 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(wintun: &Wintun) {
if SET_LOGGER
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
.is_ok()
{
set_logger(wintun, Some(default_logger));
}
}
+88
View File
@@ -0,0 +1,88 @@
use crate::session;
use std::sync::Arc;
pub(crate) enum Kind {
SendPacketPending, //Send packet type, but not sent yet
SendPacketSent, //Send packet type - sent
ReceivePacket,
}
/// Represents a wintun packet
pub struct Packet {
pub(crate) kind: Kind,
//This lifetime is not actually 'static, however before you get your pitchforks let me explain...
//The bytes in this slice live for as long at the session that allocated them, or until
//WintunReleaseReceivePacket, or WintunSendPacket is called on them (whichever happens first).
//The wrapper functions that call into WintunReleaseReceivePacket, and WintunSendPacket
//consume the packet, meaning the end of this packet's lifetime coincides with the end of byte's
//lifetime. Because we never copy out of bytes, this pointer becomes inaccessible when the
//packet is dropped.
//
//This just leaves packets potentially outliving the session that allocated them posing a
//problem.
//Fortunately we have an Arc to the session that allocated this packet, meaning that the lifetime
//of the session that created this packet is at least as long as the packet.
//Because this is private (to external users) and we only write to this field when allocating
//new packets, it is impossible for the memory that is pointed to by bytes to outlive the
//underlying memory allocated by wintun.
//
//So what I told you was true, from a certain point of view.
//From the point of view of this packet, bytes' lifetime is 'static because we are always
//dropped before the underlying memory is freed
//
//Its also important to know that WintunAllocateSendPacket and WintunReceivePacket always
//return sections of memory that never overlap, so we have exclusive access to the memory,
//therefore mut is okay here.
pub(crate) bytes: &'static mut [u8],
//Share ownership of session to prevent the session from being dropped before packets that
//belong to it
pub(crate) session: Arc<session::Session>,
}
impl Packet {
/// 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] {
self.bytes
}
/// 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] {
self.bytes
}
}
impl Drop for Packet {
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
self.session
.wintun
.WintunReleaseReceivePacket(self.session.session.0, self.bytes.as_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
#[cfg(feature = "panic_on_unsent_packets")]
panic!("Packet was never sent!");
}
Kind::SendPacketSent => {
//Nop
}
}
}
}
+182
View File
@@ -0,0 +1,182 @@
extern crate winapi;
use crate::packet;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Adapter;
use crate::Wintun;
use once_cell::sync::OnceCell;
use winapi::shared::winerror;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi;
use winapi::um::synchapi;
use winapi::um::winbase;
use winapi::um::winnt;
use std::sync::Arc;
use std::{ptr, slice};
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_session_handle>
pub struct Session {
/// The session handle given to us by WintunStartSession
pub(crate) session: UnsafeHandle<wintun_raw::WINTUN_SESSION_HANDLE>,
/// Shared dll for required wintun driver functions
pub(crate) wintun: Wintun,
/// Windows event handle that is signaled by the wintun driver when data becomes available to
/// read
pub(crate) read_event: OnceCell<UnsafeHandle<winnt::HANDLE>>,
/// Windows event handle that is signaled when [`Session::shutdown`] is called force blocking
/// readers to exit
pub(crate) shutdown_event: UnsafeHandle<winnt::HANDLE>,
/// The adapter that owns this session
pub(crate) adapter: Arc<Adapter>,
}
impl Session {
/// Allocates a send packet of the specified size. Wraps WintunAllocateSendPacket
///
/// All packets returned from this function must be sent using [`Session::send_packet`] because
/// wintun establishes the send packet order based on the invocation order of this function.
/// Therefore if a packet is allocated using this function, and then never sent, it will hold
/// up the send queue for all other packets allocated in the future. It is okay for the session
/// to shutdown with allocated packets that have not yet been sent
pub fn allocate_send_packet(self: &Arc<Self>, size: u16) -> Result<packet::Packet, ()> {
let ptr = unsafe {
self.wintun
.WintunAllocateSendPacket(self.session.0, size as u32)
};
if ptr.is_null() {
Err(())
} else {
Ok(packet::Packet {
//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: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
session: self.clone(),
kind: packet::Kind::SendPacketPending,
})
}
}
/// Sends a packet previously allocated with [`Session::allocate_send_packet`]
pub fn send_packet(&self, mut packet: packet::Packet) {
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
unsafe {
self.wintun
.WintunSendPacket(self.session.0, packet.bytes.as_ptr())
};
//Mark the packet at sent
packet.kind = packet::Kind::SendPacketSent;
}
/// Attempts to receive a packet from the virtual interface without blocking.
/// If there are no packets currently in the receive queue, this function returns Ok(None)
/// without blocking. If blocking until a packet is desirable, use [`Session::receive_blocking`]
pub fn try_receive(self: &Arc<Self>) -> Result<Option<packet::Packet>, ()> {
let mut size = 0u32;
let ptr = unsafe {
self.wintun
.WintunReceivePacket(self.session.0, &mut size as *mut u32)
};
debug_assert!(size <= u16::MAX as u32);
if ptr.is_null() {
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
let last_error = unsafe { GetLastError() };
if last_error == winerror::ERROR_NO_MORE_ITEMS {
Ok(None)
} else {
Err(())
}
} else {
Ok(Some(packet::Packet {
kind: packet::Kind::ReceivePacket,
//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: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
session: self.clone(),
}))
}
}
/// Returns the low level read event handle that is signaled when more data becomes available
/// to read
pub(crate) fn get_read_wait_event(&self) -> Result<winnt::HANDLE, ()> {
Ok(self
.read_event
.get_or_init(|| unsafe {
UnsafeHandle(self.wintun.WintunGetReadWaitEvent(self.session.0) as winnt::HANDLE)
})
.0)
}
/// Blocks until a packet is available, returning the next packet in the receive queue once this happens.
/// If the session is closed via [`Session::shutdown`] all threads currently blocking inside this function
/// will return Err(())
pub fn receive_blocking(self: &Arc<Self>) -> Result<packet::Packet, ()> {
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() {
Err(err) => return Err(err),
Ok(Some(packet)) => return Ok(packet),
Ok(None) => {
//Try again
continue;
}
}
}
//Wait on both the read handle and the shutdown handle so that we stop when requested
let handles = [self.get_read_wait_event()?, self.shutdown_event.0];
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(()),
_ => {
if result == winbase::WAIT_OBJECT_0 {
//We have data!
continue;
} else if result == winbase::WAIT_OBJECT_0 + 1 {
//Shutdown event triggered
return Err(());
}
}
}
}
}
/// Cancels any active calls to [`Session::receive_blocking`] making them instantly return Err(_) so that session can be shutdown cleanly
pub fn shutdown(&self) {
let _ = unsafe { synchapi::SetEvent(self.shutdown_event.0) };
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event.0) };
}
}
impl Drop for Session {
fn drop(&mut self) {
let _ = Arc::clone(&self.adapter);
unsafe { self.wintun.WintunEndSession(self.session.0) };
self.session.0 = ptr::null_mut();
//Adapter must be dropped after we call `WintunEndSession`,
//if `self.adapter is the last reference
//drop(self.adapter)
}
}
+66
View File
@@ -0,0 +1,66 @@
use winapi::{
shared::ntdef::{LANG_NEUTRAL, SUBLANG_DEFAULT},
um::{winbase, winnt::MAKELANGID},
};
use std::mem::MaybeUninit;
use std::ptr;
use widestring::U16Str;
/// A wrapper struct that allows a type to be Send and Sync
pub(crate) struct UnsafeHandle<T>(pub T);
/// We never read from the pointer. It only serves as a handle we pass to the kernel or C code that
/// doesn't have the same mutable aliasing restrictions we have in Rust
unsafe impl<T> Send for UnsafeHandle<T> {}
unsafe impl<T> Sync for UnsafeHandle<T> {}
/// Returns a a human readable error message from a windows error code
pub fn get_error_message(err_code: u32) -> String {
const LEN: usize = 256;
let mut buf = MaybeUninit::<[u16; LEN]>::uninit();
//SAFETY: name is a allocated on the stack above therefore it must be valid, non-null and
//aligned for u16
let first = unsafe { *buf.as_mut_ptr() }.as_mut_ptr();
//Write default null terminator in case WintunGetAdapterName leaves name unchanged
unsafe { first.write(0u16) };
let chars_written = unsafe {
winbase::FormatMessageW(
winbase::FORMAT_MESSAGE_FROM_SYSTEM | winbase::FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null(),
err_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as u32,
first,
LEN as u32,
ptr::null_mut(),
)
};
//SAFETY: first is a valid, non-null, aligned, pointer
format!(
"{} ({})",
unsafe { U16Str::from_ptr(first, chars_written as usize) }.to_string_lossy(),
err_code
)
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Version {
pub major: u16,
pub minor: u16,
}
/// Returns the major and minor version of the wintun driver
pub fn get_running_driver_version(wintun: &crate::Wintun) -> Result<Version, ()> {
let version = unsafe { wintun.WintunGetRunningDriverVersion() };
if version == 0 {
Err(())
} else {
Ok(Version {
major: ((version >> 16) & 0xFF) as u16,
minor: (version & 0xFF) as u16,
})
}
}
+448
View File
@@ -0,0 +1,448 @@
/* 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)
}
}