From 0951afa4993d8e3bb9c5f7acc1714460b2d85dbc Mon Sep 17 00:00:00 2001 From: ospab Date: Sun, 31 May 2026 21:01:28 +0300 Subject: [PATCH] feat(linux): implement SystemProxyGuard with GNOME/KDE support and headless proxy prompt --- ostp-client/src/runner.rs | 18 +++----- ostp-client/src/sysproxy.rs | 84 +++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/ostp-client/src/runner.rs b/ostp-client/src/runner.rs index fc85517..b1522f6 100644 --- a/ostp-client/src/runner.rs +++ b/ostp-client/src/runner.rs @@ -188,21 +188,15 @@ pub async fn run_client_core( if config.mode == "proxy" { println!("\n[ostp] ==========================================================================="); println!("[ostp] Proxy mode initialized on {}", config.local_proxy.bind_addr); - println!("[ostp] To use this proxy in your current terminal session, run:"); - println!("[ostp] export http_proxy=\"http://{}\"", config.local_proxy.bind_addr); - println!("[ostp] export https_proxy=\"http://{}\"", config.local_proxy.bind_addr); - println!("[ostp] export all_proxy=\"socks5://{}\"", config.local_proxy.bind_addr); - println!("[ostp] "); - println!("[ostp] For GNOME desktop system-wide proxy, you can use:"); - println!("[ostp] gsettings set org.gnome.system.proxy mode 'manual'"); - let mut parts = config.local_proxy.bind_addr.split(':'); - let host = parts.next().unwrap_or("127.0.0.1"); - let port = parts.next().unwrap_or("1088"); - println!("[ostp] gsettings set org.gnome.system.proxy.http host '{}'", host); - println!("[ostp] gsettings set org.gnome.system.proxy.http port {}", port); println!("[ostp] ===========================================================================\n"); } + let _sysproxy_guard = if config.mode == "proxy" { + Some(crate::sysproxy::SystemProxyGuard::enable(&config.local_proxy.bind_addr)) + } else { + None + }; + if config.mode == "tun" && !config.exclusions.processes.is_empty() { println!("[ostp] Process exclusions are not supported in TUN mode"); } diff --git a/ostp-client/src/sysproxy.rs b/ostp-client/src/sysproxy.rs index 0cc5a01..dd814ef 100644 --- a/ostp-client/src/sysproxy.rs +++ b/ostp-client/src/sysproxy.rs @@ -118,26 +118,96 @@ fn refresh_wininet() { } #[cfg(not(target_os = "windows"))] -pub fn enable_windows_proxy(_proxy_addr: &str) {} +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_windows_proxy() {} +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(); + } +} -pub struct WindowsProxyGuard { +#[cfg(target_os = "windows")] +pub fn enable_system_proxy(proxy_addr: &str) { + enable_windows_proxy(proxy_addr); +} + +#[cfg(target_os = "windows")] +pub fn disable_system_proxy() { + disable_windows_proxy(); +} + +pub struct SystemProxyGuard { active: bool, } -impl WindowsProxyGuard { +impl SystemProxyGuard { pub fn enable(proxy_addr: &str) -> Self { - enable_windows_proxy(proxy_addr); + enable_system_proxy(proxy_addr); Self { active: true } } } -impl Drop for WindowsProxyGuard { +impl Drop for SystemProxyGuard { fn drop(&mut self) { if self.active { - disable_windows_proxy(); + disable_system_proxy(); } } }