From 7d8f98e1838e4cf06e9c443954216d0d0193cbfe Mon Sep 17 00:00:00 2001 From: Arcanum Date: Fri, 31 Jul 2026 09:44:56 +0800 Subject: [PATCH] fix(macos): keep DNS state outside app bundle (#7544) * fix(macos): keep DNS state outside app bundle * fix(macos): restore legacy DNS state --------- Co-authored-by: Arcanum <260421450+ArcanePivot@users.noreply.github.com> Co-authored-by: wonfen --- src-tauri/src/utils/resolve/dns.rs | 58 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/utils/resolve/dns.rs b/src-tauri/src/utils/resolve/dns.rs index 64005d047..83e8a37fb 100644 --- a/src-tauri/src/utils/resolve/dns.rs +++ b/src-tauri/src/utils/resolve/dns.rs @@ -1,4 +1,22 @@ use clash_verge_logging::{Type, logging}; +use std::path::{Path, PathBuf}; + +const DNS_STATE_FILE: &str = ".original_dns.txt"; + +fn dns_state_dir() -> anyhow::Result { + // The DNS scripts persist .original_dns.txt relative to their working directory. + let dir = crate::utils::dirs::app_home_dir()?; + std::fs::create_dir_all(&dir)?; + Ok(dir) +} + +fn restore_dns_state_dir(resource_dir: &Path, state_dir: PathBuf) -> PathBuf { + if resource_dir.join(DNS_STATE_FILE).exists() { + resource_dir.to_path_buf() + } else { + state_dir + } +} pub async fn set_public_dns(dns_server: String) { use crate::{core::handle, utils::dirs}; @@ -19,11 +37,18 @@ pub async fn set_public_dns(dns_server: String) { return; } let script = script.to_string_lossy().into_owned(); + let state_dir = match dns_state_dir() { + Ok(dir) => dir, + Err(e) => { + logging!(error, Type::Config, "Failed to get DNS state directory: {}", e); + return; + } + }; match app_handle .shell() .command("bash") .args([script, dns_server]) - .current_dir(resource_dir) + .current_dir(state_dir) .status() .await { @@ -60,11 +85,19 @@ pub async fn restore_public_dns() { return; } let script = script.to_string_lossy().into_owned(); + let state_dir = match dns_state_dir() { + Ok(dir) => dir, + Err(e) => { + logging!(error, Type::Config, "Failed to get DNS state directory: {}", e); + return; + } + }; + let state_dir = restore_dns_state_dir(&resource_dir, state_dir); match app_handle .shell() .command("bash") .args([script]) - .current_dir(resource_dir) + .current_dir(state_dir) .status() .await { @@ -81,3 +114,24 @@ pub async fn restore_public_dns() { } } } + +#[cfg(test)] +mod tests { + use super::restore_dns_state_dir; + + #[test] + #[allow(clippy::expect_used)] + fn restore_dns_state_dir_defaults_to_app_data_but_honors_legacy_file() { + let root = std::env::temp_dir().join(format!("clash-verge-dns-{}", nanoid::nanoid!())); + let resource_dir = root.join("resources"); + let state_dir = root.join("app-data"); + std::fs::create_dir_all(&resource_dir).expect("create test resource directory"); + + assert_eq!(restore_dns_state_dir(&resource_dir, state_dir.clone()), state_dir); + + std::fs::write(resource_dir.join(".original_dns.txt"), "empty").expect("create legacy DNS state"); + assert_eq!(restore_dns_state_dir(&resource_dir, state_dir), resource_dir); + + std::fs::remove_dir_all(root).expect("remove test directory"); + } +}