diff --git a/Cargo.lock b/Cargo.lock index d1b944a..cf82e75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2195,6 +2195,7 @@ dependencies = [ "anyhow", "chrono", "common", + "console", "log", "rand", "signal-hook", diff --git a/common/src/callback.rs b/common/src/callback.rs index 4fffa03..dff2f1e 100644 --- a/common/src/callback.rs +++ b/common/src/callback.rs @@ -37,7 +37,8 @@ impl VntCallback for VntHandler { | ErrorType::AddressExhausted | ErrorType::IpAlreadyExists | ErrorType::InvalidIp - | ErrorType::LocalIpExists => { + | ErrorType::LocalIpExists + | ErrorType::FailedToCrateDevice => { self.stop(); } _ => {} diff --git a/common/src/cli.rs b/common/src/cli.rs index 4199606..ceee8d2 100644 --- a/common/src/cli.rs +++ b/common/src/cli.rs @@ -302,7 +302,7 @@ pub fn parse_args_config() -> anyhow::Result, bool)> } else { Compressor::None }; - let config = match Config::new( + let config = Config::new( #[cfg(feature = "integrated_tun")] #[cfg(target_os = "windows")] tap, @@ -337,13 +337,7 @@ pub fn parse_args_config() -> anyhow::Result, bool)> !disable_stats, allow_wire_guard, local_ipv4, - ) { - Ok(config) => config, - Err(e) => { - println!("config error: {}", e); - std::process::exit(1); - } - }; + )?; (config, vnt_mapping_list, cmd) }; println!("version {}", vnt::VNT_VERSION); diff --git a/vn-link-cli/src/main.rs b/vn-link-cli/src/main.rs index 85a7a3a..e374033 100644 --- a/vn-link-cli/src/main.rs +++ b/vn-link-cli/src/main.rs @@ -17,7 +17,7 @@ fn main() { e, std::env::args().collect::>() ); - println!("{}", e); + println!("Error {:?}", e); return; } }; diff --git a/vnt-cli/Cargo.toml b/vnt-cli/Cargo.toml index b9495dd..e2239af 100644 --- a/vnt-cli/Cargo.toml +++ b/vnt-cli/Cargo.toml @@ -10,7 +10,7 @@ vnt = { path = "../vnt", package = "vnt", default-features = false, features = [ common = { path = "../common", default-features = false, features = ["integrated_tun"] } log = "0.4.17" anyhow = "1.0.82" - +console = "0.15.2" [target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies] sudo = "0.6.0" diff --git a/vnt-cli/src/main.rs b/vnt-cli/src/main.rs index edad9a6..e8152f5 100644 --- a/vnt-cli/src/main.rs +++ b/vnt-cli/src/main.rs @@ -1,4 +1,5 @@ use common::callback; +use console::style; use vnt::core::{Config, Vnt}; mod root_check; fn main() { @@ -16,7 +17,7 @@ fn main() { e, std::env::args().collect::>() ); - println!("{}", e); + println!("{}", style(format!("Error {:?}", e)).red()); return; } }; diff --git a/vnt/src/core/mod.rs b/vnt/src/core/mod.rs index ac2d54b..32cb07a 100644 --- a/vnt/src/core/mod.rs +++ b/vnt/src/core/mod.rs @@ -94,6 +94,14 @@ impl Config { allow_wire_guard: bool, local_ipv4: Option, ) -> anyhow::Result { + #[cfg(windows)] + if !tap { + if let Err(e) = tun::Device::check_tun_dll() { + log::warn!("校验平台dll {:?}", e); + // Err(e)?; + } + } + for x in stun_server.iter_mut() { if !x.contains(":") { x.push_str(":3478"); diff --git a/vnt/src/handle/callback.rs b/vnt/src/handle/callback.rs index 7d33f61..d0bf8a7 100644 --- a/vnt/src/handle/callback.rs +++ b/vnt/src/handle/callback.rs @@ -172,6 +172,7 @@ pub enum ErrorType { IpAlreadyExists, InvalidIp, LocalIpExists, + FailedToCrateDevice, Unknown, } @@ -184,6 +185,7 @@ impl Into for ErrorType { ErrorType::IpAlreadyExists => 4, ErrorType::InvalidIp => 5, ErrorType::LocalIpExists => 6, + ErrorType::FailedToCrateDevice => 101, ErrorType::Unknown => 255, } } diff --git a/vnt/src/handle/recv_data/server.rs b/vnt/src/handle/recv_data/server.rs index 5b9a732..b1bb37b 100644 --- a/vnt/src/handle/recv_data/server.rs +++ b/vnt/src/handle/recv_data/server.rs @@ -385,7 +385,7 @@ impl ServerPacketHandler { let device_fd = self.callback.generate_tun(device_config); if device_fd == 0 { self.callback.error(ErrorInfo::new_msg( - ErrorType::Unknown, + ErrorType::FailedToCrateDevice, "device_fd == 0".into(), )); } else { @@ -396,14 +396,14 @@ impl ServerPacketHandler { self.config_info.allow_wire_guard, ) { self.callback.error(ErrorInfo::new_msg( - ErrorType::Unknown, + ErrorType::FailedToCrateDevice, format!("{:?}", e), )); } } Err(e) => { self.callback.error(ErrorInfo::new_msg( - ErrorType::Unknown, + ErrorType::FailedToCrateDevice, format!("{:?}", e), )); } diff --git a/vnt/src/tun_tap_device/create_device.rs b/vnt/src/tun_tap_device/create_device.rs index 7485cde..652fd7e 100644 --- a/vnt/src/tun_tap_device/create_device.rs +++ b/vnt/src/tun_tap_device/create_device.rs @@ -15,7 +15,7 @@ pub fn create_device(config: DeviceConfig) -> Result, ErrorInfo> { Ok(device) => device, Err(e) => { return Err(ErrorInfo::new_msg( - ErrorType::Unknown, + ErrorType::FailedToCrateDevice, format!("create device {:?}", e), )); } diff --git a/vnt/tun/Cargo.toml b/vnt/tun/Cargo.toml index 08b8d16..0e56282 100644 --- a/vnt/tun/Cargo.toml +++ b/vnt/tun/Cargo.toml @@ -19,15 +19,16 @@ ioctl = { version = "0.8", package = "ioctl-sys" } [target.'cfg(target_os = "windows")'.dependencies] libloading = "0.8.0" widestring = "1.0.2" -winapi = {version = "0.3",features = [ +winapi = { version = "0.3", features = [ "errhandlingapi", + "libloaderapi", "combaseapi", "ioapiset", "winioctl", "setupapi", "synchapi", "netioapi", - "fileapi","handleapi","winerror","minwindef","ifdef","basetsd","winnt","winreg","winbase","minwinbase", + "fileapi", "handleapi", "winerror", "minwindef", "ifdef", "basetsd", "winnt", "winreg", "winbase", "minwinbase", "impl-default" -]} +] } diff --git a/vnt/tun/src/windows/check.rs b/vnt/tun/src/windows/check.rs new file mode 100644 index 0000000..2e34779 --- /dev/null +++ b/vnt/tun/src/windows/check.rs @@ -0,0 +1,167 @@ +use libloading::Library; +use std::ffi::{c_char, CStr, CString}; +use std::fs::File; +use std::io::{self, Read, Seek}; +use std::path::PathBuf; +use winapi::shared::minwindef::HINSTANCE; +use winapi::um::libloaderapi::{GetModuleFileNameA, GetModuleHandleA}; + +#[repr(C)] +#[derive(Debug)] +struct DosHeader { + e_magic: u16, + e_cblp: u16, + e_cp: u16, + e_crlc: u16, + e_cparhdr: u16, + e_minalloc: u16, + e_maxalloc: u16, + e_ss: u16, + e_sp: u16, + e_csum: u16, + e_ip: u16, + e_cs: u16, + e_lfarlc: u16, + e_ovno: u16, + e_res: [u16; 4], + e_oemid: u16, + e_oeminfo: u16, + e_res2: [u16; 10], + e_lfanew: i32, +} + +#[repr(C)] +#[derive(Debug)] +struct FileHeader { + machine: u16, + number_of_sections: u16, + time_date_stamp: u32, + pointer_to_symbol_table: u32, + number_of_symbols: u32, + size_of_optional_header: u16, + characteristics: u16, +} + +const IMAGE_FILE_MACHINE_I386: u16 = 0x014C; +const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664; +const IMAGE_FILE_MACHINE_ARM: u16 = 0x01C4; +const IMAGE_FILE_MACHINE_ARM64: u16 = 0xAA64; + +fn get_dll_path(dll_name: &str) -> Result { + unsafe { + // 使用libloading加载DLL + + // 转换DLL名称为C字符串 + let dll_name_c = + CString::new(dll_name).map_err(|e| format!("Failed to convert to CString: {}", e))?; + + // 获取DLL的模块句柄 + let h_instance: HINSTANCE = GetModuleHandleA(dll_name_c.as_ptr() as *const c_char); + + if h_instance.is_null() { + return Err("Failed to get module handle".to_string()); + } + + // 获取DLL文件路径 + let mut buffer: [c_char; 260] = [0; 260]; + let length = GetModuleFileNameA(h_instance, buffer.as_mut_ptr(), buffer.len() as u32); + + if length == 0 { + return Err("Failed to get module file name".to_string()); + } + + let path = CStr::from_ptr(buffer.as_ptr()); + let path_str = path + .to_str() + .map_err(|e| format!("Failed to convert to &str: {}", e))?; + Ok(PathBuf::from(path_str)) + } +} + +pub fn check_win_tun_dll() -> io::Result<()> { + let _lib = unsafe { + Library::new("wintun.dll").map_err(|_| { + io::Error::new( + io::ErrorKind::NotFound, + "wintun.dll not found,Please download https://www.wintun.net", + ) + }) + }; + match get_dll_path("wintun.dll") { + Ok(path) => match_platform(path), + Err(e) => { + // 能加载说明存在wintun,这里获取不到路径是代码的问题 + log::info!("{:?}", e); + Ok(()) + } + } +} + +fn match_platform(path: PathBuf) -> io::Result<()> { + let current_arch = if cfg!(target_arch = "x86") { + "x86" + } else if cfg!(target_arch = "x86_64") { + "AMD64" + } else if cfg!(target_arch = "arm") { + "ARM" + } else if cfg!(target_arch = "aarch64") { + "ARM64" + } else { + return Ok(()); + }; + + let mut file = File::open(&path)?; + + // 读取 DOS 头部 + let mut dos_header = [0u8; std::mem::size_of::()]; + file.read_exact(&mut dos_header)?; + let dos_header: DosHeader = unsafe { std::ptr::read(dos_header.as_ptr() as *const _) }; + + if dos_header.e_magic != 0x5A4D { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Not a valid PE file {:?}", path), + )); + } + + // 跳转到 PE 头部 + file.seek(io::SeekFrom::Start(dos_header.e_lfanew as u64))?; + + // 读取 PE 头部 + let mut pe_signature = [0u8; 4]; + file.read_exact(&mut pe_signature)?; + if &pe_signature != b"PE\0\0" { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Not a valid PE file {:?}", path), + )); + } + + // 读取文件头部 + let mut file_header = [0u8; std::mem::size_of::()]; + file.read_exact(&mut file_header)?; + let file_header: FileHeader = unsafe { std::ptr::read(file_header.as_ptr() as *const _) }; + let dll_arch = match file_header.machine { + IMAGE_FILE_MACHINE_I386 => "x86", + IMAGE_FILE_MACHINE_AMD64 => "AMD64", + IMAGE_FILE_MACHINE_ARM => "ARM", + IMAGE_FILE_MACHINE_ARM64 => "ARM64", + _ => { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Unknown machine type: {}", file_header.machine), + )) + } + }; + + if dll_arch != current_arch { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "wintun.dll architecture ({}) does not match the current platform architecture ({}).", + dll_arch, current_arch + ), + )); + } + Ok(()) +} diff --git a/vnt/tun/src/windows/device.rs b/vnt/tun/src/windows/device.rs index a557125..d0f81e8 100644 --- a/vnt/tun/src/windows/device.rs +++ b/vnt/tun/src/windows/device.rs @@ -16,6 +16,9 @@ impl Device { Ok(Device::Tun(tun::Device::new(name)?)) } } + pub fn check_tun_dll() -> io::Result<()> { + crate::windows::check::check_win_tun_dll() + } } impl IFace for Device { diff --git a/vnt/tun/src/windows/mod.rs b/vnt/tun/src/windows/mod.rs index 238b073..cc3a17a 100644 --- a/vnt/tun/src/windows/mod.rs +++ b/vnt/tun/src/windows/mod.rs @@ -3,6 +3,7 @@ use std::os::windows::process::CommandExt; use winapi::shared::minwindef::DWORD; use winapi::um::winbase::CREATE_NO_WINDOW; +mod check; mod device; mod ffi; mod netsh;