├── core ├── src │ ├── lib.rs │ ├── queue │ │ ├── mod.rs │ │ ├── syncfd.rs │ │ └── tokiofd.rs │ ├── config.rs │ ├── error.rs │ └── traits.rs └── Cargo.toml ├── .idea ├── vcs.xml ├── .gitignore ├── modules.xml └── tunio.iml ├── wintun-sys ├── bindgen.ps1 ├── src │ └── lib.rs ├── Cargo.toml ├── wintun │ ├── wintun_functions.h │ ├── LICENSE.txt │ └── wintun.h └── build.rs ├── platforms ├── wintun │ ├── src │ │ ├── wrappers │ │ │ ├── mod.rs │ │ │ ├── handle.rs │ │ │ ├── adapter.rs │ │ │ └── session.rs │ │ ├── lib.rs │ │ ├── event.rs │ │ ├── logger.rs │ │ ├── queue.rs │ │ ├── driver.rs │ │ ├── config.rs │ │ ├── async_interface.rs │ │ ├── interface.rs │ │ └── async_queue.rs │ └── Cargo.toml ├── linux │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── queue.rs │ │ └── interface.rs └── utun │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── queue.rs │ └── interface.rs ├── src ├── platform.rs └── lib.rs ├── .github ├── dependabot.yml └── workflows │ ├── wintun.yml │ └── tunio.yml ├── LICENSE ├── examples ├── simple_std.rs └── simple.rs ├── Cargo.toml ├── README.md ├── .gitignore └── Cargo.lock /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | mod error; 3 | #[cfg(unix)] 4 | pub mod queue; 5 | pub mod traits; 6 | 7 | pub use error::Error; 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /wintun-sys/bindgen.ps1: -------------------------------------------------------------------------------- 1 | bindgen --allowlist-function "Wintun.*" --allowlist-type "WINTUN_.*" --allowlist-var "WINTUN_.*" --dynamic-loading wintun --dynamic-link-require-all wintun/wintun_functions.h > src/wintun_raw.rs -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /platforms/wintun/src/wrappers/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod adapter; 2 | pub(crate) mod handle; 3 | pub(crate) mod session; 4 | 5 | pub(crate) use adapter::Adapter; 6 | pub(crate) use handle::HandleWrapper; 7 | pub(crate) use session::Session; 8 | -------------------------------------------------------------------------------- /core/src/queue/mod.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::io::OwnedFd; 2 | 3 | pub mod syncfd; 4 | #[cfg(feature = "tokio")] 5 | pub mod tokiofd; 6 | 7 | pub trait FdQueueT { 8 | const BLOCKING: bool; 9 | 10 | fn new(device: OwnedFd) -> Self; 11 | } 12 | -------------------------------------------------------------------------------- /src/platform.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "linux")] 2 | pub mod linux { 3 | pub use tunio_linux::*; 4 | } 5 | #[cfg(target_os = "macos")] 6 | pub mod utun { 7 | pub use tunio_utun::*; 8 | } 9 | #[cfg(target_os = "windows")] 10 | pub mod wintun { 11 | pub use tunio_wintun::*; 12 | } 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | target-branch: main 9 | ignore: 10 | - dependency-name: "*" 11 | update-types: ["version-update:semver-patch"] 12 | -------------------------------------------------------------------------------- /platforms/wintun/src/wrappers/handle.rs: -------------------------------------------------------------------------------- 1 | use core::marker::{Send, Sync}; 2 | 3 | pub struct HandleWrapper(pub T); 4 | 5 | impl HandleWrapper { 6 | pub(crate) fn clone(&self) -> Self { 7 | Self(self.0) 8 | } 9 | } 10 | 11 | unsafe impl Send for HandleWrapper {} 12 | 13 | unsafe impl Sync for HandleWrapper {} 14 | -------------------------------------------------------------------------------- /platforms/wintun/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod driver; 3 | mod event; 4 | mod interface; 5 | mod logger; 6 | mod queue; 7 | mod wrappers; 8 | 9 | pub use config::{PlatformIfConfig, PlatformIfConfigBuilder}; 10 | pub use driver::Driver; 11 | pub use interface::Interface; 12 | pub use queue::Queue; 13 | 14 | mod async_interface; 15 | mod async_queue; 16 | 17 | pub use async_interface::AsyncInterface; 18 | pub use async_queue::AsyncQueue; 19 | -------------------------------------------------------------------------------- /wintun-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types)] 2 | #![cfg(target_os = "windows")] 3 | 4 | use windows::core::GUID; 5 | use windows::core::PCWSTR as LPCWSTR; 6 | use windows::Win32::Foundation::HANDLE; 7 | use windows::Win32::NetworkManagement::Ndis::NET_LUID_LH as NET_LUID; 8 | type DWORD = core::ffi::c_ulong; 9 | type BOOL = core::ffi::c_int; 10 | type BYTE = core::ffi::c_uchar; 11 | type DWORD64 = core::ffi::c_ulonglong; 12 | 13 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 14 | -------------------------------------------------------------------------------- /platforms/linux/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tunio-linux" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | categories.workspace = true 9 | keywords.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | log.workspace = true 15 | futures.workspace = true 16 | netconfig.workspace = true 17 | derive_builder.workspace = true 18 | delegate.workspace = true 19 | tunio-core.workspace = true 20 | nix.workspace = true 21 | libc.workspace = true 22 | 23 | [features] 24 | tokio = ["tunio-core/tokio"] 25 | -------------------------------------------------------------------------------- /platforms/utun/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tunio-utun" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | categories.workspace = true 9 | keywords.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | log.workspace = true 15 | futures.workspace = true 16 | netconfig.workspace = true 17 | derive_builder.workspace = true 18 | delegate.workspace = true 19 | tunio-core.workspace = true 20 | socket2 = "0.4.7" 21 | nix.workspace = true 22 | libc.workspace = true 23 | 24 | [features] 25 | tokio = ["tunio-core/tokio"] 26 | -------------------------------------------------------------------------------- /wintun-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wintun-sys" 3 | version = "0.2.0" 4 | edition = "2021" 5 | license = "MIT" 6 | repository = "https://github.com/GamePad64/tunio" 7 | description = "Wintun wrapper, generated using bindgen" 8 | categories = ["os", "network-programming"] 9 | keywords = ["network", "networking", "cross-platform", "tun"] 10 | rust-version = "1.64" 11 | 12 | [target.'cfg(target_os = "windows")'.dependencies] 13 | libloading = "0.7.3" 14 | windows = { version = "0.42.0", features = ["Win32_Foundation", "Win32_NetworkManagement_Ndis"] } 15 | 16 | [target.'cfg(target_os = "windows")'.build-dependencies] 17 | bindgen = "0.61.0" 18 | 19 | [package.metadata.docs.rs] 20 | targets = ["x86_64-pc-windows-msvc", "i686-pc-windows-msvc"] 21 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tunio-core" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | categories.workspace = true 9 | keywords.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | log.workspace = true 15 | futures.workspace = true 16 | netconfig.workspace = true 17 | derive_builder.workspace = true 18 | delegate.workspace = true 19 | thiserror = "1.0.31" 20 | tokio = { workspace = true, features = ["net"], optional = true } 21 | 22 | [features] 23 | tokio = ["dep:tokio"] 24 | 25 | [package.metadata.docs.rs] 26 | all-features = true 27 | rustdoc-args = ["--cfg", "docsrs"] 28 | -------------------------------------------------------------------------------- /platforms/wintun/src/event.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use windows::Win32::Foundation::{CloseHandle, HANDLE}; 3 | use windows::Win32::System::Threading::{CreateEventA, SetEvent}; 4 | 5 | pub(crate) struct SafeEvent(HANDLE); 6 | 7 | impl SafeEvent { 8 | pub fn new(manual_reset: bool, initial_state: bool) -> Self { 9 | Self(unsafe { CreateEventA(None, manual_reset, initial_state, None).unwrap() }) 10 | } 11 | 12 | pub fn set_event(&self) { 13 | unsafe { 14 | SetEvent(self.handle()); 15 | } 16 | } 17 | 18 | pub fn handle(&self) -> HANDLE { 19 | self.0 20 | } 21 | } 22 | 23 | impl Drop for SafeEvent { 24 | fn drop(&mut self) { 25 | let _ = unsafe { CloseHandle(self.0) }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /platforms/utun/src/lib.rs: -------------------------------------------------------------------------------- 1 | use derive_builder::Builder; 2 | use tunio_core::traits::{DriverT, PlatformIfConfigT}; 3 | use tunio_core::Error; 4 | 5 | mod interface; 6 | mod queue; 7 | 8 | pub use interface::Interface; 9 | #[cfg(feature = "tokio")] 10 | pub use interface::TokioInterface; 11 | 12 | pub struct Driver {} 13 | 14 | impl DriverT for Driver { 15 | type PlatformIfConfig = PlatformIfConfig; 16 | 17 | fn new() -> Result { 18 | Ok(Driver {}) 19 | } 20 | } 21 | 22 | #[derive(Builder, Clone)] 23 | pub struct PlatformIfConfig {} 24 | 25 | impl PlatformIfConfigT for PlatformIfConfig { 26 | type Builder = PlatformIfConfigBuilder; 27 | } 28 | 29 | impl Default for PlatformIfConfig { 30 | fn default() -> Self { 31 | PlatformIfConfigBuilder::default().build().unwrap() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /platforms/wintun/src/logger.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info, warn}; 2 | use widestring::U16CStr; 3 | use windows::core::PCWSTR; 4 | use wintun_sys::{ 5 | WINTUN_LOGGER_LEVEL, WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR, WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO, 6 | WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN, 7 | }; 8 | 9 | pub unsafe extern "C" fn wintun_logger( 10 | level: WINTUN_LOGGER_LEVEL, 11 | _timestamp: u64, 12 | message: PCWSTR, 13 | ) { 14 | let message = U16CStr::from_ptr_str(message.as_ptr()); 15 | let message_utf8 = message.to_string_lossy(); 16 | 17 | match level { 18 | WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO => info!("{message_utf8}"), 19 | WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN => warn!("{message_utf8}"), 20 | WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR => error!("{message_utf8}"), 21 | _ => error!("[invalid log level: {level}] {message_utf8}"), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /platforms/wintun/src/queue.rs: -------------------------------------------------------------------------------- 1 | use super::wrappers::Session; 2 | use std::io::{self, Read, Write}; 3 | use tunio_core::traits::SyncQueueT; 4 | 5 | pub trait SessionQueueT { 6 | fn new(session: Session) -> Self; 7 | } 8 | 9 | impl SyncQueueT for Queue {} 10 | 11 | pub struct Queue { 12 | session: Session, 13 | } 14 | 15 | impl SessionQueueT for Queue { 16 | fn new(session: Session) -> Self { 17 | Self { session } 18 | } 19 | } 20 | 21 | impl Read for Queue { 22 | delegate::delegate! { 23 | to self.session { 24 | fn read(&mut self, buf: &mut [u8]) -> io::Result; 25 | } 26 | } 27 | } 28 | 29 | impl Write for Queue { 30 | delegate::delegate! { 31 | to self.session { 32 | fn write(&mut self, buf: &[u8]) -> io::Result; 33 | fn flush(&mut self) -> io::Result<()>; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wintun-sys/wintun/wintun_functions.h: -------------------------------------------------------------------------------- 1 | // Information about functions taken from: 2 | // https://git.zx2c4.com/wintun/tree/example/example.c 3 | 4 | #include "wintun.h" 5 | 6 | WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter; 7 | WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter; 8 | WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter; 9 | WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID; 10 | WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion; 11 | WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver; 12 | WINTUN_SET_LOGGER_FUNC WintunSetLogger; 13 | WINTUN_START_SESSION_FUNC WintunStartSession; 14 | WINTUN_END_SESSION_FUNC WintunEndSession; 15 | WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent; 16 | WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket; 17 | WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket; 18 | WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket; 19 | WINTUN_SEND_PACKET_FUNC WintunSendPacket; 20 | -------------------------------------------------------------------------------- /platforms/wintun/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tunio-wintun" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | categories.workspace = true 9 | keywords.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | log.workspace = true 15 | futures.workspace = true 16 | netconfig.workspace = true 17 | derive_builder.workspace = true 18 | delegate.workspace = true 19 | tunio-core.workspace = true 20 | blocking = "1.3.0" 21 | async-task = "4.3.0" 22 | widestring = "1.0.2" 23 | bytes = "1.4.0" 24 | wintun-sys = { version = "0.2.0", path = "../../wintun-sys" } 25 | windows = { version = "0.42.0", features = ["Win32_System_Threading", "Win32_Foundation", "Win32_Security", "Win32_System_WindowsProgramming", "Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis"] } 26 | -------------------------------------------------------------------------------- /platforms/wintun/src/driver.rs: -------------------------------------------------------------------------------- 1 | use super::logger::wintun_logger; 2 | use super::PlatformIfConfig; 3 | use std::sync::Arc; 4 | use tunio_core::traits::DriverT; 5 | use tunio_core::Error; 6 | 7 | pub struct Driver { 8 | pub wintun: Arc, 9 | } 10 | 11 | impl DriverT for Driver { 12 | type PlatformIfConfig = PlatformIfConfig; 13 | 14 | fn new() -> Result { 15 | let library_name = "wintun".to_string(); 16 | let wintun = Arc::new( 17 | unsafe { wintun_sys::wintun::new(library_name) }.map_err(|e| { 18 | Error::LibraryNotLoaded { 19 | reason: format!("{e:?}"), 20 | } 21 | })?, 22 | ); 23 | 24 | unsafe { 25 | wintun.WintunSetLogger(Some(wintun_logger)); 26 | } 27 | 28 | Ok(Self { wintun }) 29 | } 30 | } 31 | 32 | impl Driver { 33 | pub(crate) fn wintun(&self) -> &Arc { 34 | &self.wintun 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/queue/syncfd.rs: -------------------------------------------------------------------------------- 1 | use crate::queue::FdQueueT; 2 | use crate::traits::SyncQueueT; 3 | use delegate::delegate; 4 | use std::fs; 5 | use std::io::{self, Read, Write}; 6 | use std::os::unix::io::{AsRawFd, OwnedFd, RawFd}; 7 | 8 | pub struct SyncFdQueue(fs::File); 9 | 10 | impl SyncQueueT for SyncFdQueue {} 11 | 12 | impl FdQueueT for SyncFdQueue { 13 | const BLOCKING: bool = true; 14 | 15 | fn new(device: OwnedFd) -> Self { 16 | Self(device.into()) 17 | } 18 | } 19 | 20 | impl Read for SyncFdQueue { 21 | delegate! { 22 | to self.0 { 23 | fn read(&mut self, buf: &mut [u8]) -> Result; 24 | } 25 | } 26 | } 27 | 28 | impl Write for SyncFdQueue { 29 | delegate! { 30 | to self.0 { 31 | fn write(&mut self, buf: &[u8]) -> io::Result; 32 | fn flush(&mut self) -> io::Result<()>; 33 | } 34 | } 35 | } 36 | 37 | impl AsRawFd for SyncFdQueue { 38 | fn as_raw_fd(&self) -> RawFd { 39 | self.0.as_raw_fd() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /platforms/wintun/src/config.rs: -------------------------------------------------------------------------------- 1 | use derive_builder::Builder; 2 | use tunio_core::traits::PlatformIfConfigT; 3 | 4 | /// It is generally better to use [`PlatformIfConfigBuilder`] to create a new PlatformIfConfig instance. 5 | #[derive(Builder, Clone)] 6 | pub struct PlatformIfConfig { 7 | /// Wintun ring capacity. Must be power of 2 between 128KiB and 64MiB 8 | #[builder(default = "2 * 1024 * 1024")] 9 | pub capacity: u32, 10 | #[builder(default = "String::new()")] 11 | pub description: String, 12 | /// GUID of this network interface. It is recommended to set it manually, 13 | /// or new device will be created on each invocation, and it will quickly 14 | /// pollute Windows registry. 15 | #[builder(default = "windows::core::GUID::new().unwrap().to_u128()")] 16 | pub guid: u128, 17 | } 18 | 19 | impl Default for PlatformIfConfig { 20 | fn default() -> Self { 21 | PlatformIfConfigBuilder::default().build().unwrap() 22 | } 23 | } 24 | 25 | impl PlatformIfConfigT for PlatformIfConfig { 26 | type Builder = PlatformIfConfigBuilder; 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/wintun.yml: -------------------------------------------------------------------------------- 1 | name: Wintun-sys 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | tags: [ 'wintun_sys-v*' ] 7 | pull_request: 8 | branches: [ main ] 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | runs-on: windows-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Release build 19 | uses: actions-rs/cargo@v1 20 | with: 21 | command: build 22 | args: -p wintun-sys --release --all-features 23 | - name: Check if can be packaged 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: package 27 | args: -p wintun-sys 28 | 29 | publish: 30 | runs-on: windows-latest 31 | needs: [build] 32 | if: startsWith(github.ref, 'refs/tags/') 33 | 34 | steps: 35 | - uses: actions/checkout@v3 36 | - name: Publish to Crates.io 37 | uses: actions-rs/cargo@v1 38 | with: 39 | command: publish 40 | args: -p wintun-sys 41 | env: 42 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 43 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 3 | pub mod platform; 4 | 5 | pub use tunio_core::config::*; 6 | pub use tunio_core::Error; 7 | 8 | pub use tunio_core::config; 9 | pub use tunio_core::traits; 10 | 11 | cfg_if::cfg_if! { 12 | if #[cfg(target_os = "windows")] { 13 | pub type DefaultDriver = platform::wintun::Driver; 14 | pub type DefaultInterface = platform::wintun::Interface; 15 | pub type DefaultAsyncInterface = platform::wintun::AsyncInterface; 16 | }else if #[cfg(target_os = "linux")] { 17 | pub type DefaultDriver = platform::linux::Driver; 18 | pub type DefaultInterface = platform::linux::Interface; 19 | #[cfg(feature = "tokio")] 20 | pub type DefaultAsyncInterface = platform::linux::TokioInterface; 21 | }else if #[cfg(target_os = "macos")] { 22 | pub type DefaultDriver = platform::utun::Driver; 23 | pub type DefaultInterface = platform::utun::Interface; 24 | #[cfg(feature = "tokio")] 25 | pub type DefaultAsyncInterface = platform::utun::TokioInterface; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::traits::PlatformIfConfigT; 2 | use derive_builder::Builder; 3 | 4 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] 5 | pub enum Layer { 6 | /// TAP, Ethernet-like interface with L2 capabilities 7 | L2, 8 | /// TUN, point-to-point IP interface 9 | #[default] 10 | L3, 11 | } 12 | 13 | #[derive(Builder)] 14 | pub struct IfConfig { 15 | /// Interface name on Unix and interface alias on Windows. 16 | pub name: String, 17 | /// Interface type: TUN or TAP. 18 | #[builder(default = "Layer::default()")] 19 | pub layer: Layer, 20 | 21 | #[allow(dead_code)] 22 | #[builder(setter(custom))] 23 | #[builder(default = "P::default()")] 24 | pub platform: P, 25 | } 26 | 27 | impl IfConfigBuilder

