nd_300/diagnostics/
proxy.rs1use serde::Serialize;
2
3#[derive(Debug, Clone, Serialize)]
4pub struct ProxyConfig {
5 pub http_proxy: Option<String>,
6 pub https_proxy: Option<String>,
7 pub socks_proxy: Option<String>,
8 pub no_proxy: Option<String>,
9 pub pac_url: Option<String>,
10 pub proxy_enabled: bool,
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub pac_reachable: Option<bool>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub pac_size_bytes: Option<u64>,
16 #[serde(skip_serializing_if = "Option::is_none")]
19 pub wpad_dns_detected: Option<bool>,
20}
21
22pub async fn collect() -> Option<ProxyConfig> {
23 let http_proxy = std::env::var("HTTP_PROXY")
25 .or_else(|_| std::env::var("http_proxy"))
26 .ok();
27 let https_proxy = std::env::var("HTTPS_PROXY")
28 .or_else(|_| std::env::var("https_proxy"))
29 .ok();
30 let socks_proxy = std::env::var("ALL_PROXY")
31 .or_else(|_| std::env::var("all_proxy"))
32 .ok();
33 let no_proxy = std::env::var("NO_PROXY")
34 .or_else(|_| std::env::var("no_proxy"))
35 .ok();
36
37 let mut config = ProxyConfig {
38 http_proxy,
39 https_proxy,
40 socks_proxy,
41 no_proxy,
42 pac_url: None,
43 proxy_enabled: false,
44 pac_reachable: None,
45 pac_size_bytes: None,
46 wpad_dns_detected: None,
47 };
48
49 #[cfg(windows)]
51 {
52 check_windows_proxy(&mut config).await;
53 }
54
55 #[cfg(target_os = "macos")]
56 {
57 check_macos_proxy(&mut config).await;
58 }
59
60 #[cfg(target_os = "linux")]
63 {
64 if config.http_proxy.is_none() && config.https_proxy.is_none() {
65 check_gnome_proxy(&mut config).await;
66 }
67 }
68
69 config.proxy_enabled =
70 config.http_proxy.is_some() || config.https_proxy.is_some() || config.socks_proxy.is_some();
71
72 if let Some(ref pac_url) = config.pac_url {
75 if let Ok(client) = reqwest::Client::builder()
76 .timeout(std::time::Duration::from_secs(5))
77 .no_proxy()
78 .build()
79 {
80 match client.get(pac_url).send().await {
81 Ok(resp) if resp.status().is_success() => {
82 config.pac_reachable = Some(true);
83 config.pac_size_bytes = resp.bytes().await.ok().map(|b| b.len() as u64);
84 }
85 _ => config.pac_reachable = Some(false),
86 }
87 }
88 }
89
90 config.wpad_dns_detected = Some(
93 super::util::lookup_host_timeout("wpad:80".to_string(), super::util::RESOLVE)
94 .await
95 .is_some_and(|a| !a.is_empty()),
96 );
97
98 Some(config)
99}
100
101#[cfg(target_os = "linux")]
103async fn check_gnome_proxy(config: &mut ProxyConfig) {
104 let mut mode_cmd = tokio::process::Command::new("gsettings");
105 mode_cmd.args(["get", "org.gnome.system.proxy", "mode"]);
106 let Some(output) = super::util::run_with_timeout(mode_cmd, super::util::QUICK).await else {
107 return;
108 };
109 let mode = String::from_utf8_lossy(&output.stdout)
110 .trim()
111 .replace('\'', "");
112 match mode.as_str() {
113 "manual" => {
114 let mut host_cmd = tokio::process::Command::new("gsettings");
115 host_cmd.args(["get", "org.gnome.system.proxy.http", "host"]);
116 if let Some(host_out) =
117 super::util::run_with_timeout(host_cmd, super::util::QUICK).await
118 {
119 let host = String::from_utf8_lossy(&host_out.stdout)
120 .trim()
121 .replace('\'', "");
122 if !host.is_empty() {
123 config.http_proxy = Some(host);
124 }
125 }
126 }
127 "auto" => {
128 let mut url_cmd = tokio::process::Command::new("gsettings");
129 url_cmd.args(["get", "org.gnome.system.proxy", "autoconfig-url"]);
130 if let Some(url_out) = super::util::run_with_timeout(url_cmd, super::util::QUICK).await
131 {
132 let url = String::from_utf8_lossy(&url_out.stdout)
133 .trim()
134 .replace('\'', "");
135 if !url.is_empty() {
136 config.pac_url = Some(url);
137 }
138 }
139 }
140 _ => {}
141 }
142}
143
144#[cfg(windows)]
145async fn check_windows_proxy(config: &mut ProxyConfig) {
146 let mut cmd = tokio::process::Command::new("reg");
148 cmd.args([
149 "query",
150 r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings",
151 "/v",
152 "ProxyEnable",
153 ]);
154 if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
155 let text = String::from_utf8_lossy(&output.stdout);
156 if text.contains("0x1") {
157 config.proxy_enabled = true;
158
159 let mut cmd = tokio::process::Command::new("reg");
161 cmd.args([
162 "query",
163 r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings",
164 "/v",
165 "ProxyServer",
166 ]);
167 if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
168 let text = String::from_utf8_lossy(&output.stdout);
169 for line in text.lines() {
170 if line.contains("ProxyServer") {
171 if let Some(val) = line.split_whitespace().last() {
172 if config.http_proxy.is_none() {
173 config.http_proxy = Some(val.to_string());
174 }
175 }
176 }
177 }
178 }
179
180 let mut cmd = tokio::process::Command::new("reg");
182 cmd.args([
183 "query",
184 r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings",
185 "/v",
186 "AutoConfigURL",
187 ]);
188 if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
189 let text = String::from_utf8_lossy(&output.stdout);
190 for line in text.lines() {
191 if line.contains("AutoConfigURL") {
192 config.pac_url = line.split_whitespace().last().map(|s| s.to_string());
193 }
194 }
195 }
196 }
197 }
198}
199
200#[cfg(target_os = "macos")]
201async fn check_macos_proxy(config: &mut ProxyConfig) {
202 let mut cmd = tokio::process::Command::new("scutil");
203 cmd.args(["--proxy"]);
204 if let Some(output) = super::util::run_with_timeout(cmd, super::util::QUICK).await {
205 let text = String::from_utf8_lossy(&output.stdout);
206
207 for line in text.lines() {
208 let line = line.trim();
209 if line.contains("HTTPEnable") && line.contains("1") {
210 config.proxy_enabled = true;
211 }
212 if line.contains("HTTPProxy") {
213 config.http_proxy = line.split_once(':').map(|x| x.1.trim().to_string());
214 }
215 if line.contains("HTTPSProxy") {
216 config.https_proxy = line.split_once(':').map(|x| x.1.trim().to_string());
217 }
218 if line.contains("SOCKSProxy") {
219 config.socks_proxy = line.split_once(':').map(|x| x.1.trim().to_string());
220 }
221 if line.contains("ProxyAutoConfigURLString") {
222 config.pac_url = line.split_once(':').map(|x| x.1.trim().to_string());
223 }
224 }
225 }
226}