mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-06 00:32:13 +08:00
fix(macos): restore system DNS reliably after TUN
Detached tasks spawned on every config generation set and restored the placeholder DNS without ordering, so a set could back up the placeholder itself and a later restore reported success while leaving it in place. Reconcile from the applied config under the lifecycle lock, and restore whenever the core stops. Keep one backup per network service, never back up the placeholder, and restore only while the service still holds it. Scripts verify every read and write and are killed after 5 s. Refs #8070, #8168
This commit is contained in:
@@ -311,6 +311,12 @@ impl CoreManager {
|
||||
let run_path = Config::write_runtime_file(&yaml).await?;
|
||||
self.apply_config(run_path).await?;
|
||||
transaction.commit();
|
||||
// Under the lifecycle lock, so a stop for exit waits for this write instead of outrunning it.
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let _lifecycle = self.lifecycle_lock.lock().await;
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
}
|
||||
Ok(ValidationOutcome::Valid)
|
||||
}
|
||||
|
||||
|
||||
@@ -515,7 +515,13 @@ impl CoreManager {
|
||||
}
|
||||
|
||||
async fn stop_sidecar_after_proxy_clear(&self) -> Result<()> {
|
||||
run_sidecar_termination_transition(proxy_control::clear, || self.stop_core_by_sidecar_unprepared()).await
|
||||
let result =
|
||||
run_sidecar_termination_transition(proxy_control::clear, || self.stop_core_by_sidecar_unprepared()).await;
|
||||
#[cfg(target_os = "macos")]
|
||||
if matches!(*self.get_running_mode(), RunningMode::NotRunning) {
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) async fn replace_service_core_with_config(&self, config_file: &Path) -> Result<()> {
|
||||
@@ -544,6 +550,8 @@ impl CoreManager {
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("cannot apply system proxy before core readiness").context(SysproxyFailure::CoreNotReady)
|
||||
})?;
|
||||
#[cfg(target_os = "macos")]
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
// At login the app usually beats the network. Leave the write to the network watcher
|
||||
// rather than fail — only if the watcher is actually live; a user toggling while
|
||||
// offline still fails fast.
|
||||
@@ -615,6 +623,8 @@ impl CoreManager {
|
||||
tracing::Span::current().record("decision", tracing::field::debug(&startup));
|
||||
if matches!(startup, StartupDecision::Wait) {
|
||||
self.rollback_failed_start().await;
|
||||
#[cfg(target_os = "macos")]
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
#[cfg(target_os = "windows")]
|
||||
self.try_sidecar_after_service_unavailable().await?;
|
||||
return Ok(());
|
||||
@@ -649,6 +659,8 @@ impl CoreManager {
|
||||
// A failed start must not leave a locally reported running mode.
|
||||
if result.is_err() {
|
||||
self.rollback_failed_start().await;
|
||||
#[cfg(target_os = "macos")]
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -737,11 +749,17 @@ impl CoreManager {
|
||||
|
||||
async fn stop_core_unprepared_inner(&self) -> Result<()> {
|
||||
CLASH_LOGGER.clear_logs();
|
||||
match *self.get_running_mode() {
|
||||
let result = match *self.get_running_mode() {
|
||||
RunningMode::Service => self.stop_core_by_service().await,
|
||||
RunningMode::Sidecar => self.stop_core_by_sidecar_unprepared().await,
|
||||
RunningMode::NotRunning => Ok(()),
|
||||
};
|
||||
// A Sidecar whose exit wait failed is already marked stopped; a core still running keeps its DNS.
|
||||
#[cfg(target_os = "macos")]
|
||||
if matches!(*self.get_running_mode(), RunningMode::NotRunning) {
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info")]
|
||||
|
||||
@@ -394,6 +394,8 @@ impl CoreManager {
|
||||
self.set_job_handle(None);
|
||||
proxy_control::stop_guard().await;
|
||||
self.core_stopped();
|
||||
#[cfg(target_os = "macos")]
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1761,12 +1761,18 @@ async fn recover_after_owner_loss_while_locked(reason: OwnerRecoveryReason) {
|
||||
);
|
||||
mark_service_unavailable_after_owner_loss(&RUN_STATE, reason);
|
||||
proxy_control::stop_guard().await;
|
||||
let policy = owner_recovery_policy(reason, cfg!(target_os = "macos"));
|
||||
// Clear while still in Service mode with the session: on macOS it routes through the helper.
|
||||
if owner_recovery_policy(reason, cfg!(target_os = "macos")).reset_system_proxy {
|
||||
if policy.reset_system_proxy {
|
||||
clear_proxy_after_owner_loss().await;
|
||||
}
|
||||
clear_active_service_session();
|
||||
CoreManager::global().core_stopped();
|
||||
// A displaced Service may still run TUN for its new owner, and DNS settings are system-wide.
|
||||
#[cfg(target_os = "macos")]
|
||||
if policy.reset_system_proxy {
|
||||
crate::utils::resolve::dns::sync_public_dns().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn clear_proxy_after_owner_loss() {
|
||||
|
||||
Reference in New Issue
Block a user