mirror of https://github.com/ospab/ostp.git
279 lines
9.6 KiB
Rust
279 lines
9.6 KiB
Rust
#[cfg(target_os = "windows")]
|
|
use std::process::Command;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
use std::os::windows::process::CommandExt;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
#[link(name = "wininet")]
|
|
extern "system" {
|
|
fn InternetSetOptionW(
|
|
hInternet: *mut std::ffi::c_void,
|
|
dwOption: u32,
|
|
lpBuffer: *mut std::ffi::c_void,
|
|
dwBufferLength: u32,
|
|
) -> i32;
|
|
}
|
|
#[cfg(target_os = "windows")]
|
|
const INTERNET_OPTION_SETTINGS_CHANGED: u32 = 39;
|
|
#[cfg(target_os = "windows")]
|
|
const INTERNET_OPTION_REFRESH: u32 = 37;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
pub fn enable_windows_proxy(proxy_addr: &str) {
|
|
tracing::info!("Enabling Windows system proxy: {}", proxy_addr);
|
|
|
|
let result = Command::new("reg")
|
|
.creation_flags(CREATE_NO_WINDOW)
|
|
.args([
|
|
"add",
|
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|
"/v", "ProxyEnable",
|
|
"/t", "REG_DWORD",
|
|
"/d", "1",
|
|
"/f",
|
|
])
|
|
.output();
|
|
match result {
|
|
Ok(out) if !out.status.success() => {
|
|
tracing::error!("Failed to set ProxyEnable: {}", String::from_utf8_lossy(&out.stderr));
|
|
}
|
|
Err(e) => tracing::error!("Failed to execute reg.exe (ProxyEnable): {}", e),
|
|
_ => {}
|
|
}
|
|
|
|
let result = Command::new("reg")
|
|
.creation_flags(CREATE_NO_WINDOW)
|
|
.args([
|
|
"add",
|
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|
"/v", "ProxyServer",
|
|
"/t", "REG_SZ",
|
|
"/d", proxy_addr,
|
|
"/f",
|
|
])
|
|
.output();
|
|
match result {
|
|
Ok(out) if !out.status.success() => {
|
|
tracing::error!("Failed to set ProxyServer: {}", String::from_utf8_lossy(&out.stderr));
|
|
}
|
|
Err(e) => tracing::error!("Failed to execute reg.exe (ProxyServer): {}", e),
|
|
_ => {}
|
|
}
|
|
|
|
// Set initial bypass list (will be expanded by update_proxy_bypass_list)
|
|
update_proxy_bypass_list_windows(&[], &[]);
|
|
|
|
refresh_wininet();
|
|
tracing::info!("System proxy enabled successfully");
|
|
}
|
|
|
|
/// Update the Windows ProxyOverride registry value to include user-configured
|
|
/// excluded domains and IPs. This makes excluded hosts bypass the OSTP proxy
|
|
/// entirely at the OS level — the most reliable split-tunneling mechanism.
|
|
///
|
|
/// For each domain `d`, adds both `d` and `*.d` so both the root and all
|
|
/// subdomains bypass the proxy.
|
|
/// For IPs, adds them verbatim (Windows supports exact IPs and wildcards like
|
|
/// `192.168.*`).
|
|
#[cfg(target_os = "windows")]
|
|
pub fn update_proxy_bypass_list(domains: &[String], ips: &[String]) {
|
|
update_proxy_bypass_list_windows(domains, ips);
|
|
refresh_wininet();
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
pub fn update_proxy_bypass_list(_domains: &[String], _ips: &[String]) {
|
|
// Linux/macOS: no-op (gnome/kde proxy bypass list update not implemented)
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn update_proxy_bypass_list_windows(domains: &[String], ips: &[String]) {
|
|
// Base list: always bypass local addresses
|
|
let mut parts: Vec<String> = vec![
|
|
"localhost".into(),
|
|
"127.*".into(),
|
|
"10.*".into(),
|
|
"172.16.*".into(),
|
|
"172.17.*".into(),
|
|
"172.18.*".into(),
|
|
"172.19.*".into(),
|
|
"172.20.*".into(),
|
|
"172.21.*".into(),
|
|
"172.22.*".into(),
|
|
"172.23.*".into(),
|
|
"172.24.*".into(),
|
|
"172.25.*".into(),
|
|
"172.26.*".into(),
|
|
"172.27.*".into(),
|
|
"172.28.*".into(),
|
|
"172.29.*".into(),
|
|
"172.30.*".into(),
|
|
"172.31.*".into(),
|
|
"192.168.*".into(),
|
|
"<local>".into(),
|
|
];
|
|
|
|
// Add excluded domains: both exact and wildcard subdomain form
|
|
for d in domains {
|
|
let d = d.trim().trim_start_matches('.').to_lowercase();
|
|
if d.is_empty() { continue; }
|
|
parts.push(d.clone());
|
|
parts.push(format!("*.{}", d));
|
|
}
|
|
|
|
// Add excluded IPs verbatim
|
|
for ip in ips {
|
|
let ip = ip.trim();
|
|
if ip.is_empty() { continue; }
|
|
// Strip CIDR suffix if present — Windows ProxyOverride doesn't support CIDR
|
|
let host = ip.split('/').next().unwrap_or(ip);
|
|
parts.push(host.to_string());
|
|
}
|
|
|
|
let override_value = parts.join(";");
|
|
tracing::info!("Updating ProxyOverride: {}", override_value);
|
|
|
|
let _ = Command::new("reg")
|
|
.creation_flags(CREATE_NO_WINDOW)
|
|
.args([
|
|
"add",
|
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|
"/v", "ProxyOverride",
|
|
"/t", "REG_SZ",
|
|
"/d", &override_value,
|
|
"/f",
|
|
])
|
|
.output();
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
pub fn disable_system_proxy() {
|
|
tracing::info!("Disabling Windows system proxy");
|
|
let _ = Command::new("reg")
|
|
.creation_flags(CREATE_NO_WINDOW)
|
|
.args([
|
|
"add",
|
|
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|
"/v", "ProxyEnable",
|
|
"/t", "REG_DWORD",
|
|
"/d", "0",
|
|
"/f",
|
|
])
|
|
.output();
|
|
|
|
refresh_wininet();
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn refresh_wininet() {
|
|
unsafe {
|
|
InternetSetOptionW(
|
|
std::ptr::null_mut(),
|
|
INTERNET_OPTION_SETTINGS_CHANGED,
|
|
std::ptr::null_mut(),
|
|
0,
|
|
);
|
|
InternetSetOptionW(
|
|
std::ptr::null_mut(),
|
|
INTERNET_OPTION_REFRESH,
|
|
std::ptr::null_mut(),
|
|
0,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
pub fn enable_system_proxy(proxy_addr: &str) {
|
|
let parts: Vec<&str> = proxy_addr.split(':').collect();
|
|
let host = parts.get(0).unwrap_or(&"127.0.0.1");
|
|
let port = parts.get(1).unwrap_or(&"1088");
|
|
|
|
let is_gui = std::env::var("DISPLAY").is_ok() || std::env::var("WAYLAND_DISPLAY").is_ok();
|
|
|
|
if is_gui {
|
|
tracing::info!("Enabling Linux system proxy (GNOME/KDE): {}", proxy_addr);
|
|
|
|
// Try GNOME gsettings
|
|
let gnome_res = std::process::Command::new("gsettings")
|
|
.args(["set", "org.gnome.system.proxy", "mode", "manual"])
|
|
.output();
|
|
|
|
if let Ok(out) = gnome_res {
|
|
if out.status.success() {
|
|
let _ = std::process::Command::new("gsettings").args(["set", "org.gnome.system.proxy.socks", "host", host]).output();
|
|
let _ = std::process::Command::new("gsettings").args(["set", "org.gnome.system.proxy.socks", "port", port]).output();
|
|
let _ = std::process::Command::new("gsettings").args(["set", "org.gnome.system.proxy", "ignore-hosts", "['localhost', '127.0.0.0/8', '10.0.0.0/8', '192.168.0.0/16']"]).output();
|
|
tracing::info!("GNOME system proxy enabled.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Try KDE kwriteconfig5/6
|
|
for cmd in ["kwriteconfig5", "kwriteconfig6"] {
|
|
let kde_res = std::process::Command::new(cmd)
|
|
.args(["--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"])
|
|
.output();
|
|
|
|
if let Ok(out) = kde_res {
|
|
if out.status.success() {
|
|
let socks_val = format!("socks://{}:{}", host, port);
|
|
let _ = std::process::Command::new(cmd).args(["--file", "kioslaverc", "--group", "Proxy Settings", "--key", "socksProxy", &socks_val]).output();
|
|
let _ = std::process::Command::new("dbus-send").args(["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]).output();
|
|
tracing::info!("KDE system proxy enabled.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Headless fallback
|
|
println!("\n===================================================================");
|
|
println!("OSTP Local Proxy is running at socks5://{}", proxy_addr);
|
|
println!("Since you are in a headless/terminal environment, OSTP cannot automatically");
|
|
println!("configure your system proxy. To route traffic from this terminal, run:");
|
|
println!("\n eval $(ostp --proxy-env)\n");
|
|
println!("Or configure your application (e.g. curl -x socks5://{})", proxy_addr);
|
|
println!("===================================================================\n");
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
pub fn disable_system_proxy() {
|
|
let is_gui = std::env::var("DISPLAY").is_ok() || std::env::var("WAYLAND_DISPLAY").is_ok();
|
|
if is_gui {
|
|
tracing::info!("Disabling Linux system proxy...");
|
|
let _ = std::process::Command::new("gsettings").args(["set", "org.gnome.system.proxy", "mode", "none"]).output();
|
|
let _ = std::process::Command::new("kwriteconfig5").args(["--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]).output();
|
|
let _ = std::process::Command::new("kwriteconfig6").args(["--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]).output();
|
|
let _ = std::process::Command::new("dbus-send").args(["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]).output();
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
pub fn enable_system_proxy(proxy_addr: &str) {
|
|
enable_windows_proxy(proxy_addr);
|
|
}
|
|
|
|
|
|
pub struct SystemProxyGuard {
|
|
active: bool,
|
|
}
|
|
|
|
impl SystemProxyGuard {
|
|
pub fn enable(proxy_addr: &str) -> Self {
|
|
enable_system_proxy(proxy_addr);
|
|
Self { active: true }
|
|
}
|
|
}
|
|
|
|
impl Drop for SystemProxyGuard {
|
|
fn drop(&mut self) {
|
|
if self.active {
|
|
disable_system_proxy();
|
|
}
|
|
}
|
|
}
|