From ec9ef0f85eb44d3d2a0cfa49b825e4ad43e7737b Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Mon, 25 May 2026 21:29:55 +0800 Subject: [PATCH] fix: report legacy TLS subscription failures --- Changelog.md | 1 + src-tauri/src/cmd/profile.rs | 10 +++++++++- src-tauri/src/config/prfitem.rs | 2 +- src-tauri/src/utils/network.rs | 28 +++++++++++++++++++++++----- src/pages/profiles.tsx | 6 +++++- 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 14655af37..f3f60011c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ ### 🐞 修复问题 - macOS 托盘速率可能的样式错误 +- 订阅导入 TLS 1.0/1.1 等过旧协议时显示更明确错误原因
✨ 新增功能 diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index 932f466a0..8eb631dc7 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -24,6 +24,14 @@ use std::time::Duration; static CURRENT_SWITCHING_PROFILE: AtomicBool = AtomicBool::new(false); +fn profile_import_error(err: &anyhow::Error) -> std::string::String { + if let Some(cause) = err.chain().find(|cause| cause.to_string().contains("TLS 1.0/1.1")) { + return cause.to_string(); + } + + format!("导入订阅失败: {err:#}") +} + #[tauri::command] pub async fn get_profiles() -> CmdResult> { logging!(debug, Type::Cmd, "获取配置文件列表"); @@ -70,7 +78,7 @@ pub async fn import_profile(url: std::string::String, option: Option) } Err(e) => { logging!(error, Type::Cmd, "[导入订阅] 下载失败: {}", e); - return Err(format!("导入订阅失败: {}", e).into()); + return Err(profile_import_error(&e).into()); } }; diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index 58cc378a6..6b992ba1f 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -297,7 +297,7 @@ impl PrfItem { Ok(r) => r, Err(e) => { tokio::time::sleep(Duration::from_millis(100)).await; - bail!("failed to fetch remote profile: {}", e); + return Err(e).context("failed to fetch remote profile"); } }; diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index 5c3ed4e67..9fc30f36f 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -121,6 +121,10 @@ impl NetworkManager { } fn should_retry_with_static_webpki_roots(err: &anyhow::Error) -> bool { + if err.chain().any(Self::is_legacy_tls_protocol_error) { + return false; + } + err.chain().any(|e| { let msg = e.to_string().to_ascii_lowercase(); [ @@ -141,6 +145,22 @@ impl NetworkManager { }) } + fn context_reqwest_error(err: reqwest::Error, context: &'static str) -> anyhow::Error { + let legacy_tls = Self::is_legacy_tls_protocol_error(&err); + let err = anyhow::Error::new(err).context(context); + + if legacy_tls { + err.context("Subscription server uses legacy TLS; only TLS 1.2/1.3 is supported. TLS 1.0/1.1 is insecure") + } else { + err + } + } + + fn is_legacy_tls_protocol_error(err: &(dyn std::error::Error + 'static)) -> bool { + let detail = format!("{err:#?}").to_ascii_lowercase(); + detail.contains("protocolversion") || detail.contains("protocol version") + } + pub async fn create_request( &self, proxy_type: ProxyType, @@ -207,7 +227,7 @@ impl NetworkManager { let response = match request_builder.send().await { Ok(resp) => resp, Err(e) => { - return Err(anyhow::Error::new(e).context("Request failed")); + return Err(Self::context_reqwest_error(e, "Request failed")); } }; @@ -216,7 +236,7 @@ impl NetworkManager { let body = match response.text().await { Ok(text) => text.into(), Err(e) => { - return Err(anyhow::anyhow!("Failed to read response body: {}", e)); + return Err(Self::context_reqwest_error(e, "Failed to read response body")); } }; @@ -299,9 +319,7 @@ impl NetworkManager { ) .await .map_err(|fallback_err| { - anyhow::anyhow!( - "platform TLS verifier failed: {err}; static webpki roots fallback failed: {fallback_err}" - ) + fallback_err.context("static webpki roots fallback failed after platform TLS verifier failed") }), Err(err) => Err(err), } diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index 35d1062f9..922b305bb 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -292,7 +292,6 @@ const ProfilePage = () => { setUrl('') await performRobustRefresh() } - try { // 尝试正常导入 await importProfile(url) @@ -300,6 +299,11 @@ const ProfilePage = () => { } catch (initialErr) { console.warn('[订阅导入] 首次导入失败:', initialErr) + if (String(initialErr).toLowerCase().includes('legacy tls')) { + showNotice.error(String(initialErr)) + return + } + showNotice.info('profiles.page.feedback.notifications.importRetry') try { // 使用自身代理尝试导入