Skip to content

Commit 5a9a3e7

Browse files
committed
System proxy settings enabled
1 parent 3dad3bf commit 5a9a3e7

9 files changed

Lines changed: 142 additions & 26 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ screen-shot = { version = "0.0.8" }
4040
serde = { version = "1.0.229", features = ["derive"] }
4141
serde_json = "1.0.150"
4242
strum = { version = "0.28.0", features = ["derive"] }
43+
systemproxy = { version = "0.3.1", default-features = false, features = [
44+
"iptools",
45+
] }
4346
tokio = { version = "1.53.0", features = ["full"] }
4447
tun2proxy = { version = "0.8.2", default-features = false }
4548
url = { version = "2.5.8", default-features = false, features = [

assets/overtls.png

14.8 KB
Loading

assets/proxy.png

5.56 KB
Loading

assets/settings.png

9.34 KB
Loading

assets/tun2proxy.png

21 KB
Loading

src/core.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,82 @@ pub fn get_running_overtls_node() -> Option<overtls::Config> {
168168
OVERTLS_RUNNING_NODE.lock().unwrap().clone()
169169
}
170170

171+
pub fn disable_system_proxy_if_overtls_stopped() {
172+
if is_overtls_running() {
173+
return;
174+
}
175+
176+
let Ok(current) = systemproxy::SystemProxy::get_system_proxy() else {
177+
return;
178+
};
179+
if !current.enable {
180+
return;
181+
}
182+
183+
let disabled = systemproxy::SystemProxy { enable: false, ..current };
184+
if let Err(e) = disabled.set_system_proxy() {
185+
log::error!("Failed to disable the system proxy after OverTLS stopped: {e}");
186+
} else {
187+
log::info!("System proxy disabled because OverTLS is not running.");
188+
}
189+
}
190+
191+
pub fn toggle_system_proxy(parent: &dyn WxWidget) {
192+
if !is_overtls_running() {
193+
disable_system_proxy_if_overtls_stopped();
194+
show_proxy_error(parent, "Start OverTLS before using the system proxy.".to_string());
195+
return;
196+
}
197+
198+
let current = match systemproxy::SystemProxy::get_system_proxy() {
199+
Ok(proxy) => proxy,
200+
Err(e) => {
201+
show_proxy_error(parent, format!("Failed to read the system proxy settings: {e}"));
202+
return;
203+
}
204+
};
205+
206+
if current.enable {
207+
let disabled = systemproxy::SystemProxy { enable: false, ..current };
208+
if let Err(e) = disabled.set_system_proxy() {
209+
show_proxy_error(parent, format!("Failed to disable the system proxy: {e}"));
210+
} else {
211+
log::info!("System proxy disabled.");
212+
}
213+
return;
214+
}
215+
216+
let Some(node) = get_running_overtls_node() else {
217+
show_proxy_error(parent, "Start OverTLS before enabling the system proxy.".to_string());
218+
return;
219+
};
220+
let Some(client) = node.client else {
221+
show_proxy_error(parent, "The running OverTLS node has no client listen address.".to_string());
222+
return;
223+
};
224+
225+
let proxy = systemproxy::SystemProxy {
226+
enable: true,
227+
host: normalize_connect_host(&client.listen_host).to_string(),
228+
port: client.listen_port,
229+
bypass: String::new(),
230+
};
231+
if let Err(e) = proxy.set_system_proxy() {
232+
show_proxy_error(parent, format!("Failed to enable the system proxy: {e}"));
233+
} else {
234+
log::info!("System proxy enabled at {}:{}.", proxy.host, proxy.port);
235+
}
236+
}
237+
238+
fn show_proxy_error(parent: &dyn WxWidget, message: String) {
239+
log::error!("{message}");
240+
let dlg = MessageDialog::builder(parent, &message, "System Proxy")
241+
.with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning)
242+
.build();
243+
let _ = dlg.show_modal();
244+
dlg.destroy();
245+
}
246+
171247
#[inline]
172248
pub fn stop_overtls_only() -> std::io::Result<()> {
173249
stop_thread_with_cancel_token(&OVERTLS_TOKEN, &OVERTLS_HANDLE)

src/main.rs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ pub enum MenuId {
99
New = 1004,
1010
OverTls = 1005,
1111
Tun2proxy = 1006,
12-
Open = 1007,
13-
Quit = 1008,
12+
SystemProxy = 1007,
13+
Open = 1008,
14+
Quit = 1009,
1415
ViewDetails = 3001,
1516
ExportNode = 3002,
1617
ShowQrCode = 3003,
@@ -47,6 +48,7 @@ impl TryFrom<i32> for MenuId {
4748
x if x == MenuId::AutoRefreshSubscriptions as i32 => Ok(MenuId::AutoRefreshSubscriptions),
4849
x if x == MenuId::OverTls as i32 => Ok(MenuId::OverTls),
4950
x if x == MenuId::Tun2proxy as i32 => Ok(MenuId::Tun2proxy),
51+
x if x == MenuId::SystemProxy as i32 => Ok(MenuId::SystemProxy),
5052
x if x == MenuId::Open as i32 => Ok(MenuId::Open),
5153
x if x == MenuId::Quit as i32 => Ok(MenuId::Quit),
5254
x if x == MenuId::ViewDetails as i32 => Ok(MenuId::ViewDetails),
@@ -81,7 +83,7 @@ mod single_instance;
8183

8284
use model::{ServerList, create_server_tree_model};
8385
pub(crate) use overtls::Config as ServerNode;
84-
use settings::{ConfigRef, MAIN_ICON, WindowConfig, create_bitmap_from_memory};
86+
use settings::{ConfigRef, MAIN_ICON, OVERTLS_ICON, PROXY_ICON, SETTINGS_ICON, TUN2PROXY_ICON, WindowConfig, create_bitmap_from_memory};
8587
use std::{
8688
cell::RefCell,
8789
net::SocketAddr,
@@ -94,6 +96,7 @@ use wxdragon::prelude::*;
9496
// Toolbar tool IDs (distinct from menu IDs)
9597
const ID_TOOL_OVERTLS: Id = MenuId::OverTls as Id; // can reuse menu ID since it's the same action
9698
const ID_TOOL_TUN2PROXY: Id = MenuId::Tun2proxy as Id; // can reuse menu ID since it's the same action
99+
const ID_TOOL_SYSTEM_PROXY: Id = MenuId::SystemProxy as Id;
97100
const ID_TOOL_SETTINGS: Id = MenuId::Settings as Id; // simple button to open settings
98101

99102
fn main() -> std::io::Result<()> {
@@ -231,13 +234,7 @@ fn main() -> std::io::Result<()> {
231234
if let Some(toolbar) = &toolbar_opt {
232235
let icon_size = ArtProvider::get_native_dip_size_hint(ArtClient::Toolbar);
233236

234-
if let Some(bundle) = ArtProvider::get_bitmap_bundle(ArtId::HelpSettings, ArtClient::Toolbar, None) {
235-
if let Some(bmp) = bundle.get_bitmap_for(&frame) {
236-
toolbar.add_tool(ID_TOOL_SETTINGS, "Settings", &bmp, "Open Settings");
237-
}
238-
} else if let Some(icon) = ArtProvider::get_bitmap(ArtId::HelpSettings, ArtClient::Toolbar, None) {
239-
toolbar.add_tool(ID_TOOL_SETTINGS, "Settings", &icon, "Open Settings");
240-
} else if let Ok(bmp) = create_bitmap_from_memory(MAIN_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
237+
if let Ok(bmp) = create_bitmap_from_memory(SETTINGS_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
241238
toolbar.add_tool(ID_TOOL_SETTINGS, "Settings", &bmp, "Open Settings");
242239
}
243240

@@ -250,25 +247,17 @@ fn main() -> std::io::Result<()> {
250247
sep.set_foreground_color(colours::gray::GRAY_600);
251248
toolbar.add_control(&sep);
252249

253-
// OverTLS tool (toggle)
254-
if let Some(bundle) = ArtProvider::get_bitmap_bundle(ArtId::New, ArtClient::Toolbar, None) {
255-
if let Some(bmp) = bundle.get_bitmap_for(&frame) {
256-
toolbar.add_check_tool(ID_TOOL_OVERTLS, "OverTLS", &bmp, "Start OverTLS (SOCKS5)");
257-
}
258-
} else if let Some(icon) = ArtProvider::get_bitmap(ArtId::New, ArtClient::Toolbar, None) {
259-
toolbar.add_check_tool(ID_TOOL_OVERTLS, "OverTLS", &icon, "Start OverTLS (SOCKS5)");
260-
} else if let Ok(bmp) = create_bitmap_from_memory(MAIN_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
250+
// OverTLS tool (toggle) OVERTLS_ICON
251+
if let Ok(bmp) = create_bitmap_from_memory(OVERTLS_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
261252
toolbar.add_check_tool(ID_TOOL_OVERTLS, "OverTLS", &bmp, "Start OverTLS (SOCKS5)");
262253
}
263254

255+
if let Ok(bmp) = create_bitmap_from_memory(PROXY_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
256+
toolbar.add_check_tool(ID_TOOL_SYSTEM_PROXY, "System Proxy", &bmp, "Use OverTLS as the system proxy");
257+
}
258+
264259
// Tun2Proxy tool (toggle)
265-
if let Some(bundle) = ArtProvider::get_bitmap_bundle(ArtId::FileOpen, ArtClient::Toolbar, None) {
266-
if let Some(bmp) = bundle.get_bitmap_for(&frame) {
267-
toolbar.add_check_tool(ID_TOOL_TUN2PROXY, "Tun2Proxy", &bmp, "Start Tun2Proxy");
268-
}
269-
} else if let Some(icon) = ArtProvider::get_bitmap(ArtId::FileOpen, ArtClient::Toolbar, None) {
270-
toolbar.add_check_tool(ID_TOOL_TUN2PROXY, "Tun2Proxy", &icon, "Start Tun2Proxy");
271-
} else if let Ok(bmp) = create_bitmap_from_memory(MAIN_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
260+
if let Ok(bmp) = create_bitmap_from_memory(TUN2PROXY_ICON, Some((icon_size.width as u32, icon_size.height as u32))) {
272261
toolbar.add_check_tool(ID_TOOL_TUN2PROXY, "Tun2Proxy", &bmp, "Start Tun2Proxy");
273262
}
274263

@@ -345,6 +334,7 @@ fn main() -> std::io::Result<()> {
345334
.append_item(MenuId::New.into(), "New", "Create new node")
346335
.append_separator()
347336
.append_check_item(MenuId::OverTls.into(), "OverTls\tF5", "Run OverTls node")
337+
.append_check_item(MenuId::SystemProxy.into(), "System Proxy", "Use OverTLS as the system proxy")
348338
.append_check_item(MenuId::Tun2proxy.into(), "Tun2proxy\tShift+F5", "Tun2proxy service")
349339
.append_separator()
350340
.append_item(MenuId::Quit.into(), "Quit\tCtrl+Q", "Quit the application")
@@ -393,6 +383,21 @@ fn main() -> std::io::Result<()> {
393383
sync_menu(&mbar, &cfg_clone);
394384
}
395385

386+
let proxy_safety_timer_holder: Rc<RefCell<Option<Timer<Frame>>>> = Rc::new(RefCell::new(None));
387+
let proxy_safety_toolbar = toolbar_opt.clone();
388+
let proxy_safety_cfg = cfg_clone.clone();
389+
let proxy_safety_timer = Timer::new(&frame);
390+
proxy_safety_timer.on_tick(move |_evt| {
391+
if let Some(toolbar) = &proxy_safety_toolbar {
392+
sync_toolbar(toolbar);
393+
}
394+
if let Some(mbar) = frame.get_menu_bar() {
395+
sync_menu(&mbar, &proxy_safety_cfg);
396+
}
397+
});
398+
proxy_safety_timer.start(500, false);
399+
*proxy_safety_timer_holder.borrow_mut() = Some(proxy_safety_timer);
400+
396401
let cfg_for_auto_refresh = cfg_clone.clone();
397402
let refresh_request_tx_for_auto = refresh_request_tx.clone();
398403
std::thread::spawn(move || {
@@ -749,12 +754,27 @@ pub fn restart_as_admin() -> std::io::Result<std::process::ExitStatus> {
749754
// helper to update all three toggle buttons according to actual running state
750755
fn sync_toolbar(tb: &wxdragon::widgets::ToolBar) {
751756
let running = core::is_overtls_running();
757+
if !running {
758+
core::disable_system_proxy_if_overtls_stopped();
759+
}
752760
tb.toggle_tool(ID_TOOL_OVERTLS, running);
753761
tb.set_tool_short_help(ID_TOOL_OVERTLS, if running { "Stop OverTLS" } else { "Start OverTLS (SOCKS5)" });
754762

755763
let t2p = core::is_tun2proxy_running();
756764
tb.toggle_tool(ID_TOOL_TUN2PROXY, t2p);
757765
tb.set_tool_short_help(ID_TOOL_TUN2PROXY, if t2p { "Stop Tun2Proxy" } else { "Start Tun2Proxy" });
766+
767+
let system_proxy = running && systemproxy::SystemProxy::is_enabled();
768+
tb.enable_tool(ID_TOOL_SYSTEM_PROXY, running);
769+
tb.toggle_tool(ID_TOOL_SYSTEM_PROXY, system_proxy);
770+
tb.set_tool_short_help(
771+
ID_TOOL_SYSTEM_PROXY,
772+
if system_proxy {
773+
"Disable System Proxy"
774+
} else {
775+
"Use OverTLS as the system proxy"
776+
},
777+
);
758778
}
759779

760780
// helper to update the checked state of the same three actions on the main menu
@@ -764,8 +784,16 @@ fn sync_menu(mb: &wxdragon::menus::MenuBar, cfg: &std::sync::Arc<std::sync::Mute
764784
mb.enable_item(MenuId::Tun2proxy.into(), false);
765785
}
766786

767-
mb.check_item(MenuId::OverTls.into(), core::is_overtls_running());
787+
let overtls_running = core::is_overtls_running();
788+
if !overtls_running {
789+
core::disable_system_proxy_if_overtls_stopped();
790+
}
791+
792+
mb.check_item(MenuId::OverTls.into(), overtls_running);
768793
mb.check_item(MenuId::Tun2proxy.into(), core::is_tun2proxy_running());
794+
let system_proxy = overtls_running && systemproxy::SystemProxy::is_enabled();
795+
mb.enable_item(MenuId::SystemProxy.into(), overtls_running);
796+
mb.check_item(MenuId::SystemProxy.into(), system_proxy);
769797

770798
let enable_auto_refresh = cfg.lock().unwrap().subscription_auto_refresh.unwrap_or(false);
771799
mb.check_item(MenuId::AutoRefreshSubscriptions.into(), enable_auto_refresh);

src/menu_actions.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ pub fn handle_menu_command(parent: &dyn WxWidget, model: &CustomDataViewTreeMode
264264
log::info!("Menu/Toolbar: Tun2proxy clicked!");
265265
}
266266

267+
MenuId::SystemProxy => {
268+
log::info!("Menu/Toolbar: System Proxy clicked!");
269+
crate::core::toggle_system_proxy(parent);
270+
}
271+
267272
_ => {
268273
log::warn!("Unhandled Menu ID: {menu_id:?}");
269274
}

src/settings.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ pub(crate) type ConfigRef = std::sync::Arc<std::sync::Mutex<Config>>;
4646
pub(crate) const WIDGET_MARGIN: i32 = 2;
4747
pub(crate) const APP_TITLE: &str = "OverTLS-GUI";
4848
pub(crate) const MAIN_ICON: &[u8] = include_bytes!("../assets/main.png");
49+
pub(crate) const SETTINGS_ICON: &[u8] = include_bytes!("../assets/settings.png");
50+
pub(crate) const OVERTLS_ICON: &[u8] = include_bytes!("../assets/overtls.png");
51+
pub(crate) const PROXY_ICON: &[u8] = include_bytes!("../assets/proxy.png");
52+
pub(crate) const TUN2PROXY_ICON: &[u8] = include_bytes!("../assets/tun2proxy.png");
4953
pub(crate) const ICON_SIZE: u32 = 72;
5054

5155
static DIRTY_FLAG: AtomicBool = AtomicBool::new(false);

0 commit comments

Comments
 (0)