sysopt.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. use anyhow::Result;
  2. use serde::{Deserialize, Serialize};
  3. #[cfg(target_os = "windows")]
  4. 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>";
  5. #[cfg(target_os = "linux")]
  6. static DEFAULT_BYPASS: &str = "localhost,127.0.0.1/8,::1";
  7. #[cfg(target_os = "macos")]
  8. static DEFAULT_BYPASS: &str =
  9. "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";
  10. #[cfg(target_os = "macos")]
  11. static MACOS_SERVICE: &str = "Wi-Fi";
  12. #[derive(Debug, Deserialize, Serialize, Clone)]
  13. pub struct SysProxyConfig {
  14. pub enable: bool,
  15. pub server: String,
  16. pub bypass: String,
  17. }
  18. impl Default for SysProxyConfig {
  19. fn default() -> Self {
  20. SysProxyConfig {
  21. enable: false,
  22. server: String::from(""),
  23. bypass: String::from(""),
  24. }
  25. }
  26. }
  27. impl SysProxyConfig {
  28. pub fn new(enable: bool, port: String, bypass: Option<String>) -> Self {
  29. SysProxyConfig {
  30. enable,
  31. server: format!("127.0.0.1:{}", port),
  32. bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()),
  33. }
  34. }
  35. }
  36. #[cfg(target_os = "windows")]
  37. impl SysProxyConfig {
  38. /// Get the windows system proxy config
  39. pub fn get_sys() -> Result<Self> {
  40. use winreg::enums::*;
  41. use winreg::RegKey;
  42. let hkcu = RegKey::predef(HKEY_CURRENT_USER);
  43. let cur_var = hkcu.open_subkey_with_flags(
  44. "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
  45. KEY_READ,
  46. )?;
  47. Ok(SysProxyConfig {
  48. enable: cur_var.get_value::<u32, _>("ProxyEnable")? == 1u32,
  49. server: cur_var.get_value("ProxyServer")?,
  50. bypass: cur_var.get_value("ProxyOverride")?,
  51. })
  52. }
  53. /// Set the windows system proxy config
  54. pub fn set_sys(&self) -> Result<()> {
  55. use winreg::enums::*;
  56. use winreg::RegKey;
  57. let hkcu = RegKey::predef(HKEY_CURRENT_USER);
  58. let cur_var = hkcu.open_subkey_with_flags(
  59. "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
  60. KEY_SET_VALUE,
  61. )?;
  62. let enable: u32 = if self.enable { 1u32 } else { 0u32 };
  63. cur_var.set_value("ProxyEnable", &enable)?;
  64. cur_var.set_value("ProxyServer", &self.server)?;
  65. cur_var.set_value("ProxyOverride", &self.bypass)?;
  66. Ok(())
  67. }
  68. }
  69. #[cfg(target_os = "macos")]
  70. impl SysProxyConfig {
  71. /// Get the macos system proxy config
  72. pub fn get_sys() -> Result<Self> {
  73. use std::process::Command;
  74. let http = macproxy::get_proxy(&["-getwebproxy", MACOS_SERVICE])?;
  75. let https = macproxy::get_proxy(&["-getsecurewebproxy", MACOS_SERVICE])?;
  76. let sock = macproxy::get_proxy(&["-getsocksfirewallproxy", MACOS_SERVICE])?;
  77. let mut enable = false;
  78. let mut server = "".into();
  79. if sock.0 == "Yes" {
  80. enable = true;
  81. server = sock.1;
  82. }
  83. if https.0 == "Yes" {
  84. enable = true;
  85. server = https.1;
  86. }
  87. if http.0 == "Yes" || !enable {
  88. enable = http.0 == "Yes";
  89. server = http.1;
  90. }
  91. let bypass_output = Command::new("networksetup")
  92. .args(["-getproxybypassdomains", MACOS_SERVICE])
  93. .output()?;
  94. // change the format to xxx,xxx
  95. let bypass = std::str::from_utf8(&bypass_output.stdout)
  96. .unwrap_or(DEFAULT_BYPASS)
  97. .to_string()
  98. .split('\n')
  99. .collect::<Vec<_>>()
  100. .join(",");
  101. Ok(SysProxyConfig {
  102. enable,
  103. server,
  104. bypass,
  105. })
  106. }
  107. /// Set the macos system proxy config
  108. pub fn set_sys(&self) -> Result<()> {
  109. use std::process::Command;
  110. let enable = self.enable;
  111. let server = self.server.as_str();
  112. let bypass = self.bypass.clone();
  113. macproxy::set_proxy("-setwebproxy", MACOS_SERVICE, enable, server)?;
  114. macproxy::set_proxy("-setsecurewebproxy", MACOS_SERVICE, enable, server)?;
  115. macproxy::set_proxy("-setsocksfirewallproxy", MACOS_SERVICE, enable, server)?;
  116. let domains = bypass.split(",").collect::<Vec<_>>();
  117. Command::new("networksetup")
  118. .args([["-setproxybypassdomains", MACOS_SERVICE].to_vec(), domains].concat())
  119. .status()?;
  120. Ok(())
  121. }
  122. }
  123. #[cfg(target_os = "macos")]
  124. mod macproxy {
  125. use super::*;
  126. use anyhow::bail;
  127. use std::process::Command;
  128. /// use networksetup
  129. /// get the target proxy config
  130. pub(super) fn get_proxy(args: &[&str; 2]) -> Result<(String, String)> {
  131. let output = Command::new("networksetup").args(args).output()?;
  132. let stdout = std::str::from_utf8(&output.stdout)?;
  133. let enable = parse(stdout, "Enabled:");
  134. let server = parse(stdout, "Server:");
  135. let port = parse(stdout, "Port:");
  136. let server = format!("{server}:{port}");
  137. Ok((enable.into(), server))
  138. }
  139. /// use networksetup
  140. /// set the target proxy config
  141. pub(super) fn set_proxy(
  142. target: &str, // like: -setwebproxy
  143. device: &str,
  144. enable: bool,
  145. server: &str,
  146. ) -> Result<()> {
  147. let mut split = server.split(":");
  148. let host = split.next();
  149. let port = split.next();
  150. // can not parse the field
  151. if host.is_none() || port.is_none() {
  152. bail!("failed to parse the server into host:port");
  153. }
  154. let args = vec![target, device, host.unwrap(), port.unwrap()];
  155. Command::new("networksetup").args(&args).status()?;
  156. let target_state = String::from(target) + "state";
  157. let enable = if enable { "on" } else { "off" };
  158. let args = vec![target_state.as_str(), device, enable];
  159. Command::new("networksetup").args(&args).status()?;
  160. Ok(())
  161. }
  162. /// parse the networksetup output
  163. fn parse<'a>(target: &'a str, key: &'a str) -> &'a str {
  164. match target.find(key) {
  165. Some(idx) => {
  166. let idx = idx + key.len();
  167. let value = &target[idx..];
  168. let value = match value.find("\n") {
  169. Some(end) => &value[..end],
  170. None => value,
  171. };
  172. value.trim()
  173. }
  174. None => "",
  175. }
  176. }
  177. #[test]
  178. fn test_get() {
  179. use std::process::Command;
  180. let output = Command::new("networksetup")
  181. .args(["-getwebproxy", "Wi-Fi"])
  182. .output();
  183. let output = output.unwrap();
  184. let stdout = std::str::from_utf8(&output.stdout).unwrap();
  185. let enable = parse(stdout, "Enabled:");
  186. let server = parse(stdout, "Server:");
  187. let port = parse(stdout, "Port:");
  188. println!("enable: {}, server: {}, port: {}", enable, server, port);
  189. dbg!(SysProxyConfig::get_sys().unwrap());
  190. }
  191. #[test]
  192. fn test_set() {
  193. let sysproxy = SysProxyConfig::new(true, "7890".into(), None);
  194. dbg!(sysproxy.set_sys().unwrap());
  195. }
  196. }
  197. ///
  198. /// Linux Desktop System Proxy Supports
  199. /// by using `gsettings`
  200. #[cfg(target_os = "linux")]
  201. impl SysProxyConfig {
  202. /// Get the system proxy config [http/https/socks]
  203. pub fn get_sys() -> Result<Self> {
  204. use std::process::Command;
  205. let schema = "org.gnome.system.proxy";
  206. // get enable
  207. let mode = Command::new("gsettings")
  208. .args(["get", schema, "mode"])
  209. .output()?;
  210. let mode = std::str::from_utf8(&mode.stdout)?;
  211. let enable = mode == "manual";
  212. // get bypass
  213. // Todo: parse the ignore-hosts
  214. // ['aaa', 'bbb'] -> aaa,bbb
  215. let ignore = Command::new("gsettings")
  216. .args(["get", schema, "ignore-hosts"])
  217. .output()?;
  218. let ignore = std::str::from_utf8(&ignore.stdout)?;
  219. let bypass = ignore.to_string();
  220. let http = Self::get_proxy("http")?;
  221. let https = Self::get_proxy("https")?;
  222. let socks = Self::get_proxy("socks")?;
  223. let mut server = "".into();
  224. if socks.len() > 0 {
  225. server = socks;
  226. }
  227. if https.len() > 0 {
  228. server = https;
  229. }
  230. if http.len() > 0 {
  231. server = http;
  232. }
  233. Ok(SysProxyConfig {
  234. enable,
  235. server,
  236. bypass,
  237. })
  238. }
  239. /// Get the system proxy config [http/https/socks]
  240. pub fn set_sys(&self) -> Result<()> {
  241. use anyhow::bail;
  242. use std::process::Command;
  243. let enable = self.enable;
  244. let server = self.server.as_str();
  245. let bypass = self.bypass.clone();
  246. let schema = "org.gnome.system.proxy";
  247. if enable {
  248. let mut split = server.split(":");
  249. let host = split.next();
  250. let port = split.next();
  251. if port.is_none() {
  252. bail!("failed to parse the port");
  253. }
  254. let host = format!("'{}'", host.unwrap_or("127.0.0.1"));
  255. let host = host.as_str();
  256. let port = port.unwrap();
  257. let http = format!("{schema}.http");
  258. Command::new("gsettings")
  259. .args(["set", http.as_str(), "host", host])
  260. .status()?;
  261. Command::new("gsettings")
  262. .args(["set", http.as_str(), "port", port])
  263. .status()?;
  264. let https = format!("{schema}.https");
  265. Command::new("gsettings")
  266. .args(["set", https.as_str(), "host", host])
  267. .status()?;
  268. Command::new("gsettings")
  269. .args(["set", https.as_str(), "port", port])
  270. .status()?;
  271. let socks = format!("{schema}.socks");
  272. Command::new("gsettings")
  273. .args(["set", socks.as_str(), "host", host])
  274. .status()?;
  275. Command::new("gsettings")
  276. .args(["set", socks.as_str(), "port", port])
  277. .status()?;
  278. // set bypass
  279. // Todo: parse the ignore-hosts
  280. // aaa,bbb,cccc -> ['aaa', 'bbb', 'ccc']
  281. Command::new("gsettings")
  282. .args(["set", schema, "ignore-hosts", bypass.as_str()]) // todo
  283. .status()?;
  284. }
  285. let mode = if enable { "'manual'" } else { "'none'" };
  286. Command::new("gsettings")
  287. .args(["set", schema, "mode", mode])
  288. .status()?;
  289. Ok(())
  290. }
  291. /// help function
  292. fn get_proxy(typ: &str) -> Result<String> {
  293. use std::process::Command;
  294. let schema = format!("org.gnome.system.proxy.{typ}");
  295. let schema = schema.as_str();
  296. let host = Command::new("gsettings")
  297. .args(["get", schema, "host"])
  298. .output()?;
  299. let host = std::str::from_utf8(&host.stdout)?;
  300. let port = Command::new("gsettings")
  301. .args(["get", schema, "port"])
  302. .output()?;
  303. let port = std::str::from_utf8(&port.stdout)?;
  304. Ok(format!("{host}:{port}"))
  305. }
  306. }