Skip to content

Commit 6bf88ba

Browse files
committed
Fix Windows hosts precedence and DNS refresh
1 parent 5dd070b commit 6bf88ba

2 files changed

Lines changed: 90 additions & 2 deletions

File tree

src/hosts.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashSet;
12
use std::fs;
23
use std::path::PathBuf;
34

@@ -111,7 +112,14 @@ pub fn validate_hosts_backup_file(paths: &AppPaths) -> Result<()> {
111112
#[cfg(not(target_os = "android"))]
112113
fn render_managed_hosts(original: &str, config: &AppConfig) -> String {
113114
let newline = detect_newline(original);
114-
let mut content = strip_managed_block(original);
115+
let managed_hosts = config
116+
.hosts_domains()
117+
.iter()
118+
.filter(|host| !host.starts_with("*."))
119+
.map(|host| host.to_ascii_lowercase())
120+
.collect::<HashSet<_>>();
121+
let mut content =
122+
strip_conflicting_host_entries(&strip_managed_block(original), &managed_hosts);
115123

116124
if !content.is_empty() && !content.ends_with(newline) {
117125
content.push_str(newline);
@@ -131,6 +139,57 @@ fn render_managed_hosts(original: &str, config: &AppConfig) -> String {
131139
content
132140
}
133141

142+
#[cfg(not(target_os = "android"))]
143+
fn strip_conflicting_host_entries(content: &str, managed_hosts: &HashSet<String>) -> String {
144+
if managed_hosts.is_empty() {
145+
return content.to_string();
146+
}
147+
148+
let newline = detect_newline(content);
149+
let mut output = Vec::new();
150+
151+
for raw_line in content.lines() {
152+
let trimmed = raw_line.trim();
153+
if trimmed.is_empty() || trimmed.starts_with('#') {
154+
output.push(raw_line.to_string());
155+
continue;
156+
}
157+
158+
let (line_body, comment) = match raw_line.find('#') {
159+
Some(index) => (&raw_line[..index], Some(&raw_line[index..])),
160+
None => (raw_line, None),
161+
};
162+
163+
let fields = line_body.split_whitespace().collect::<Vec<_>>();
164+
if fields.len() < 2 {
165+
output.push(raw_line.to_string());
166+
continue;
167+
}
168+
169+
let address = fields[0];
170+
let remaining_hosts = fields[1..]
171+
.iter()
172+
.copied()
173+
.filter(|host| !managed_hosts.contains(&host.to_ascii_lowercase()))
174+
.collect::<Vec<_>>();
175+
176+
if remaining_hosts.is_empty() {
177+
continue;
178+
}
179+
180+
let mut rebuilt = format!("{address} {}", remaining_hosts.join(" "));
181+
if let Some(comment) = comment {
182+
if !comment.trim().is_empty() {
183+
rebuilt.push(' ');
184+
rebuilt.push_str(comment.trim_start());
185+
}
186+
}
187+
output.push(rebuilt);
188+
}
189+
190+
output.join(newline)
191+
}
192+
134193
#[cfg(not(target_os = "android"))]
135194
fn backup_baseline_content(original: &str) -> String {
136195
strip_managed_block(original)
@@ -245,8 +304,12 @@ fn hosts_path() -> PathBuf {
245304

246305
#[cfg(test)]
247306
mod tests {
248-
use super::{backup_baseline_content, render_managed_hosts, strip_managed_block};
307+
use super::{
308+
backup_baseline_content, render_managed_hosts, strip_conflicting_host_entries,
309+
strip_managed_block,
310+
};
249311
use crate::config::AppConfig;
312+
use std::collections::HashSet;
250313

251314
#[test]
252315
fn strip_managed_block_only_removes_owned_lines() {
@@ -318,4 +381,25 @@ mod tests {
318381
let backup = backup_baseline_content(&content);
319382
assert_eq!(backup, "127.0.0.1 localhost\n1.1.1.1 example.com");
320383
}
384+
385+
#[test]
386+
fn strip_conflicting_host_entries_removes_older_duplicate_host_mappings() {
387+
let content = [
388+
"127.0.0.1 localhost",
389+
"172.66.166.61 linux.do",
390+
"8.8.8.8 example.com www.linux.do # keep example only",
391+
"1.1.1.1 untouched.example",
392+
]
393+
.join("\n");
394+
let managed_hosts = ["linux.do", "www.linux.do"]
395+
.into_iter()
396+
.map(str::to_string)
397+
.collect::<HashSet<_>>();
398+
399+
let stripped = strip_conflicting_host_entries(&content, &managed_hosts);
400+
401+
assert!(!stripped.contains("172.66.166.61 linux.do"));
402+
assert!(stripped.contains("8.8.8.8 example.com # keep example only"));
403+
assert!(stripped.contains("1.1.1.1 untouched.example"));
404+
}
321405
}

src/service.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub fn setup(config_path: Option<PathBuf>) -> Result<()> {
4141
let bundle = ensure_bundle(&config, &paths.cert_dir)?;
4242
install_ca(&bundle.ca_cert_path, &config.ca_common_name)?;
4343
apply_hosts(&config, &paths)?;
44+
let _ = flush_dns_cache();
4445
state::mark_stopped(&paths, "系统加速环境已准备")?;
4546
Ok(())
4647
})();
@@ -89,6 +90,7 @@ pub async fn run_foreground(config_path: Option<PathBuf>, with_setup: bool) -> R
8990
if with_setup {
9091
install_ca(&bundle.ca_cert_path, &config.ca_common_name)?;
9192
apply_hosts(&config, &paths)?;
93+
let _ = flush_dns_cache();
9294
}
9395

9496
let pid = std::process::id();
@@ -129,6 +131,7 @@ pub fn helper_start(config_path: Option<PathBuf>) -> Result<()> {
129131
#[cfg(target_os = "macos")]
130132
let _ = bundle;
131133
apply_hosts(&config, &paths)?;
134+
let _ = flush_dns_cache();
132135
state::mark_starting(&paths)?;
133136

134137
let cli_binary = current_cli_binary()?;
@@ -285,6 +288,7 @@ pub fn apply_hosts_only(config_path: Option<PathBuf>) -> Result<()> {
285288
ensure_elevated(&config, true)?;
286289
ensure_loopback_alias(&config)?;
287290
apply_hosts(&config, &paths)?;
291+
let _ = flush_dns_cache();
288292
Ok(())
289293
})();
290294
match &result {

0 commit comments

Comments
 (0)