{ 28 | /// Platform-specific settings 29 | pub fn platform(&mut self, f: F) -> Result<&mut Self, E> 30 | where 31 | F: Fn(P::Builder) -> Result, 32 | { 33 | let builder = P::Builder::default(); 34 | self.platform = Some(f(builder)?); 35 | Ok(self) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alexander Shishenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/simple_std.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use tunio::traits::{DriverT, InterfaceT}; 3 | use tunio::{DefaultDriver, DefaultInterface}; 4 | 5 | fn main() { 6 | // DefaultDriver is an alias for a supported driver for current platform. 7 | // It may be not optimal for your needs (for example, it can lack support of TAP), 8 | // but it will work in some cases. If you need another driver, then import and use it instead. 9 | let mut driver = DefaultDriver::new().unwrap(); 10 | // Preparing configuration for new interface. We use `Builder` pattern for this. 11 | let if_config = DefaultInterface::config_builder() 12 | .name("iface1".to_string()) 13 | .build() 14 | .unwrap(); 15 | 16 | // Then, we create the interface using config and start it immediately. 17 | let mut interface = DefaultInterface::new_up(&mut driver, if_config).unwrap(); 18 | 19 | // The interface is created and running. 20 | 21 | // Write to interface using Write trait 22 | let buf = [0u8; 4096]; 23 | let _ = interface.write(&buf); 24 | 25 | // Read from interface using Read trait 26 | let mut mut_buf = [0u8; 4096]; 27 | let _ = interface.read(&mut mut_buf); 28 | } 29 | -------------------------------------------------------------------------------- /platforms/linux/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # [Universal TUN/TAP device driver](https://www.kernel.org/doc/Documentation/networking/tuntap.txt) support for tunio. 2 | //! 3 | //! This module provides support for TUN/TAP driver, used in Linux. 4 | //! 5 | //! Supported features: 6 | //! - TUN/TAP modes 7 | //! - Sync and async mode 8 | //! 9 | //! Low-level documentation for this driver can be found [here](https://www.kernel.org/doc/Documentation/networking/tuntap.txt). 10 | 11 | mod interface; 12 | mod queue; 13 | 14 | use derive_builder::Builder; 15 | use tunio_core::traits::{DriverT, PlatformIfConfigT}; 16 | use tunio_core::Error; 17 | 18 | #[cfg(feature = "tokio")] 19 | pub use interface::TokioInterface; 20 | pub use interface::{Interface, LinuxInterface}; 21 | 22 | pub struct Driver {} 23 | 24 | #[derive(Builder, Clone)] 25 | pub struct PlatformIfConfig {} 26 | 27 | impl PlatformIfConfigT for PlatformIfConfig { 28 | type Builder = PlatformIfConfigBuilder; 29 | } 30 | 31 | impl Default for PlatformIfConfig { 32 | fn default() -> Self { 33 | PlatformIfConfigBuilder::default().build().unwrap() 34 | } 35 | } 36 | 37 | impl DriverT for Driver { 38 | type PlatformIfConfig = PlatformIfConfig; 39 | 40 | fn new() -> Result { 41 | Ok(Self {}) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/tunio.yml: -------------------------------------------------------------------------------- 1 | name: Tunio 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | tags: [ 'v*' ] 7 | pull_request: 8 | branches: [ main ] 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest] 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Release build 23 | uses: actions-rs/cargo@v1 24 | with: 25 | command: build 26 | args: -p tunio --release --all-features 27 | - name: Run tests 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: test 31 | args: -p tunio --verbose 32 | - name: Check if can be packaged 33 | uses: actions-rs/cargo@v1 34 | with: 35 | command: package 36 | args: -p tunio 37 | 38 | publish: 39 | runs-on: ubuntu-latest 40 | needs: [build] 41 | if: startsWith(github.ref, 'refs/tags/') 42 | 43 | steps: 44 | - uses: actions/checkout@v3 45 | - name: Publish to Crates.io 46 | uses: actions-rs/cargo@v1 47 | with: 48 | command: publish 49 | args: -p tunio 50 | env: 51 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 52 | -------------------------------------------------------------------------------- /core/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::config::Layer; 2 | use std::io; 3 | use thiserror::Error as ThisError; 4 | 5 | #[non_exhaustive] 6 | #[derive(Debug, ThisError)] 7 | pub enum Error { 8 | #[error("I/O error: {0}")] 9 | Io(io::Error), 10 | #[error("interface name is not valid Unicode")] 11 | InterfaceNameUnicodeError, 12 | #[error("interface name too long: {0} > {1}")] 13 | InterfaceNameTooLong(usize, usize), 14 | #[error("interface name is invalid")] 15 | InterfaceNameInvalid, 16 | #[error("library not loaded: {reason}")] 17 | LibraryNotLoaded { reason: String }, 18 | #[error("netconfig error: {0}")] 19 | NetConfigError(netconfig::Error), 20 | #[error("interface name error: {0}")] 21 | InterfaceNameError(String), 22 | #[error("config value is invalid ({reason}): {name}={value}")] 23 | InvalidConfigValue { 24 | name: String, 25 | value: String, 26 | reason: String, 27 | }, 28 | #[error("layer is unsupported: {0:?}")] 29 | LayerUnsupported(Layer), 30 | } 31 | 32 | impl From for Error { 33 | fn from(err: io::Error) -> Self { 34 | Error::Io(err) 35 | } 36 | } 37 | 38 | impl From for Error { 39 | fn from(err: netconfig::Error) -> Self { 40 | Error::NetConfigError(err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.idea/tunio.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /core/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::config::{IfConfig, IfConfigBuilder}; 2 | use crate::Error; 3 | use futures::{AsyncRead, AsyncWrite}; 4 | use std::io::{Read, Write}; 5 | 6 | pub trait PlatformIfConfigT: Default + Clone { 7 | type Builder: Default; 8 | } 9 | 10 | pub trait DriverT: Sized { 11 | type PlatformIfConfig: PlatformIfConfigT; 12 | 13 | fn new() -> Result; 14 | } 15 | 16 | pub trait InterfaceT: Sized { 17 | type PlatformDriver: DriverT; 18 | type PlatformIfConfig: PlatformIfConfigT; 19 | 20 | fn new( 21 | driver: &mut Self::PlatformDriver, 22 | params: IfConfig, 23 | ) -> Result; 24 | fn new_up( 25 | driver: &mut Self::PlatformDriver, 26 | params: IfConfig, 27 | ) -> Result { 28 | let mut interface = Self::new(driver, params)?; 29 | interface.up()?; 30 | Ok(interface) 31 | } 32 | 33 | fn up(&mut self) -> Result<(), Error>; 34 | fn down(&mut self) -> Result<(), Error>; 35 | fn handle(&self) -> netconfig::Interface; 36 | 37 | fn config_builder() -> IfConfigBuilder { 38 | IfConfigBuilder::default() 39 | } 40 | } 41 | 42 | pub trait SyncQueueT: Read + Write {} 43 | pub trait AsyncQueueT: AsyncRead + AsyncWrite + Unpin {} 44 | -------------------------------------------------------------------------------- /platforms/utun/src/queue.rs: -------------------------------------------------------------------------------- 1 | use crate::Error; 2 | use libc::{PF_SYSTEM, SYSPROTO_CONTROL}; 3 | use nix::sys::socket::SysControlAddr; 4 | use socket2::{Domain, Protocol, SockAddr, Socket, Type}; 5 | use std::mem; 6 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd}; 7 | 8 | const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control"; 9 | 10 | pub(crate) fn create_device(name: &str, blocking: bool) -> Result { 11 | let mut id = match name { 12 | s if s.starts_with("utun") => s[4..].parse().map_err(|_| Error::InterfaceNameInvalid), 13 | _ => Err(Error::InterfaceNameInvalid), 14 | }?; 15 | id += 1; 16 | 17 | let tun_device = Socket::new( 18 | Domain::from(PF_SYSTEM), 19 | Type::DGRAM, 20 | Some(Protocol::from(SYSPROTO_CONTROL)), 21 | ) 22 | .unwrap(); 23 | 24 | let sa = SysControlAddr::from_name(tun_device.as_raw_fd(), UTUN_CONTROL_NAME, id).unwrap(); 25 | 26 | let (_, sa) = unsafe { 27 | SockAddr::init(|sa_storage, len| { 28 | let sockaddr = sa_storage as *mut libc::sockaddr_ctl; 29 | *sockaddr = *sa.as_ref(); 30 | *len = mem::size_of::() as _; 31 | Ok(()) 32 | }) 33 | } 34 | .unwrap(); 35 | if !blocking { 36 | tun_device.set_nonblocking(true)?; 37 | } 38 | tun_device.connect(&sa).unwrap(); 39 | 40 | Ok(unsafe { OwnedFd::from_raw_fd(tun_device.into_raw_fd()) }) 41 | } 42 | -------------------------------------------------------------------------------- /wintun-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | fn main() { 5 | println!("cargo:rerun-if-changed=wintun/wintun_functions.h"); 6 | 7 | #[cfg(target_os = "windows")] 8 | { 9 | let bindings = bindgen::Builder::default() 10 | .header("wintun/wintun_functions.h") 11 | .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 12 | .allowlist_function("Wintun.*") 13 | .allowlist_type("WINTUN_.*") 14 | .allowlist_var("WINTUN_.*") 15 | .blocklist_type("_GUID") 16 | .blocklist_type("BOOL") 17 | .blocklist_type("BYTE") 18 | .blocklist_type("DWORD") 19 | .blocklist_type("DWORD64") 20 | .blocklist_type("GUID") 21 | .blocklist_type("HANDLE") 22 | .blocklist_type("LPCWSTR") 23 | .blocklist_type("NET_LUID") 24 | .blocklist_type("WCHAR") 25 | .blocklist_type("wchar_t") 26 | .dynamic_library_name("wintun") 27 | .dynamic_link_require_all(true) 28 | .opaque_type("NET_LUID") 29 | .generate() 30 | .expect("Unable to generate bindings"); 31 | 32 | // Write the bindings to the $OUT_DIR/bindings.rs file. 33 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 34 | bindings 35 | .write_to_file(out_path.join("bindings.rs")) 36 | .expect("Couldn't write bindings!"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /platforms/linux/src/queue.rs: -------------------------------------------------------------------------------- 1 | use crate::Error; 2 | use libc::{IFF_NO_PI, IFF_TAP, IFF_TUN}; 3 | use netconfig::sys::posix::ifreq::ifreq; 4 | use std::fs; 5 | use std::os::unix::fs::OpenOptionsExt; 6 | use std::os::unix::io::AsRawFd; 7 | use tunio_core::config::Layer; 8 | 9 | mod ioctls { 10 | nix::ioctl_write_int!(tunsetiff, b'T', 202); 11 | nix::ioctl_write_int!(tunsetpersist, b'T', 203); 12 | nix::ioctl_write_int!(tunsetowner, b'T', 204); 13 | nix::ioctl_write_int!(tunsetgroup, b'T', 206); 14 | } 15 | 16 | pub(crate) struct Device { 17 | pub device: fs::File, 18 | pub name: String, 19 | } 20 | 21 | pub(crate) fn create_device(name: &str, layer: Layer, blocking: bool) -> Result { 22 | let mut open_opts = fs::OpenOptions::new(); 23 | open_opts.read(true).write(true); 24 | if !blocking { 25 | open_opts.custom_flags(libc::O_NONBLOCK); 26 | } 27 | let tun_device = open_opts.open("/dev/net/tun")?; 28 | 29 | let mut init_flags = match layer { 30 | Layer::L2 => IFF_TAP, 31 | Layer::L3 => IFF_TUN, 32 | }; 33 | init_flags |= IFF_NO_PI; 34 | 35 | let mut req = ifreq::new(name); 36 | req.ifr_ifru.ifru_flags = init_flags as _; 37 | 38 | unsafe { ioctls::tunsetiff(tun_device.as_raw_fd(), &req as *const _ as _) }.unwrap(); 39 | 40 | // Name can change due to formatting 41 | Ok(Device { 42 | device: tun_device, 43 | name: String::try_from(req.ifr_ifrn) 44 | .map_err(|e| Error::InterfaceNameError(format!("{e:?}")))?, 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /platforms/wintun/src/async_interface.rs: -------------------------------------------------------------------------------- 1 | use super::async_queue::AsyncQueue; 2 | use super::interface::CommonInterface; 3 | use futures::{AsyncRead, AsyncWrite}; 4 | use std::io::{self}; 5 | use std::pin::Pin; 6 | use std::task::{Context, Poll}; 7 | 8 | pub type AsyncInterface = CommonInterface; 9 | 10 | impl AsyncRead for AsyncInterface { 11 | fn poll_read( 12 | mut self: Pin<&mut Self>, 13 | cx: &mut Context<'_>, 14 | buf: &mut [u8], 15 | ) -> Poll> { 16 | match self.inner_queue_mut() { 17 | Ok(queue) => Pin::new(queue).poll_read(cx, buf), 18 | Err(e) => Poll::Ready(Err(e)), 19 | } 20 | } 21 | } 22 | 23 | impl AsyncWrite for AsyncInterface { 24 | fn poll_write( 25 | mut self: Pin<&mut Self>, 26 | cx: &mut Context<'_>, 27 | buf: &[u8], 28 | ) -> Poll> { 29 | match self.inner_queue_mut() { 30 | Ok(queue) => Pin::new(queue).poll_write(cx, buf), 31 | Err(e) => Poll::Ready(Err(e)), 32 | } 33 | } 34 | 35 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 36 | match self.inner_queue_mut() { 37 | Ok(queue) => Pin::new(queue).poll_flush(cx), 38 | Err(e) => Poll::Ready(Err(e)), 39 | } 40 | } 41 | 42 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 43 | match self.inner_queue_mut() { 44 | Ok(queue) => Pin::new(queue).poll_close(cx), 45 | Err(e) => Poll::Ready(Err(e)), 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | use etherparse::PacketBuilder; 2 | use futures::AsyncReadExt; 3 | use futures::AsyncWriteExt; 4 | use std::thread::sleep; 5 | use std::time::Duration; 6 | use tunio::traits::{DriverT, InterfaceT}; 7 | use tunio::{DefaultAsyncInterface, DefaultDriver}; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | env_logger::init(); 12 | let mut driver = DefaultDriver::new().unwrap(); 13 | 14 | let mut interface_config = DefaultAsyncInterface::config_builder(); 15 | interface_config.name("name".into()); 16 | #[cfg(target_os = "windows")] 17 | interface_config 18 | .platform(|mut b| b.description("description".into()).build()) 19 | .unwrap(); 20 | let interface_config = interface_config.build().unwrap(); 21 | 22 | let mut interface = DefaultAsyncInterface::new_up(&mut driver, interface_config).unwrap(); 23 | let iff = interface.handle(); 24 | 25 | iff.add_address("18.3.5.6/24".parse().unwrap()).unwrap(); 26 | iff.add_address("20.3.5.6/24".parse().unwrap()).unwrap(); 27 | iff.remove_address("18.3.5.6/24".parse().unwrap()).unwrap(); 28 | iff.add_address("fd3c:dea:7f96:2b14::/64".parse().unwrap()) 29 | .unwrap(); 30 | 31 | for _ in 1..10 { 32 | let builder = PacketBuilder::ipv6( 33 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 34 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 35 | 5, 36 | ) 37 | .udp(8080, 8080); 38 | 39 | let mut packet = Vec::with_capacity(builder.size(0)); 40 | builder.write(&mut packet, &[]).unwrap(); 41 | 42 | interface.write(&*packet).await; 43 | 44 | sleep(Duration::from_secs(1)); 45 | } 46 | 47 | let mut buf = vec![0u8; 4096]; 48 | while let Ok(n) = interface.read(buf.as_mut_slice()).await { 49 | buf.truncate(n); 50 | println!("{buf:x?}"); 51 | buf.resize(4096, 0u8); 52 | } 53 | 54 | tokio::signal::ctrl_c().await; 55 | } 56 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tunio" 3 | version = "0.4.0" 4 | edition.workspace = true 5 | license.workspace = true 6 | repository.workspace = true 7 | description = "Crate for creating and managing TUN/TAP interfaces with async support. Works best with netconfig crate." 8 | categories.workspace = true 9 | keywords.workspace = true 10 | readme = "README.md" 11 | rust-version.workspace = true 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | log.workspace = true 17 | futures.workspace = true 18 | netconfig.workspace = true 19 | derive_builder.workspace = true 20 | delegate.workspace = true 21 | tunio-core.workspace = true 22 | cfg-if = "1.0.0" 23 | 24 | [target.'cfg(target_os = "windows")'.dependencies] 25 | tunio-wintun = { version = "0.1.0", path = "platforms/wintun" } 26 | 27 | [target.'cfg(target_os = "linux")'.dependencies] 28 | tunio-linux = { version = "0.1.0", path = "platforms/linux" } 29 | 30 | [target.'cfg(target_os = "macos")'.dependencies] 31 | tunio-utun = { version = "0.1.0", path = "platforms/utun"} 32 | 33 | [features] 34 | default = [] 35 | tokio = ["tunio-linux/tokio", "tunio-utun/tokio"] 36 | 37 | [dev-dependencies] 38 | tokio = { workspace = true, features = ["rt", "macros", "rt-multi-thread", "signal", "sync", "io-util"] } 39 | etherparse = "0.12.0" 40 | env_logger = "0.9.0" 41 | 42 | [package.metadata.docs.rs] 43 | all-features = true 44 | rustdoc-args = ["--cfg", "docsrs"] 45 | 46 | [workspace] 47 | members = ["wintun-sys", "core", "platforms/wintun", "platforms/linux", "platforms/utun"] 48 | 49 | [[example]] 50 | name = "simple" 51 | path = "examples/simple.rs" 52 | required-features = ["tokio"] 53 | 54 | [workspace.package] 55 | repository = "https://github.com/GamePad64/tunio" 56 | keywords = ["network", "networking", "cross-platform", "tun"] 57 | license = "MIT" 58 | categories = ["os", "network-programming"] 59 | edition = "2021" 60 | rust-version = "1.64" 61 | 62 | [workspace.dependencies] 63 | log = "0.4.17" 64 | netconfig = "0.4.0" 65 | futures = "0.3.21" 66 | derive_builder = "0.11.2" 67 | delegate = "0.8.0" 68 | tunio-core = { version = "0.1.0", path = "core" } 69 | nix = "0.25.0" 70 | libc = "0.2.126" 71 | tokio = "1.21.2" 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tunio 2 | [![Crates.io](https://img.shields.io/crates/v/tunio?style=flat-square)](https://crates.io/crates/tunio) 3 | [![docs.rs](https://img.shields.io/docsrs/tunio/latest?style=flat-square)](https://docs.rs/tunio) 4 | 5 | Create TUN/TAP interfaces in cross-platform and idiomatic Rust! 6 | 7 | ## Features ⭐ 8 | - [Tokio](https://tokio.rs/) support (optional). 9 | - TUN/TAP support. 10 | - Extensible architecture for adding other platforms later. 11 | 12 | ## Short example 📜 13 | ```rust,no_run 14 | use std::io::{Read, Write}; 15 | use tunio::traits::{DriverT, InterfaceT}; 16 | use tunio::{DefaultDriver, DefaultInterface}; 17 | 18 | fn main() { 19 | // DefaultDriver is an alias for a supported driver for current platform. 20 | // It may be not optimal for your needs (for example, it can lack support of TAP), 21 | // but it will work in some cases. If you need another driver, then import and use it instead. 22 | let mut driver = DefaultDriver::new().unwrap(); 23 | // Preparing configuration for new interface. We use `Builder` pattern for this. 24 | let if_config = DefaultInterface::config_builder() 25 | .name("iface1".to_string()) 26 | .build() 27 | .unwrap(); 28 | 29 | // Then, we create the interface using config and start it immediately. 30 | let mut interface = DefaultInterface::new_up(&mut driver, if_config).unwrap(); 31 | 32 | // The interface is created and running. 33 | 34 | // Write to interface using Write trait 35 | let buf = [0u8; 4096]; 36 | let _ = interface.write(&buf); 37 | 38 | // Read from interface using Read trait 39 | let mut mut_buf = [0u8; 4096]; 40 | let _ = interface.read(&mut mut_buf); 41 | } 42 | 43 | ``` 44 | 45 | ## Supported platforms 🖥️ 46 | - **Windows**, TUN only (using [`Wintun`] driver). 47 | - [`Wintun`] driver requires a prebuilt DLL inside application folder. Please, refer to [`Wintun`] documentation for more details. 48 | - **Linux** 49 | 50 | [`Wintun`]: https://www.wintun.net/ 51 | 52 | macOS support for utun and feth drivers is planned. Feel free to post a PR, it is always greatly appreciated 😉 53 | 54 | ## Related projects 🔗 55 | - [`netconfig`]: A high-level abstraction for gathering and changing network interface configuration. 56 | 57 | [`netconfig`]: https://github.com/GamePad64/netconfig 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Rust template 2 | # Generated by Cargo 3 | # will have compiled files and executables 4 | debug/ 5 | target/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | tunio/Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | ### JetBrains template 15 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 16 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 17 | 18 | # User-specific stuff 19 | .idea/**/workspace.xml 20 | .idea/**/tasks.xml 21 | .idea/**/usage.statistics.xml 22 | .idea/**/dictionaries 23 | .idea/**/shelf 24 | 25 | # Generated files 26 | .idea/**/contentModel.xml 27 | 28 | # Sensitive or high-churn files 29 | .idea/**/dataSources/ 30 | .idea/**/dataSources.ids 31 | .idea/**/dataSources.local.xml 32 | .idea/**/sqlDataSources.xml 33 | .idea/**/dynamic.xml 34 | .idea/**/uiDesigner.xml 35 | .idea/**/dbnavigator.xml 36 | 37 | # Gradle 38 | .idea/**/gradle.xml 39 | .idea/**/libraries 40 | 41 | # Gradle and Maven with auto-import 42 | # When using Gradle or Maven with auto-import, you should exclude module files, 43 | # since they will be recreated, and may cause churn. Uncomment if using 44 | # auto-import. 45 | # .idea/artifacts 46 | # .idea/compiler.xml 47 | # .idea/jarRepositories.xml 48 | # .idea/modules.xml 49 | # .idea/*.iml 50 | # .idea/modules 51 | # *.iml 52 | # *.ipr 53 | 54 | # CMake 55 | cmake-build-*/ 56 | 57 | # Mongo Explorer plugin 58 | .idea/**/mongoSettings.xml 59 | 60 | # File-based project format 61 | *.iws 62 | 63 | # IntelliJ 64 | out/ 65 | 66 | # mpeltonen/sbt-idea plugin 67 | .idea_modules/ 68 | 69 | # JIRA plugin 70 | atlassian-ide-plugin.xml 71 | 72 | # Cursive Clojure plugin 73 | .idea/replstate.xml 74 | 75 | # Crashlytics plugin (for Android Studio and IntelliJ) 76 | com_crashlytics_export_strings.xml 77 | crashlytics.properties 78 | crashlytics-build.properties 79 | fabric.properties 80 | 81 | # Editor-based Rest Client 82 | .idea/httpRequests 83 | 84 | # Android studio 3.1+ serialized cache file 85 | .idea/caches/build_file_checksums.ser 86 | 87 | -------------------------------------------------------------------------------- /platforms/wintun/src/wrappers/adapter.rs: -------------------------------------------------------------------------------- 1 | use super::HandleWrapper; 2 | use log::error; 3 | use std::io; 4 | use std::sync::Arc; 5 | use tunio_core::Error; 6 | use widestring::U16CString; 7 | use windows::core::{GUID, PCWSTR}; 8 | use windows::Win32::NetworkManagement::Ndis::NET_LUID_LH; 9 | use wintun_sys::WINTUN_ADAPTER_HANDLE; 10 | 11 | const MAX_NAME: usize = 255; 12 | 13 | pub struct Adapter { 14 | wintun: Arc, 15 | handle: HandleWrapper, 16 | } 17 | 18 | impl Adapter { 19 | pub fn new( 20 | guid: GUID, 21 | name: &str, 22 | description: &str, 23 | wintun: Arc, 24 | ) -> Result { 25 | let [name_u16, description_u16] = [name, description].map(encode_name); 26 | let (name_u16, description_u16) = (name_u16?, description_u16?); 27 | 28 | let adapter_handle = unsafe { 29 | wintun.WintunCreateAdapter( 30 | PCWSTR::from_raw(name_u16.as_ptr()), 31 | PCWSTR::from_raw(description_u16.as_ptr()), 32 | &guid, 33 | ) 34 | }; 35 | 36 | if adapter_handle.is_null() { 37 | let err = io::Error::last_os_error(); 38 | error!("Failed to create adapter: {err}"); 39 | return Err(Error::from(err)); 40 | } 41 | 42 | Ok(Self { 43 | wintun, 44 | handle: HandleWrapper(adapter_handle), 45 | }) 46 | } 47 | 48 | pub fn luid(&self) -> u64 { 49 | let mut luid_buf = NET_LUID_LH::default(); 50 | unsafe { 51 | self.wintun 52 | .WintunGetAdapterLUID(self.handle.0, &mut luid_buf as _); 53 | luid_buf.Value 54 | } 55 | } 56 | 57 | pub fn handle(&self) -> WINTUN_ADAPTER_HANDLE { 58 | self.handle.0 59 | } 60 | } 61 | 62 | impl Drop for Adapter { 63 | fn drop(&mut self) { 64 | unsafe { self.wintun.WintunCloseAdapter(self.handle.0) }; 65 | } 66 | } 67 | 68 | fn encode_name(string: &str) -> Result { 69 | let result = U16CString::from_str(string).map_err(|_| Error::InterfaceNameUnicodeError)?; 70 | match result.len() { 71 | 0..=MAX_NAME => Ok(result), 72 | l => Err(Error::InterfaceNameTooLong(l, MAX_NAME)), 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/queue/tokiofd.rs: -------------------------------------------------------------------------------- 1 | use crate::queue::syncfd::SyncFdQueue; 2 | use crate::queue::FdQueueT; 3 | use crate::traits::AsyncQueueT; 4 | use futures::{AsyncRead, AsyncWrite}; 5 | use std::io::{self, Read, Write}; 6 | use std::os::unix::io::OwnedFd; 7 | use std::pin::Pin; 8 | use std::task::{ready, Context, Poll}; 9 | use tokio::io::unix::AsyncFd; 10 | 11 | pub struct TokioFdQueue { 12 | inner: AsyncFd, 13 | } 14 | 15 | impl AsyncQueueT for TokioFdQueue {} 16 | 17 | impl FdQueueT for TokioFdQueue { 18 | const BLOCKING: bool = false; 19 | 20 | fn new(device: OwnedFd) -> Self { 21 | Self { 22 | inner: AsyncFd::new(SyncFdQueue::new(device)).unwrap(), 23 | } 24 | } 25 | } 26 | 27 | impl AsyncRead for TokioFdQueue { 28 | fn poll_read( 29 | self: Pin<&mut Self>, 30 | cx: &mut Context<'_>, 31 | buf: &mut [u8], 32 | ) -> Poll> { 33 | let self_mut = self.get_mut(); 34 | loop { 35 | let mut guard = ready!(self_mut.inner.poll_read_ready_mut(cx))?; 36 | 37 | match guard.try_io(|inner| inner.get_mut().read(buf)) { 38 | Ok(Ok(n)) => { 39 | return Poll::Ready(Ok(n)); 40 | } 41 | Ok(Err(e)) => return Poll::Ready(Err(e)), 42 | Err(_) => continue, 43 | } 44 | } 45 | } 46 | } 47 | 48 | impl AsyncWrite for TokioFdQueue { 49 | fn poll_write( 50 | self: Pin<&mut Self>, 51 | cx: &mut Context<'_>, 52 | buf: &[u8], 53 | ) -> Poll> { 54 | let self_mut = self.get_mut(); 55 | loop { 56 | let mut guard = ready!(self_mut.inner.poll_write_ready_mut(cx))?; 57 | 58 | match guard.try_io(|inner| inner.get_mut().write(buf)) { 59 | Ok(Ok(n)) => { 60 | return Poll::Ready(Ok(n)); 61 | } 62 | Ok(Err(e)) => return Poll::Ready(Err(e)), 63 | Err(_) => continue, 64 | } 65 | } 66 | } 67 | 68 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 69 | let self_mut = self.get_mut(); 70 | loop { 71 | let mut guard = ready!(self_mut.inner.poll_write_ready_mut(cx))?; 72 | 73 | match guard.try_io(|inner| inner.get_mut().flush()) { 74 | Ok(Ok(())) => { 75 | return Poll::Ready(Ok(())); 76 | } 77 | Ok(Err(e)) => return Poll::Ready(Err(e)), 78 | Err(_) => continue, 79 | } 80 | } 81 | } 82 | 83 | fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { 84 | Poll::Ready(Ok(())) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /platforms/wintun/src/interface.rs: -------------------------------------------------------------------------------- 1 | use super::queue::SessionQueueT; 2 | use super::wrappers::{Adapter, Session}; 3 | use super::PlatformIfConfig; 4 | use super::Queue; 5 | use crate::Driver; 6 | use std::io; 7 | use std::io::{ErrorKind, Read, Write}; 8 | use std::sync::Arc; 9 | use tunio_core::config::{IfConfig, Layer}; 10 | use tunio_core::traits::InterfaceT; 11 | use tunio_core::Error; 12 | use windows::core::GUID; 13 | use windows::Win32::NetworkManagement::IpHelper::ConvertInterfaceLuidToIndex; 14 | use windows::Win32::NetworkManagement::Ndis::NET_LUID_LH; 15 | 16 | pub struct CommonInterface { 17 | wintun: Arc, 18 | adapter: Arc, 19 | config: IfConfig, 20 | pub(crate) queue: Option, 21 | } 22 | 23 | impl InterfaceT for CommonInterface { 24 | type PlatformDriver = Driver; 25 | type PlatformIfConfig = PlatformIfConfig; 26 | 27 | fn new( 28 | driver: &mut Self::PlatformDriver, 29 | params: IfConfig, 30 | ) -> Result { 31 | let _ = Session::validate_capacity(params.platform.capacity); 32 | if params.layer == Layer::L2 { 33 | return Err(Error::LayerUnsupported(params.layer)); 34 | } 35 | 36 | let wintun = driver.wintun().clone(); 37 | 38 | let adapter = Arc::new(Adapter::new( 39 | GUID::from_u128(params.platform.guid), 40 | ¶ms.name, 41 | ¶ms.platform.description, 42 | wintun.clone(), 43 | )?); 44 | 45 | Ok(Self { 46 | wintun, 47 | adapter, 48 | config: params, 49 | queue: None, 50 | }) 51 | } 52 | 53 | fn up(&mut self) -> Result<(), Error> { 54 | let session = Session::new( 55 | self.adapter.clone(), 56 | self.wintun.clone(), 57 | self.config.platform.capacity, 58 | )?; 59 | self.queue = Some(Q::new(session)); 60 | 61 | Ok(()) 62 | } 63 | 64 | fn down(&mut self) -> Result<(), Error> { 65 | let _ = self.queue.take(); 66 | Ok(()) 67 | } 68 | 69 | fn handle(&self) -> netconfig::Interface { 70 | let mut index = 0; 71 | let luid = NET_LUID_LH { 72 | Value: self.adapter.luid(), 73 | }; 74 | 75 | unsafe { 76 | ConvertInterfaceLuidToIndex(&luid, &mut index).unwrap(); 77 | } 78 | 79 | netconfig::Interface::try_from_index(index).unwrap() 80 | } 81 | } 82 | 83 | impl CommonInterface { 84 | pub(crate) fn inner_queue_mut(&mut self) -> io::Result<&mut Q> { 85 | match &mut self.queue { 86 | Some(queue) => Ok(queue), 87 | None => Err(ErrorKind::BrokenPipe.into()), 88 | } 89 | } 90 | } 91 | 92 | pub type Interface = CommonInterface; 93 | 94 | impl Read for Interface { 95 | delegate::delegate! { 96 | to self.inner_queue_mut()? { 97 | fn read(&mut self, buf: &mut [u8]) -> io::Result; 98 | } 99 | } 100 | } 101 | 102 | impl Write for Interface { 103 | delegate::delegate! { 104 | to self.inner_queue_mut()? { 105 | fn write(&mut self, buf: &[u8]) -> io::Result; 106 | fn flush(&mut self) -> io::Result<()>; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /platforms/linux/src/interface.rs: -------------------------------------------------------------------------------- 1 | use super::queue::{create_device, Device}; 2 | use super::Driver; 3 | use super::PlatformIfConfig; 4 | use delegate::delegate; 5 | use futures::{AsyncRead, AsyncWrite}; 6 | use log::debug; 7 | use netconfig::sys::InterfaceExt; 8 | use std::io; 9 | use std::io::{Read, Write}; 10 | use std::pin::Pin; 11 | use std::task::{Context, Poll}; 12 | use tunio_core::config::IfConfig; 13 | use tunio_core::queue::syncfd::SyncFdQueue; 14 | #[cfg(feature = "tokio")] 15 | use tunio_core::queue::tokiofd::TokioFdQueue; 16 | use tunio_core::queue::FdQueueT; 17 | use tunio_core::traits::{AsyncQueueT, InterfaceT, SyncQueueT}; 18 | use tunio_core::Error; 19 | 20 | pub struct LinuxInterface { 21 | name: String, 22 | pub(crate) queue: Q, 23 | } 24 | 25 | impl LinuxInterface { 26 | pub fn name(&self) -> &str { 27 | &self.name 28 | } 29 | } 30 | 31 | impl InterfaceT for LinuxInterface { 32 | type PlatformDriver = Driver; 33 | type PlatformIfConfig = PlatformIfConfig; 34 | 35 | fn new( 36 | _driver: &mut Self::PlatformDriver, 37 | params: IfConfig, 38 | ) -> Result { 39 | let Device { device, name } = create_device(¶ms.name, params.layer, Q::BLOCKING)?; 40 | let queue = Q::new(device.into()); 41 | 42 | if params.name != name { 43 | debug!( 44 | "Interface name is changed \"{}\" -> \"{}\"", 45 | params.name, name 46 | ); 47 | } 48 | 49 | Ok(Self { name, queue }) 50 | } 51 | 52 | fn up(&mut self) -> Result<(), Error> { 53 | Ok(self.handle().set_up(true)?) 54 | } 55 | 56 | fn down(&mut self) -> Result<(), Error> { 57 | Ok(self.handle().set_up(false)?) 58 | } 59 | 60 | fn handle(&self) -> netconfig::Interface { 61 | netconfig::Interface::try_from_name(self.name()).unwrap() 62 | } 63 | } 64 | 65 | pub type Interface = LinuxInterface; 66 | impl SyncQueueT for Interface {} 67 | 68 | impl Read for LinuxInterface { 69 | delegate! { 70 | to self.queue { 71 | fn read(&mut self, buf: &mut [u8]) -> Result; 72 | } 73 | } 74 | } 75 | 76 | impl Write for LinuxInterface { 77 | delegate! { 78 | to self.queue { 79 | fn write(&mut self, buf: &[u8]) -> io::Result; 80 | fn flush(&mut self) -> io::Result<()>; 81 | } 82 | } 83 | } 84 | 85 | #[cfg(feature = "tokio")] 86 | pub type TokioInterface = LinuxInterface; 87 | #[cfg(feature = "tokio")] 88 | impl AsyncQueueT for TokioInterface {} 89 | 90 | impl AsyncRead for LinuxInterface { 91 | delegate! { 92 | to Pin::new(&mut self.queue) { 93 | fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll>; 94 | } 95 | } 96 | } 97 | 98 | impl AsyncWrite for LinuxInterface { 99 | delegate! { 100 | to Pin::new(&mut self.queue) { 101 | fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll>; 102 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 103 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /platforms/utun/src/interface.rs: -------------------------------------------------------------------------------- 1 | use crate::queue::create_device; 2 | use crate::{Driver, PlatformIfConfig}; 3 | use delegate::delegate; 4 | use futures::{AsyncRead, AsyncWrite}; 5 | use netconfig::sys::InterfaceExt; 6 | use std::io::{self, Read, Write}; 7 | use std::pin::Pin; 8 | use std::task::{Context, Poll}; 9 | use tunio_core::config::IfConfig; 10 | use tunio_core::queue::syncfd::SyncFdQueue; 11 | #[cfg(feature = "tokio")] 12 | use tunio_core::queue::tokiofd::TokioFdQueue; 13 | use tunio_core::queue::FdQueueT; 14 | use tunio_core::traits::{AsyncQueueT, InterfaceT, SyncQueueT}; 15 | use tunio_core::Error; 16 | 17 | pub struct UtunInterface { 18 | name: String, 19 | queue: Q, 20 | } 21 | 22 | impl InterfaceT for UtunInterface { 23 | type PlatformDriver = Driver; 24 | type PlatformIfConfig = PlatformIfConfig; 25 | 26 | fn new_up( 27 | _driver: &mut Self::PlatformDriver, 28 | params: IfConfig, 29 | ) -> Result { 30 | let queue = Q::new(create_device(¶ms.name, Q::BLOCKING)?); 31 | 32 | Ok(Self { 33 | name: params.name, 34 | queue, 35 | }) 36 | } 37 | 38 | fn new( 39 | driver: &mut Self::PlatformDriver, 40 | params: IfConfig, 41 | ) -> Result { 42 | let mut interface = Self::new_up(driver, params)?; 43 | interface.down()?; 44 | Ok(interface) 45 | } 46 | 47 | fn up(&mut self) -> Result<(), Error> { 48 | let handle = self.handle(); 49 | handle.set_up(true)?; 50 | handle.set_running(true)?; 51 | 52 | Ok(()) 53 | } 54 | 55 | fn down(&mut self) -> Result<(), Error> { 56 | let handle = self.handle(); 57 | handle.set_up(false)?; 58 | handle.set_running(false)?; 59 | 60 | Ok(()) 61 | } 62 | 63 | fn handle(&self) -> netconfig::Interface { 64 | netconfig::Interface::try_from_name(self.name()).unwrap() 65 | } 66 | } 67 | 68 | impl UtunInterface { 69 | pub fn name(&self) -> &str { 70 | &self.name 71 | } 72 | } 73 | 74 | pub type Interface = UtunInterface; 75 | 76 | impl SyncQueueT for Interface {} 77 | 78 | impl Read for UtunInterface { 79 | delegate! { 80 | to self.queue { 81 | fn read(&mut self, buf: &mut [u8]) -> Result; 82 | } 83 | } 84 | } 85 | 86 | impl Write for UtunInterface { 87 | delegate! { 88 | to self.queue { 89 | fn write(&mut self, buf: &[u8]) -> io::Result; 90 | fn flush(&mut self) -> io::Result<()>; 91 | } 92 | } 93 | } 94 | 95 | #[cfg(feature = "tokio")] 96 | pub type TokioInterface = UtunInterface; 97 | #[cfg(feature = "tokio")] 98 | impl AsyncQueueT for TokioInterface {} 99 | 100 | impl AsyncRead for UtunInterface { 101 | delegate! { 102 | to Pin::new(&mut self.queue) { 103 | fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll>; 104 | } 105 | } 106 | } 107 | 108 | impl AsyncWrite for UtunInterface { 109 | delegate! { 110 | to Pin::new(&mut self.queue) { 111 | fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll>; 112 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 113 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /platforms/wintun/src/async_queue.rs: -------------------------------------------------------------------------------- 1 | use super::event::SafeEvent; 2 | use super::wrappers::Session; 3 | use crate::queue::SessionQueueT; 4 | use futures::{AsyncRead, AsyncWrite}; 5 | use std::future::Future; 6 | use std::io::{self, Read, Write}; 7 | use std::pin::Pin; 8 | use std::sync::Arc; 9 | use std::task::{Context, Poll}; 10 | use windows::Win32::Foundation::WIN32_ERROR; 11 | use windows::{ 12 | Win32::Foundation::HANDLE, Win32::Foundation::WAIT_ABANDONED_0, 13 | Win32::Foundation::WAIT_OBJECT_0, Win32::System::Threading::WaitForMultipleObjects, 14 | Win32::System::WindowsProgramming::INFINITE, 15 | }; 16 | 17 | enum WaitingStopReason { 18 | Shutdown, 19 | Ready, 20 | } 21 | 22 | enum ReadState { 23 | Waiting(Option>), 24 | Idle, 25 | Closed, 26 | } 27 | 28 | pub struct AsyncQueue { 29 | session: Session, 30 | 31 | read_state: ReadState, 32 | shutdown_event: Arc, 33 | } 34 | 35 | impl SessionQueueT for AsyncQueue { 36 | fn new(session: Session) -> Self { 37 | Self { 38 | session, 39 | 40 | read_state: ReadState::Idle, 41 | 42 | // Manual reset, because we use this event once and it must fire on all threads 43 | shutdown_event: Arc::new(SafeEvent::new(true, false)), 44 | } 45 | } 46 | } 47 | 48 | impl Drop for AsyncQueue { 49 | fn drop(&mut self) { 50 | self.shutdown_event.set_event(); 51 | } 52 | } 53 | 54 | fn wait_for_read(read_event: HANDLE, shutdown_event: Arc) -> WaitingStopReason { 55 | const WAIT_OBJECT_1: WIN32_ERROR = WIN32_ERROR(WAIT_OBJECT_0.0 + 1); 56 | const WAIT_ABANDONED_1: WIN32_ERROR = WIN32_ERROR(WAIT_ABANDONED_0.0 + 1); 57 | 58 | match unsafe { WaitForMultipleObjects(&[shutdown_event.handle(), read_event], false, INFINITE) } 59 | { 60 | // Shutdown 61 | WAIT_OBJECT_0 | WAIT_ABANDONED_0 => WaitingStopReason::Shutdown, 62 | // Ready for read 63 | WAIT_OBJECT_1 => WaitingStopReason::Ready, 64 | // Read event deleted 65 | WAIT_ABANDONED_1 => { 66 | panic!("Read event deleted unexpectedly"); 67 | } 68 | 69 | e => { 70 | panic!("Unexpected event result: {e:?}"); 71 | } 72 | } 73 | } 74 | 75 | impl AsyncRead for AsyncQueue { 76 | fn poll_read( 77 | mut self: Pin<&mut Self>, 78 | cx: &mut Context<'_>, 79 | buf: &mut [u8], 80 | ) -> Poll> { 81 | loop { 82 | match &mut self.read_state { 83 | ReadState::Waiting(task) => { 84 | let mut task = task.take().unwrap(); 85 | 86 | self.read_state = match Pin::new(&mut task).poll(cx) { 87 | Poll::Ready(WaitingStopReason::Shutdown) => ReadState::Closed, 88 | Poll::Ready(WaitingStopReason::Ready) => ReadState::Idle, 89 | Poll::Pending => ReadState::Waiting(Some(task)), 90 | }; 91 | 92 | if let ReadState::Waiting(..) = self.read_state { 93 | return Poll::Pending; 94 | } 95 | } 96 | ReadState::Idle => match self.session.read(buf) { 97 | Ok(n) => return Poll::Ready(Ok(n)), 98 | Err(e) => { 99 | if e.kind() == io::ErrorKind::WouldBlock { 100 | let read_event = self.session.read_event(); 101 | let inner_shutdown_event = self.shutdown_event.clone(); 102 | 103 | self.read_state = 104 | ReadState::Waiting(Some(blocking::unblock(move || { 105 | wait_for_read(read_event, inner_shutdown_event) 106 | }))); 107 | } else { 108 | return Poll::Ready(Err(e)); 109 | } 110 | } 111 | }, 112 | ReadState::Closed => return Poll::Ready(Ok(0)), 113 | } 114 | } 115 | } 116 | } 117 | 118 | impl AsyncWrite for AsyncQueue { 119 | // Write to wintun is already nonblocking 120 | fn poll_write( 121 | mut self: Pin<&mut Self>, 122 | _cx: &mut Context<'_>, 123 | buf: &[u8], 124 | ) -> Poll> { 125 | Poll::Ready(self.session.write(buf)) 126 | } 127 | 128 | fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { 129 | // Not implemented by driver 130 | Poll::Ready(Ok(())) 131 | } 132 | 133 | fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { 134 | // Not implemented by driver 135 | Poll::Ready(Ok(())) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /platforms/wintun/src/wrappers/session.rs: -------------------------------------------------------------------------------- 1 | use super::Adapter; 2 | use super::HandleWrapper; 3 | use bytes::BufMut; 4 | use log::error; 5 | use std::io; 6 | use std::io::{Read, Write}; 7 | use std::sync::Arc; 8 | use tunio_core::Error; 9 | use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_NO_MORE_ITEMS, HANDLE, WIN32_ERROR}; 10 | use wintun_sys::{WINTUN_MAX_RING_CAPACITY, WINTUN_MIN_RING_CAPACITY, WINTUN_SESSION_HANDLE}; 11 | 12 | struct PacketReader<'a> { 13 | handle: HandleWrapper, 14 | wintun: &'a wintun_sys::wintun, 15 | 16 | ptr: *const u8, 17 | len: usize, 18 | } 19 | 20 | impl<'a> PacketReader<'a> { 21 | pub fn read( 22 | handle: HandleWrapper, 23 | wintun: &'a wintun_sys::wintun, 24 | ) -> io::Result { 25 | let mut len: u32 = 0; 26 | let ptr = unsafe { wintun.WintunReceivePacket(handle.0, &mut len) }; 27 | 28 | if !ptr.is_null() { 29 | Ok(Self { 30 | handle, 31 | wintun, 32 | ptr, 33 | len: len as _, 34 | }) 35 | } else { 36 | Err(io::Error::last_os_error()) 37 | } 38 | } 39 | 40 | pub fn as_slice(&self) -> &[u8] { 41 | unsafe { std::slice::from_raw_parts(self.ptr, self.len) } 42 | } 43 | } 44 | 45 | impl<'a> Drop for PacketReader<'a> { 46 | fn drop(&mut self) { 47 | if !self.ptr.is_null() { 48 | unsafe { 49 | self.wintun 50 | .WintunReleaseReceivePacket(self.handle.0, self.ptr); 51 | } 52 | } 53 | } 54 | } 55 | 56 | pub struct Session { 57 | handle: HandleWrapper, 58 | wintun: Arc, 59 | } 60 | 61 | impl Session { 62 | pub fn new( 63 | adapter: Arc, 64 | wintun: Arc, 65 | capacity: u32, 66 | ) -> Result { 67 | let _ = Self::validate_capacity(capacity)?; 68 | 69 | let session_handle = unsafe { wintun.WintunStartSession(adapter.handle(), capacity) }; 70 | 71 | if session_handle.is_null() { 72 | let err = io::Error::last_os_error(); 73 | error!("Failed to create session: {err}"); 74 | return Err(err.into()); 75 | } 76 | 77 | Ok(Self { 78 | handle: HandleWrapper(session_handle), 79 | wintun, 80 | }) 81 | } 82 | 83 | #[allow(dead_code)] 84 | pub fn read_event(&self) -> HANDLE { 85 | unsafe { self.wintun.WintunGetReadWaitEvent(self.handle.0) } 86 | } 87 | 88 | pub fn validate_capacity(capacity: u32) -> Result<(), Error> { 89 | let range = WINTUN_MIN_RING_CAPACITY..=WINTUN_MAX_RING_CAPACITY; 90 | if !range.contains(&capacity) || !capacity.is_power_of_two() { 91 | return Err(Error::InvalidConfigValue { 92 | name: "capacity".to_string(), 93 | value: capacity.to_string(), 94 | reason: format!( 95 | "must be power of 2 between {} and {}", 96 | WINTUN_MIN_RING_CAPACITY, WINTUN_MAX_RING_CAPACITY 97 | ), 98 | }); 99 | } 100 | Ok(()) 101 | } 102 | } 103 | 104 | impl Read for Session { 105 | fn read(&mut self, mut buf: &mut [u8]) -> io::Result { 106 | let packet = PacketReader::read(self.handle.clone(), &self.wintun); 107 | match packet { 108 | Ok(packet) => { 109 | let packet_slice = packet.as_slice(); 110 | buf.put(packet_slice); 111 | Ok(packet_slice.len()) 112 | } 113 | Err(e) => match error_eq(&e, ERROR_NO_MORE_ITEMS) { 114 | true => Err(io::ErrorKind::WouldBlock.into()), 115 | false => Err(e), 116 | }, 117 | } 118 | } 119 | } 120 | 121 | impl Write for Session { 122 | // does not block, as WintunAllocateSendPacket and WintunSendPacket are executed right one ofter another 123 | fn write(&mut self, buf: &[u8]) -> io::Result { 124 | let packet = unsafe { 125 | self.wintun 126 | .WintunAllocateSendPacket(self.handle.0, buf.len() as _) 127 | }; 128 | if !packet.is_null() { 129 | // Copy buffer to allocated packet 130 | unsafe { 131 | packet.copy_from_nonoverlapping(buf.as_ptr(), buf.len()); 132 | self.wintun.WintunSendPacket(self.handle.0, packet); // Deallocates packet 133 | } 134 | Ok(buf.len()) 135 | } else { 136 | let e = io::Error::last_os_error(); 137 | match error_eq(&e, ERROR_BUFFER_OVERFLOW) { 138 | true => panic!("send buffer overflow"), 139 | false => Err(e), 140 | } 141 | } 142 | } 143 | 144 | fn flush(&mut self) -> io::Result<()> { 145 | Ok(()) 146 | } 147 | } 148 | 149 | impl Drop for Session { 150 | fn drop(&mut self) { 151 | unsafe { 152 | self.wintun.WintunEndSession(self.handle.0); 153 | } 154 | } 155 | } 156 | 157 | fn error_eq(err: &io::Error, win32_error: WIN32_ERROR) -> bool { 158 | match err.raw_os_error() { 159 | None => false, 160 | Some(os_error) => os_error == win32_error.0 as _, 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /wintun-sys/wintun/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Prebuilt Binaries License 2 | ------------------------- 3 | 4 | 1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll" 5 | files that are included in the .zip file that contains this document as 6 | downloaded from wintun.net/builds. 7 | 8 | 2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and 9 | non-transferable right to use Software for lawful purposes under certain 10 | obligations and limited rights as set forth in this agreement. 11 | 12 | 3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is 13 | licensed, not sold. Title to Software and all associated intellectual 14 | property rights are retained by WireGuard. You must not: 15 | a. reverse engineer, decompile, disassemble, extract from, or otherwise 16 | modify the Software; 17 | b. modify or create derivative work based upon Software in whole or in 18 | parts, except insofar as only the API interfaces of the "wintun.h" file 19 | distributed alongside the Software (the "Permitted API") are used; 20 | c. remove any proprietary notices, labels, or copyrights from the Software; 21 | d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise 22 | transfer rights of the Software without the prior written consent of 23 | WireGuard LLC, except insofar as the Software is distributed alongside 24 | other software that uses the Software only via the Permitted API; 25 | e. use the name of WireGuard LLC, the WireGuard project, the Wintun 26 | project, or the names of its contributors to endorse or promote products 27 | derived from the Software without specific prior written consent. 28 | 29 | 4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF 30 | ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR 31 | STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS 32 | FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS, 33 | EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE 34 | ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE 35 | RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT 36 | WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW, 37 | THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER 38 | SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 39 | WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR 40 | A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE 41 | EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. 42 | 43 | 5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event 44 | WireGuard LLC or any third-party-developer will be liable for any lost 45 | revenue, profit or data or for special, indirect, consequential, incidental 46 | or punitive damages, however caused regardless of the theory of liability, 47 | arising out of or related to the use of or inability to use Software, even 48 | if WireGuard LLC has been advised of the possibility of such damages. 49 | Solely you are responsible for determining the appropriateness of using 50 | Software and accept full responsibility for all risks associated with its 51 | exercise of rights under this agreement, including but not limited to the 52 | risks and costs of program errors, compliance with applicable laws, damage 53 | to or loss of data, programs or equipment, and unavailability or 54 | interruption of operations. The foregoing limitations will apply even if 55 | the above stated warranty fails of its essential purpose. You acknowledge, 56 | that it is in the nature of software that software is complex and not 57 | completely free of errors. In no event shall WireGuard LLC or any 58 | third-party-developer be liable to you under any theory for any damages 59 | suffered by you or any user of Software or for any special, incidental, 60 | indirect, consequential or similar damages (including without limitation 61 | damages for loss of business profits, business interruption, loss of 62 | business information or any other pecuniary loss) arising out of the use or 63 | inability to use Software, even if WireGuard LLC has been advised of the 64 | possibility of such damages and regardless of the legal or quitable theory 65 | (contract, tort, or otherwise) upon which the claim is based. 66 | 67 | 6. TERMINATION. This agreement is affected until terminated. You may 68 | terminate this agreement at any time. This agreement will terminate 69 | immediately without notice from WireGuard LLC if you fail to comply with 70 | the terms and conditions of this agreement. Upon termination, you must 71 | delete Software and all copies of Software and cease all forms of 72 | distribution of Software. 73 | 74 | 7. SEVERABILITY. If any provision of this agreement is held to be 75 | unenforceable, this agreement will remain in effect with the provision 76 | omitted, unless omission would frustrate the intent of the parties, in 77 | which case this agreement will immediately terminate. 78 | 79 | 8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement 80 | are reserved by WireGuard LLC. For example, WireGuard LLC reserves the 81 | right at any time to cease development of Software, to alter distribution 82 | details, features, specifications, capabilities, functions, licensing 83 | terms, release dates, APIs, ABIs, general availability, or other 84 | characteristics of the Software. 85 | -------------------------------------------------------------------------------- /wintun-sys/wintun/wintun.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT 2 | * 3 | * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #ifndef ALIGNED 19 | # if defined(_MSC_VER) 20 | # define ALIGNED(n) __declspec(align(n)) 21 | # elif defined(__GNUC__) 22 | # define ALIGNED(n) __attribute__((aligned(n))) 23 | # else 24 | # error "Unable to define ALIGNED" 25 | # endif 26 | #endif 27 | 28 | /* MinGW is missing this one, unfortunately. */ 29 | #ifndef _Post_maybenull_ 30 | # define _Post_maybenull_ 31 | #endif 32 | 33 | #pragma warning(push) 34 | #pragma warning(disable : 4324) /* structure was padded due to alignment specifier */ 35 | 36 | /** 37 | * A handle representing Wintun adapter 38 | */ 39 | typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE; 40 | 41 | /** 42 | * Creates a new Wintun adapter. 43 | * 44 | * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 45 | * characters. 46 | * 47 | * @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1 48 | * characters. 49 | * 50 | * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically. 51 | * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is 52 | * created for each new adapter. It is called "requested" GUID because the API it uses is 53 | * completely undocumented, and so there could be minor interesting complications with its usage. 54 | * 55 | * @return If the function succeeds, the return value is the adapter handle. Must be released with 56 | * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call 57 | * GetLastError. 58 | */ 59 | typedef _Must_inspect_result_ 60 | _Return_type_success_(return != NULL) 61 | _Post_maybenull_ 62 | WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC) 63 | (_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID); 64 | 65 | /** 66 | * Opens an existing Wintun adapter. 67 | * 68 | * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 69 | * characters. 70 | * 71 | * @return If the function succeeds, the return value is the adapter handle. Must be released with 72 | * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call 73 | * GetLastError. 74 | */ 75 | typedef _Must_inspect_result_ 76 | _Return_type_success_(return != NULL) 77 | _Post_maybenull_ 78 | WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name); 79 | 80 | /** 81 | * Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. 82 | * 83 | * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. 84 | */ 85 | typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter); 86 | 87 | /** 88 | * Deletes the Wintun driver if there are no more adapters in use. 89 | * 90 | * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To 91 | * get extended error information, call GetLastError. 92 | */ 93 | typedef _Return_type_success_(return != FALSE) 94 | BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID); 95 | 96 | /** 97 | * Returns the LUID of the adapter. 98 | * 99 | * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter 100 | * 101 | * @param Luid Pointer to LUID to receive adapter LUID. 102 | */ 103 | typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid); 104 | 105 | /** 106 | * Determines the version of the Wintun driver currently loaded. 107 | * 108 | * @return If the function succeeds, the return value is the version number. If the function fails, the return value is 109 | * zero. To get extended error information, call GetLastError. Possible errors include the following: 110 | * ERROR_FILE_NOT_FOUND Wintun not loaded 111 | */ 112 | typedef _Return_type_success_(return != 0) 113 | DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID); 114 | 115 | /** 116 | * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK. 117 | */ 118 | typedef enum 119 | { 120 | WINTUN_LOG_INFO, /**< Informational */ 121 | WINTUN_LOG_WARN, /**< Warning */ 122 | WINTUN_LOG_ERR /**< Error */ 123 | } WINTUN_LOGGER_LEVEL; 124 | 125 | /** 126 | * Called by internal logger to report diagnostic messages 127 | * 128 | * @param Level Message level. 129 | * 130 | * @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC. 131 | * 132 | * @param Message Message text. 133 | */ 134 | typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)( 135 | _In_ WINTUN_LOGGER_LEVEL Level, 136 | _In_ DWORD64 Timestamp, 137 | _In_z_ LPCWSTR Message); 138 | 139 | /** 140 | * Sets logger callback function. 141 | * 142 | * @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various 143 | * threads concurrently. Should the logging require serialization, you must handle serialization in 144 | * NewLogger. Set to NULL to disable. 145 | */ 146 | typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger); 147 | 148 | /** 149 | * Minimum ring capacity. 150 | */ 151 | #define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */ 152 | 153 | /** 154 | * Maximum ring capacity. 155 | */ 156 | #define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */ 157 | 158 | /** 159 | * A handle representing Wintun session 160 | */ 161 | typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE; 162 | 163 | /** 164 | * Starts Wintun session. 165 | * 166 | * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter 167 | * 168 | * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.) 169 | * Must be a power of two. 170 | * 171 | * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is 172 | * NULL. To get extended error information, call GetLastError. 173 | */ 174 | typedef _Must_inspect_result_ 175 | _Return_type_success_(return != NULL) 176 | _Post_maybenull_ 177 | WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity); 178 | 179 | /** 180 | * Ends Wintun session. 181 | * 182 | * @param Session Wintun session handle obtained with WintunStartSession 183 | */ 184 | typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); 185 | 186 | /** 187 | * Gets Wintun session's read-wait event handle. 188 | * 189 | * @param Session Wintun session handle obtained with WintunStartSession 190 | * 191 | * @return Pointer to receive event handle to wait for available data when reading. Should 192 | * WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy 193 | * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call 194 | * CloseHandle on this event - it is managed by the session. 195 | */ 196 | typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); 197 | 198 | /** 199 | * Maximum IP packet size 200 | */ 201 | #define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF 202 | 203 | /** 204 | * Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned 205 | * from this function to release internal buffer. This function is thread-safe. 206 | * 207 | * @param Session Wintun session handle obtained with WintunStartSession 208 | * 209 | * @param PacketSize Pointer to receive packet size. 210 | * 211 | * @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the 212 | * return value is NULL. To get extended error information, call GetLastError. Possible errors include the 213 | * following: 214 | * ERROR_HANDLE_EOF Wintun adapter is terminating; 215 | * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted; 216 | * ERROR_INVALID_DATA Wintun buffer is corrupt 217 | */ 218 | typedef _Must_inspect_result_ 219 | _Return_type_success_(return != NULL) 220 | _Post_maybenull_ 221 | _Post_writable_byte_size_(*PacketSize) 222 | BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize); 223 | 224 | /** 225 | * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe. 226 | * 227 | * @param Session Wintun session handle obtained with WintunStartSession 228 | * 229 | * @param Packet Packet obtained with WintunReceivePacket 230 | */ 231 | typedef VOID( 232 | WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); 233 | 234 | /** 235 | * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send 236 | * and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of 237 | * calls define the packet sending order. 238 | * 239 | * @param Session Wintun session handle obtained with WintunStartSession 240 | * 241 | * @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE. 242 | * 243 | * @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, 244 | * the return value is NULL. To get extended error information, call GetLastError. Possible errors include the 245 | * following: 246 | * ERROR_HANDLE_EOF Wintun adapter is terminating; 247 | * ERROR_BUFFER_OVERFLOW Wintun buffer is full; 248 | */ 249 | typedef _Must_inspect_result_ 250 | _Return_type_success_(return != NULL) 251 | _Post_maybenull_ 252 | _Post_writable_byte_size_(PacketSize) 253 | BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize); 254 | 255 | /** 256 | * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket 257 | * order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the 258 | * WintunSendPacket yet. 259 | * 260 | * @param Session Wintun session handle obtained with WintunStartSession 261 | * 262 | * @param Packet Packet obtained with WintunAllocateSendPacket 263 | */ 264 | typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); 265 | 266 | #pragma warning(pop) 267 | 268 | #ifdef __cplusplus 269 | } 270 | #endif 271 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "advmac" 7 | version = "1.0.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "41c1bfd911d1173023132c681f7434ed956ec5459d7520b4f85331fc00d93bf0" 10 | dependencies = [ 11 | "arrayvec", 12 | "rand", 13 | "serde", 14 | ] 15 | 16 | [[package]] 17 | name = "aho-corasick" 18 | version = "0.7.19" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 21 | dependencies = [ 22 | "memchr", 23 | ] 24 | 25 | [[package]] 26 | name = "anyhow" 27 | version = "1.0.65" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" 30 | 31 | [[package]] 32 | name = "arrayvec" 33 | version = "0.7.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 36 | dependencies = [ 37 | "serde", 38 | ] 39 | 40 | [[package]] 41 | name = "async-channel" 42 | version = "1.7.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" 45 | dependencies = [ 46 | "concurrent-queue", 47 | "event-listener", 48 | "futures-core", 49 | ] 50 | 51 | [[package]] 52 | name = "async-lock" 53 | version = "2.6.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" 56 | dependencies = [ 57 | "event-listener", 58 | "futures-lite", 59 | ] 60 | 61 | [[package]] 62 | name = "async-task" 63 | version = "4.3.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" 66 | 67 | [[package]] 68 | name = "atomic-waker" 69 | version = "1.0.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 72 | 73 | [[package]] 74 | name = "atty" 75 | version = "0.2.14" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 78 | dependencies = [ 79 | "hermit-abi", 80 | "libc", 81 | "winapi", 82 | ] 83 | 84 | [[package]] 85 | name = "autocfg" 86 | version = "1.1.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 89 | 90 | [[package]] 91 | name = "bindgen" 92 | version = "0.61.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" 95 | dependencies = [ 96 | "bitflags", 97 | "cexpr", 98 | "clang-sys", 99 | "lazy_static", 100 | "lazycell", 101 | "log", 102 | "peeking_take_while", 103 | "proc-macro2", 104 | "quote", 105 | "regex", 106 | "rustc-hash", 107 | "shlex", 108 | "syn", 109 | "which", 110 | ] 111 | 112 | [[package]] 113 | name = "bitflags" 114 | version = "1.3.2" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 117 | 118 | [[package]] 119 | name = "blocking" 120 | version = "1.3.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" 123 | dependencies = [ 124 | "async-channel", 125 | "async-lock", 126 | "async-task", 127 | "atomic-waker", 128 | "fastrand", 129 | "futures-lite", 130 | ] 131 | 132 | [[package]] 133 | name = "byteorder" 134 | version = "1.4.3" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 137 | 138 | [[package]] 139 | name = "bytes" 140 | version = "1.4.0" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 143 | 144 | [[package]] 145 | name = "cache-padded" 146 | version = "1.2.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" 149 | 150 | [[package]] 151 | name = "cexpr" 152 | version = "0.6.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 155 | dependencies = [ 156 | "nom", 157 | ] 158 | 159 | [[package]] 160 | name = "cfg-if" 161 | version = "1.0.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 164 | 165 | [[package]] 166 | name = "clang-sys" 167 | version = "1.4.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" 170 | dependencies = [ 171 | "glob", 172 | "libc", 173 | "libloading", 174 | ] 175 | 176 | [[package]] 177 | name = "concurrent-queue" 178 | version = "1.2.4" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" 181 | dependencies = [ 182 | "cache-padded", 183 | ] 184 | 185 | [[package]] 186 | name = "core-foundation" 187 | version = "0.9.3" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 190 | dependencies = [ 191 | "core-foundation-sys", 192 | "libc", 193 | ] 194 | 195 | [[package]] 196 | name = "core-foundation-sys" 197 | version = "0.8.3" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 200 | 201 | [[package]] 202 | name = "darling" 203 | version = "0.14.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" 206 | dependencies = [ 207 | "darling_core", 208 | "darling_macro", 209 | ] 210 | 211 | [[package]] 212 | name = "darling_core" 213 | version = "0.14.1" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" 216 | dependencies = [ 217 | "fnv", 218 | "ident_case", 219 | "proc-macro2", 220 | "quote", 221 | "strsim", 222 | "syn", 223 | ] 224 | 225 | [[package]] 226 | name = "darling_macro" 227 | version = "0.14.1" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" 230 | dependencies = [ 231 | "darling_core", 232 | "quote", 233 | "syn", 234 | ] 235 | 236 | [[package]] 237 | name = "delegate" 238 | version = "0.8.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" 241 | dependencies = [ 242 | "proc-macro2", 243 | "quote", 244 | "syn", 245 | ] 246 | 247 | [[package]] 248 | name = "derive_builder" 249 | version = "0.11.2" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" 252 | dependencies = [ 253 | "derive_builder_macro", 254 | ] 255 | 256 | [[package]] 257 | name = "derive_builder_core" 258 | version = "0.11.2" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" 261 | dependencies = [ 262 | "darling", 263 | "proc-macro2", 264 | "quote", 265 | "syn", 266 | ] 267 | 268 | [[package]] 269 | name = "derive_builder_macro" 270 | version = "0.11.2" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" 273 | dependencies = [ 274 | "derive_builder_core", 275 | "syn", 276 | ] 277 | 278 | [[package]] 279 | name = "either" 280 | version = "1.8.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 283 | 284 | [[package]] 285 | name = "env_logger" 286 | version = "0.9.1" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" 289 | dependencies = [ 290 | "atty", 291 | "humantime", 292 | "log", 293 | "regex", 294 | "termcolor", 295 | ] 296 | 297 | [[package]] 298 | name = "etherparse" 299 | version = "0.12.0" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "bcb08c4aab4e2985045305551e67126b43f1b6b136bc4e1cd87fb0327877a611" 302 | dependencies = [ 303 | "arrayvec", 304 | ] 305 | 306 | [[package]] 307 | name = "event-listener" 308 | version = "2.5.3" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 311 | 312 | [[package]] 313 | name = "fastrand" 314 | version = "1.8.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 317 | dependencies = [ 318 | "instant", 319 | ] 320 | 321 | [[package]] 322 | name = "fnv" 323 | version = "1.0.7" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 326 | 327 | [[package]] 328 | name = "futures" 329 | version = "0.3.24" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" 332 | dependencies = [ 333 | "futures-channel", 334 | "futures-core", 335 | "futures-executor", 336 | "futures-io", 337 | "futures-sink", 338 | "futures-task", 339 | "futures-util", 340 | ] 341 | 342 | [[package]] 343 | name = "futures-channel" 344 | version = "0.3.24" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" 347 | dependencies = [ 348 | "futures-core", 349 | "futures-sink", 350 | ] 351 | 352 | [[package]] 353 | name = "futures-core" 354 | version = "0.3.24" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" 357 | 358 | [[package]] 359 | name = "futures-executor" 360 | version = "0.3.24" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" 363 | dependencies = [ 364 | "futures-core", 365 | "futures-task", 366 | "futures-util", 367 | ] 368 | 369 | [[package]] 370 | name = "futures-io" 371 | version = "0.3.24" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" 374 | 375 | [[package]] 376 | name = "futures-lite" 377 | version = "1.12.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 380 | dependencies = [ 381 | "fastrand", 382 | "futures-core", 383 | "futures-io", 384 | "memchr", 385 | "parking", 386 | "pin-project-lite", 387 | "waker-fn", 388 | ] 389 | 390 | [[package]] 391 | name = "futures-macro" 392 | version = "0.3.24" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" 395 | dependencies = [ 396 | "proc-macro2", 397 | "quote", 398 | "syn", 399 | ] 400 | 401 | [[package]] 402 | name = "futures-sink" 403 | version = "0.3.24" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" 406 | 407 | [[package]] 408 | name = "futures-task" 409 | version = "0.3.24" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" 412 | 413 | [[package]] 414 | name = "futures-util" 415 | version = "0.3.24" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" 418 | dependencies = [ 419 | "futures-channel", 420 | "futures-core", 421 | "futures-io", 422 | "futures-macro", 423 | "futures-sink", 424 | "futures-task", 425 | "memchr", 426 | "pin-project-lite", 427 | "pin-utils", 428 | "slab", 429 | ] 430 | 431 | [[package]] 432 | name = "getrandom" 433 | version = "0.2.7" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 436 | dependencies = [ 437 | "cfg-if", 438 | "libc", 439 | "wasi", 440 | ] 441 | 442 | [[package]] 443 | name = "glob" 444 | version = "0.3.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 447 | 448 | [[package]] 449 | name = "hermit-abi" 450 | version = "0.1.19" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 453 | dependencies = [ 454 | "libc", 455 | ] 456 | 457 | [[package]] 458 | name = "humantime" 459 | version = "2.1.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 462 | 463 | [[package]] 464 | name = "ident_case" 465 | version = "1.0.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 468 | 469 | [[package]] 470 | name = "instant" 471 | version = "0.1.12" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 474 | dependencies = [ 475 | "cfg-if", 476 | ] 477 | 478 | [[package]] 479 | name = "ipnet" 480 | version = "2.5.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" 483 | 484 | [[package]] 485 | name = "ipnetwork" 486 | version = "0.20.0" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" 489 | dependencies = [ 490 | "serde", 491 | ] 492 | 493 | [[package]] 494 | name = "lazy_static" 495 | version = "1.4.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 498 | 499 | [[package]] 500 | name = "lazycell" 501 | version = "1.3.0" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 504 | 505 | [[package]] 506 | name = "libc" 507 | version = "0.2.135" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" 510 | 511 | [[package]] 512 | name = "libloading" 513 | version = "0.7.3" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" 516 | dependencies = [ 517 | "cfg-if", 518 | "winapi", 519 | ] 520 | 521 | [[package]] 522 | name = "log" 523 | version = "0.4.17" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 526 | dependencies = [ 527 | "cfg-if", 528 | ] 529 | 530 | [[package]] 531 | name = "memchr" 532 | version = "2.5.0" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 535 | 536 | [[package]] 537 | name = "memoffset" 538 | version = "0.6.5" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 541 | dependencies = [ 542 | "autocfg", 543 | ] 544 | 545 | [[package]] 546 | name = "minimal-lexical" 547 | version = "0.2.1" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 550 | 551 | [[package]] 552 | name = "mio" 553 | version = "0.8.4" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 556 | dependencies = [ 557 | "libc", 558 | "log", 559 | "wasi", 560 | "windows-sys", 561 | ] 562 | 563 | [[package]] 564 | name = "netconfig" 565 | version = "0.4.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "9470678982a5926708b6b9d4dae8f6c4e9b0817dce979f4b88dddab70e5619cf" 568 | dependencies = [ 569 | "advmac", 570 | "cfg-if", 571 | "core-foundation", 572 | "delegate", 573 | "ipnet", 574 | "ipnetwork", 575 | "libc", 576 | "log", 577 | "netlink-packet-route", 578 | "netlink-sys", 579 | "nix", 580 | "scopeguard", 581 | "system-configuration-sys", 582 | "thiserror", 583 | "widestring", 584 | "windows", 585 | ] 586 | 587 | [[package]] 588 | name = "netlink-packet-core" 589 | version = "0.4.2" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" 592 | dependencies = [ 593 | "anyhow", 594 | "byteorder", 595 | "libc", 596 | "netlink-packet-utils", 597 | ] 598 | 599 | [[package]] 600 | name = "netlink-packet-route" 601 | version = "0.13.0" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "f5dee5ed749373c298237fe694eb0a51887f4cc1a27370c8464bac4382348f1a" 604 | dependencies = [ 605 | "anyhow", 606 | "bitflags", 607 | "byteorder", 608 | "libc", 609 | "netlink-packet-core", 610 | "netlink-packet-utils", 611 | ] 612 | 613 | [[package]] 614 | name = "netlink-packet-utils" 615 | version = "0.5.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" 618 | dependencies = [ 619 | "anyhow", 620 | "byteorder", 621 | "paste", 622 | "thiserror", 623 | ] 624 | 625 | [[package]] 626 | name = "netlink-sys" 627 | version = "0.8.3" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "92b654097027250401127914afb37cb1f311df6610a9891ff07a757e94199027" 630 | dependencies = [ 631 | "bytes", 632 | "libc", 633 | "log", 634 | ] 635 | 636 | [[package]] 637 | name = "nix" 638 | version = "0.25.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" 641 | dependencies = [ 642 | "autocfg", 643 | "bitflags", 644 | "cfg-if", 645 | "libc", 646 | "memoffset", 647 | "pin-utils", 648 | ] 649 | 650 | [[package]] 651 | name = "nom" 652 | version = "7.1.1" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 655 | dependencies = [ 656 | "memchr", 657 | "minimal-lexical", 658 | ] 659 | 660 | [[package]] 661 | name = "num_cpus" 662 | version = "1.13.1" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 665 | dependencies = [ 666 | "hermit-abi", 667 | "libc", 668 | ] 669 | 670 | [[package]] 671 | name = "once_cell" 672 | version = "1.14.0" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" 675 | 676 | [[package]] 677 | name = "parking" 678 | version = "2.0.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 681 | 682 | [[package]] 683 | name = "paste" 684 | version = "1.0.9" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" 687 | 688 | [[package]] 689 | name = "peeking_take_while" 690 | version = "0.1.2" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 693 | 694 | [[package]] 695 | name = "pin-project-lite" 696 | version = "0.2.9" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 699 | 700 | [[package]] 701 | name = "pin-utils" 702 | version = "0.1.0" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 705 | 706 | [[package]] 707 | name = "proc-macro2" 708 | version = "1.0.43" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 711 | dependencies = [ 712 | "unicode-ident", 713 | ] 714 | 715 | [[package]] 716 | name = "quote" 717 | version = "1.0.21" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 720 | dependencies = [ 721 | "proc-macro2", 722 | ] 723 | 724 | [[package]] 725 | name = "rand" 726 | version = "0.8.5" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 729 | dependencies = [ 730 | "rand_core", 731 | ] 732 | 733 | [[package]] 734 | name = "rand_core" 735 | version = "0.6.4" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 738 | dependencies = [ 739 | "getrandom", 740 | ] 741 | 742 | [[package]] 743 | name = "regex" 744 | version = "1.6.0" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 747 | dependencies = [ 748 | "aho-corasick", 749 | "memchr", 750 | "regex-syntax", 751 | ] 752 | 753 | [[package]] 754 | name = "regex-syntax" 755 | version = "0.6.27" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 758 | 759 | [[package]] 760 | name = "rustc-hash" 761 | version = "1.1.0" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 764 | 765 | [[package]] 766 | name = "scopeguard" 767 | version = "1.1.0" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 770 | 771 | [[package]] 772 | name = "serde" 773 | version = "1.0.145" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 776 | 777 | [[package]] 778 | name = "shlex" 779 | version = "1.1.0" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" 782 | 783 | [[package]] 784 | name = "signal-hook-registry" 785 | version = "1.4.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 788 | dependencies = [ 789 | "libc", 790 | ] 791 | 792 | [[package]] 793 | name = "slab" 794 | version = "0.4.7" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 797 | dependencies = [ 798 | "autocfg", 799 | ] 800 | 801 | [[package]] 802 | name = "socket2" 803 | version = "0.4.7" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 806 | dependencies = [ 807 | "libc", 808 | "winapi", 809 | ] 810 | 811 | [[package]] 812 | name = "strsim" 813 | version = "0.10.0" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 816 | 817 | [[package]] 818 | name = "syn" 819 | version = "1.0.99" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 822 | dependencies = [ 823 | "proc-macro2", 824 | "quote", 825 | "unicode-ident", 826 | ] 827 | 828 | [[package]] 829 | name = "system-configuration-sys" 830 | version = "0.5.0" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 833 | dependencies = [ 834 | "core-foundation-sys", 835 | "libc", 836 | ] 837 | 838 | [[package]] 839 | name = "termcolor" 840 | version = "1.1.3" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 843 | dependencies = [ 844 | "winapi-util", 845 | ] 846 | 847 | [[package]] 848 | name = "thiserror" 849 | version = "1.0.37" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 852 | dependencies = [ 853 | "thiserror-impl", 854 | ] 855 | 856 | [[package]] 857 | name = "thiserror-impl" 858 | version = "1.0.37" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 861 | dependencies = [ 862 | "proc-macro2", 863 | "quote", 864 | "syn", 865 | ] 866 | 867 | [[package]] 868 | name = "tokio" 869 | version = "1.21.2" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 872 | dependencies = [ 873 | "autocfg", 874 | "bytes", 875 | "libc", 876 | "memchr", 877 | "mio", 878 | "num_cpus", 879 | "pin-project-lite", 880 | "signal-hook-registry", 881 | "socket2", 882 | "tokio-macros", 883 | "winapi", 884 | ] 885 | 886 | [[package]] 887 | name = "tokio-macros" 888 | version = "1.8.0" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 891 | dependencies = [ 892 | "proc-macro2", 893 | "quote", 894 | "syn", 895 | ] 896 | 897 | [[package]] 898 | name = "tunio" 899 | version = "0.4.0" 900 | dependencies = [ 901 | "cfg-if", 902 | "delegate", 903 | "derive_builder", 904 | "env_logger", 905 | "etherparse", 906 | "futures", 907 | "log", 908 | "netconfig", 909 | "tokio", 910 | "tunio-core", 911 | "tunio-linux", 912 | "tunio-utun", 913 | "tunio-wintun", 914 | ] 915 | 916 | [[package]] 917 | name = "tunio-core" 918 | version = "0.1.0" 919 | dependencies = [ 920 | "delegate", 921 | "derive_builder", 922 | "futures", 923 | "log", 924 | "netconfig", 925 | "thiserror", 926 | "tokio", 927 | ] 928 | 929 | [[package]] 930 | name = "tunio-linux" 931 | version = "0.1.0" 932 | dependencies = [ 933 | "delegate", 934 | "derive_builder", 935 | "futures", 936 | "libc", 937 | "log", 938 | "netconfig", 939 | "nix", 940 | "tunio-core", 941 | ] 942 | 943 | [[package]] 944 | name = "tunio-utun" 945 | version = "0.1.0" 946 | dependencies = [ 947 | "delegate", 948 | "derive_builder", 949 | "futures", 950 | "libc", 951 | "log", 952 | "netconfig", 953 | "nix", 954 | "socket2", 955 | "tunio-core", 956 | ] 957 | 958 | [[package]] 959 | name = "tunio-wintun" 960 | version = "0.1.0" 961 | dependencies = [ 962 | "async-task", 963 | "blocking", 964 | "bytes", 965 | "delegate", 966 | "derive_builder", 967 | "futures", 968 | "log", 969 | "netconfig", 970 | "tunio-core", 971 | "widestring", 972 | "windows", 973 | "wintun-sys", 974 | ] 975 | 976 | [[package]] 977 | name = "unicode-ident" 978 | version = "1.0.4" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" 981 | 982 | [[package]] 983 | name = "waker-fn" 984 | version = "1.1.0" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 987 | 988 | [[package]] 989 | name = "wasi" 990 | version = "0.11.0+wasi-snapshot-preview1" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 993 | 994 | [[package]] 995 | name = "which" 996 | version = "4.3.0" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" 999 | dependencies = [ 1000 | "either", 1001 | "libc", 1002 | "once_cell", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "widestring" 1007 | version = "1.0.2" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" 1010 | 1011 | [[package]] 1012 | name = "winapi" 1013 | version = "0.3.9" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1016 | dependencies = [ 1017 | "winapi-i686-pc-windows-gnu", 1018 | "winapi-x86_64-pc-windows-gnu", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "winapi-i686-pc-windows-gnu" 1023 | version = "0.4.0" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1026 | 1027 | [[package]] 1028 | name = "winapi-util" 1029 | version = "0.1.5" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1032 | dependencies = [ 1033 | "winapi", 1034 | ] 1035 | 1036 | [[package]] 1037 | name = "winapi-x86_64-pc-windows-gnu" 1038 | version = "0.4.0" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1041 | 1042 | [[package]] 1043 | name = "windows" 1044 | version = "0.42.0" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "0286ba339aa753e70765d521bb0242cc48e1194562bfa2a2ad7ac8a6de28f5d5" 1047 | dependencies = [ 1048 | "windows_aarch64_gnullvm", 1049 | "windows_aarch64_msvc 0.42.0", 1050 | "windows_i686_gnu 0.42.0", 1051 | "windows_i686_msvc 0.42.0", 1052 | "windows_x86_64_gnu 0.42.0", 1053 | "windows_x86_64_gnullvm", 1054 | "windows_x86_64_msvc 0.42.0", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "windows-sys" 1059 | version = "0.36.1" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1062 | dependencies = [ 1063 | "windows_aarch64_msvc 0.36.1", 1064 | "windows_i686_gnu 0.36.1", 1065 | "windows_i686_msvc 0.36.1", 1066 | "windows_x86_64_gnu 0.36.1", 1067 | "windows_x86_64_msvc 0.36.1", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "windows_aarch64_gnullvm" 1072 | version = "0.42.0" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1075 | 1076 | [[package]] 1077 | name = "windows_aarch64_msvc" 1078 | version = "0.36.1" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1081 | 1082 | [[package]] 1083 | name = "windows_aarch64_msvc" 1084 | version = "0.42.0" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1087 | 1088 | [[package]] 1089 | name = "windows_i686_gnu" 1090 | version = "0.36.1" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1093 | 1094 | [[package]] 1095 | name = "windows_i686_gnu" 1096 | version = "0.42.0" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1099 | 1100 | [[package]] 1101 | name = "windows_i686_msvc" 1102 | version = "0.36.1" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1105 | 1106 | [[package]] 1107 | name = "windows_i686_msvc" 1108 | version = "0.42.0" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1111 | 1112 | [[package]] 1113 | name = "windows_x86_64_gnu" 1114 | version = "0.36.1" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1117 | 1118 | [[package]] 1119 | name = "windows_x86_64_gnu" 1120 | version = "0.42.0" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1123 | 1124 | [[package]] 1125 | name = "windows_x86_64_gnullvm" 1126 | version = "0.42.0" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1129 | 1130 | [[package]] 1131 | name = "windows_x86_64_msvc" 1132 | version = "0.36.1" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 1135 | 1136 | [[package]] 1137 | name = "windows_x86_64_msvc" 1138 | version = "0.42.0" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1141 | 1142 | [[package]] 1143 | name = "wintun-sys" 1144 | version = "0.2.0" 1145 | dependencies = [ 1146 | "bindgen", 1147 | "libloading", 1148 | "windows", 1149 | ] 1150 | --------------------------------------------------------------------------------