diff --git a/Cargo.toml b/Cargo.toml index f02db239..4adc3b8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,9 @@ pnet_datalink = "0.35.0" [target.'cfg(target_os = "macos")'.dependencies] system-configuration = { version = "0.7", optional = true } +[target.'cfg(target_os = "ios")'.dependencies] +core-foundation = { version = "0.9", optional = true } + [target.'cfg(windows)'.dependencies] windows-registry = { version = ">=0.3, <0.7", optional = true } @@ -78,7 +81,7 @@ client = ["hyper/client", "dep:tracing", "dep:futures-channel", "dep:tower-servi client-legacy = ["client", "tokio/net", "dep:socket2", "tokio/sync", "dep:libc", "dep:futures-util"] client-pool = ["client", "dep:futures-util", "dep:tower-layer", "tokio/sync"] client-proxy = ["client", "dep:base64", "dep:ipnet", "dep:percent-encoding"] -client-proxy-system = ["dep:system-configuration", "dep:windows-registry"] +client-proxy-system = ["dep:system-configuration", "dep:windows-registry", "dep:core-foundation"] server = ["hyper/server"] server-auto = ["server", "http1", "http2"] diff --git a/src/client/proxy/matcher.rs b/src/client/proxy/matcher.rs index b91b7aa9..d02087fe 100644 --- a/src/client/proxy/matcher.rs +++ b/src/client/proxy/matcher.rs @@ -242,6 +242,9 @@ impl Builder { #[cfg(all(feature = "client-proxy-system", target_os = "macos"))] mac::with_system(&mut builder); + #[cfg(all(feature = "client-proxy-system", target_os = "ios"))] + ios::with_system(&mut builder); + #[cfg(all(feature = "client-proxy-system", windows))] win::with_system(&mut builder); @@ -659,6 +662,86 @@ mod mac { } } +#[cfg(feature = "client-proxy-system")] +#[cfg(target_os = "ios")] +mod ios { + use core_foundation::base::{CFType, CFTypeRef, TCFType}; + use core_foundation::dictionary::CFDictionary; + use core_foundation::number::CFNumber; + use core_foundation::string::{CFString, CFStringRef}; + + #[link(name = "CFNetwork", kind = "framework")] + unsafe extern "C" { + fn CFNetworkCopySystemProxySettings() -> CFTypeRef; + static kCFNetworkProxiesHTTPEnable: CFStringRef; + static kCFNetworkProxiesHTTPProxy: CFStringRef; + static kCFNetworkProxiesHTTPPort: CFStringRef; + static kCFNetworkProxiesHTTPSEnable: CFStringRef; + static kCFNetworkProxiesHTTPSProxy: CFStringRef; + static kCFNetworkProxiesHTTPSPort: CFStringRef; + } + + pub(super) fn with_system(builder: &mut super::Builder) { + unsafe { + let raw = CFNetworkCopySystemProxySettings(); + if raw.is_null() { + return; + } + let dict: CFDictionary = + CFDictionary::wrap_under_create_rule(raw as _); + if builder.http.is_empty() { + if let Some(proxy) = parse_proxy( + &dict, + kCFNetworkProxiesHTTPEnable, + kCFNetworkProxiesHTTPProxy, + kCFNetworkProxiesHTTPPort, + ) { + builder.http = proxy; + } + } + if builder.https.is_empty() { + if let Some(proxy) = parse_proxy( + &dict, + kCFNetworkProxiesHTTPSEnable, + kCFNetworkProxiesHTTPSProxy, + kCFNetworkProxiesHTTPSPort, + ) { + builder.https = proxy; + } + } + } + } + + fn parse_proxy( + dict: &CFDictionary, + enable_key: CFStringRef, + host_key: CFStringRef, + port_key: CFStringRef, + ) -> Option { + let enabled = dict + .find(enable_key) + .and_then(|v| v.downcast::()) + .and_then(|n| n.to_i32()) + .unwrap_or(0) + == 1; + if !enabled { + return None; + } + let host = dict + .find(host_key) + .and_then(|v| v.downcast::()) + .map(|s| s.to_string())?; + let port = dict + .find(port_key) + .and_then(|v| v.downcast::()) + .and_then(|n| n.to_i32()); + Some(match port { + Some(p) => format!("{host}:{p}"), + None => host, + }) + } +} + #[cfg(feature = "client-proxy-system")] #[cfg(windows)] mod win {