Skip to content

Commit

Permalink
feat: Support PAC for windows (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pylogmon authored May 19, 2024
1 parent 1402eba commit 1c290ef
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 10 deletions.
16 changes: 16 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}`")]
Expand Down Expand Up @@ -52,3 +58,13 @@ impl Sysproxy {
))
}
}

impl Autoproxy {
pub fn is_support() -> bool {
cfg!(any(
target_os = "linux",
target_os = "macos",
target_os = "windows",
))
}
}
63 changes: 57 additions & 6 deletions src/windows.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -40,6 +40,39 @@ fn unset_proxy() -> Result<()> {
res
}

fn set_auto_proxy(server: String) -> Result<()> {
let mut p_opts = ManuallyDrop::new(Vec::<INTERNET_PER_CONN_OPTIONW>::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::<Vec<u16>>());
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::<INTERNET_PER_CONN_OPTION_LISTW>() 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::<INTERNET_PER_CONN_OPTIONW>::with_capacity(3));
Expand Down Expand Up @@ -111,7 +144,6 @@ impl Sysproxy {
pub fn get_system_proxy() -> Result<Sysproxy> {
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::<u32, _>("ProxyEnable").unwrap_or(0u32) == 1u32;
let server = cur_var
.get_value::<String, _>("ProxyServer")
Expand Down Expand Up @@ -145,3 +177,22 @@ impl Sysproxy {
}
}
}

impl Autoproxy {
pub fn get_auto_proxy() -> Result<Autoproxy> {
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::<String, _>("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(),
}
}
}
38 changes: 34 additions & 4 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -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(),
Expand All @@ -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);
}
}

0 comments on commit 1c290ef

Please sign in to comment.