From 1c290ef2b7d5bd4fb5bd5e1beb68d4f8a8249e71 Mon Sep 17 00:00:00 2001 From: Pylogmon Date: Sun, 19 May 2024 21:07:03 +0800 Subject: [PATCH] feat: Support PAC for windows (#9) --- src/lib.rs | 16 +++++++++++++ src/windows.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++----- tests/test.rs | 38 ++++++++++++++++++++++++++---- 3 files changed, 107 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a4dbbae..8a7e591 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,12 @@ pub struct Sysproxy { pub bypass: String, } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Autoproxy { + pub enable: bool, + pub url: String, +} + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("failed to parse string `{0}`")] @@ -52,3 +58,13 @@ impl Sysproxy { )) } } + +impl Autoproxy { + pub fn is_support() -> bool { + cfg!(any( + target_os = "linux", + target_os = "macos", + target_os = "windows", + )) + } +} diff --git a/src/windows.rs b/src/windows.rs index ad4c733..734ae5f 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,13 +1,13 @@ -use crate::{Error, Result, Sysproxy}; +use crate::{Autoproxy, Error, Result, Sysproxy}; use std::ffi::c_void; use std::{mem::size_of, mem::ManuallyDrop, net::SocketAddr, str::FromStr}; use windows::core::PWSTR; use windows::Win32::Networking::WinInet::{ InternetSetOptionW, INTERNET_OPTION_PER_CONNECTION_OPTION, - INTERNET_OPTION_PROXY_SETTINGS_CHANGED, INTERNET_OPTION_REFRESH, INTERNET_PER_CONN_FLAGS, - INTERNET_PER_CONN_OPTIONW, INTERNET_PER_CONN_OPTIONW_0, INTERNET_PER_CONN_OPTION_LISTW, - INTERNET_PER_CONN_PROXY_BYPASS, INTERNET_PER_CONN_PROXY_SERVER, PROXY_TYPE_DIRECT, - PROXY_TYPE_PROXY, + INTERNET_OPTION_PROXY_SETTINGS_CHANGED, INTERNET_OPTION_REFRESH, + INTERNET_PER_CONN_AUTOCONFIG_URL, INTERNET_PER_CONN_FLAGS, INTERNET_PER_CONN_OPTIONW, + INTERNET_PER_CONN_OPTIONW_0, INTERNET_PER_CONN_OPTION_LISTW, INTERNET_PER_CONN_PROXY_BYPASS, + INTERNET_PER_CONN_PROXY_SERVER, PROXY_TYPE_AUTO_PROXY_URL, PROXY_TYPE_DIRECT, PROXY_TYPE_PROXY, }; use winreg::{enums, RegKey}; @@ -40,6 +40,39 @@ fn unset_proxy() -> Result<()> { res } +fn set_auto_proxy(server: String) -> Result<()> { + let mut p_opts = ManuallyDrop::new(Vec::::with_capacity(2)); + p_opts.push(INTERNET_PER_CONN_OPTIONW { + dwOption: INTERNET_PER_CONN_FLAGS, + Value: INTERNET_PER_CONN_OPTIONW_0 { + dwValue: PROXY_TYPE_AUTO_PROXY_URL | PROXY_TYPE_DIRECT, + }, + }); + + let mut s = ManuallyDrop::new(server.encode_utf16().chain([0u16]).collect::>()); + p_opts.push(INTERNET_PER_CONN_OPTIONW { + dwOption: INTERNET_PER_CONN_AUTOCONFIG_URL, + Value: INTERNET_PER_CONN_OPTIONW_0 { + pszValue: PWSTR::from_raw(s.as_ptr() as *mut u16), + }, + }); + + let opts = INTERNET_PER_CONN_OPTION_LISTW { + dwSize: size_of::() as u32, + dwOptionCount: 2, + dwOptionError: 0, + pOptions: p_opts.as_mut_ptr() as *mut INTERNET_PER_CONN_OPTIONW, + pszConnection: PWSTR::null(), + }; + + let res = apply(&opts); + unsafe { + ManuallyDrop::drop(&mut s); + ManuallyDrop::drop(&mut p_opts); + } + res +} + /// set global proxy fn set_global_proxy(server: String, bypass: String) -> Result<()> { let mut p_opts = ManuallyDrop::new(Vec::::with_capacity(3)); @@ -111,7 +144,6 @@ impl Sysproxy { pub fn get_system_proxy() -> Result { let hkcu = RegKey::predef(enums::HKEY_CURRENT_USER); let cur_var = hkcu.open_subkey_with_flags(SUB_KEY, enums::KEY_READ)?; - let enable = cur_var.get_value::("ProxyEnable").unwrap_or(0u32) == 1u32; let server = cur_var .get_value::("ProxyServer") @@ -145,3 +177,22 @@ impl Sysproxy { } } } + +impl Autoproxy { + pub fn get_auto_proxy() -> Result { + let hkcu = RegKey::predef(enums::HKEY_CURRENT_USER); + let cur_var = hkcu.open_subkey_with_flags(SUB_KEY, enums::KEY_READ)?; + let url = cur_var.get_value::("AutoConfigURL"); + let enable = url.is_ok(); + let url = url.unwrap_or("".into()); + + Ok(Autoproxy { enable, url }) + } + + pub fn set_auto_proxy(&self) -> Result<()> { + match self.enable { + true => set_auto_proxy(self.url.clone()), + false => unset_proxy(), + } + } +} diff --git a/tests/test.rs b/tests/test.rs index 3d0b9e7..b0e960b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,19 +1,29 @@ #[cfg(test)] mod tests { - use sysproxy::Sysproxy; + use sysproxy::{Autoproxy, Sysproxy}; #[test] - fn test_support() { + fn test_sys_support() { assert!(Sysproxy::is_support()); } #[test] - fn test_get() { + fn test_auto_support() { + assert!(Autoproxy::is_support()); + } + + #[test] + fn test_sys_get() { Sysproxy::get_system_proxy().unwrap(); } #[test] - fn test_enable() { + fn test_auto_get() { + Autoproxy::get_auto_proxy().unwrap(); + } + + #[test] + fn test_system_enable() { let mut sysproxy = Sysproxy { enable: true, host: "127.0.0.1".into(), @@ -35,4 +45,24 @@ mod tests { let current = Sysproxy::get_system_proxy().unwrap(); assert_eq!(current, sysproxy); } + + #[test] + fn test_auto_enable() { + let mut autoproxy = Autoproxy { + enable: true, + url: "http://127.0.0.1:1234/".into(), + }; + autoproxy.set_auto_proxy().unwrap(); + + let cur_proxy = Autoproxy::get_auto_proxy().unwrap(); + + assert_eq!(cur_proxy, autoproxy); + + autoproxy.enable = false; + autoproxy.url = "".into(); + autoproxy.set_auto_proxy().unwrap(); + + let current = Autoproxy::get_auto_proxy().unwrap(); + assert_eq!(current, autoproxy); + } }