mirror of
https://github.com/telemt/telemt.git
synced 2026-06-11 13:31:45 +03:00
Add dynamic SNI mask target mode
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -620,6 +620,7 @@ fn warn_non_hot_changes(old: &ProxyConfig, new: &ProxyConfig, non_hot_changed: b
|
||||
|| old.censorship.tls_domains != new.censorship.tls_domains
|
||||
|| old.censorship.tls_fetch_scope != new.censorship.tls_fetch_scope
|
||||
|| old.censorship.mask != new.censorship.mask
|
||||
|| old.censorship.mask_dynamic != new.censorship.mask_dynamic
|
||||
|| old.censorship.mask_host != new.censorship.mask_host
|
||||
|| old.censorship.mask_port != new.censorship.mask_port
|
||||
|| old.censorship.exclusive_mask != new.censorship.exclusive_mask
|
||||
|
||||
@@ -372,6 +372,7 @@ const CENSORSHIP_CONFIG_KEYS: &[&str] = &[
|
||||
"tls_fetch_scope",
|
||||
"tls_fetch",
|
||||
"mask",
|
||||
"mask_dynamic",
|
||||
"mask_host",
|
||||
"mask_port",
|
||||
"exclusive_mask",
|
||||
@@ -2047,11 +2048,6 @@ impl ProxyConfig {
|
||||
*mask_host = normalize_mask_host_to_ascii(mask_host, "censorship.mask_host")?;
|
||||
}
|
||||
|
||||
// Default mask_host to tls_domain if not set and no unix socket configured.
|
||||
if config.censorship.mask_host.is_none() && config.censorship.mask_unix_sock.is_none() {
|
||||
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!(
|
||||
|
||||
@@ -1726,6 +1726,10 @@ pub struct AntiCensorshipConfig {
|
||||
#[serde(default = "default_true")]
|
||||
pub mask: bool,
|
||||
|
||||
/// Use the ClientHello SNI as the mask TCP target for configured TLS domains.
|
||||
#[serde(default = "default_true")]
|
||||
pub mask_dynamic: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub mask_host: Option<String>,
|
||||
|
||||
@@ -1861,6 +1865,7 @@ impl Default for AntiCensorshipConfig {
|
||||
tls_fetch_scope: default_tls_fetch_scope(),
|
||||
tls_fetch: TlsFetchConfig::default(),
|
||||
mask: default_true(),
|
||||
mask_dynamic: default_true(),
|
||||
mask_host: None,
|
||||
mask_port: default_mask_port(),
|
||||
exclusive_mask: HashMap::new(),
|
||||
|
||||
@@ -385,7 +385,7 @@ mod tls_domain_mask_host_tests {
|
||||
let mut config = ProxyConfig::default();
|
||||
config.censorship.tls_domain = "a.com".to_string();
|
||||
config.censorship.tls_domains = vec!["b.com".to_string(), "c.com".to_string()];
|
||||
config.censorship.mask_host = Some("a.com".to_string());
|
||||
config.censorship.mask_host = None;
|
||||
config
|
||||
}
|
||||
|
||||
@@ -419,6 +419,15 @@ mod tls_domain_mask_host_tests {
|
||||
assert_eq!(mask_host_for_initial_data(&config, &initial_data), "b.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_host_uses_primary_domain_when_dynamic_masking_is_disabled() {
|
||||
let mut config = config_with_tls_domains();
|
||||
config.censorship.mask_dynamic = false;
|
||||
let initial_data = client_hello_with_sni("b.com");
|
||||
|
||||
assert_eq!(mask_host_for_initial_data(&config, &initial_data), "a.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclusive_mask_target_overrides_only_matching_sni() {
|
||||
let mut config = config_with_tls_domains();
|
||||
@@ -577,24 +586,32 @@ fn default_mask_tcp_target_for_initial_data<'a>(
|
||||
.as_deref()
|
||||
.unwrap_or(&config.censorship.tls_domain);
|
||||
|
||||
if !configured_mask_host.eq_ignore_ascii_case(&config.censorship.tls_domain) {
|
||||
if config.censorship.mask_host.is_none() && config.censorship.mask_dynamic {
|
||||
let extracted_sni = if sni.is_none() {
|
||||
tls::extract_sni_from_client_hello(initial_data)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(host) = sni
|
||||
.or(extracted_sni.as_deref())
|
||||
.and_then(|sni| matching_tls_domain_for_sni(config, sni))
|
||||
{
|
||||
return MaskTcpTarget {
|
||||
host,
|
||||
port: config.censorship.mask_port,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mask_host) = config.censorship.mask_host.as_deref() {
|
||||
return MaskTcpTarget {
|
||||
host: configured_mask_host,
|
||||
host: mask_host,
|
||||
port: config.censorship.mask_port,
|
||||
};
|
||||
}
|
||||
|
||||
let extracted_sni = if sni.is_none() {
|
||||
tls::extract_sni_from_client_hello(initial_data)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let host = sni
|
||||
.or(extracted_sni.as_deref())
|
||||
.and_then(|sni| matching_tls_domain_for_sni(config, sni))
|
||||
.unwrap_or(configured_mask_host);
|
||||
MaskTcpTarget {
|
||||
host,
|
||||
host: configured_mask_host,
|
||||
port: config.censorship.mask_port,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user