解决win7不能启动的问题
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user