Skip to content

Commit 03a98e9

Browse files
committed
feat: 根据 DNS 自动启用 localhost 补丁
- 删除公开的 AllowLocalhost 配置 section - 检测所有 DNS 服务器字段中的 localhost 与回环 IP - DNS 启用且指向本机时自动应用 AM Daemon localhost 补丁 - 添加 URL、端口、IPv4 与 IPv6 回环地址回归测试
1 parent 8e2381a commit 03a98e9

5 files changed

Lines changed: 110 additions & 26 deletions

File tree

packages/applechu-amdaemon/src/amdaemon_patch.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use applechu::amdaemon::{AllowLocalhostConfig, CreditFreezeConfig};
1+
use applechu::amdaemon::{CreditFreezeConfig, DnsConfig};
22
use applechu::config::Config;
33
use applechu::patch_engine::{apply_patch, PatchVariant, VersionedPatch};
44
use applechu::util::api::Api;
@@ -19,8 +19,8 @@ pub(crate) fn apply_pre_tls() {
1919
return;
2020
};
2121
if config
22-
.section::<AllowLocalhostConfig>()
23-
.is_some_and(|section| section.enabled)
22+
.section::<DnsConfig>()
23+
.is_some_and(|section| section.enabled && section.requires_localhost_patch())
2424
{
2525
apply_localhost(&api);
2626
}

packages/applechu-schema/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,6 @@ mod tests {
10241024
"System",
10251025
"Amdaemon",
10261026
"Dns",
1027-
"AllowLocalhost",
10281027
"Keychip",
10291028
"Aime",
10301029
"Io4",

packages/applechu/src/amdaemon.rs

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,6 @@ crate::config_section! {
129129
}
130130
}
131131

