解决win7不能启动的问题
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use clap::{Parser, Subcommand};
|
||||
use console::style;
|
||||
|
||||
@@ -155,7 +157,7 @@ fn main() {
|
||||
pub fn console_listen(switch: &Switch) {
|
||||
use console::Term;
|
||||
let term = Term::stdout();
|
||||
println!("{}", style("started").green());
|
||||
println!("{}", style("启动成功 started").green());
|
||||
let current_device = switch.current_device();
|
||||
println!(
|
||||
"当前虚拟ip(virtual ip): {:?}",
|
||||
@@ -181,6 +183,7 @@ pub fn console_listen(switch: &Switch) {
|
||||
if let Err(e) = switch.stop() {
|
||||
println!("stop:{:?}", e);
|
||||
}
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ protobuf = "3.2.0"
|
||||
tun = { path = "./rust-tun" }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
wintun = "0.2.1"
|
||||
wintun = { path = "./wintun" }
|
||||
libloading = "0.7.4"
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -6,6 +6,9 @@ use libloading::Library;
|
||||
use parking_lot::Mutex;
|
||||
use wintun::{Adapter, Packet, Session};
|
||||
|
||||
pub const INTERFACE_NAME: &str = "Switch-V1";
|
||||
pub const POOL_NAME: &str = "Switch-V1";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TunWriter(Arc<Session>, Arc<Mutex<u32>>);
|
||||
|
||||
@@ -69,22 +72,58 @@ pub fn create_tun(
|
||||
}
|
||||
}
|
||||
};
|
||||
let adapter = match Adapter::open(&win_tun, "Switch-V1") {
|
||||
Ok(a) => a,
|
||||
Err(_) => match Adapter::create(&win_tun, "Switch-V1", "Switch-V1", None) {
|
||||
Ok(adapter) => adapter,
|
||||
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e))),
|
||||
},
|
||||
if let Ok(adapter) = Adapter::open(&win_tun, INTERFACE_NAME) {
|
||||
log::warn!("Switch-V1 未正常退出");
|
||||
drop(adapter);
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
};
|
||||
let adapter = match Adapter::create(&win_tun, POOL_NAME, INTERFACE_NAME, None) {
|
||||
Ok(adapter) => adapter,
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e))),
|
||||
};
|
||||
let index = adapter.get_adapter_index().unwrap();
|
||||
config_ip(index, address, netmask, gateway)?;
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
let index = match adapter.get_adapter_index() {
|
||||
Ok(index) => {
|
||||
index
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("get_adapter_index err {:?}",e);
|
||||
get_if_index()
|
||||
}
|
||||
};
|
||||
config_ip(index, address, netmask, gateway)?;
|
||||
let reader_session = session.clone();
|
||||
Ok((TunWriter(session.clone(), Arc::new(Mutex::new(index))), TunReader(reader_session)))
|
||||
}
|
||||
|
||||
fn get_if_index() -> u32 {
|
||||
let cmd = format!("netsh int ipv4 show interfaces {} |findstr IfIndex", INTERFACE_NAME);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&cmd)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::warn!("1获取网络接口索引失败:cmd={:?},out={:?}",cmd,out);
|
||||
return 0;
|
||||
}
|
||||
if let Ok(stdout) = String::from_utf8(out.stdout) {
|
||||
if let Some(start) = stdout.find(":") {
|
||||
if let Some(end) = stdout.find("\r\n") {
|
||||
if let Ok(index) = stdout[start + 1..end].trim().parse::<u32>() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::warn!("2获取网络接口索引失败:cmd={:?}",cmd);
|
||||
0
|
||||
}
|
||||
|
||||
fn config_ip(index: u32, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
if index == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
|
||||
}
|
||||
let set_mtu = format!(
|
||||
"netsh interface ipv4 set subinterface {} mtu=1420 store=persistent",
|
||||
index
|
||||
@@ -113,10 +152,11 @@ fn config_ip(index: u32, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_address)
|
||||
.arg(&set_address)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_address,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
|
||||
}
|
||||
let dest = {
|
||||
@@ -136,16 +176,20 @@ fn config_ip(index: u32, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr
|
||||
// 执行添加路由命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_route)
|
||||
.arg(&set_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_route,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_route(index: u32, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
if index == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
|
||||
}
|
||||
let mask = netmask.octets();
|
||||
let ip = gateway.octets();
|
||||
let dest = Ipv4Addr::from([
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
out.pcap
|
||||
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
/.idea
|
||||
@@ -0,0 +1,60 @@
|
||||
# ChangeLog
|
||||
|
||||
This format is based on [Keep a Changelog](https://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [0.2.1] - 2021-12-03
|
||||
|
||||
### Fixed
|
||||
Type in readme
|
||||
|
||||
## [0.2.0] - 2021-12-03
|
||||
|
||||
Added support for wintun 0.14.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Wintun driver versions before `0.14` are no longer support due to beraking
|
||||
changes in the C API
|
||||
- `Adapter::create` returns a `Result<Adapter, ...>` instead of a `Result<CreateData, ...>`.
|
||||
This was done because the underlying Wintun function was changed to only return an adapter handle
|
||||
- `Adapter::create` the pool parameter was removed because it was also removed from the C function
|
||||
- `Adapter::delete` takes no parameters and returns a `Result<(), ()>`.
|
||||
The `force_close_sessions` parameter was removed because it was removed from the
|
||||
C function. Same for the bool inside the Ok(..) variant
|
||||
- `Adapter::create` and `Adapter::open` return `Arc<Adapter>` instead of `Adapter`
|
||||
- `get_running_driver_version` now returns a proper Result<Version, ()>.
|
||||
|
||||
### Added
|
||||
|
||||
- `reset_logger` function to disable logging after a logger has been set.
|
||||
|
||||
## [0.1.5] - 2021-08-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Readme on crates.io
|
||||
|
||||
## [0.1.4] - 2021-08-27
|
||||
|
||||
### Added
|
||||
- `panic_on_unsent_packets` feature flag to help in debugging ring buffer blockage issues
|
||||
|
||||
## [0.1.3] - 2021-06-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- Cargo.toml metadata to include `package.metadata.docs.rs.default-target`.
|
||||
Fixes build issue on docs.rs (we can only build docs on windows, 0.1.1 doesn't work)
|
||||
|
||||
## [0.1.2] - 2021-06-28
|
||||
docs.rs testing
|
||||
|
||||
## [0.1.1] - 2021-06-28
|
||||
|
||||
- Cargo.toml metadata to build on linux
|
||||
|
||||
## [0.1.0] - 2021-06-28
|
||||
|
||||
First release with initial api
|
||||
|
||||
Generated
+427
@@ -0,0 +1,427 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "derive-into-owned"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62"
|
||||
dependencies = [
|
||||
"quote 0.3.15",
|
||||
"syn 0.11.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hwaddr"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e414433a9e4338f4e87fa29d0670c883a5e73e7955c45f4a49130c0aa992c85b"
|
||||
dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "packet"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c136c7ad0619ed4f88894aecf66ad86c80683e7b5d707996e6a3a7e0e3916944"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"hwaddr",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcap-file"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"derive-into-owned",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "subprocess"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "055cf3ebc2981ad8f0a5a17ef6652f652d87831f79fddcba2ac57bcb9a0aa407"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
dependencies = [
|
||||
"quote 0.3.15",
|
||||
"synom",
|
||||
"unicode-xid 0.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.10",
|
||||
"unicode-xid 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synom"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
dependencies = [
|
||||
"unicode-xid 0.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.10",
|
||||
"syn 1.0.82",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "wintun"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"itertools",
|
||||
"libloading",
|
||||
"log",
|
||||
"once_cell",
|
||||
"packet",
|
||||
"pcap-file",
|
||||
"rand",
|
||||
"subprocess",
|
||||
"widestring",
|
||||
"winapi",
|
||||
]
|
||||
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "wintun"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
authors = ["null.black Inc. <[email protected]>", "Troy Neubauer <[email protected]>"]
|
||||
repository = "https://github.com/nulldotblack/wintun"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/wintun/"
|
||||
description = "Safe idiomatic bindings to the WinTun C library"
|
||||
license = "MIT"
|
||||
keywords = ["wintun", "tap", "tun", "vpn", "wireguard"]
|
||||
categories = ["api-bindings"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-pc-windows-msvc"
|
||||
targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-msvc"]
|
||||
|
||||
[features]
|
||||
panic_on_unsent_packets = []
|
||||
|
||||
[dependencies]
|
||||
winapi = { version = "0.3", features = ["synchapi", "winbase", "winerror", "ipexport", "iphlpapi", "handleapi"] }
|
||||
widestring = "0.4"
|
||||
libloading = "0.7"
|
||||
once_cell = "1.8"
|
||||
log = "0.4"
|
||||
rand = "0.8.3"
|
||||
itertools = "0.10.1"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8"
|
||||
winapi = { version = "0.3", features = ["netioapi", "iptypes", "iphlpapi", "nldef"] }
|
||||
packet = "0.1.4"
|
||||
pcap-file = "1.1.1"
|
||||
subprocess = "0.2.7"
|
||||
@@ -0,0 +1,7 @@
|
||||
Copyright 2021 null.black Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,80 @@
|
||||
# wintun
|
||||
|
||||
Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
|
||||
|
||||
All features of the Wintun library are wrapped using pure rust types and functions to make
|
||||
usage feel ergonomic.
|
||||
|
||||
## Usage
|
||||
|
||||
Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
|
||||
using [`load`], [`load_from_path`] or [`load_from_library`].
|
||||
|
||||
Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
|
||||
adapter. Start a session with [`Adapter::start_session`].
|
||||
|
||||
## Example
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
|
||||
//Must be run as Administrator because we create network adapters
|
||||
//Load the wintun dll file so that we can call the underlying C functions
|
||||
//Unsafe because we are loading an arbitrary dll file
|
||||
let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
|
||||
.expect("Failed to load wintun dll");
|
||||
|
||||
//Try to open an adapter with the name "Demo"
|
||||
let adapter = match wintun::Adapter::open(&wintun, "Demo") {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
//If loading failed (most likely it didn't exist), create a new one
|
||||
wintun::Adapter::create(&wintun, "Example", "Demo", None)
|
||||
.expect("Failed to create wintun adapter!")
|
||||
}
|
||||
};
|
||||
//Specify the size of the ring buffer the wintun driver should use.
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
|
||||
//Get a 20 byte packet from the ring buffer
|
||||
let mut packet = session.allocate_send_packet(20).unwrap();
|
||||
let bytes: &mut [u8] = packet.bytes_mut();
|
||||
//Write IPV4 version and header length
|
||||
bytes[0] = 0x40;
|
||||
|
||||
//Finish writing IP header
|
||||
bytes[9] = 0x69;
|
||||
bytes[10] = 0x04;
|
||||
bytes[11] = 0x20;
|
||||
//...
|
||||
|
||||
//Send the packet to wintun virtual adapter for processing by the system
|
||||
session.send_packet(packet);
|
||||
|
||||
//Stop any readers blocking for data on other threads
|
||||
//Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
|
||||
//session, blocking it from being dropped
|
||||
session.shutdown();
|
||||
|
||||
//the session is stopped on drop
|
||||
//drop(session);
|
||||
|
||||
//drop(adapter)
|
||||
//And the adapter closes its resources when dropped
|
||||
```
|
||||
|
||||
See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
|
||||
file.
|
||||
|
||||
## Features
|
||||
|
||||
- `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
|
||||
debugging packet issues because unsent packets that are dropped without being sent hold up
|
||||
wintun's internal ring buffer.
|
||||
|
||||
## TODO:
|
||||
- Add async support
|
||||
Requires hooking into a windows specific reactor and registering read interest on wintun's read
|
||||
handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
|
||||
|
||||
|
||||
License: MIT
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
bindgen \
|
||||
--allowlist-function "Wintun.*" \
|
||||
--allowlist-type "WINTUN_.*" \
|
||||
--dynamic-loading wintun \
|
||||
--dynamic-link-require-all \
|
||||
wintun/wintun_functions.h > src/wintun_raw.rs
|
||||
@@ -0,0 +1,345 @@
|
||||
/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
|
||||
/// the WintunAdapter* C functions.
|
||||
///
|
||||
/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
|
||||
/// wintun functionality
|
||||
use crate::error;
|
||||
use crate::session;
|
||||
use crate::util;
|
||||
use crate::util::UnsafeHandle;
|
||||
use crate::wintun_raw;
|
||||
use crate::Wintun;
|
||||
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
use rand::Rng;
|
||||
|
||||
use widestring::U16CStr;
|
||||
use widestring::U16CString;
|
||||
|
||||
use winapi::{
|
||||
shared::winerror,
|
||||
um::{ipexport, iphlpapi, synchapi},
|
||||
};
|
||||
|
||||
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
|
||||
pub struct Adapter {
|
||||
adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
|
||||
wintun: Wintun,
|
||||
guid: u128,
|
||||
}
|
||||
|
||||
fn encode_utf16(string: &str, max_characters: usize) -> Result<U16CString, error::WintunError> {
|
||||
let utf16 = U16CString::from_str(string)?;
|
||||
if utf16.len() >= max_characters {
|
||||
//max_characters is the maximum number of characters including the null terminator. And .len() measures the
|
||||
//number of characters (excluding the null terminator). Therefore we can hold a string with
|
||||
//max_characters - 1 because the null terminator sits in the last element. However a string
|
||||
//of length max_characters needs max_characters + 1 to store the null terminator the >=
|
||||
//check holds
|
||||
Err(format!(
|
||||
//TODO: Better error handling
|
||||
"Length too large. Size: {}, Max: {}",
|
||||
utf16.len(),
|
||||
max_characters
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Ok(utf16)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_pool_name(name: &str) -> Result<U16CString, error::WintunError> {
|
||||
encode_utf16(name, crate::MAX_POOL)
|
||||
}
|
||||
|
||||
fn encode_adapter_name(name: &str) -> Result<U16CString, error::WintunError> {
|
||||
encode_utf16(name, crate::MAX_POOL)
|
||||
}
|
||||
|
||||
fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> u64 {
|
||||
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
|
||||
unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
|
||||
unsafe { std::mem::transmute(luid) }
|
||||
}
|
||||
|
||||
impl Adapter {
|
||||
//TODO: Call get last error for error information on failure and improve error types
|
||||
|
||||
/// Creates a new wintun adapter inside the pool `pool` with name `name`
|
||||
///
|
||||
/// Optionally a GUID can be specified that will become the GUID of this adapter once created.
|
||||
/// Adapters obtained via this function will be able to return their adapter index via
|
||||
/// [`Adapter::get_adapter_index`]
|
||||
pub fn create(
|
||||
wintun: &Wintun,
|
||||
pool: &str,
|
||||
name: &str,
|
||||
guid: Option<u128>,
|
||||
) -> Result<Arc<Adapter>, error::WintunError> {
|
||||
let pool_utf16 = encode_pool_name(pool)?;
|
||||
let name_utf16 = encode_adapter_name(name)?;
|
||||
|
||||
let guid = match guid {
|
||||
Some(guid) => guid,
|
||||
None => {
|
||||
// Use random bytes so that we can identify this adapter in get_adapter_index
|
||||
let mut guid_bytes: [u8; 16] = [0u8; 16];
|
||||
rand::thread_rng().fill(&mut guid_bytes);
|
||||
u128::from_ne_bytes(guid_bytes)
|
||||
}
|
||||
};
|
||||
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
|
||||
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
|
||||
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
|
||||
//TODO: The guid of the adapter once created might differ from the one provided because of
|
||||
//the byte order of the segments of the GUID struct that are larger than a byte. Verify
|
||||
//that this works as expected
|
||||
|
||||
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
|
||||
|
||||
crate::log::set_default_logger_if_unset(wintun);
|
||||
|
||||
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
|
||||
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
|
||||
//applies for all Wintun* functions below
|
||||
let result = unsafe {
|
||||
wintun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr)
|
||||
};
|
||||
|
||||
if result.is_null() {
|
||||
Err("Failed to crate adapter".into())
|
||||
} else {
|
||||
Ok(Arc::new(Adapter {
|
||||
adapter: UnsafeHandle(result),
|
||||
wintun: wintun.clone(),
|
||||
guid,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to open an existing wintun interface name `name`.
|
||||
///
|
||||
/// Adapters opened via this call will have an unknown GUID meaning [`Adapter::get_adapter_index`]
|
||||
/// will always fail because knowing the adapter's GUID is required to determine its index.
|
||||
/// Currently a workaround is to delete and re-create a new adapter every time one is needed so
|
||||
/// that it gets created with a known GUID, allowing [`Adapter::get_adapter_index`] to works as
|
||||
/// expected. There is likely a way to get the GUID of our adapter using the Windows Registry
|
||||
/// or via the Win32 API, so PR's that solve this issue are always welcome!
|
||||
pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, error::WintunError> {
|
||||
let name_utf16 = encode_adapter_name(name)?;
|
||||
|
||||
crate::log::set_default_logger_if_unset(wintun);
|
||||
|
||||
let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
|
||||
|
||||
if result.is_null() {
|
||||
Err("WintunOpenAdapter failed".into())
|
||||
} else {
|
||||
Ok(Arc::new(Adapter {
|
||||
adapter: UnsafeHandle(result),
|
||||
wintun: wintun.clone(),
|
||||
// TODO: get GUID somehow
|
||||
guid: 0,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete an adapter, consuming it in the process
|
||||
pub fn delete(self) -> Result<(), ()> {
|
||||
//Dropping an adapter closes it
|
||||
drop(self);
|
||||
// Return a result here so that if later the API changes to be fallible, we can support it
|
||||
// without making a breaking change
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initiates a new wintun session on the given adapter.
|
||||
///
|
||||
/// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
|
||||
/// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MIN_RING_CAPACITY`].
|
||||
pub fn start_session(
|
||||
self: &Arc<Self>,
|
||||
capacity: u32,
|
||||
) -> Result<session::Session, error::WintunError> {
|
||||
let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
|
||||
if !range.contains(&capacity) {
|
||||
return Err(Box::new(error::ApiError::CapacityOutOfRange(
|
||||
error::OutOfRangeData {
|
||||
range,
|
||||
value: capacity,
|
||||
},
|
||||
)));
|
||||
}
|
||||
if !capacity.is_power_of_two() {
|
||||
return Err(Box::new(error::ApiError::CapacityNotPowerOfTwo(capacity)));
|
||||
}
|
||||
|
||||
let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
|
||||
|
||||
if result.is_null() {
|
||||
Err("WintunStartSession failed".into())
|
||||
} else {
|
||||
Ok(session::Session {
|
||||
session: UnsafeHandle(result),
|
||||
wintun: self.wintun.clone(),
|
||||
read_event: OnceCell::new(),
|
||||
shutdown_event: unsafe {
|
||||
//SAFETY: We follow the contract required by CreateEventA. See MSDN
|
||||
//(the pointers are allowed to be null, and 0 is okay for the others)
|
||||
UnsafeHandle(synchapi::CreateEventA(
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
))
|
||||
},
|
||||
adapter: Arc::clone(self),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Win32 LUID for this adapter
|
||||
pub fn get_luid(&self) -> u64 {
|
||||
get_adapter_luid(&self.wintun, self.adapter.0)
|
||||
}
|
||||
|
||||
/// Returns the Win32 interface index of this adapter. Useful for specifying the interface
|
||||
/// when executing `netsh interface ip` commands
|
||||
pub fn get_adapter_index(&self) -> Result<u32, error::WintunError> {
|
||||
let mut buf_len: u32 = 0;
|
||||
//First figure out the size of the buffer needed to store the adapter info
|
||||
//SAFETY: We are upholding the contract of GetInterfaceInfo. buf_len is a valid pointer to
|
||||
//stack memory
|
||||
let result =
|
||||
unsafe { iphlpapi::GetInterfaceInfo(std::ptr::null_mut(), &mut buf_len as *mut u32) };
|
||||
if result != winerror::NO_ERROR && result != winerror::ERROR_INSUFFICIENT_BUFFER {
|
||||
let err_msg = util::get_error_message(result);
|
||||
error!("Failed to get interface info: {}", err_msg);
|
||||
//TODO: Better error types
|
||||
return Err(format!("GetInterfaceInfo failed: {}", err_msg).into());
|
||||
}
|
||||
|
||||
//Allocate a buffer of the requested size
|
||||
//IP_INTERFACE_INFO must be aligned by at least 4 byte boundaries so use u32 as the
|
||||
//underlying data storage type
|
||||
let buf_elements = buf_len as usize / std::mem::size_of::<u32>() + 1;
|
||||
//Round up incase integer division truncated a byte that filled a partial element
|
||||
let mut buf: Vec<u32> = vec![0; buf_elements];
|
||||
|
||||
let buf_bytes = buf.len() * std::mem::size_of::<u32>();
|
||||
assert!(buf_bytes >= buf_len as usize);
|
||||
|
||||
//SAFETY:
|
||||
//
|
||||
// 1. We are upholding the contract of GetInterfaceInfo.
|
||||
// 2. `final_buf_len` is an aligned, valid pointer to stack memory
|
||||
// 3. buf is a valid, non-null pointer to at least `buf_len` bytes of heap memory,
|
||||
// aligned to at least 4 byte boundaries
|
||||
//
|
||||
//Get the info
|
||||
let mut final_buf_len: u32 = buf_len;
|
||||
let result = unsafe {
|
||||
iphlpapi::GetInterfaceInfo(
|
||||
buf.as_mut_ptr() as *mut ipexport::IP_INTERFACE_INFO,
|
||||
&mut final_buf_len as *mut u32,
|
||||
)
|
||||
};
|
||||
if result != winerror::NO_ERROR {
|
||||
let err_msg = util::get_error_message(result);
|
||||
//TODO: maybe over allocate the buffer in case the needed size changes between the two
|
||||
//calls to GetInterfaceInfo if another adapter is added
|
||||
error!(
|
||||
"Failed to get interface info a second time: {}. Original len: {}, final len: {}",
|
||||
err_msg, buf_len, final_buf_len
|
||||
);
|
||||
return Err(format!("GetInterfaceInfo failed a second time: {}", err_msg).into());
|
||||
}
|
||||
let info = buf.as_mut_ptr() as *const ipexport::IP_INTERFACE_INFO;
|
||||
//SAFETY:
|
||||
// info is a valid, non-null, at least 4 byte aligned pointer obtained from
|
||||
// Vec::with_capacity that is readable for up to `buf_len` bytes which is guaranteed to be
|
||||
// larger than on IP_INTERFACE_INFO struct as the kernel would never ask for less memory then
|
||||
// what it will write. The largest type inside IP_INTERFACE_INFO is a u32 therefore
|
||||
// a painter to IP_INTERFACE_INFO requires an alignment of at leant 4 bytes, which
|
||||
// Vec<u32>::as_mut_ptr() provides
|
||||
let adapter_base = unsafe { &*info };
|
||||
let adapter_count = adapter_base.NumAdapters;
|
||||
let first_adapter = &adapter_base.Adapter as *const ipexport::IP_ADAPTER_INDEX_MAP;
|
||||
|
||||
// SAFETY:
|
||||
// 1. first_adapter is a valid, non null pointer, aligned to at least 4 byte boundaries
|
||||
// obtained from moving a multiple of 4 offset into the buf given by Vec::with_capacity.
|
||||
// 2. We gave GetInterfaceInfo a buffer of at least least `buf_len` bytes to work with and it
|
||||
// succeeded in writing the adapter information within the bounds of that buffer, otherwise
|
||||
// it would've failed. Because the operation succeeded, we know that reading n=NumAdapters
|
||||
// IP_ADAPTER_INDEX_MAP structs stays within the bounds of buf's buffer
|
||||
let interfaces =
|
||||
unsafe { std::slice::from_raw_parts(first_adapter, adapter_count as usize) };
|
||||
let mut tmp = Vec::new();
|
||||
for interface in interfaces {
|
||||
let name =
|
||||
unsafe { U16CStr::from_ptr_str(&interface.Name as *const u16).to_string_lossy() };
|
||||
//Nam is something like: \DEVICE\TCPIP_{29C47F55-C7BD-433A-8BF7-408DFD3B3390}
|
||||
//where the GUID is the {29C4...90}, separated by dashes
|
||||
let open = name.chars().position(|c| c == '{').ok_or(format!(
|
||||
"Failed to find {{ character inside adapter name: {}",
|
||||
name
|
||||
))?;
|
||||
let close = name.chars().position(|c| c == '}').ok_or(format!(
|
||||
"Failed to find }} character inside adapter name: {}",
|
||||
name
|
||||
))?;
|
||||
let digits: Vec<u8> = name[open..close]
|
||||
.chars()
|
||||
.filter(|c| c.is_digit(16))
|
||||
.chunks(2)
|
||||
.into_iter()
|
||||
.filter_map(|mut chunk| {
|
||||
//Filter out chunks that have < 2 digits
|
||||
if let Some(a) = chunk.next() {
|
||||
if let Some(b) = chunk.next() {
|
||||
return Some((a, b));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.map(|digits| {
|
||||
let chars: [u8; 2] = [digits.0 as u8, digits.1 as u8];
|
||||
let s = std::str::from_utf8(&chars).unwrap();
|
||||
u8::from_str_radix(s, 16).unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
//Our index is the adapter which has a guid in its name that matches ours
|
||||
//For now we just check for a guid with the same hex bytes in any order
|
||||
//TODO: byte swap GUID from name so that we can compare self.guid with the parsed GUID
|
||||
//directly
|
||||
let mut match_count = 0;
|
||||
for byte in self.guid.to_ne_bytes() {
|
||||
if digits.contains(&byte) {
|
||||
match_count += 1;
|
||||
}
|
||||
}
|
||||
tmp.push(format!("interfaces name={:?},digits={:?},index={:?}", name,digits, interface.Index));
|
||||
if match_count == digits.len() {
|
||||
return Ok(interface.Index);
|
||||
}
|
||||
}
|
||||
log::info!("interfaces:{:?},guid={}",tmp,self.guid);
|
||||
Err("Unable to find matching GUID".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Adapter {
|
||||
fn drop(&mut self) {
|
||||
//Close adapter on drop
|
||||
//This is why we need an Arc of wintun
|
||||
unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
|
||||
self.adapter = UnsafeHandle(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
pub type WintunError = Box<dyn std::error::Error>;
|
||||
|
||||
/// Error type used to convey that a value is outside of a range that it must fall inside
|
||||
#[derive(Debug)]
|
||||
pub struct OutOfRangeData<T> {
|
||||
pub range: std::ops::RangeInclusive<T>,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
/// Error type returned when preconditions of this API are broken
|
||||
#[derive(Debug)]
|
||||
pub enum ApiError {
|
||||
CapacityNotPowerOfTwo(u32),
|
||||
CapacityOutOfRange(OutOfRangeData<u32>),
|
||||
}
|
||||
|
||||
impl Display for ApiError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self {
|
||||
ApiError::CapacityOutOfRange(data) => write!(
|
||||
f,
|
||||
"Capacity {} out of range. Must be within {}..={}",
|
||||
data.value,
|
||||
data.range.start(),
|
||||
data.range.end()
|
||||
),
|
||||
ApiError::CapacityNotPowerOfTwo(cap) => {
|
||||
write!(f, "Capacity {} is not a power of two", cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ApiError {}
|
||||
@@ -0,0 +1,174 @@
|
||||
//! Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
|
||||
//!
|
||||
//! All features of the Wintun library are wrapped using pure rust types and functions to make
|
||||
//! usage feel ergonomic.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
|
||||
//! using [`load`], [`load_from_path`] or [`load_from_library`].
|
||||
//!
|
||||
//! Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
|
||||
//! adapter. Start a session with [`Adapter::start_session`].
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! //Must be run as Administrator because we create network adapters
|
||||
//! //Load the wintun dll file so that we can call the underlying C functions
|
||||
//! //Unsafe because we are loading an arbitrary dll file
|
||||
//! let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
|
||||
//! .expect("Failed to load wintun dll");
|
||||
//!
|
||||
//! //Try to open an adapter with the name "Demo"
|
||||
//! let adapter = match wintun::Adapter::open(&wintun, "Demo") {
|
||||
//! Ok(a) => a,
|
||||
//! Err(_) => {
|
||||
//! //If loading failed (most likely it didn't exist), create a new one
|
||||
//! wintun::Adapter::create(&wintun, "Example", "Demo", None)
|
||||
//! .expect("Failed to create wintun adapter!")
|
||||
//! }
|
||||
//! };
|
||||
//! //Specify the size of the ring buffer the wintun driver should use.
|
||||
//! let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
//!
|
||||
//! //Get a 20 byte packet from the ring buffer
|
||||
//! let mut packet = session.allocate_send_packet(20).unwrap();
|
||||
//! let bytes: &mut [u8] = packet.bytes_mut();
|
||||
//! //Write IPV4 version and header length
|
||||
//! bytes[0] = 0x40;
|
||||
//!
|
||||
//! //Finish writing IP header
|
||||
//! bytes[9] = 0x69;
|
||||
//! bytes[10] = 0x04;
|
||||
//! bytes[11] = 0x20;
|
||||
//! //...
|
||||
//!
|
||||
//! //Send the packet to wintun virtual adapter for processing by the system
|
||||
//! session.send_packet(packet);
|
||||
//!
|
||||
//! //Stop any readers blocking for data on other threads
|
||||
//! //Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
|
||||
//! //session, blocking it from being dropped
|
||||
//! session.shutdown();
|
||||
//!
|
||||
//! //the session is stopped on drop
|
||||
//! //drop(session);
|
||||
//!
|
||||
//! //drop(adapter)
|
||||
//! //And the adapter closes its resources when dropped
|
||||
//! ```
|
||||
//!
|
||||
//! See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
|
||||
//! file.
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
|
||||
//! debugging packet issues because unsent packets that are dropped without being sent hold up
|
||||
//! wintun's internal ring buffer.
|
||||
//!
|
||||
//! # TODO:
|
||||
//! - Add async support
|
||||
//! Requires hooking into a windows specific reactor and registering read interest on wintun's read
|
||||
//! handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
|
||||
//!
|
||||
|
||||
mod adapter;
|
||||
mod error;
|
||||
mod log;
|
||||
mod packet;
|
||||
mod session;
|
||||
mod util;
|
||||
|
||||
//Generated by bingen
|
||||
#[allow(
|
||||
non_snake_case,
|
||||
dead_code,
|
||||
unused_variables,
|
||||
non_camel_case_types,
|
||||
deref_nullptr,
|
||||
clippy::all
|
||||
)]
|
||||
mod wintun_raw;
|
||||
|
||||
pub use crate::adapter::Adapter;
|
||||
pub use crate::error::{ApiError, OutOfRangeData, WintunError};
|
||||
pub use crate::log::{default_logger, reset_logger, set_logger};
|
||||
pub use crate::packet::Packet;
|
||||
pub use crate::session::Session;
|
||||
pub use crate::util::get_running_driver_version;
|
||||
|
||||
// TODO: Get bindgen to scrape these from the `wintun.h`
|
||||
// We need to make sure these stay up to date
|
||||
/// The maximum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
|
||||
|
||||
/// The minimum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
|
||||
|
||||
/// Maximum pool name length including zero terminator
|
||||
pub const MAX_POOL: usize = 256;
|
||||
|
||||
pub type Wintun = Arc<wintun_raw::wintun>;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Attempts to load the Wintun library from the current directory using the default name "wintun.dll".
|
||||
///
|
||||
/// Use [`load_from_path`] with an absolute path when more control is needed as to where wintun.dll is
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This function loads a dll file with the name wintun.dll using the default system search paths.
|
||||
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
|
||||
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
|
||||
/// file which exports all of the required functions for these bindings to work is loaded. Because
|
||||
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
|
||||
/// Hoverer one can never be too cautious when loading a dll file.
|
||||
///
|
||||
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
|
||||
pub unsafe fn load() -> Result<Wintun, libloading::Error> {
|
||||
load_from_path("wintun")
|
||||
}
|
||||
|
||||
/// Attempts to load the Wintun library as a dynamic library from the given path.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This function loads a dll file with the path provided.
|
||||
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
|
||||
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
|
||||
/// file which exports all of the required functions for these bindings to work is loaded. Because
|
||||
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
|
||||
/// Hoverer one can never be too cautious when loading a dll file.
|
||||
///
|
||||
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
|
||||
pub unsafe fn load_from_path<P>(path: P) -> Result<Wintun, libloading::Error>
|
||||
where
|
||||
P: AsRef<::std::ffi::OsStr>,
|
||||
{
|
||||
check_version(wintun_raw::wintun::new(path)?)
|
||||
}
|
||||
|
||||
/// Attempts to load the Wintun library from an existing [`libloading::Library`].
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This function loads the required WinTun functions using the provided library. Reading a symbol table
|
||||
/// of a dynamic library and transmuting the function pointers inside to have the parameters and return
|
||||
/// values expected by the functions documented at: <https://git.zx2c4.com/wintun/about/#reference>
|
||||
/// is inherently unsafe.
|
||||
///
|
||||
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading::Library::new`]
|
||||
pub unsafe fn load_from_library<L>(library: L) -> Result<Wintun, libloading::Error>
|
||||
where
|
||||
L: Into<libloading::Library>,
|
||||
{
|
||||
check_version(wintun_raw::wintun::from_library(library)?)
|
||||
}
|
||||
|
||||
fn check_version(lib: wintun_raw::wintun) -> Result<Wintun, libloading::Error> {
|
||||
Ok(Arc::new(lib))
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use crate::wintun_raw;
|
||||
use crate::Wintun;
|
||||
use log::*;
|
||||
use widestring::U16CStr;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
|
||||
pub fn set_logger(wintun: &Wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
|
||||
unsafe { wintun.WintunSetLogger(f) };
|
||||
}
|
||||
|
||||
pub fn reset_logger(wintun: &Wintun) {
|
||||
set_logger(wintun, None);
|
||||
}
|
||||
|
||||
static SET_LOGGER: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// The logger that is active by default. Logs messages to the log crate
|
||||
///
|
||||
/// # Safety
|
||||
/// `message` must be a valid pointer that points to an aligned null terminated UTF-16 string
|
||||
pub unsafe extern "C" fn default_logger(
|
||||
level: wintun_raw::WINTUN_LOGGER_LEVEL,
|
||||
_timestamp: wintun_raw::DWORD64,
|
||||
message: *const wintun_raw::WCHAR,
|
||||
) {
|
||||
//Cant wait for RFC 2585
|
||||
#[allow(unused_unsafe)]
|
||||
//Wintun will always give us a valid UTF16 null termineted string
|
||||
let msg = unsafe { U16CStr::from_ptr_str(message) };
|
||||
let utf8_msg = msg.to_string_lossy();
|
||||
match level {
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO => info!("WinTun: {}", utf8_msg),
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN => warn!("WinTun: {}", utf8_msg),
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR => error!("WinTun: {}", utf8_msg),
|
||||
_ => error!("WinTun: {} (with invalid log level {})", utf8_msg, level),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_default_logger_if_unset(wintun: &Wintun) {
|
||||
if SET_LOGGER
|
||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
set_logger(wintun, Some(default_logger));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use crate::session;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) enum Kind {
|
||||
SendPacketPending, //Send packet type, but not sent yet
|
||||
SendPacketSent, //Send packet type - sent
|
||||
ReceivePacket,
|
||||
}
|
||||
|
||||
/// Represents a wintun packet
|
||||
pub struct Packet {
|
||||
pub(crate) kind: Kind,
|
||||
|
||||
//This lifetime is not actually 'static, however before you get your pitchforks let me explain...
|
||||
//The bytes in this slice live for as long at the session that allocated them, or until
|
||||
//WintunReleaseReceivePacket, or WintunSendPacket is called on them (whichever happens first).
|
||||
//The wrapper functions that call into WintunReleaseReceivePacket, and WintunSendPacket
|
||||
//consume the packet, meaning the end of this packet's lifetime coincides with the end of byte's
|
||||
//lifetime. Because we never copy out of bytes, this pointer becomes inaccessible when the
|
||||
//packet is dropped.
|
||||
//
|
||||
//This just leaves packets potentially outliving the session that allocated them posing a
|
||||
//problem.
|
||||
//Fortunately we have an Arc to the session that allocated this packet, meaning that the lifetime
|
||||
//of the session that created this packet is at least as long as the packet.
|
||||
//Because this is private (to external users) and we only write to this field when allocating
|
||||
//new packets, it is impossible for the memory that is pointed to by bytes to outlive the
|
||||
//underlying memory allocated by wintun.
|
||||
//
|
||||
//So what I told you was true, from a certain point of view.
|
||||
//From the point of view of this packet, bytes' lifetime is 'static because we are always
|
||||
//dropped before the underlying memory is freed
|
||||
//
|
||||
//Its also important to know that WintunAllocateSendPacket and WintunReceivePacket always
|
||||
//return sections of memory that never overlap, so we have exclusive access to the memory,
|
||||
//therefore mut is okay here.
|
||||
pub(crate) bytes: &'static mut [u8],
|
||||
|
||||
//Share ownership of session to prevent the session from being dropped before packets that
|
||||
//belong to it
|
||||
pub(crate) session: Arc<session::Session>,
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
/// Returns the bytes this packet holds as &mut.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the bytes this packet holds.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Packet {
|
||||
fn drop(&mut self) {
|
||||
match self.kind {
|
||||
Kind::ReceivePacket => {
|
||||
unsafe {
|
||||
//SAFETY:
|
||||
//
|
||||
// 1. We share ownership of the session therefore it hasn't been dropped yet
|
||||
// 2. Bytes is valid because each packet holds exclusive access to a region of the
|
||||
// ring buffer that the wintun session owns. We return that region of
|
||||
// memory back to wintun here
|
||||
self.session
|
||||
.wintun
|
||||
.WintunReleaseReceivePacket(self.session.session.0, self.bytes.as_ptr())
|
||||
};
|
||||
}
|
||||
Kind::SendPacketPending => {
|
||||
//If someone allocates a packet with session.allocate_send_packet() and then it is
|
||||
//dropped without being sent, this will hold up the send queue because wintun expects
|
||||
//that every allocated packet is sent
|
||||
|
||||
#[cfg(feature = "panic_on_unsent_packets")]
|
||||
panic!("Packet was never sent!");
|
||||
}
|
||||
Kind::SendPacketSent => {
|
||||
//Nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
extern crate winapi;
|
||||
|
||||
use crate::packet;
|
||||
use crate::util::UnsafeHandle;
|
||||
use crate::wintun_raw;
|
||||
use crate::Adapter;
|
||||
use crate::Wintun;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use winapi::shared::winerror;
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::handleapi;
|
||||
use winapi::um::synchapi;
|
||||
use winapi::um::winbase;
|
||||
use winapi::um::winnt;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{ptr, slice};
|
||||
|
||||
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_session_handle>
|
||||
pub struct Session {
|
||||
/// The session handle given to us by WintunStartSession
|
||||
pub(crate) session: UnsafeHandle<wintun_raw::WINTUN_SESSION_HANDLE>,
|
||||
|
||||
/// Shared dll for required wintun driver functions
|
||||
pub(crate) wintun: Wintun,
|
||||
|
||||
/// Windows event handle that is signaled by the wintun driver when data becomes available to
|
||||
/// read
|
||||
pub(crate) read_event: OnceCell<UnsafeHandle<winnt::HANDLE>>,
|
||||
|
||||
/// Windows event handle that is signaled when [`Session::shutdown`] is called force blocking
|
||||
/// readers to exit
|
||||
pub(crate) shutdown_event: UnsafeHandle<winnt::HANDLE>,
|
||||
|
||||
/// The adapter that owns this session
|
||||
pub(crate) adapter: Arc<Adapter>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// Allocates a send packet of the specified size. Wraps WintunAllocateSendPacket
|
||||
///
|
||||
/// All packets returned from this function must be sent using [`Session::send_packet`] because
|
||||
/// wintun establishes the send packet order based on the invocation order of this function.
|
||||
/// Therefore if a packet is allocated using this function, and then never sent, it will hold
|
||||
/// up the send queue for all other packets allocated in the future. It is okay for the session
|
||||
/// to shutdown with allocated packets that have not yet been sent
|
||||
pub fn allocate_send_packet(self: &Arc<Self>, size: u16) -> Result<packet::Packet, ()> {
|
||||
let ptr = unsafe {
|
||||
self.wintun
|
||||
.WintunAllocateSendPacket(self.session.0, size as u32)
|
||||
};
|
||||
if ptr.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(packet::Packet {
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
|
||||
session: self.clone(),
|
||||
kind: packet::Kind::SendPacketPending,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a packet previously allocated with [`Session::allocate_send_packet`]
|
||||
pub fn send_packet(&self, mut packet: packet::Packet) {
|
||||
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
|
||||
|
||||
unsafe {
|
||||
self.wintun
|
||||
.WintunSendPacket(self.session.0, packet.bytes.as_ptr())
|
||||
};
|
||||
//Mark the packet at sent
|
||||
packet.kind = packet::Kind::SendPacketSent;
|
||||
}
|
||||
|
||||
/// Attempts to receive a packet from the virtual interface without blocking.
|
||||
/// If there are no packets currently in the receive queue, this function returns Ok(None)
|
||||
/// without blocking. If blocking until a packet is desirable, use [`Session::receive_blocking`]
|
||||
pub fn try_receive(self: &Arc<Self>) -> Result<Option<packet::Packet>, ()> {
|
||||
let mut size = 0u32;
|
||||
|
||||
let ptr = unsafe {
|
||||
self.wintun
|
||||
.WintunReceivePacket(self.session.0, &mut size as *mut u32)
|
||||
};
|
||||
|
||||
debug_assert!(size <= u16::MAX as u32);
|
||||
if ptr.is_null() {
|
||||
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
|
||||
let last_error = unsafe { GetLastError() };
|
||||
if last_error == winerror::ERROR_NO_MORE_ITEMS {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
Ok(Some(packet::Packet {
|
||||
kind: packet::Kind::ReceivePacket,
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
|
||||
session: self.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the low level read event handle that is signaled when more data becomes available
|
||||
/// to read
|
||||
pub(crate) fn get_read_wait_event(&self) -> Result<winnt::HANDLE, ()> {
|
||||
Ok(self
|
||||
.read_event
|
||||
.get_or_init(|| unsafe {
|
||||
UnsafeHandle(self.wintun.WintunGetReadWaitEvent(self.session.0) as winnt::HANDLE)
|
||||
})
|
||||
.0)
|
||||
}
|
||||
|
||||
/// Blocks until a packet is available, returning the next packet in the receive queue once this happens.
|
||||
/// If the session is closed via [`Session::shutdown`] all threads currently blocking inside this function
|
||||
/// will return Err(())
|
||||
pub fn receive_blocking(self: &Arc<Self>) -> Result<packet::Packet, ()> {
|
||||
loop {
|
||||
//Try 5 times to receive without blocking so we don't have to issue a syscall to wait
|
||||
//for the event if packets are being received at a rapid rate
|
||||
for _ in 0..5 {
|
||||
match self.try_receive() {
|
||||
Err(err) => return Err(err),
|
||||
Ok(Some(packet)) => return Ok(packet),
|
||||
Ok(None) => {
|
||||
//Try again
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Wait on both the read handle and the shutdown handle so that we stop when requested
|
||||
let handles = [self.get_read_wait_event()?, self.shutdown_event.0];
|
||||
let result = unsafe {
|
||||
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
|
||||
//pointer to valid, aligned, stack memory
|
||||
synchapi::WaitForMultipleObjects(
|
||||
2,
|
||||
&handles as *const winnt::HANDLE,
|
||||
0,
|
||||
winbase::INFINITE,
|
||||
)
|
||||
};
|
||||
match result {
|
||||
winbase::WAIT_FAILED => return Err(()),
|
||||
_ => {
|
||||
if result == winbase::WAIT_OBJECT_0 {
|
||||
//We have data!
|
||||
continue;
|
||||
} else if result == winbase::WAIT_OBJECT_0 + 1 {
|
||||
//Shutdown event triggered
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancels any active calls to [`Session::receive_blocking`] making them instantly return Err(_) so that session can be shutdown cleanly
|
||||
pub fn shutdown(&self) {
|
||||
let _ = unsafe { synchapi::SetEvent(self.shutdown_event.0) };
|
||||
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event.0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Session {
|
||||
fn drop(&mut self) {
|
||||
let _ = Arc::clone(&self.adapter);
|
||||
unsafe { self.wintun.WintunEndSession(self.session.0) };
|
||||
self.session.0 = ptr::null_mut();
|
||||
|
||||
//Adapter must be dropped after we call `WintunEndSession`,
|
||||
//if `self.adapter is the last reference
|
||||
//drop(self.adapter)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use winapi::{
|
||||
shared::ntdef::{LANG_NEUTRAL, SUBLANG_DEFAULT},
|
||||
um::{winbase, winnt::MAKELANGID},
|
||||
};
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
use widestring::U16Str;
|
||||
|
||||
/// A wrapper struct that allows a type to be Send and Sync
|
||||
pub(crate) struct UnsafeHandle<T>(pub T);
|
||||
|
||||
/// We never read from the pointer. It only serves as a handle we pass to the kernel or C code that
|
||||
/// doesn't have the same mutable aliasing restrictions we have in Rust
|
||||
unsafe impl<T> Send for UnsafeHandle<T> {}
|
||||
unsafe impl<T> Sync for UnsafeHandle<T> {}
|
||||
|
||||
/// Returns a a human readable error message from a windows error code
|
||||
pub fn get_error_message(err_code: u32) -> String {
|
||||
const LEN: usize = 256;
|
||||
let mut buf = MaybeUninit::<[u16; LEN]>::uninit();
|
||||
|
||||
//SAFETY: name is a allocated on the stack above therefore it must be valid, non-null and
|
||||
//aligned for u16
|
||||
let first = unsafe { *buf.as_mut_ptr() }.as_mut_ptr();
|
||||
//Write default null terminator in case WintunGetAdapterName leaves name unchanged
|
||||
unsafe { first.write(0u16) };
|
||||
let chars_written = unsafe {
|
||||
winbase::FormatMessageW(
|
||||
winbase::FORMAT_MESSAGE_FROM_SYSTEM | winbase::FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
ptr::null(),
|
||||
err_code,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as u32,
|
||||
first,
|
||||
LEN as u32,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
//SAFETY: first is a valid, non-null, aligned, pointer
|
||||
format!(
|
||||
"{} ({})",
|
||||
unsafe { U16Str::from_ptr(first, chars_written as usize) }.to_string_lossy(),
|
||||
err_code
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Version {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
}
|
||||
|
||||
/// Returns the major and minor version of the wintun driver
|
||||
pub fn get_running_driver_version(wintun: &crate::Wintun) -> Result<Version, ()> {
|
||||
let version = unsafe { wintun.WintunGetRunningDriverVersion() };
|
||||
if version == 0 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(Version {
|
||||
major: ((version >> 16) & 0xFF) as u16,
|
||||
minor: (version & 0xFF) as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
/* automatically generated by rust-bindgen 0.59.1 */
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct __BindgenBitfieldUnit<Storage> {
|
||||
storage: Storage,
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage> {
|
||||
#[inline]
|
||||
pub const fn new(storage: Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage>
|
||||
where
|
||||
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn get_bit(&self, index: usize) -> bool {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = self.storage.as_ref()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
byte & mask == mask
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = &mut self.storage.as_mut()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
if val {
|
||||
*byte |= mask;
|
||||
} else {
|
||||
*byte &= !mask;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
let mut val = 0;
|
||||
for i in 0..(bit_width as usize) {
|
||||
if self.get_bit(i + bit_offset) {
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
val |= 1 << index;
|
||||
}
|
||||
}
|
||||
val
|
||||
}
|
||||
#[inline]
|
||||
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
for i in 0..(bit_width as usize) {
|
||||
let mask = 1 << i;
|
||||
let val_bit_is_set = val & mask == mask;
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type wchar_t = ::std::os::raw::c_ushort;
|
||||
pub type DWORD = ::std::os::raw::c_ulong;
|
||||
pub type BOOL = ::std::os::raw::c_int;
|
||||
pub type BYTE = ::std::os::raw::c_uchar;
|
||||
pub type ULONG64 = ::std::os::raw::c_ulonglong;
|
||||
pub type DWORD64 = ::std::os::raw::c_ulonglong;
|
||||
pub type WCHAR = wchar_t;
|
||||
pub type LPCWSTR = *const WCHAR;
|
||||
pub type HANDLE = *mut ::std::os::raw::c_void;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _GUID {
|
||||
pub Data1: ::std::os::raw::c_ulong,
|
||||
pub Data2: ::std::os::raw::c_ushort,
|
||||
pub Data3: ::std::os::raw::c_ushort,
|
||||
pub Data4: [::std::os::raw::c_uchar; 8usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__GUID() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_GUID>(),
|
||||
16usize,
|
||||
concat!("Size of: ", stringify!(_GUID))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_GUID>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(_GUID))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data1 as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data1)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data2 as *const _ as usize },
|
||||
4usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data2)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data3 as *const _ as usize },
|
||||
6usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data3)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data4 as *const _ as usize },
|
||||
8usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data4)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type GUID = _GUID;
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union _NET_LUID_LH {
|
||||
pub Value: ULONG64,
|
||||
pub Info: _NET_LUID_LH__bindgen_ty_1,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[repr(align(8))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _NET_LUID_LH__bindgen_ty_1 {
|
||||
pub _bitfield_align_1: [u32; 0],
|
||||
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__NET_LUID_LH__bindgen_ty_1() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_NET_LUID_LH__bindgen_ty_1>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(_NET_LUID_LH__bindgen_ty_1))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_NET_LUID_LH__bindgen_ty_1>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(_NET_LUID_LH__bindgen_ty_1))
|
||||
);
|
||||
}
|
||||
impl _NET_LUID_LH__bindgen_ty_1 {
|
||||
#[inline]
|
||||
pub fn Reserved(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_Reserved(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(0usize, 24u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn NetLuidIndex(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 24u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_NetLuidIndex(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(24usize, 24u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn IfType(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 16u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_IfType(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(48usize, 16u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn new_bitfield_1(
|
||||
Reserved: ULONG64,
|
||||
NetLuidIndex: ULONG64,
|
||||
IfType: ULONG64,
|
||||
) -> __BindgenBitfieldUnit<[u8; 8usize]> {
|
||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default();
|
||||
__bindgen_bitfield_unit.set(0usize, 24u8, {
|
||||
let Reserved: u64 = unsafe { ::std::mem::transmute(Reserved) };
|
||||
Reserved as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(24usize, 24u8, {
|
||||
let NetLuidIndex: u64 = unsafe { ::std::mem::transmute(NetLuidIndex) };
|
||||
NetLuidIndex as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(48usize, 16u8, {
|
||||
let IfType: u64 = unsafe { ::std::mem::transmute(IfType) };
|
||||
IfType as u64
|
||||
});
|
||||
__bindgen_bitfield_unit
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__NET_LUID_LH() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_NET_LUID_LH>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(_NET_LUID_LH))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_NET_LUID_LH>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(_NET_LUID_LH))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Value as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_NET_LUID_LH),
|
||||
"::",
|
||||
stringify!(Value)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Info as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_NET_LUID_LH),
|
||||
"::",
|
||||
stringify!(Info)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type NET_LUID_LH = _NET_LUID_LH;
|
||||
pub type NET_LUID = NET_LUID_LH;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _WINTUN_ADAPTER {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[doc = " A handle representing Wintun adapter"]
|
||||
pub type WINTUN_ADAPTER_HANDLE = *mut _WINTUN_ADAPTER;
|
||||
#[doc = "< Informational"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO: WINTUN_LOGGER_LEVEL = 0;
|
||||
#[doc = "< Warning"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN: WINTUN_LOGGER_LEVEL = 1;
|
||||
#[doc = "< Error"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR: WINTUN_LOGGER_LEVEL = 2;
|
||||
#[doc = " Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK."]
|
||||
pub type WINTUN_LOGGER_LEVEL = ::std::os::raw::c_int;
|
||||
#[doc = " Called by internal logger to report diagnostic messages"]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Level Message level."]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC."]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Message Message text."]
|
||||
pub type WINTUN_LOGGER_CALLBACK = ::std::option::Option<
|
||||
unsafe extern "C" fn(Level: WINTUN_LOGGER_LEVEL, Timestamp: DWORD64, Message: LPCWSTR),
|
||||
>;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _TUN_SESSION {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[doc = " A handle representing Wintun session"]
|
||||
pub type WINTUN_SESSION_HANDLE = *mut _TUN_SESSION;
|
||||
extern crate libloading;
|
||||
pub struct wintun {
|
||||
__library: ::libloading::Library,
|
||||
pub WintunCreateAdapter: unsafe extern "C" fn(
|
||||
arg1: LPCWSTR,
|
||||
arg2: LPCWSTR,
|
||||
arg3: *const GUID,
|
||||
) -> WINTUN_ADAPTER_HANDLE,
|
||||
pub WintunCloseAdapter: unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE),
|
||||
pub WintunOpenAdapter: unsafe extern "C" fn(arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE,
|
||||
pub WintunGetAdapterLUID:
|
||||
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: *mut NET_LUID),
|
||||
pub WintunGetRunningDriverVersion: unsafe extern "C" fn() -> DWORD,
|
||||
pub WintunDeleteDriver: unsafe extern "C" fn() -> BOOL,
|
||||
pub WintunSetLogger: unsafe extern "C" fn(arg1: WINTUN_LOGGER_CALLBACK),
|
||||
pub WintunStartSession:
|
||||
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: DWORD) -> WINTUN_SESSION_HANDLE,
|
||||
pub WintunEndSession: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE),
|
||||
pub WintunGetReadWaitEvent: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE) -> HANDLE,
|
||||
pub WintunReceivePacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *mut DWORD) -> *mut BYTE,
|
||||
pub WintunReleaseReceivePacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
|
||||
pub WintunAllocateSendPacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: DWORD) -> *mut BYTE,
|
||||
pub WintunSendPacket: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
|
||||
}
|
||||
impl wintun {
|
||||
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
|
||||
where
|
||||
P: AsRef<::std::ffi::OsStr>,
|
||||
{
|
||||
let library = ::libloading::Library::new(path)?;
|
||||
Self::from_library(library)
|
||||
}
|
||||
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
|
||||
where
|
||||
L: Into<::libloading::Library>,
|
||||
{
|
||||
let __library = library.into();
|
||||
let WintunCreateAdapter = __library.get(b"WintunCreateAdapter\0").map(|sym| *sym)?;
|
||||
let WintunCloseAdapter = __library.get(b"WintunCloseAdapter\0").map(|sym| *sym)?;
|
||||
let WintunOpenAdapter = __library.get(b"WintunOpenAdapter\0").map(|sym| *sym)?;
|
||||
let WintunGetAdapterLUID = __library.get(b"WintunGetAdapterLUID\0").map(|sym| *sym)?;
|
||||
let WintunGetRunningDriverVersion = __library
|
||||
.get(b"WintunGetRunningDriverVersion\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunDeleteDriver = __library.get(b"WintunDeleteDriver\0").map(|sym| *sym)?;
|
||||
let WintunSetLogger = __library.get(b"WintunSetLogger\0").map(|sym| *sym)?;
|
||||
let WintunStartSession = __library.get(b"WintunStartSession\0").map(|sym| *sym)?;
|
||||
let WintunEndSession = __library.get(b"WintunEndSession\0").map(|sym| *sym)?;
|
||||
let WintunGetReadWaitEvent = __library.get(b"WintunGetReadWaitEvent\0").map(|sym| *sym)?;
|
||||
let WintunReceivePacket = __library.get(b"WintunReceivePacket\0").map(|sym| *sym)?;
|
||||
let WintunReleaseReceivePacket = __library
|
||||
.get(b"WintunReleaseReceivePacket\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunAllocateSendPacket = __library
|
||||
.get(b"WintunAllocateSendPacket\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunSendPacket = __library.get(b"WintunSendPacket\0").map(|sym| *sym)?;
|
||||
Ok(wintun {
|
||||
__library,
|
||||
WintunCreateAdapter,
|
||||
WintunCloseAdapter,
|
||||
WintunOpenAdapter,
|
||||
WintunGetAdapterLUID,
|
||||
WintunGetRunningDriverVersion,
|
||||
WintunDeleteDriver,
|
||||
WintunSetLogger,
|
||||
WintunStartSession,
|
||||
WintunEndSession,
|
||||
WintunGetReadWaitEvent,
|
||||
WintunReceivePacket,
|
||||
WintunReleaseReceivePacket,
|
||||
WintunAllocateSendPacket,
|
||||
WintunSendPacket,
|
||||
})
|
||||
}
|
||||
pub unsafe fn WintunCreateAdapter(
|
||||
&self,
|
||||
arg1: LPCWSTR,
|
||||
arg2: LPCWSTR,
|
||||
arg3: *const GUID,
|
||||
) -> WINTUN_ADAPTER_HANDLE {
|
||||
(self.WintunCreateAdapter)(arg1, arg2, arg3)
|
||||
}
|
||||
pub unsafe fn WintunCloseAdapter(&self, arg1: WINTUN_ADAPTER_HANDLE) -> () {
|
||||
(self.WintunCloseAdapter)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunOpenAdapter(&self, arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE {
|
||||
(self.WintunOpenAdapter)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunGetAdapterLUID(
|
||||
&self,
|
||||
arg1: WINTUN_ADAPTER_HANDLE,
|
||||
arg2: *mut NET_LUID,
|
||||
) -> () {
|
||||
(self.WintunGetAdapterLUID)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunGetRunningDriverVersion(&self) -> DWORD {
|
||||
(self.WintunGetRunningDriverVersion)()
|
||||
}
|
||||
pub unsafe fn WintunDeleteDriver(&self) -> BOOL {
|
||||
(self.WintunDeleteDriver)()
|
||||
}
|
||||
pub unsafe fn WintunSetLogger(&self, arg1: WINTUN_LOGGER_CALLBACK) -> () {
|
||||
(self.WintunSetLogger)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunStartSession(
|
||||
&self,
|
||||
arg1: WINTUN_ADAPTER_HANDLE,
|
||||
arg2: DWORD,
|
||||
) -> WINTUN_SESSION_HANDLE {
|
||||
(self.WintunStartSession)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunEndSession(&self, arg1: WINTUN_SESSION_HANDLE) -> () {
|
||||
(self.WintunEndSession)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunGetReadWaitEvent(&self, arg1: WINTUN_SESSION_HANDLE) -> HANDLE {
|
||||
(self.WintunGetReadWaitEvent)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunReceivePacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: *mut DWORD,
|
||||
) -> *mut BYTE {
|
||||
(self.WintunReceivePacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunReleaseReceivePacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: *const BYTE,
|
||||
) -> () {
|
||||
(self.WintunReleaseReceivePacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunAllocateSendPacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: DWORD,
|
||||
) -> *mut BYTE {
|
||||
(self.WintunAllocateSendPacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunSendPacket(&self, arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE) -> () {
|
||||
(self.WintunSendPacket)(arg1, arg2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
Prebuilt Binaries License
|
||||
-------------------------
|
||||
|
||||
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
|
||||
files that are included in the .zip file that contains this document as
|
||||
downloaded from wintun.net/builds.
|
||||
|
||||
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
|
||||
non-transferable right to use Software for lawful purposes under certain
|
||||
obligations and limited rights as set forth in this agreement.
|
||||
|
||||
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
|
||||
licensed, not sold. Title to Software and all associated intellectual
|
||||
property rights are retained by WireGuard. You must not:
|
||||
a. reverse engineer, decompile, disassemble, extract from, or otherwise
|
||||
modify the Software;
|
||||
b. modify or create derivative work based upon Software in whole or in
|
||||
parts, except insofar as only the API interfaces of the "wintun.h" file
|
||||
distributed alongside the Software (the "Permitted API") are used;
|
||||
c. remove any proprietary notices, labels, or copyrights from the Software;
|
||||
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
|
||||
transfer rights of the Software without the prior written consent of
|
||||
WireGuard LLC, except insofar as the Software is distributed alongside
|
||||
other software that uses the Software only via the Permitted API;
|
||||
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
|
||||
project, or the names of its contributors to endorse or promote products
|
||||
derived from the Software without specific prior written consent.
|
||||
|
||||
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
|
||||
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
|
||||
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
|
||||
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
|
||||
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
|
||||
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
|
||||
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
|
||||
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
|
||||
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
|
||||
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
|
||||
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
|
||||
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
|
||||
|
||||
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
|
||||
WireGuard LLC or any third-party-developer will be liable for any lost
|
||||
revenue, profit or data or for special, indirect, consequential, incidental
|
||||
or punitive damages, however caused regardless of the theory of liability,
|
||||
arising out of or related to the use of or inability to use Software, even
|
||||
if WireGuard LLC has been advised of the possibility of such damages.
|
||||
Solely you are responsible for determining the appropriateness of using
|
||||
Software and accept full responsibility for all risks associated with its
|
||||
exercise of rights under this agreement, including but not limited to the
|
||||
risks and costs of program errors, compliance with applicable laws, damage
|
||||
to or loss of data, programs or equipment, and unavailability or
|
||||
interruption of operations. The foregoing limitations will apply even if
|
||||
the above stated warranty fails of its essential purpose. You acknowledge,
|
||||
that it is in the nature of software that software is complex and not
|
||||
completely free of errors. In no event shall WireGuard LLC or any
|
||||
third-party-developer be liable to you under any theory for any damages
|
||||
suffered by you or any user of Software or for any special, incidental,
|
||||
indirect, consequential or similar damages (including without limitation
|
||||
damages for loss of business profits, business interruption, loss of
|
||||
business information or any other pecuniary loss) arising out of the use or
|
||||
inability to use Software, even if WireGuard LLC has been advised of the
|
||||
possibility of such damages and regardless of the legal or quitable theory
|
||||
(contract, tort, or otherwise) upon which the claim is based.
|
||||
|
||||
6. TERMINATION. This agreement is affected until terminated. You may
|
||||
terminate this agreement at any time. This agreement will terminate
|
||||
immediately without notice from WireGuard LLC if you fail to comply with
|
||||
the terms and conditions of this agreement. Upon termination, you must
|
||||
delete Software and all copies of Software and cease all forms of
|
||||
distribution of Software.
|
||||
|
||||
7. SEVERABILITY. If any provision of this agreement is held to be
|
||||
unenforceable, this agreement will remain in effect with the provision
|
||||
omitted, unless omission would frustrate the intent of the parties, in
|
||||
which case this agreement will immediately terminate.
|
||||
|
||||
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
|
||||
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
|
||||
right at any time to cease development of Software, to alter distribution
|
||||
details, features, specifications, capabilities, functions, licensing
|
||||
terms, release dates, APIs, ABIs, general availability, or other
|
||||
characteristics of the Software.
|
||||
@@ -0,0 +1,270 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
*
|
||||
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ipexport.h>
|
||||
#include <ifdef.h>
|
||||
#include <ws2ipdef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ALIGNED
|
||||
# if defined(_MSC_VER)
|
||||
# define ALIGNED(n) __declspec(align(n))
|
||||
# elif defined(__GNUC__)
|
||||
# define ALIGNED(n) __attribute__((aligned(n)))
|
||||
# else
|
||||
# error "Unable to define ALIGNED"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* MinGW is missing this one, unfortunately. */
|
||||
#ifndef _Post_maybenull_
|
||||
# define _Post_maybenull_
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */
|
||||
|
||||
/**
|
||||
* A handle representing Wintun adapter
|
||||
*/
|
||||
typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE;
|
||||
|
||||
/**
|
||||
* Creates a new Wintun adapter.
|
||||
*
|
||||
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||
* characters.
|
||||
*
|
||||
* @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||
* characters.
|
||||
*
|
||||
* @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
|
||||
* If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
|
||||
* created for each new adapter. It is called "requested" GUID because the API it uses is
|
||||
* completely undocumented, and so there could be minor interesting complications with its usage.
|
||||
*
|
||||
* @return If the function succeeds, the return value is the adapter handle. Must be released with
|
||||
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
|
||||
* GetLastError.
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC)
|
||||
(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID);
|
||||
|
||||
/**
|
||||
* Opens an existing Wintun adapter.
|
||||
*
|
||||
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||
* characters.
|
||||
*
|
||||
* @return If the function succeeds, the return value is the adapter handle. Must be released with
|
||||
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
|
||||
* GetLastError.
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name);
|
||||
|
||||
/**
|
||||
* Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
|
||||
*
|
||||
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter);
|
||||
|
||||
/**
|
||||
* Deletes the Wintun driver if there are no more adapters in use.
|
||||
*
|
||||
* @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
|
||||
* get extended error information, call GetLastError.
|
||||
*/
|
||||
typedef _Return_type_success_(return != FALSE)
|
||||
BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID);
|
||||
|
||||
/**
|
||||
* Returns the LUID of the adapter.
|
||||
*
|
||||
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter
|
||||
*
|
||||
* @param Luid Pointer to LUID to receive adapter LUID.
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
|
||||
|
||||
/**
|
||||
* Determines the version of the Wintun driver currently loaded.
|
||||
*
|
||||
* @return If the function succeeds, the return value is the version number. If the function fails, the return value is
|
||||
* zero. To get extended error information, call GetLastError. Possible errors include the following:
|
||||
* ERROR_FILE_NOT_FOUND Wintun not loaded
|
||||
*/
|
||||
typedef _Return_type_success_(return != 0)
|
||||
DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID);
|
||||
|
||||
/**
|
||||
* Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
WINTUN_LOG_INFO, /**< Informational */
|
||||
WINTUN_LOG_WARN, /**< Warning */
|
||||
WINTUN_LOG_ERR /**< Error */
|
||||
} WINTUN_LOGGER_LEVEL;
|
||||
|
||||
/**
|
||||
* Called by internal logger to report diagnostic messages
|
||||
*
|
||||
* @param Level Message level.
|
||||
*
|
||||
* @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC.
|
||||
*
|
||||
* @param Message Message text.
|
||||
*/
|
||||
typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(
|
||||
_In_ WINTUN_LOGGER_LEVEL Level,
|
||||
_In_ DWORD64 Timestamp,
|
||||
_In_z_ LPCWSTR Message);
|
||||
|
||||
/**
|
||||
* Sets logger callback function.
|
||||
*
|
||||
* @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
|
||||
* threads concurrently. Should the logging require serialization, you must handle serialization in
|
||||
* NewLogger. Set to NULL to disable.
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
|
||||
|
||||
/**
|
||||
* Minimum ring capacity.
|
||||
*/
|
||||
#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
|
||||
|
||||
/**
|
||||
* Maximum ring capacity.
|
||||
*/
|
||||
#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
|
||||
|
||||
/**
|
||||
* A handle representing Wintun session
|
||||
*/
|
||||
typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE;
|
||||
|
||||
/**
|
||||
* Starts Wintun session.
|
||||
*
|
||||
* @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
|
||||
*
|
||||
* @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
|
||||
* Must be a power of two.
|
||||
*
|
||||
* @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
|
||||
* NULL. To get extended error information, call GetLastError.
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
|
||||
|
||||
/**
|
||||
* Ends Wintun session.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
|
||||
|
||||
/**
|
||||
* Gets Wintun session's read-wait event handle.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @return Pointer to receive event handle to wait for available data when reading. Should
|
||||
* WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
|
||||
* load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
|
||||
* CloseHandle on this event - it is managed by the session.
|
||||
*/
|
||||
typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
|
||||
|
||||
/**
|
||||
* Maximum IP packet size
|
||||
*/
|
||||
#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
|
||||
|
||||
/**
|
||||
* Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
|
||||
* from this function to release internal buffer. This function is thread-safe.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param PacketSize Pointer to receive packet size.
|
||||
*
|
||||
* @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
|
||||
* return value is NULL. To get extended error information, call GetLastError. Possible errors include the
|
||||
* following:
|
||||
* ERROR_HANDLE_EOF Wintun adapter is terminating;
|
||||
* ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
|
||||
* ERROR_INVALID_DATA Wintun buffer is corrupt
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
_Post_writable_byte_size_(*PacketSize)
|
||||
BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
|
||||
|
||||
/**
|
||||
* Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param Packet Packet obtained with WintunReceivePacket
|
||||
*/
|
||||
typedef VOID(
|
||||
WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
|
||||
|
||||
/**
|
||||
* Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
|
||||
* and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
|
||||
* calls define the packet sending order.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
|
||||
*
|
||||
* @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
|
||||
* the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
|
||||
* following:
|
||||
* ERROR_HANDLE_EOF Wintun adapter is terminating;
|
||||
* ERROR_BUFFER_OVERFLOW Wintun buffer is full;
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
_Post_writable_byte_size_(PacketSize)
|
||||
BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
|
||||
|
||||
/**
|
||||
* Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
|
||||
* order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
|
||||
* WintunSendPacket yet.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param Packet Packet obtained with WintunAllocateSendPacket
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
// Information about functions taken from:
|
||||
// https://git.zx2c4.com/wintun/tree/example/example.c
|
||||
|
||||
#include "wintun.h"
|
||||
|
||||
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
|
||||
WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
|
||||
WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
|
||||
WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
|
||||
WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
|
||||
WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
|
||||
WINTUN_SET_LOGGER_FUNC WintunSetLogger;
|
||||
WINTUN_START_SESSION_FUNC WintunStartSession;
|
||||
WINTUN_END_SESSION_FUNC WintunEndSession;
|
||||
WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
|
||||
WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
|
||||
WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
|
||||
WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
|
||||
WINTUN_SEND_PACKET_FUNC WintunSendPacket;
|
||||
Reference in New Issue
Block a user