1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
/// 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) };
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;
}
}
if match_count == digits.len() {
return Ok(interface.Index);
}
}
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());
}
}