diff --git a/Cargo.toml b/Cargo.toml index 168aae6..9fddbed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ windows = { version = "0.62.2", features = [ "Win32_Networking_WinInet", "Win32_NetworkManagement_Rras", "Win32_Foundation", + "Win32_System_Memory" ] } [dev-dependencies] diff --git a/src/windows.rs b/src/windows.rs index b9c97a2..7513a9c 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,21 +1,35 @@ use crate::{Autoproxy, Result, Sysproxy}; use std::{ffi::c_void, mem::ManuallyDrop, mem::size_of}; use url::Url; -use windows::Win32::Networking::WinInet::{ - INTERNET_OPTION_PER_CONNECTION_OPTION, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, - INTERNET_OPTION_REFRESH, INTERNET_PER_CONN_AUTOCONFIG_URL, INTERNET_PER_CONN_FLAGS, - INTERNET_PER_CONN_OPTION_LISTW, INTERNET_PER_CONN_OPTIONW, INTERNET_PER_CONN_OPTIONW_0, - INTERNET_PER_CONN_PROXY_BYPASS, INTERNET_PER_CONN_PROXY_SERVER, InternetSetOptionW, - PROXY_TYPE_AUTO_DETECT, PROXY_TYPE_AUTO_PROXY_URL, PROXY_TYPE_DIRECT, PROXY_TYPE_PROXY, +use windows::{ + Win32::{ + NetworkManagement::Rras::{ERROR_BUFFER_TOO_SMALL, RASENTRYNAMEW, RasEnumEntriesW}, + Networking::WinInet::{ + INTERNET_OPTION_PER_CONNECTION_OPTION, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, + INTERNET_OPTION_REFRESH, INTERNET_PER_CONN_AUTOCONFIG_URL, INTERNET_PER_CONN_FLAGS, + INTERNET_PER_CONN_OPTION_LISTW, INTERNET_PER_CONN_OPTIONW, INTERNET_PER_CONN_OPTIONW_0, + INTERNET_PER_CONN_PROXY_BYPASS, INTERNET_PER_CONN_PROXY_SERVER, InternetSetOptionW, + PROXY_TYPE_AUTO_DETECT, PROXY_TYPE_AUTO_PROXY_URL, PROXY_TYPE_DIRECT, PROXY_TYPE_PROXY, + }, + System::Memory::{GetProcessHeap, HEAP_NONE, HEAP_ZERO_MEMORY, HeapAlloc, HeapFree}, + }, + core::{PCWSTR, PWSTR}, }; -use windows::core::PWSTR; use winreg::{RegKey, enums}; pub use windows::core::Error as Win32Error; const SUB_KEY: &str = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; +fn encode_wide>(string: S) -> Vec { + std::os::windows::prelude::OsStrExt::encode_wide(string.as_ref()) + .chain(std::iter::once(0)) + .collect::>() +} + /// unset proxy +/// +/// **对于包含中文字符的拨号连接或 VPN 连接,可能无法正确设置其代理,建议使用全英文重命名该连接名称** #[inline] fn unset_proxy() -> Result<()> { let mut p_opts = ManuallyDrop::new(Vec::::with_capacity(1)); @@ -27,20 +41,34 @@ fn unset_proxy() -> Result<()> { v }, }); - let opts = INTERNET_PER_CONN_OPTION_LISTW { + let mut opts = INTERNET_PER_CONN_OPTION_LISTW { dwSize: size_of::() as u32, dwOptionCount: 1, dwOptionError: 0, pOptions: p_opts.as_mut_ptr(), pszConnection: PWSTR::null(), }; - let res = apply(&opts); + + // 局域网 LAN 代理设置 + apply(&opts)?; + // 拨号连接/VPN 代理设置 + let ras_conns = get_ras_connections()?; + for ras_conn in ras_conns.iter() { + opts.pszConnection = PWSTR::from_raw(encode_wide(ras_conn).as_ptr() as *mut u16); + apply(&opts)?; + println!("unset RAS[{ras_conn}] proxy success"); + } + unsafe { ManuallyDrop::drop(&mut p_opts); } - res + + Ok(()) } +/// set auto proxy +/// +/// **对于包含中文字符的拨号连接或 VPN 连接,可能无法正确设置其代理,建议使用全英文重命名该连接名称** #[inline] fn set_auto_proxy(server: String) -> Result<()> { let mut p_opts = ManuallyDrop::new(Vec::::with_capacity(2)); @@ -51,7 +79,7 @@ fn set_auto_proxy(server: String) -> Result<()> { }, }); - let mut s = ManuallyDrop::new(server.encode_utf16().chain([0u16]).collect::>()); + let mut s = ManuallyDrop::new(encode_wide(&server)); p_opts.push(INTERNET_PER_CONN_OPTIONW { dwOption: INTERNET_PER_CONN_AUTOCONFIG_URL, Value: INTERNET_PER_CONN_OPTIONW_0 { @@ -59,7 +87,7 @@ fn set_auto_proxy(server: String) -> Result<()> { }, }); - let opts = INTERNET_PER_CONN_OPTION_LISTW { + let mut opts = INTERNET_PER_CONN_OPTION_LISTW { dwSize: size_of::() as u32, dwOptionCount: 2, dwOptionError: 0, @@ -67,15 +95,27 @@ fn set_auto_proxy(server: String) -> Result<()> { pszConnection: PWSTR::null(), }; - let res = apply(&opts); + // 局域网 LAN 代理设置 + apply(&opts)?; + // 拨号连接/VPN 代理设置 + let ras_conns = get_ras_connections()?; + for ras_conn in ras_conns.iter() { + opts.pszConnection = PWSTR::from_raw(encode_wide(ras_conn).as_ptr() as *mut u16); + apply(&opts)?; + println!("set RAS[{ras_conn}] auto proxy success"); + } + unsafe { ManuallyDrop::drop(&mut s); ManuallyDrop::drop(&mut p_opts); } - res + + Ok(()) } /// set global proxy +/// +/// **对于包含中文字符的拨号连接或 VPN 连接,可能无法正确设置其代理,建议使用全英文重命名该连接名称** #[inline] fn set_global_proxy(server: String, bypass: String) -> Result<()> { let mut p_opts = ManuallyDrop::new(Vec::::with_capacity(3)); @@ -86,7 +126,7 @@ fn set_global_proxy(server: String, bypass: String) -> Result<()> { }, }); - let mut s = ManuallyDrop::new(server.encode_utf16().chain([0u16]).collect::>()); + let mut s = ManuallyDrop::new(encode_wide(&server)); p_opts.push(INTERNET_PER_CONN_OPTIONW { dwOption: INTERNET_PER_CONN_PROXY_SERVER, Value: INTERNET_PER_CONN_OPTIONW_0 { @@ -94,13 +134,7 @@ fn set_global_proxy(server: String, bypass: String) -> Result<()> { }, }); - let mut b = ManuallyDrop::new( - bypass - .clone() - .encode_utf16() - .chain([0u16]) - .collect::>(), - ); + let mut b = ManuallyDrop::new(encode_wide(&bypass)); p_opts.push(INTERNET_PER_CONN_OPTIONW { dwOption: INTERNET_PER_CONN_PROXY_BYPASS, Value: INTERNET_PER_CONN_OPTIONW_0 { @@ -108,21 +142,30 @@ fn set_global_proxy(server: String, bypass: String) -> Result<()> { }, }); - let opts = INTERNET_PER_CONN_OPTION_LISTW { + let mut opts = INTERNET_PER_CONN_OPTION_LISTW { dwSize: size_of::() as u32, dwOptionCount: 3, dwOptionError: 0, pOptions: p_opts.as_mut_ptr(), pszConnection: PWSTR::null(), }; + // 局域网 LAN 代理设置 + apply(&opts)?; + // 拨号连接/VPN 代理设置 + let ras_conns = get_ras_connections()?; + for ras_conn in ras_conns.iter() { + opts.pszConnection = PWSTR::from_raw(encode_wide(ras_conn).as_ptr() as *mut u16); + apply(&opts)?; + println!("set RAS[{ras_conn}] global proxy success"); + } - let res = apply(&opts); unsafe { ManuallyDrop::drop(&mut s); ManuallyDrop::drop(&mut b); ManuallyDrop::drop(&mut p_opts); } - res + + Ok(()) } #[inline] @@ -242,3 +285,75 @@ fn parse_proxy_address(address: &str, host: &mut String, port: &mut u16) { *host = address.to_string(); *port = 80; } + +/// refer: https://learn.microsoft.com/zh-cn/windows/win32/api/ras/nf-ras-rasenumentriesw +/// +/// 获取所有远程访问服务 (包含拨号连接和 VPN 连接) +fn get_ras_connections() -> Result> { + println!("start get RAS connections..."); + let mut connections = Vec::new(); + + unsafe { + let mut buffer_size = 0u32; + let mut entry_count = 0u32; + + // 第一次调用获取所需缓冲区大小 + let result_code = RasEnumEntriesW( + PCWSTR::null(), + PCWSTR::null(), + None, + &mut buffer_size, + &mut entry_count, + ); + + println!("get allocate buffer size result code: {result_code}"); + if result_code == ERROR_BUFFER_TOO_SMALL { + // Allocate the memory needed for the array of RAS entry names. + let buffer_ptr = HeapAlloc(GetProcessHeap()?, HEAP_ZERO_MEMORY, buffer_size as usize); + if buffer_ptr.is_null() { + log::error!("HeapAlloc failed!"); + return Ok(connections); + } + let lp_ras_entry_name = buffer_ptr as *mut RASENTRYNAMEW; + // The first RASENTRYNAME structure in the array must contain the structure size + (*lp_ras_entry_name).dwSize = std::mem::size_of::() as u32; + + // 获取所有 RAS 列表 + let result_code = RasEnumEntriesW( + PCWSTR::null(), + PCWSTR::null(), + Some(lp_ras_entry_name), + &mut buffer_size, + &mut entry_count, + ); + // 如果函数成功,则返回值 ERROR_SUCCESS, 但是该 API 返回 u32, 参照对比 ERROR_SUCCESS 后,该值应该为 0 + println!("get RAS entries result code: {result_code}"); + if result_code == 0 && entry_count > 0 { + for i in 0..entry_count as isize { + let entry = &*lp_ras_entry_name.offset(i); + let name_arr = entry.szEntryName; + // 去除宽字符多余的 0,以便更好的打印 RAS 名称 + let len = name_arr.iter().position(|&x| x == 0).unwrap_or(0); + let name = String::from_utf16_lossy(&name_arr[..len]); + connections.push(name); + } + println!( + "找到 {} 个拨号连接/VPN, {:?}", + connections.len(), + connections + ); + } + // Deallocate memory for the connection buffer + HeapFree(GetProcessHeap()?, HEAP_NONE, Some(buffer_ptr))?; + return Ok(connections); + } + + if entry_count >= 1 { + println!("The operation failed to acquire the buffer size"); + } else { + println!("There were no RAS entry names found"); + } + } + + Ok(connections) +} diff --git a/tests/test.rs b/tests/test.rs index 0909c88..1abac94 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -30,7 +30,7 @@ mod tests { let mut sysproxy = Sysproxy { enable: true, host: "127.0.0.1".into(), - port: 9090, + port: 7897, #[cfg(target_os = "windows")] bypass: "localhost;127.*".into(), #[cfg(not(target_os = "windows"))] @@ -54,7 +54,7 @@ mod tests { fn test_auto_enable() { let mut autoproxy = Autoproxy { enable: true, - url: "http://127.0.0.1:1234/".into(), + url: "http://127.0.0.1:33331/commands/pac".into(), }; autoproxy.set_auto_proxy().unwrap();