Exclusive Mask + Startup Speed-up

Signed-off-by: Alexey <247128645+axkurcom@users.noreply.github.com>
This commit is contained in:
Alexey
2026-05-19 21:56:26 +03:00
parent 9e877e45c9
commit 914f141715
14 changed files with 529 additions and 109 deletions
+1
View File
@@ -617,6 +617,7 @@ fn warn_non_hot_changes(old: &ProxyConfig, new: &ProxyConfig, non_hot_changed: b
|| old.censorship.mask != new.censorship.mask
|| old.censorship.mask_host != new.censorship.mask_host
|| old.censorship.mask_port != new.censorship.mask_port
|| old.censorship.exclusive_mask != new.censorship.exclusive_mask
|| old.censorship.mask_unix_sock != new.censorship.mask_unix_sock
|| old.censorship.fake_cert_len != new.censorship.fake_cert_len
|| old.censorship.tls_emulation != new.censorship.tls_emulation
+81
View File
@@ -31,6 +31,30 @@ fn is_valid_tls_domain_name(domain: &str) -> bool {
.any(|ch| ch.is_whitespace() || matches!(ch, '/' | '\\'))
}
fn parse_exclusive_mask_target(target: &str) -> Option<(&str, u16)> {
let target = target.trim();
if target.is_empty() {
return None;
}
if target.starts_with('[') {
let end = target.find(']')?;
if target.get(end + 1..end + 2)? != ":" {
return None;
}
let host = &target[..=end];
let port = target[end + 2..].parse::<u16>().ok()?;
return (port > 0).then_some((host, port));
}
let (host, port) = target.rsplit_once(':')?;
if host.is_empty() || host.contains(':') {
return None;
}
let port = port.parse::<u16>().ok()?;
(port > 0).then_some((host, port))
}
const TOP_LEVEL_CONFIG_KEYS: &[&str] = &[
"general",
"network",
@@ -291,6 +315,7 @@ const CENSORSHIP_CONFIG_KEYS: &[&str] = &[
"mask",
"mask_host",
"mask_port",
"exclusive_mask",
"mask_unix_sock",
"fake_cert_len",
"tls_emulation",
@@ -1923,6 +1948,21 @@ impl ProxyConfig {
config.censorship.mask_host = Some(config.censorship.tls_domain.clone());
}
for (domain, target) in &config.censorship.exclusive_mask {
if !is_valid_tls_domain_name(domain) {
return Err(ProxyError::Config(format!(
"Invalid censorship.exclusive_mask domain: '{}'. Must be a valid domain name",
domain
)));
}
if parse_exclusive_mask_target(target).is_none() {
return Err(ProxyError::Config(format!(
"Invalid censorship.exclusive_mask target for '{}': '{}'. Expected host:port with port > 0",
domain, target
)));
}
}
// Normalize optional TLS fetch scope: whitespace-only values disable scoped routing.
config.censorship.tls_fetch_scope = config.censorship.tls_fetch_scope.trim().to_string();
@@ -2126,6 +2166,21 @@ impl ProxyConfig {
}
}
for (domain, target) in &self.censorship.exclusive_mask {
if !is_valid_tls_domain_name(domain) {
return Err(ProxyError::Config(format!(
"Invalid censorship.exclusive_mask domain: '{}'. Must be a valid domain name",
domain
)));
}
if parse_exclusive_mask_target(target).is_none() {
return Err(ProxyError::Config(format!(
"Invalid censorship.exclusive_mask target for '{}': '{}'. Expected host:port with port > 0",
domain, target
)));
}
}
for (user, tag) in &self.access.user_ad_tags {
let zeros = "00000000000000000000000000000000";
if !is_valid_ad_tag(tag) {
@@ -2667,6 +2722,32 @@ mod tests {
);
}
#[test]
fn exclusive_mask_parses_domain_target_map() {
let cfg = load_config_from_temp_toml(
r#"
[general]
[network]
[server]
[access]
[censorship]
tls_domain = "example.com"
[censorship.exclusive_mask]
"my-site.com" = "127.0.0.1:8443"
"ipv6.example" = "[::1]:9443"
"#,
);
assert_eq!(
cfg.censorship.exclusive_mask.get("my-site.com"),
Some(&"127.0.0.1:8443".to_string())
);
assert_eq!(
cfg.censorship.exclusive_mask.get("ipv6.example"),
Some(&"[::1]:9443".to_string())
);
}
#[test]
fn api_gray_action_parses_and_defaults_to_drop() {
let cfg_default: ProxyConfig = toml::from_str(
+5
View File
@@ -1719,6 +1719,10 @@ pub struct AntiCensorshipConfig {
#[serde(default = "default_mask_port")]
pub mask_port: u16,
/// Per-SNI TCP mask targets. Keys are SNI domains, values are `host:port`.
#[serde(default)]
pub exclusive_mask: HashMap<String, String>,
#[serde(default)]
pub mask_unix_sock: Option<String>,
@@ -1842,6 +1846,7 @@ impl Default for AntiCensorshipConfig {
mask: default_true(),
mask_host: None,
mask_port: default_mask_port(),
exclusive_mask: HashMap::new(),
mask_unix_sock: None,
fake_cert_len: default_fake_cert_len(),
tls_emulation: true,