From a4534b66623cb7f228e5f30aeed62fca6f9940b0 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Nov 2025 12:39:11 +0800 Subject: [PATCH] refact: tls fallback, rustls -> native-tls Signed-off-by: fufesou --- src/proxy.rs | 153 +++++++++++++++++++++++++---------------------- src/websocket.rs | 16 ++--- 2 files changed, 89 insertions(+), 80 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index a69c110..d3b8a76 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -411,9 +411,9 @@ impl Proxy { let url = format!("https://{}", self.intercept.get_host_and_port()?); let tls_type = get_cached_tls_type(&url); let danger_accept_invalid_cert = get_cached_tls_accept_invalid_cert(&url); - let stream = match tls_type.unwrap_or(TlsType::NativeTls) { - TlsType::NativeTls => { - self.https_connect_nativetls_wrap_danger( + let stream = match tls_type.unwrap_or(TlsType::Rustls) { + TlsType::Rustls => { + self.https_connect_rustls_wrap_danger( &url, local, proxy, @@ -425,8 +425,8 @@ impl Proxy { ) .await? } - TlsType::Rustls => { - self.https_connect_rustls_wrap_danger( + TlsType::NativeTls => { + self.https_connect_nativetls_wrap_danger( &url, local, proxy, @@ -477,77 +477,30 @@ impl Proxy { }; } - #[async_recursion] async fn https_connect_nativetls_wrap_danger<'a>( &self, url: &str, local: SocketAddr, proxy: SocketAddr, - stream: Option, target_addr: &TargetAddr<'a>, - is_tls_type_cached: bool, danger_accept_invalid_cert: Option, - origin_danger_accept_invalid_cert: Option, ) -> ResultType { - let stream = stream.unwrap_or(self.new_stream(local, proxy).await?); - match super::timeout( + let stream = self.new_stream(local, proxy).await?; + let s = super::timeout( self.ms_timeout, self.https_connect_nativetls( stream, - target_addr, + &target_addr, danger_accept_invalid_cert.unwrap_or(false), ), ) - .await? - { - Ok(s) => { - upsert_tls_cache( - &url, - TlsType::NativeTls, - danger_accept_invalid_cert.unwrap_or(false), - ); - Ok(DynTcpStream(Box::new(s))) - } - Err(ProxyError::NativeTlsError(e)) => { - let s = if danger_accept_invalid_cert.is_none() { - log::warn!( - "Falling back to native-tls (accept invalid cert) for HTTPS proxy server." - ); - self.https_connect_nativetls_wrap_danger( - &url, - local, - proxy, - None, - target_addr, - is_tls_type_cached, - Some(true), - origin_danger_accept_invalid_cert, - ) - .await? - } else if !is_tls_type_cached { - log::warn!("Falling back to rustls for HTTPS proxy server."); - self.https_connect_rustls_wrap_danger( - &url, - local, - proxy, - &target_addr, - origin_danger_accept_invalid_cert, - ) - .await? - } else { - log::error!( - "Failed to connect to HTTPS proxy server with native-tls: {:?}.", - e - ); - bail!(e) - }; - Ok(s) - } - Err(e) => { - log::error!("Failed to connect to HTTPS proxy server: {:?}.", e); - bail!(e) - } - } + .await??; + upsert_tls_cache( + url, + TlsType::NativeTls, + danger_accept_invalid_cert.unwrap_or(false), + ); + Ok(DynTcpStream(Box::new(s))) } pub async fn https_connect_nativetls<'a, Input>( @@ -570,30 +523,86 @@ impl Proxy { self.http_connect(stream, target_addr).await } + #[async_recursion] async fn https_connect_rustls_wrap_danger<'a>( &self, url: &str, local: SocketAddr, proxy: SocketAddr, + stream: Option, target_addr: &TargetAddr<'a>, + is_tls_type_cached: bool, danger_accept_invalid_cert: Option, + origin_danger_accept_invalid_cert: Option, ) -> ResultType { - let stream = self.new_stream(local, proxy).await?; - let s = super::timeout( + let stream = stream.unwrap_or(self.new_stream(local, proxy).await?); + match super::timeout( self.ms_timeout, self.https_connect_rustls( stream, - &target_addr, + target_addr, danger_accept_invalid_cert.unwrap_or(false), ), ) - .await??; - upsert_tls_cache( - url, - TlsType::Rustls, - danger_accept_invalid_cert.unwrap_or(false), - ); - Ok(DynTcpStream(Box::new(s))) + .await? + { + Ok(s) => { + upsert_tls_cache( + &url, + TlsType::Rustls, + danger_accept_invalid_cert.unwrap_or(false), + ); + Ok(DynTcpStream(Box::new(s))) + } + Err(e) => { + // NOTE: Maybe it's better to check if the error is related to TLS here. (ProxyError::IoError(e), or ProxyError::NativeTlsError(e)) + // But we can only get the error when the TLS protocol is TLSv1.1. + // The error message of the following is unclear: + // https://github.com/rustdesk/rustdesk-server-pro/issues/189#issuecomment-1895701480 + // So we just try to fallback unconditionally here. + // + // If the protocol is TLS 1.1, the error is: + // 1. "IO Error: received fatal alert: ProtocolVersion" + // 2. "IO Error: An existing connection was forcibly closed by the remote host. (os error 10054)" on Windows sometimes. + // + // If the cert verification fails, the error is: + // "IO Error: invalid peer certificate: UnknownIssuer" + + let s = if danger_accept_invalid_cert.is_none() { + log::warn!( + "Falling back to rustls-tls (accept invalid cert) for HTTPS proxy server." + ); + self.https_connect_rustls_wrap_danger( + &url, + local, + proxy, + None, + target_addr, + is_tls_type_cached, + Some(true), + origin_danger_accept_invalid_cert, + ) + .await? + } else if !is_tls_type_cached { + log::warn!("Falling back to native-tls for HTTPS proxy server."); + self.https_connect_nativetls_wrap_danger( + &url, + local, + proxy, + &target_addr, + origin_danger_accept_invalid_cert, + ) + .await? + } else { + log::error!( + "Failed to connect to HTTPS proxy server with native-tls: {:?}.", + e + ); + bail!(e) + }; + Ok(s) + } + } } pub async fn https_connect_rustls<'a, Input>( diff --git a/src/websocket.rs b/src/websocket.rs index dc1e981..9be3e89 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -73,7 +73,7 @@ impl WsFramedStream { let tls_type = get_cached_tls_type(url); let is_tls_type_cached = tls_type.is_some(); - let tls_type = tls_type.unwrap_or(TlsType::NativeTls); + let tls_type = tls_type.unwrap_or(TlsType::Rustls); let danger_accept_invalid_cert = get_cached_tls_accept_invalid_cert(&url); Self::try_connect( url, @@ -113,9 +113,9 @@ impl WsFramedStream { Ok(ws_stream) } Err(e) => match (tls_type, is_tls_type_cached, danger_accept_invalid_cert) { - (TlsType::NativeTls, _, None) => { + (TlsType::Rustls, _, None) => { log::warn!( - "WebSocket connection with native-tls failed, try accept invalid certs: {}, {:?}", + "WebSocket connection with rustls-tls failed, try accept invalid certs: {}, {:?}", url, e ); @@ -129,25 +129,25 @@ impl WsFramedStream { ) .await } - (TlsType::NativeTls, false, Some(_)) => { + (TlsType::Rustls, false, Some(_)) => { log::warn!( - "WebSocket connection with native-tls failed, try rustls: {}, {:?}", + "WebSocket connection with rustls-tls failed, try native-tls: {}, {:?}", url, e ); Self::try_connect( url, ms_timeout, - TlsType::Rustls, + TlsType::NativeTls, is_tls_type_cached, original_danger_accept_invalid_certs, original_danger_accept_invalid_certs, ) .await } - (TlsType::Rustls, _, None) => { + (TlsType::NativeTls, _, None) => { log::warn!( - "WebSocket connection with rustls failed, try accept invalid certs: {}, {:?}", + "WebSocket connection with native-tls failed, try accept invalid certs: {}, {:?}", url, e );