132-
crate::config_section! {
133-
pub struct AllowLocalhostConfig => ALLOW_LOCALHOST_CONFIG_SECTION {
134-
section: "AllowLocalhost",
135-
order: 35,
136-
default_on: false,
137-
always_enabled: false,
138-
hidden: false,
139-
comment: "允许 127.0.0.1/localhost 作为网络服务器",
140-
fields: {}
141-
}
142-
}
143-
144132
crate::config_section! {
145133
pub struct CreditFreezeConfig => CREDIT_FREEZE_CONFIG_SECTION {
146134
section: "CreditFreeze",
@@ -153,6 +141,58 @@ crate::config_section! {
153141
}
154142
}
155143

144+
impl DnsConfig {
145+
pub fn requires_localhost_patch(&self) -> bool {
146+
[
147+
&self.default,
148+
&self.router,
149+
&self.startup,
150+
&self.billing,
151+
&self.aimedb,
152+
&self.title,
153+
]
154+
.into_iter()
155+
.any(|target| is_loopback_target(target))
156+
}
157+
}
158+
159+
fn is_loopback_target(target: &str) -> bool {
160+
let target = target.trim();
161+
let authority = target
162+
.split_once("://")
163+
.map_or(target, |(_, authority)| authority)
164+
.split(['/', '?', '#'])
165+
.next()
166+
.unwrap_or("");
167+
let authority = authority
168+
.rsplit_once('@')
169+
.map_or(authority, |(_, host)| host);
170+
let host = endpoint_host(authority).trim_end_matches('.');
171+
host.eq_ignore_ascii_case("localhost")
172+
|| host
173+
.to_ascii_lowercase()
174+
.strip_suffix(".localhost")
175+
.is_some_and(|prefix| !prefix.is_empty())
176+
|| host
177+
.parse::<std::net::IpAddr>()
178+
.is_ok_and(|address| address.is_loopback())
179+
}
180+
181+
fn endpoint_host(authority: &str) -> &str {
182+
if authority.parse::<std::net::IpAddr>().is_ok() {
183+
return authority;
184+
}
185+
if let Some(bracketed) = authority.strip_prefix('[') {
186+
return bracketed
187+
.split_once(']')
188+
.map_or(bracketed, |(host, _)| host);
189+
}
190+
authority
191+
.rsplit_once(':')
192+
.filter(|(_, port)| !port.is_empty() && port.bytes().all(|byte| byte.is_ascii_digit()))
193+
.map_or(authority, |(host, _)| host)
194+
}
195+
156196
crate::config_section! {
157197
pub struct DnsConfig => DNS_CONFIG_SECTION {
158198
section: "Dns",

packages/applechu/src/config/tests.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{Config, DiagnosticLevel};
2-
use crate::amdaemon::{AllowLocalhostConfig, AmdaemonConfig, CreditFreezeConfig, EpayConfig};
2+
use crate::amdaemon::{AmdaemonConfig, CreditFreezeConfig, EpayConfig};
33
use crate::system_config::SystemConfig;
44

55
mod amdaemon;
@@ -106,22 +106,14 @@ fn amdaemon_is_a_container_with_independent_controls() {
106106
}
107107

108108
#[test]
109-
fn amdaemon_patch_features_have_independent_sections() {
109+
fn credit_freeze_has_independent_section() {
110110
let source = concat!(
111111
"config_version = 1\n",
112-
"[AllowLocalhost]\n",
113-
"enable = true\n",
114112
"[CreditFreeze]\n",
115113
"enable = false\n",
116114
);
117115
let config = Config::parse(".", source).expect("测试配置必须有效");
118116

119-
assert!(
120-
config
121-
.section::<AllowLocalhostConfig>()
122-
.expect("localhost section 必须存在")
123-
.enabled
124-
);
125117
assert!(
126118
!config
127119
.section::<CreditFreezeConfig>()

packages/applechu/src/config/tests/amdaemon.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,59 @@ fn empty_dns_section_is_filled_by_game_proxy() {
5353
assert!(output.contains("#Title = \"\""));
5454
}
5555

56+
#[test]
57+
fn dns_detects_loopback_targets_across_server_fields() {
58+
// Given: 每个 DNS 服务器字段分别使用一种本机地址写法。
59+
for entry in [
60+
"Default = \"localhost\"",
61+
"Router = \"LOCALHOST.\"",
62+
"Startup = \"127.8.9.10:8080\"",
63+
"Billing = \"http://127.0.0.1:8443\"",
64+
"Aimedb = \"[::1]:22345\"",
65+
"Title = \"::1\"",
66+
"Title = \"service.localhost\"",
67+
] {
68+
let source = format!("ConfigVersion = 1\n[Dns]\n{entry}\n");
69+
let config = Config::parse(".", &source).expect("测试配置必须有效");
70+
let dns = config
71+
.section::<crate::amdaemon::DnsConfig>()
72+
.expect("DNS 配置必须完成注入");
73+
74+
// When: AM Daemon 判断是否需要本机服务器补丁。
75+
let required = dns.enabled && dns.requires_localhost_patch();
76+
77+
// Then: 任一字段指向回环目标都会自动启用补丁。
78+
assert!(required, "未识别本机 DNS 目标: {entry}");
79+
}
80+
}
81+
82+
#[test]
83+
fn dns_does_not_enable_localhost_patch_for_remote_or_disabled_mapping() {
84+
// Given: 一个远程 DNS 映射,以及一个明确禁用的本机映射。
85+
let remote = Config::parse(
86+
".",
87+
"ConfigVersion = 1\n[Dns]\nDefault = \"server.example.com\"\n",
88+
)
89+
.expect("远程 DNS 配置必须有效");
90+
let disabled = Config::parse(
91+
".",
92+
"ConfigVersion = 1\n[Dns]\nEnable = false\nDefault = \"127.0.0.1\"\n",
93+
)
94+
.expect("禁用 DNS 配置必须有效");
95+
96+
// When: AM Daemon 计算 localhost 补丁状态。
97+
let remote = remote
98+
.section::<crate::amdaemon::DnsConfig>()
99+
.expect("DNS 配置必须完成注入");
100+
let disabled = disabled
101+
.section::<crate::amdaemon::DnsConfig>()
102+
.expect("DNS 配置必须完成注入");
103+
104+
// Then: 远程目标和禁用的 DNS 均不启用补丁。
105+
assert!(!remote.requires_localhost_patch());
106+
assert!(!(disabled.enabled && disabled.requires_localhost_patch()));
107+
}
108+
56109
#[test]
57110
fn required_internal_sections_are_not_emitted() {
58111
let source = concat!(

0 commit comments

Comments
 (0)