From bf9a79fda52d3945f7b444cc55079ba92e66ad32 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 21 Oct 2025 20:56:55 +0800 Subject: [PATCH] mobile wss use rustls_platform_verifier Signed-off-by: 21pages --- Cargo.toml | 2 +- src/config.rs | 3 ++- src/lib.rs | 4 ++- src/proxy.rs | 5 ++-- src/websocket.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0689f62..6cc1e0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ tokio-rustls = { version = "0.26", features = [ "tls12", "ring", ], default-features = false } -rustls-platform-verifier = "0.5" +rustls-platform-verifier = "0.6" rustls-pki-types = "1.11" tokio-tungstenite = { version = "0.26", features = ["rustls-tls-native-roots", "rustls-tls-webpki-roots"] } tungstenite = { version = "0.26", features = ["rustls-tls-native-roots", "rustls-tls-webpki-roots"] } diff --git a/src/config.rs b/src/config.rs index 2820342..7cee14f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,7 +5,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, ops::{Deref, DerefMut}, path::{Path, PathBuf}, - sync::{Mutex, RwLock}, + sync::{atomic::AtomicBool, Mutex, RwLock}, time::{Duration, Instant, SystemTime}, }; @@ -70,6 +70,7 @@ lazy_static::lazy_static! { pub static ref OVERWRITE_LOCAL_SETTINGS: RwLock> = Default::default(); pub static ref HARD_SETTINGS: RwLock> = Default::default(); pub static ref BUILTIN_SETTINGS: RwLock> = Default::default(); + pub static ref RUSTLS_PLATFORM_VERIFIER_INITIALIZED: AtomicBool = AtomicBool::new(false); } lazy_static::lazy_static! { diff --git a/src/lib.rs b/src/lib.rs index ef99c7b..202966b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,8 +57,10 @@ pub use toml; pub use uuid; pub mod fingerprint; pub use flexi_logger; -pub mod websocket; pub mod stream; +pub mod websocket; +#[cfg(not(any(target_os = "macos", target_os = "windows")))] +pub use rustls_platform_verifier; pub use stream::Stream; pub use whoami; diff --git a/src/proxy.rs b/src/proxy.rs index e32778f..29f8669 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -56,7 +56,6 @@ const MAXIMUM_RESPONSE_HEADERS: usize = 16; const DEFINE_TIME_OUT: u64 = 600; pub trait IntoUrl { - // Besides parsing as a valid `Url`, the `Url` must be a valid // `http::Uri`, in that it makes sense to use in a network request. fn into_url(self) -> Result; @@ -455,8 +454,10 @@ impl Proxy { Input: AsyncRead + AsyncWrite + Unpin, T: IntoTargetAddr<'a>, { + use rustls_platform_verifier::ConfigVerifierExt; use std::convert::TryFrom; - let verifier = rustls_platform_verifier::tls_config(); + let verifier = tokio_rustls::rustls::ClientConfig::with_platform_verifier() + .map_err(|e| ProxyError::IoError(std::io::Error::other(e)))?; let url_domain = self.intercept.get_domain()?; let domain = rustls_pki_types::ServerName::try_from(url_domain.as_str()) diff --git a/src/websocket.rs b/src/websocket.rs index e317a2f..829f0ca 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -8,7 +8,11 @@ use crate::{ ResultType, }; use bytes::{Bytes, BytesMut}; +#[cfg(any(target_os = "android", target_os = "ios"))] +use futures::future::{select_ok, FutureExt}; use futures::{SinkExt, StreamExt}; +#[cfg(any(target_os = "android", target_os = "ios"))] +use std::future::Future; use std::{ io::{Error, ErrorKind}, net::SocketAddr, @@ -28,6 +32,19 @@ pub struct WsFramedStream { send_timeout: u64, } +#[cfg(any(target_os = "android", target_os = "ios"))] +async fn await_timeout_result(future: F) -> ResultType +where + F: Future, tokio::time::error::Elapsed>>, + E: std::error::Error + Send + Sync + 'static, +{ + match future.await { + Ok(Ok(result)) => Ok(result), + Ok(Err(e)) => Err(e.into()), + Err(elapsed) => Err(Error::new(ErrorKind::TimedOut, elapsed).into()), + } +} + impl WsFramedStream { pub async fn new>( url: T, @@ -43,8 +60,57 @@ impl WsFramedStream { .into_client_request() .map_err(|e| Error::new(ErrorKind::Other, e))?; - let (stream, _) = - timeout(Duration::from_millis(ms_timeout), connect_async(request)).await??; + let stream; + #[cfg(any(target_os = "android", target_os = "ios"))] + { + let mut futures = vec![]; + + let is_wss = url_str.starts_with("wss://"); + let rustls_platform_verifier_initialized = !cfg!(target_os = "android") + || crate::config::RUSTLS_PLATFORM_VERIFIER_INITIALIZED + .load(std::sync::atomic::Ordering::Relaxed); + if is_wss && rustls_platform_verifier_initialized { + use rustls_platform_verifier::ConfigVerifierExt; + use std::sync::Arc; + use tokio_rustls::rustls::ClientConfig; + use tokio_tungstenite::{connect_async_tls_with_config, Connector}; + match ClientConfig::with_platform_verifier() { + Ok(config) => { + let connector = Connector::Rustls(Arc::new(config)); + futures.push( + await_timeout_result(timeout( + Duration::from_millis(ms_timeout), + connect_async_tls_with_config( + request.clone(), + None, + false, + Some(connector), + ), + )) + .boxed(), + ); + } + Err(e) => { + log::error!("with_platform_verifier failed: {:?}", e); + } + } + } + futures.push( + await_timeout_result(timeout( + Duration::from_millis(ms_timeout), + connect_async(request), + )) + .boxed(), + ); + let ((s, _), _) = select_ok(futures).await?; + stream = s; + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + let (s, _) = + timeout(Duration::from_millis(ms_timeout), connect_async(request)).await??; + stream = s; + } let addr = match stream.get_ref() { MaybeTlsStream::Plain(tcp) => tcp.peer_addr()?,