summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-11-30 16:02:52 +0100
committerDavid Lönnhager <david.l@mullvad.net>2020-12-03 11:31:35 +0100
commit1dc755dc3e1a809798a565619f9dcfbc4a67fc46 (patch)
tree42fd97a286b61d8197a61d61fdd0a92267cfc9dd
parent4687159fa1f32fab1a42bfea4a5c47f53554ba0a (diff)
downloadmullvadvpn-1dc755dc3e1a809798a565619f9dcfbc4a67fc46.tar.xz
mullvadvpn-1dc755dc3e1a809798a565619f9dcfbc4a67fc46.zip
Add FFI bindings for wintun.dll
-rw-r--r--talpid-core/src/tunnel/openvpn/mod.rs (renamed from talpid-core/src/tunnel/openvpn.rs)3
-rw-r--r--talpid-core/src/tunnel/openvpn/windows.rs243
2 files changed, 246 insertions, 0 deletions
diff --git a/talpid-core/src/tunnel/openvpn.rs b/talpid-core/src/tunnel/openvpn/mod.rs
index dcd2268772..fbcd4db391 100644
--- a/talpid-core/src/tunnel/openvpn.rs
+++ b/talpid-core/src/tunnel/openvpn/mod.rs
@@ -36,6 +36,9 @@ use tokio::task;
#[cfg(target_os = "linux")]
use which;
+#[cfg(windows)]
+mod windows;
+
lazy_static! {
static ref ENV_ROUTE_ENTRY: Regex = Regex::new(r"route_(ipv6_)?(\w+)_(\d+)").unwrap();
diff --git a/talpid-core/src/tunnel/openvpn/windows.rs b/talpid-core/src/tunnel/openvpn/windows.rs
new file mode 100644
index 0000000000..f64892fcab
--- /dev/null
+++ b/talpid-core/src/tunnel/openvpn/windows.rs
@@ -0,0 +1,243 @@
+use std::{
+ ffi::CStr,
+ fmt, io, iter,
+ os::windows::{ffi::OsStrExt, io::RawHandle},
+ path::Path,
+ ptr,
+ sync::Arc,
+};
+use talpid_types::ErrorExt;
+use widestring::U16CStr;
+use winapi::{
+ shared::{
+ guiddef::GUID,
+ minwindef::{BOOL, FARPROC, HINSTANCE, HMODULE},
+ },
+ um::libloaderapi::{
+ FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_WITH_ALTERED_SEARCH_PATH,
+ },
+};
+
+
+type WintunOpenAdapterFn =
+ unsafe extern "stdcall" fn(pool: *const u16, name: *const u16) -> RawHandle;
+
+type WintunCreateAdapterFn = unsafe extern "stdcall" fn(
+ pool: *const u16,
+ name: *const u16,
+ requested_guid: *const GUID,
+ reboot_required: *mut BOOL,
+) -> RawHandle;
+
+type WintunFreeAdapterFn = unsafe extern "stdcall" fn(adapter: RawHandle);
+
+type WintunDeleteAdapterFn = unsafe extern "stdcall" fn(
+ adapter: RawHandle,
+ force_close_sessions: BOOL,
+ reboot_required: *mut BOOL,
+) -> BOOL;
+
+
+pub struct WintunDll {
+ handle: HINSTANCE,
+ func_open: WintunOpenAdapterFn,
+ func_create: WintunCreateAdapterFn,
+ func_free: WintunFreeAdapterFn,
+ func_delete: WintunDeleteAdapterFn,
+}
+
+unsafe impl Sync for WintunDll {}
+
+type RebootRequired = bool;
+
+/// A new Wintun adapter that is destroyed when dropped.
+#[derive(Debug)]
+pub struct TemporaryWintunAdapter {
+ pub adapter: WintunAdapter,
+}
+
+impl TemporaryWintunAdapter {
+ pub fn create(
+ dll_handle: Arc<WintunDll>,
+ pool: &U16CStr,
+ name: &U16CStr,
+ requested_guid: Option<GUID>,
+ ) -> io::Result<(Self, RebootRequired)> {
+ let (adapter, reboot_required) =
+ WintunAdapter::create(dll_handle, pool, name, requested_guid)?;
+ Ok((TemporaryWintunAdapter { adapter }, reboot_required))
+ }
+}
+
+impl Drop for TemporaryWintunAdapter {
+ fn drop(&mut self) {
+ if let Err(error) = unsafe {
+ self.adapter
+ .dll_handle
+ .delete_adapter(self.adapter.handle, true)
+ } {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to delete Wintun adapter")
+ );
+ }
+ }
+}
+
+/// Represents a Wintun adapter.
+pub struct WintunAdapter {
+ dll_handle: Arc<WintunDll>,
+ handle: RawHandle,
+}
+
+impl fmt::Debug for WintunAdapter {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("WintunAdapter")
+ .field("handle", &self.handle)
+ .finish()
+ }
+}
+
+unsafe impl Send for WintunAdapter {}
+
+impl WintunAdapter {
+ pub fn open(dll_handle: Arc<WintunDll>, pool: &U16CStr, name: &U16CStr) -> io::Result<Self> {
+ Ok(Self {
+ handle: dll_handle.open_adapter(pool, name)?,
+ dll_handle,
+ })
+ }
+
+ pub fn create(
+ dll_handle: Arc<WintunDll>,
+ pool: &U16CStr,
+ name: &U16CStr,
+ requested_guid: Option<GUID>,
+ ) -> io::Result<(Self, RebootRequired)> {
+ let (handle, restart_required) = dll_handle.create_adapter(pool, name, requested_guid)?;
+ Ok((Self { dll_handle, handle }, restart_required))
+ }
+
+ pub fn delete(self, force_close_sessions: bool) -> io::Result<RebootRequired> {
+ unsafe {
+ self.dll_handle
+ .delete_adapter(self.handle, force_close_sessions)
+ }
+ }
+}
+
+impl Drop for WintunAdapter {
+ fn drop(&mut self) {
+ unsafe { self.dll_handle.free_adapter(self.handle) };
+ }
+}
+
+impl WintunDll {
+ pub fn new(resource_dir: &Path) -> io::Result<Self> {
+ let wintun_dll: Vec<u16> = resource_dir
+ .join("wintun.dll")
+ .as_os_str()
+ .encode_wide()
+ .chain(iter::once(0u16))
+ .collect();
+
+ let handle = unsafe {
+ LoadLibraryExW(
+ wintun_dll.as_ptr(),
+ ptr::null_mut(),
+ LOAD_WITH_ALTERED_SEARCH_PATH,
+ )
+ };
+ if handle == ptr::null_mut() {
+ return Err(io::Error::last_os_error());
+ }
+
+ Ok(WintunDll {
+ handle,
+ func_open: unsafe {
+ std::mem::transmute(Self::get_proc_address(
+ handle,
+ CStr::from_bytes_with_nul(b"WintunOpenAdapter\0").unwrap(),
+ )?)
+ },
+ func_create: unsafe {
+ std::mem::transmute(Self::get_proc_address(
+ handle,
+ CStr::from_bytes_with_nul(b"WintunCreateAdapter\0").unwrap(),
+ )?)
+ },
+ func_delete: unsafe {
+ std::mem::transmute(Self::get_proc_address(
+ handle,
+ CStr::from_bytes_with_nul(b"WintunDeleteAdapter\0").unwrap(),
+ )?)
+ },
+ func_free: unsafe {
+ std::mem::transmute(Self::get_proc_address(
+ handle,
+ CStr::from_bytes_with_nul(b"WintunFreeAdapter\0").unwrap(),
+ )?)
+ },
+ })
+ }
+
+ unsafe fn get_proc_address(handle: HMODULE, name: &CStr) -> io::Result<FARPROC> {
+ let handle = GetProcAddress(handle, name.as_ptr());
+ if handle == ptr::null_mut() {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(handle)
+ }
+
+ pub fn open_adapter(&self, pool: &U16CStr, name: &U16CStr) -> io::Result<RawHandle> {
+ let handle = unsafe { (self.func_open)(pool.as_ptr(), name.as_ptr()) };
+ if handle == ptr::null_mut() {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(handle)
+ }
+
+ pub fn create_adapter(
+ &self,
+ pool: &U16CStr,
+ name: &U16CStr,
+ requested_guid: Option<GUID>,
+ ) -> io::Result<(RawHandle, RebootRequired)> {
+ let guid_ptr = match requested_guid.as_ref() {
+ Some(guid) => guid as *const _,
+ None => ptr::null_mut(),
+ };
+ let mut reboot_required = 0;
+ let handle = unsafe {
+ (self.func_create)(pool.as_ptr(), name.as_ptr(), guid_ptr, &mut reboot_required)
+ };
+ if handle == ptr::null_mut() {
+ return Err(io::Error::last_os_error());
+ }
+ Ok((handle, reboot_required != 0))
+ }
+
+ pub unsafe fn delete_adapter(
+ &self,
+ adapter: RawHandle,
+ force_close_sessions: bool,
+ ) -> io::Result<RebootRequired> {
+ let mut reboot_required = 0;
+ let force_close_sessions = if force_close_sessions { 1 } else { 0 };
+ let result = (self.func_delete)(adapter, force_close_sessions, &mut reboot_required);
+ if result == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(reboot_required != 0)
+ }
+
+ pub unsafe fn free_adapter(&self, adapter: RawHandle) {
+ (self.func_free)(adapter);
+ }
+}
+
+impl Drop for WintunDll {
+ fn drop(&mut self) {
+ unsafe { FreeLibrary(self.handle) };
+ }
+}