diff --git a/Cargo.lock b/Cargo.lock index 9230b2f..5241e40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -134,26 +134,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "c2rust-bitfields" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b43c3f07ab0ef604fa6f595aa46ec2f8a22172c975e186f6f5bf9829a3b72c41" -dependencies = [ - "c2rust-bitfields-derive", -] - -[[package]] -name = "c2rust-bitfields-derive" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3cbc102e2597c9744c8bd8c15915d554300601c91a079430d309816b0912545" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "cesu8" version = "1.1.0" @@ -232,7 +212,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -309,7 +289,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -438,7 +418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -453,16 +433,6 @@ version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - [[package]] name = "log" version = "0.4.29" @@ -506,7 +476,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "ostp" -version = "0.1.20" +version = "0.1.21" dependencies = [ "anyhow", "base64", @@ -521,7 +491,7 @@ dependencies = [ [[package]] name = "ostp-client" -version = "0.1.20" +version = "0.1.21" dependencies = [ "anyhow", "bytes", @@ -532,12 +502,11 @@ dependencies = [ "serde_json", "tokio", "tracing", - "wintun", ] [[package]] name = "ostp-core" -version = "0.1.20" +version = "0.1.21" dependencies = [ "anyhow", "async-trait", @@ -569,7 +538,7 @@ dependencies = [ [[package]] name = "ostp-server" -version = "0.1.20" +version = "0.1.21" dependencies = [ "anyhow", "bytes", @@ -724,7 +693,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -799,17 +768,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.117" @@ -838,7 +796,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -865,7 +823,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -887,7 +845,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -958,25 +916,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-link" version = "0.2.1" @@ -989,7 +928,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -1007,29 +946,13 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -1038,103 +961,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wintun" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b3c8c8876c686f8a2d6376999ac1c9a24c74d2968551c9394f7e89127783685" -dependencies = [ - "c2rust-bitfields", - "libloading", - "log", - "thiserror", - "windows", -] - [[package]] name = "x25519-dalek" version = "2.0.1" @@ -1164,7 +1026,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1184,7 +1046,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] diff --git a/ostp-client/Cargo.toml b/ostp-client/Cargo.toml index 41edb4f..75766d6 100644 --- a/ostp-client/Cargo.toml +++ b/ostp-client/Cargo.toml @@ -14,6 +14,3 @@ rand.workspace = true serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" portable-atomic.workspace = true - -[target.'cfg(target_os = "windows")'.dependencies] -wintun = "0.4.0" diff --git a/ostp-client/src/runner.rs b/ostp-client/src/runner.rs index 1169c66..8cc182d 100644 --- a/ostp-client/src/runner.rs +++ b/ostp-client/src/runner.rs @@ -68,6 +68,7 @@ pub async fn run_client(config: crate::config::ClientConfig) -> Result<()> { if config.mode == "tun" { tunnel::download_wintun_dll(config.debug)?; + tunnel::download_tun2socks(config.debug)?; } let (proxy_events_tx, proxy_events_rx) = mpsc::channel(10000); @@ -153,7 +154,7 @@ pub async fn run_client(config: crate::config::ClientConfig) -> Result<()> { let wintun_shutdown_rx = shutdown_tx.subscribe(); let wintun_task = if config_clone.mode == "tun" { Some(tokio::spawn(async move { - tunnel::run_wintun_tunnel(wintun_shutdown_rx, config_clone.debug).await + tunnel::run_wintun_tunnel(config_clone, wintun_shutdown_rx).await })) } else { None diff --git a/ostp-client/src/tunnel/mod.rs b/ostp-client/src/tunnel/mod.rs index 69762f4..246dd04 100644 --- a/ostp-client/src/tunnel/mod.rs +++ b/ostp-client/src/tunnel/mod.rs @@ -3,6 +3,7 @@ mod wintun_downloader; mod wintun_handler; pub use wintun_downloader::download_wintun_dll; +pub use wintun_downloader::download_tun2socks; pub use wintun_handler::run_wintun_tunnel; use tokio::sync::{mpsc, watch}; diff --git a/ostp-client/src/tunnel/wintun_downloader.rs b/ostp-client/src/tunnel/wintun_downloader.rs index a07739d..7b7917a 100644 --- a/ostp-client/src/tunnel/wintun_downloader.rs +++ b/ostp-client/src/tunnel/wintun_downloader.rs @@ -47,3 +47,49 @@ pub fn download_wintun_dll(debug: bool) -> Result<()> { pub fn download_wintun_dll(_debug: bool) -> Result<()> { Ok(()) } + +#[cfg(target_os = "windows")] +pub fn download_tun2socks(debug: bool) -> Result<()> { + let exe = std::env::current_exe()?; + let dir = exe.parent().ok_or_else(|| anyhow!("failed to get binary directory"))?; + let tun2socks_path = dir.join("tun2socks.exe"); + + if !tun2socks_path.exists() { + if debug { + println!("[ostp-client] tun2socks.exe not found. Downloading automatically..."); + } + + let zip_path = dir.join("tun2socks.zip").to_string_lossy().replace('\\', "/"); + let temp_path = dir.join("tun2socks_temp").to_string_lossy().replace('\\', "/"); + let dest_path = tun2socks_path.to_string_lossy().replace('\\', "/"); + + let url = "https://github.com/xjasonlyu/tun2socks/releases/download/v2.5.2/tun2socks-windows-amd64.zip"; + + let ps_script = format!( + "Invoke-WebRequest -Uri '{}' -OutFile '{}' -ErrorAction Stop; \ + Expand-Archive -Path '{}' -DestinationPath '{}' -Force; \ + Get-ChildItem -Path '{}' -Filter '*.exe' -Recurse | Copy-Item -Destination '{}' -Force; \ + Remove-Item '{}', '{}' -Recurse -Force", + url, zip_path, zip_path, temp_path, temp_path, dest_path, zip_path, temp_path + ); + + let output = std::process::Command::new("powershell") + .args(["-Command", &ps_script]) + .current_dir(dir) + .output()?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(anyhow!("Failed to download tun2socks.exe: {stderr}")); + } + if debug { + println!("[ostp-client] tun2socks.exe downloaded and installed successfully!"); + } + } + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn download_tun2socks(_debug: bool) -> Result<()> { + Ok(()) +} diff --git a/ostp-client/src/tunnel/wintun_handler.rs b/ostp-client/src/tunnel/wintun_handler.rs index 16c2bd1..04f6711 100644 --- a/ostp-client/src/tunnel/wintun_handler.rs +++ b/ostp-client/src/tunnel/wintun_handler.rs @@ -1,91 +1,153 @@ use anyhow::{anyhow, Result}; -#[cfg(target_os = "windows")] -use std::sync::Arc; use tokio::sync::watch; #[cfg(target_os = "windows")] pub async fn run_wintun_tunnel( + config: crate::config::ClientConfig, mut shutdown: watch::Receiver, - debug: bool, ) -> Result<()> { - if debug { - println!("[ostp-client] Initializing Wintun adapter 'ostp_tun'..."); - } - - // 1. Load Wintun DLL - let wintun = unsafe { wintun::load_from_path("wintun.dll") } - .map_err(|e| anyhow!("Failed to load wintun.dll: {:?}", e))?; - - // 2. Create or Open Adapter with static name "ostp_tun" - let adapter = match wintun::Adapter::open(&wintun, "ostp_tun") { - Ok(a) => a, - Err(_) => wintun::Adapter::create(&wintun, "ostp_tun", "OSTP TUN Adapter", None) - .map_err(|e| anyhow!("Failed to create Wintun adapter: {:?}", e))?, - }; - - let adapter = Arc::new(adapter); + use std::net::ToSocketAddrs; + use std::process::{Command, Stdio}; - // Set IP, Subnet and Gateway natively using netsh for bulletproof routing + let debug = config.debug; + if debug { - println!("[ostp-client] Configuring Wintun network settings via netsh..."); + println!("[ostp-client] Initializing high-performance TUN tunnel via tun2socks..."); } - let output = std::process::Command::new("netsh") - .args(["interface", "ipv4", "set", "address", "name=ostp_tun", "static", "10.1.0.2", "255.255.255.0", "10.1.0.1"]) + + // 1. Get executable directory to locate tun2socks.exe and wintun.dll + let exe = std::env::current_exe()?; + let dir = exe.parent().ok_or_else(|| anyhow!("failed to get binary directory"))?; + let tun2socks_exe = dir.join("tun2socks.exe"); + + if !tun2socks_exe.exists() { + return Err(anyhow!("tun2socks.exe not found! Please ensure initialization downloaded it successfully.")); + } + + // 2. Resolve Server IP for routing table exclusion + let server_ip = config.ostp.server_addr.to_socket_addrs() + .map_err(|e| anyhow!("Failed to resolve remote server IP: {}", e))? + .next() + .map(|addr| addr.ip()) + .ok_or_else(|| anyhow!("Could not resolve host IP for routing exclusion"))?; + + let server_ip_str = server_ip.to_string(); + + if debug { + println!("[ostp-client] Resolved remote server IP: {}", server_ip_str); + } + + // 3. Run PowerShell script to configure system routes + if debug { + println!("[ostp-client] Injecting system routing tables and excluding remote proxy..."); + } + + let setup_script = format!( + "$remote_ip = '{}'\n\ + $route = Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Sort-Object RouteMetric | Select-Object -First 1\n\ + $gw = $route.NextHop\n\ + $ifIndex = $route.InterfaceIndex\n\ + New-NetRoute -DestinationPrefix \"$remote_ip/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n", + server_ip_str + ); + + let out = Command::new("powershell") + .args(["-Command", &setup_script]) .output()?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - println!("[ostp-client] Warning: netsh returned error: {}", stderr); - } else { - if debug { - println!("[ostp-client] Network configured. ostp_tun IP: 10.1.0.2, Gateway: 10.1.0.1"); - } + + if !out.status.success() && debug { + println!("[ostp-client] Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr)); } - // Start Wintun session - let session = adapter.start_session(wintun::MAX_RING_CAPACITY) - .map_err(|e| anyhow!("Failed to start Wintun session: {:?}", e))?; - let session = Arc::new(session); - - if debug { - println!("[ostp-client] TUN tunnel 'ostp_tun' is active and intercepting packets!"); - } - - // Spawn Packet Receiver Loop to read packets from Windows stack - let rx_session = session.clone(); - tokio::task::spawn_blocking(move || { - loop { - match rx_session.receive_blocking() { - Ok(packet) => { - let bytes = packet.bytes(); - if bytes.len() >= 20 { - let proto = bytes[9]; - let src_ip = format!("{}.{}.{}.{}", bytes[12], bytes[13], bytes[14], bytes[15]); - let dest_ip = format!("{}.{}.{}.{}", bytes[16], bytes[17], bytes[18], bytes[19]); - if debug { - println!("[TUN Packet] Proto={}, Src={}, Dest={}, Len={}", proto, src_ip, dest_ip, bytes.len()); - } - } - } - Err(_) => break, - } - } - }); - - // Wait for shutdown signal - let _ = shutdown.changed().await; + // 4. Prepare and launch tun2socks.exe in the background + let proxy_url = format!("socks5://{}", config.local_proxy.bind_addr); if debug { - println!("[ostp-client] Shutting down Wintun adapter..."); + println!("[ostp-client] Spawning tun2socks daemon pointing to {}", proxy_url); } + + let mut child = Command::new(&tun2socks_exe) + .args([ + "-device", "ostp_tun", + "-proxy", &proxy_url, + "-loglevel", if debug { "debug" } else { "error" } + ]) + .current_dir(dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| anyhow!("Failed to launch tun2socks.exe background process: {}", e))?; + + // 5. Once tun2socks creates the interface, apply network settings (IP, metric, DNS) + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + + if debug { + println!("[ostp-client] Applying network configurations onto 'ostp_tun' interface..."); + } + + let net_setup = "\ + netsh interface ipv4 set address name=\"ostp_tun\" static 10.1.0.2 255.255.255.0 10.1.0.1\n\ + netsh interface ipv4 set interface name=\"ostp_tun\" metric=5\n\ + netsh interface ipv4 set dnsservers name=\"ostp_tun\" static 1.1.1.1 primary\n"; + + let _ = Command::new("powershell") + .args(["-Command", net_setup]) + .output()?; + + println!("[client] TUN Tunnel established, internet traffic is now routing through OSTP."); + + // 6. Spawn thread to keep logging tun2socks output if in debug mode + let mut stdout = child.stdout.take(); + let mut stderr = child.stderr.take(); + if debug { + std::thread::spawn(move || { + use std::io::{BufRead, BufReader}; + if let Some(out) = stdout.take() { + let reader = BufReader::new(out); + for line in reader.lines().map_while(Result::ok) { + println!("[tun2socks] {}", line); + } + } + }); + std::thread::spawn(move || { + use std::io::{BufRead, BufReader}; + if let Some(err) = stderr.take() { + let reader = BufReader::new(err); + for line in reader.lines().map_while(Result::ok) { + println!("[tun2socks err] {}", line); + } + } + }); + } + + // 7. Wait for shutdown signal + let _ = shutdown.changed().await; + + println!("[client] Deactivating TUN tunnel and restoring system network topology..."); + + // 8. Terminate tun2socks + let _ = child.kill(); + + // 9. Run cleanup routing script + let cleanup_script = format!( + "$remote_ip = '{}'\n\ + Remove-NetRoute -DestinationPrefix \"$remote_ip/32\" -Confirm:$false -ErrorAction SilentlyContinue\n", + server_ip_str + ); + + let _ = Command::new("powershell") + .args(["-Command", &cleanup_script]) + .output()?; + + println!("[client] TUN Tunnel stopped."); Ok(()) } #[cfg(not(target_os = "windows"))] pub async fn run_wintun_tunnel( + _config: crate::config::ClientConfig, _shutdown: watch::Receiver, - _debug: bool, ) -> Result<()> { Err(anyhow!("Wintun is only supported on Windows!")) }