clash-verge/src-tauri/src/utils/sysopt.rs

367 lines
9.8 KiB
Rust
Raw Normal View History

2022-03-09 14:48:32 +03:00
use anyhow::Result;
2021-12-04 09:31:26 +03:00
use serde::{Deserialize, Serialize};
2021-12-06 05:31:17 +03:00
2022-01-09 16:19:35 +03:00
#[cfg(target_os = "windows")]
2022-01-07 18:29:20 +03:00
static DEFAULT_BYPASS: &str = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*;<local>";
2022-03-07 19:39:09 +03:00
#[cfg(target_os = "linux")]
static DEFAULT_BYPASS: &str = "localhost,127.0.0.1/8,::1";
2022-01-09 16:19:35 +03:00
#[cfg(target_os = "macos")]
static DEFAULT_BYPASS: &str =
"192.168.0.0/16\n10.0.0.0/8\n172.16.0.0/12\n127.0.0.1\nlocalhost\n*.local\ntimestamp.apple.com\n";
#[cfg(target_os = "macos")]
static MACOS_SERVICE: &str = "Wi-Fi";
2022-01-07 18:29:20 +03:00
2021-12-26 21:29:28 +03:00
#[derive(Debug, Deserialize, Serialize, Clone)]
2021-12-12 13:02:41 +03:00
pub struct SysProxyConfig {
2021-12-16 21:15:40 +03:00
pub enable: bool,
pub server: String,
pub bypass: String,
2021-12-04 09:31:26 +03:00
}
2021-12-26 21:29:28 +03:00
impl Default for SysProxyConfig {
fn default() -> Self {
SysProxyConfig {
enable: false,
server: String::from(""),
bypass: String::from(""),
}
}
}
2022-01-07 18:29:20 +03:00
impl SysProxyConfig {
pub fn new(enable: bool, port: String, bypass: Option<String>) -> Self {
SysProxyConfig {
enable,
server: format!("127.0.0.1:{}", port),
bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()),
}
}
2022-03-08 21:36:03 +03:00
}
2021-12-12 13:02:41 +03:00
2022-03-08 21:36:03 +03:00
#[cfg(target_os = "windows")]
impl SysProxyConfig {
2022-01-09 16:19:35 +03:00
/// Get the windows system proxy config
2022-03-09 14:48:32 +03:00
pub fn get_sys() -> Result<Self> {
2022-01-07 18:29:20 +03:00
use winreg::enums::*;
use winreg::RegKey;
2021-12-12 13:02:41 +03:00
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let cur_var = hkcu.open_subkey_with_flags(
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
KEY_READ,
)?;
Ok(SysProxyConfig {
enable: cur_var.get_value::<u32, _>("ProxyEnable")? == 1u32,
server: cur_var.get_value("ProxyServer")?,
bypass: cur_var.get_value("ProxyOverride")?,
})
}
2022-03-08 21:36:03 +03:00
/// Set the windows system proxy config
2022-03-09 14:48:32 +03:00
pub fn set_sys(&self) -> Result<()> {
2022-03-08 21:36:03 +03:00
use winreg::enums::*;
use winreg::RegKey;
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let cur_var = hkcu.open_subkey_with_flags(
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
KEY_SET_VALUE,
)?;
let enable: u32 = if self.enable { 1u32 } else { 0u32 };
cur_var.set_value("ProxyEnable", &enable)?;
cur_var.set_value("ProxyServer", &self.server)?;
2022-03-09 14:48:32 +03:00
cur_var.set_value("ProxyOverride", &self.bypass)?;
Ok(())
2022-03-08 21:36:03 +03:00
}
}
#[cfg(target_os = "macos")]
impl SysProxyConfig {
2022-01-09 16:19:35 +03:00
/// Get the macos system proxy config
2022-03-09 14:48:32 +03:00
pub fn get_sys() -> Result<Self> {
2022-01-09 16:19:35 +03:00
use std::process::Command;
let http = macproxy::get_proxy(&["-getwebproxy", MACOS_SERVICE])?;
let https = macproxy::get_proxy(&["-getsecurewebproxy", MACOS_SERVICE])?;
let sock = macproxy::get_proxy(&["-getsocksfirewallproxy", MACOS_SERVICE])?;
let mut enable = false;
let mut server = "".into();
if sock.0 == "Yes" {
enable = true;
server = sock.1;
}
if https.0 == "Yes" {
enable = true;
server = https.1;
}
if http.0 == "Yes" || !enable {
enable = http.0 == "Yes";
server = http.1;
}
let bypass_output = Command::new("networksetup")
.args(["-getproxybypassdomains", MACOS_SERVICE])
.output()?;
2022-03-08 21:36:03 +03:00
// change the format to xxx,xxx
let bypass = std::str::from_utf8(&bypass_output.stdout)
.unwrap_or(DEFAULT_BYPASS)
.to_string()
.split('\n')
.collect::<Vec<_>>()
.join(",");
2022-01-09 16:19:35 +03:00
Ok(SysProxyConfig {
enable,
server,
2022-03-08 21:36:03 +03:00
bypass,
2022-01-09 16:19:35 +03:00
})
}
/// Set the macos system proxy config
2022-03-09 14:48:32 +03:00
pub fn set_sys(&self) -> Result<()> {
2022-03-08 21:36:03 +03:00
use std::process::Command;
2022-01-09 16:19:35 +03:00
let enable = self.enable;
let server = self.server.as_str();
2022-03-08 21:36:03 +03:00
let bypass = self.bypass.clone();
2022-01-09 16:19:35 +03:00
macproxy::set_proxy("-setwebproxy", MACOS_SERVICE, enable, server)?;
macproxy::set_proxy("-setsecurewebproxy", MACOS_SERVICE, enable, server)?;
2022-03-08 21:36:03 +03:00
macproxy::set_proxy("-setsocksfirewallproxy", MACOS_SERVICE, enable, server)?;
let domains = bypass.split(",").collect::<Vec<_>>();
Command::new("networksetup")
.args([["-setproxybypassdomains", MACOS_SERVICE].to_vec(), domains].concat())
.status()?;
Ok(())
2022-01-09 16:19:35 +03:00
}
2021-12-04 09:31:26 +03:00
}
2022-01-09 16:19:35 +03:00
#[cfg(target_os = "macos")]
mod macproxy {
use super::*;
2022-03-09 14:48:32 +03:00
use anyhow::bail;
2022-01-09 16:19:35 +03:00
use std::process::Command;
/// use networksetup
/// get the target proxy config
2022-03-09 14:48:32 +03:00
pub(super) fn get_proxy(args: &[&str; 2]) -> Result<(String, String)> {
2022-01-09 16:19:35 +03:00
let output = Command::new("networksetup").args(args).output()?;
2022-03-09 14:48:32 +03:00
let stdout = std::str::from_utf8(&output.stdout)?;
let enable = parse(stdout, "Enabled:");
let server = parse(stdout, "Server:");
let port = parse(stdout, "Port:");
let server = format!("{server}:{port}");
Ok((enable.into(), server))
2022-01-09 16:19:35 +03:00
}
/// use networksetup
/// set the target proxy config
pub(super) fn set_proxy(
target: &str, // like: -setwebproxy
device: &str,
enable: bool,
server: &str,
2022-03-09 14:48:32 +03:00
) -> Result<()> {
2022-01-09 16:19:35 +03:00
let mut split = server.split(":");
2022-03-09 14:48:32 +03:00
let host = split.next();
2022-01-09 16:19:35 +03:00
let port = split.next();
// can not parse the field
2022-03-09 14:48:32 +03:00
if host.is_none() || port.is_none() {
bail!("failed to parse the server into host:port");
2022-01-09 16:19:35 +03:00
}
2022-03-09 14:48:32 +03:00
let args = vec![target, device, host.unwrap(), port.unwrap()];
2022-01-09 16:19:35 +03:00
Command::new("networksetup").args(&args).status()?;
let target_state = String::from(target) + "state";
let enable = if enable { "on" } else { "off" };
let args = vec![target_state.as_str(), device, enable];
Command::new("networksetup").args(&args).status()?;
Ok(())
}
/// parse the networksetup output
2022-03-08 21:36:03 +03:00
fn parse<'a>(target: &'a str, key: &'a str) -> &'a str {
2022-01-09 16:19:35 +03:00
match target.find(key) {
Some(idx) => {
let idx = idx + key.len();
let value = &target[idx..];
let value = match value.find("\n") {
Some(end) => &value[..end],
None => value,
};
value.trim()
}
None => "",
}
}
#[test]
fn test_get() {
use std::process::Command;
let output = Command::new("networksetup")
.args(["-getwebproxy", "Wi-Fi"])
.output();
let output = output.unwrap();
let stdout = std::str::from_utf8(&output.stdout).unwrap();
let enable = parse(stdout, "Enabled:");
let server = parse(stdout, "Server:");
let port = parse(stdout, "Port:");
println!("enable: {}, server: {}, port: {}", enable, server, port);
dbg!(SysProxyConfig::get_sys().unwrap());
}
#[test]
fn test_set() {
let sysproxy = SysProxyConfig::new(true, "7890".into(), None);
dbg!(sysproxy.set_sys().unwrap());
}
}
2022-03-09 14:48:32 +03:00
///
/// Linux Desktop System Proxy Supports
/// by using `gsettings`
#[cfg(target_os = "linux")]
impl SysProxyConfig {
/// Get the system proxy config [http/https/socks]
pub fn get_sys() -> Result<Self> {
use std::process::Command;
let schema = "org.gnome.system.proxy";
// get enable
let mode = Command::new("gsettings")
.args(["get", schema, "mode"])
.output()?;
let mode = std::str::from_utf8(&mode.stdout)?;
let enable = mode == "manual";
// get bypass
// Todo: parse the ignore-hosts
// ['aaa', 'bbb'] -> aaa,bbb
let ignore = Command::new("gsettings")
.args(["get", schema, "ignore-hosts"])
.output()?;
let ignore = std::str::from_utf8(&ignore.stdout)?;
let bypass = ignore.to_string();
let http = Self::get_proxy("http")?;
let https = Self::get_proxy("https")?;
let socks = Self::get_proxy("socks")?;
let mut server = "".into();
if socks.len() > 0 {
server = socks;
}
if https.len() > 0 {
server = https;
}
if http.len() > 0 {
server = http;
}
Ok(SysProxyConfig {
enable,
server,
bypass,
})
}
/// Get the system proxy config [http/https/socks]
pub fn set_sys(&self) -> Result<()> {
use anyhow::bail;
use std::process::Command;
let enable = self.enable;
let server = self.server.as_str();
let bypass = self.bypass.clone();
let schema = "org.gnome.system.proxy";
if enable {
let mut split = server.split(":");
let host = split.next();
let port = split.next();
if port.is_none() {
bail!("failed to parse the port");
}
let host = format!("'{}'", host.unwrap_or("127.0.0.1"));
let host = host.as_str();
let port = port.unwrap();
let http = format!("{schema}.http");
Command::new("gsettings")
.args(["set", http.as_str(), "host", host])
.status()?;
Command::new("gsettings")
.args(["set", http.as_str(), "port", port])
.status()?;
let https = format!("{schema}.https");
Command::new("gsettings")
.args(["set", https.as_str(), "host", host])
.status()?;
Command::new("gsettings")
.args(["set", https.as_str(), "port", port])
.status()?;
let socks = format!("{schema}.socks");
Command::new("gsettings")
.args(["set", socks.as_str(), "host", host])
.status()?;
Command::new("gsettings")
.args(["set", socks.as_str(), "port", port])
.status()?;
// set bypass
// Todo: parse the ignore-hosts
// aaa,bbb,cccc -> ['aaa', 'bbb', 'ccc']
Command::new("gsettings")
.args(["set", schema, "ignore-hosts", bypass.as_str()]) // todo
.status()?;
}
let mode = if enable { "'manual'" } else { "'none'" };
Command::new("gsettings")
.args(["set", schema, "mode", mode])
.status()?;
Ok(())
}
/// help function
fn get_proxy(typ: &str) -> Result<String> {
use std::process::Command;
let schema = format!("org.gnome.system.proxy.{typ}");
let schema = schema.as_str();
let host = Command::new("gsettings")
.args(["get", schema, "host"])
.output()?;
let host = std::str::from_utf8(&host.stdout)?;
let port = Command::new("gsettings")
.args(["get", schema, "port"])
.output()?;
let port = std::str::from_utf8(&port.stdout)?;
Ok(format!("{host}:{port}"))
}
}