From 896e129155684a5547ab5c5a0de15c3cca803528 Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:48:22 +0300 Subject: [PATCH 1/4] Checked defaults --- src/config/defaults.rs | 33 ++++++++++++++++---- src/config/types.rs | 68 +++++++++++++++++++++++++----------------- 2 files changed, 69 insertions(+), 32 deletions(-) diff --git a/src/config/defaults.rs b/src/config/defaults.rs index 51abe65..a2b2fc7 100644 --- a/src/config/defaults.rs +++ b/src/config/defaults.rs @@ -7,7 +7,7 @@ const DEFAULT_NETWORK_IPV6: Option = Some(false); const DEFAULT_STUN_TCP_FALLBACK: bool = true; const DEFAULT_MIDDLE_PROXY_WARM_STANDBY: usize = 16; const DEFAULT_ME_RECONNECT_MAX_CONCURRENT_PER_DC: u32 = 8; -const DEFAULT_ME_RECONNECT_FAST_RETRY_COUNT: u32 = 12; +const DEFAULT_ME_RECONNECT_FAST_RETRY_COUNT: u32 = 11; const DEFAULT_LISTEN_ADDR_IPV6: &str = "::"; const DEFAULT_ACCESS_USER: &str = "default"; const DEFAULT_ACCESS_SECRET: &str = "00000000000000000000000000000000"; @@ -21,7 +21,7 @@ pub(crate) fn default_port() -> u16 { } pub(crate) fn default_tls_domain() -> String { - "www.google.com".to_string() + "petrovich.ru".to_string() } pub(crate) fn default_mask_port() -> u16 { @@ -45,7 +45,7 @@ pub(crate) fn default_replay_window_secs() -> u64 { } pub(crate) fn default_handshake_timeout() -> u64 { - 15 + 30 } pub(crate) fn default_connect_timeout() -> u64 { @@ -60,17 +60,21 @@ pub(crate) fn default_ack_timeout() -> u64 { 300 } pub(crate) fn default_me_one_retry() -> u8 { - 3 + 12 } pub(crate) fn default_me_one_timeout() -> u64 { - 1500 + 1200 } pub(crate) fn default_listen_addr() -> String { "0.0.0.0".to_string() } +pub(crate) fn default_listen_addr_ipv4() -> Option { + Some(default_listen_addr()) +} + pub(crate) fn default_weight() -> u16 { 1 } @@ -102,6 +106,21 @@ pub(crate) fn default_pool_size() -> usize { 8 } +pub(crate) fn default_proxy_secret_path() -> Option { + Some("proxy-secret".to_string()) +} + +pub(crate) fn default_middle_proxy_nat_stun() -> Option { + Some("stun.l.google.com:19302".to_string()) +} + +pub(crate) fn default_middle_proxy_nat_stun_servers() -> Vec { + vec![ + "stun1.l.google.com:19302".to_string(), + "stun2.l.google.com:19302".to_string(), + ] +} + pub(crate) fn default_middle_proxy_warm_standby() -> usize { DEFAULT_MIDDLE_PROXY_WARM_STANDBY } @@ -303,6 +322,10 @@ pub(crate) fn default_listen_addr_ipv6() -> String { DEFAULT_LISTEN_ADDR_IPV6.to_string() } +pub(crate) fn default_listen_addr_ipv6_opt() -> Option { + Some(default_listen_addr_ipv6()) +} + pub(crate) fn default_access_users() -> HashMap { HashMap::from([( DEFAULT_ACCESS_USER.to_string(), diff --git a/src/config/types.rs b/src/config/types.rs index 1302a97..f42a94a 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -87,7 +87,7 @@ pub struct NetworkConfig { pub ipv4: bool, /// None = auto-detect IPv6 availability. - #[serde(default)] + #[serde(default = "default_network_ipv6")] pub ipv6: Option, /// 4 or 6. @@ -102,7 +102,7 @@ pub struct NetworkConfig { pub stun_servers: Vec, /// Enable TCP STUN fallback when UDP is blocked. - #[serde(default)] + #[serde(default = "default_stun_tcp_fallback")] pub stun_tcp_fallback: bool, /// HTTP-based public IP detection endpoints (fallback after STUN). @@ -140,7 +140,7 @@ pub struct GeneralConfig { #[serde(default = "default_true")] pub fast_mode: bool, - #[serde(default)] + #[serde(default = "default_true")] pub use_middle_proxy: bool, #[serde(default)] @@ -148,7 +148,7 @@ pub struct GeneralConfig { /// Path to proxy-secret binary file (auto-downloaded if absent). /// Infrastructure secret from https://core.telegram.org/getProxySecret. - #[serde(default)] + #[serde(default = "default_proxy_secret_path")] pub proxy_secret_path: Option, /// Public IP override for middle-proxy NAT environments. @@ -157,15 +157,15 @@ pub struct GeneralConfig { pub middle_proxy_nat_ip: Option, /// Enable STUN-based NAT probing to discover public IP:port for ME KDF. - #[serde(default)] + #[serde(default = "default_true")] pub middle_proxy_nat_probe: bool, /// Optional STUN server address (host:port) for NAT probing. - #[serde(default)] + #[serde(default = "default_middle_proxy_nat_stun")] pub middle_proxy_nat_stun: Option, /// Optional list of STUN servers for NAT probing fallback. - #[serde(default)] + #[serde(default = "default_middle_proxy_nat_stun_servers")] pub middle_proxy_nat_stun_servers: Vec, /// Desired size of active Middle-Proxy writer pool. @@ -173,7 +173,7 @@ pub struct GeneralConfig { pub middle_proxy_pool_size: usize, /// Number of warm standby ME connections kept pre-initialized. - #[serde(default)] + #[serde(default = "default_middle_proxy_warm_standby")] pub middle_proxy_warm_standby: usize, /// Enable ME keepalive padding frames. @@ -207,7 +207,7 @@ pub struct GeneralConfig { pub desync_all_full: bool, /// Enable per-IP forensic observation buckets for scanners and handshake failures. - #[serde(default)] + #[serde(default = "default_true")] pub beobachten: bool, /// Observation retention window in minutes for per-IP forensic buckets. @@ -240,7 +240,7 @@ pub struct GeneralConfig { pub me_warmup_step_jitter_ms: u64, /// Max concurrent reconnect attempts per DC. - #[serde(default)] + #[serde(default = "default_me_reconnect_max_concurrent_per_dc")] pub me_reconnect_max_concurrent_per_dc: u32, /// Base backoff in ms for reconnect. @@ -252,7 +252,7 @@ pub struct GeneralConfig { pub me_reconnect_backoff_cap_ms: u64, /// Fast retry attempts before backoff. - #[serde(default)] + #[serde(default = "default_me_reconnect_fast_retry_count")] pub me_reconnect_fast_retry_count: u32, /// Ignore STUN/interface IP mismatch (keep using Middle Proxy even if NAT detected). @@ -280,7 +280,7 @@ pub struct GeneralConfig { /// Unified ME updater interval in seconds for getProxyConfig/getProxyConfigV6/getProxySecret. /// When omitted, effective value falls back to legacy proxy_*_auto_reload_secs fields. - #[serde(default)] + #[serde(default = "default_update_every")] pub update_every: Option, /// Periodic ME pool reinitialization interval in seconds. @@ -371,13 +371,13 @@ impl Default for GeneralConfig { modes: ProxyModes::default(), prefer_ipv6: false, fast_mode: default_true(), - use_middle_proxy: false, + use_middle_proxy: default_true(), ad_tag: None, - proxy_secret_path: None, + proxy_secret_path: default_proxy_secret_path(), middle_proxy_nat_ip: None, - middle_proxy_nat_probe: true, - middle_proxy_nat_stun: None, - middle_proxy_nat_stun_servers: Vec::new(), + middle_proxy_nat_probe: default_true(), + middle_proxy_nat_stun: default_middle_proxy_nat_stun(), + middle_proxy_nat_stun_servers: default_middle_proxy_nat_stun_servers(), middle_proxy_pool_size: default_pool_size(), middle_proxy_warm_standby: default_middle_proxy_warm_standby(), me_keepalive_enabled: default_true(), @@ -399,7 +399,7 @@ impl Default for GeneralConfig { crypto_pending_buffer: default_crypto_pending_buffer(), max_client_frame: default_max_client_frame(), desync_all_full: default_desync_all_full(), - beobachten: true, + beobachten: default_true(), beobachten_minutes: default_beobachten_minutes(), beobachten_flush_secs: default_beobachten_flush_secs(), beobachten_file: default_beobachten_file(), @@ -450,11 +450,11 @@ impl GeneralConfig { } /// `[general.links]` — proxy link generation settings. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct LinksConfig { /// List of usernames whose tg:// links to display at startup. /// `"*"` = all users, `["alice", "bob"]` = specific users. - #[serde(default)] + #[serde(default = "default_links_show")] pub show: ShowLink, /// Public hostname/IP for tg:// link generation (overrides detected IP). @@ -466,15 +466,25 @@ pub struct LinksConfig { pub public_port: Option, } +impl Default for LinksConfig { + fn default() -> Self { + Self { + show: default_links_show(), + public_host: None, + public_port: None, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerConfig { #[serde(default = "default_port")] pub port: u16, - #[serde(default)] + #[serde(default = "default_listen_addr_ipv4")] pub listen_addr_ipv4: Option, - #[serde(default)] + #[serde(default = "default_listen_addr_ipv6_opt")] pub listen_addr_ipv6: Option, #[serde(default)] @@ -509,8 +519,8 @@ impl Default for ServerConfig { fn default() -> Self { Self { port: default_port(), - listen_addr_ipv4: Some(default_listen_addr()), - listen_addr_ipv6: Some(default_listen_addr_ipv6()), + listen_addr_ipv4: default_listen_addr_ipv4(), + listen_addr_ipv6: default_listen_addr_ipv6_opt(), listen_unix_sock: None, listen_unix_sock_perm: None, listen_tcp: None, @@ -583,7 +593,7 @@ pub struct AntiCensorshipConfig { pub fake_cert_len: usize, /// Enable TLS certificate emulation using cached real certificates. - #[serde(default)] + #[serde(default = "default_true")] pub tls_emulation: bool, /// Directory to store TLS front cache (on disk). @@ -636,7 +646,7 @@ impl Default for AntiCensorshipConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AccessConfig { - #[serde(default)] + #[serde(default = "default_access_users")] pub users: HashMap, #[serde(default)] @@ -746,7 +756,7 @@ pub struct ListenerConfig { /// In TOML, this can be: /// - `show_link = "*"` — show links for all users /// - `show_link = ["a", "b"]` — show links for specific users -/// - omitted — show no links (default) +/// - omitted — default depends on the owning config field #[derive(Debug, Clone, Default)] pub enum ShowLink { /// Don't show any links (default when omitted). @@ -758,6 +768,10 @@ pub enum ShowLink { Specific(Vec), } +fn default_links_show() -> ShowLink { + ShowLink::All +} + impl ShowLink { /// Returns true if no links should be shown. pub fn is_empty(&self) -> bool { From fb1f85559ccb3edc1b9679217e14abde57bfcf61 Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:57:28 +0300 Subject: [PATCH 2/4] Update load.rs --- src/config/load.rs | 48 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/config/load.rs b/src/config/load.rs index be6759e..35099be 100644 --- a/src/config/load.rs +++ b/src/config/load.rs @@ -116,8 +116,27 @@ impl ProxyConfig { let base_dir = path.as_ref().parent().unwrap_or(Path::new(".")); let processed = preprocess_includes(&content, base_dir, 0)?; - let mut config: ProxyConfig = + let parsed_toml: toml::Value = toml::from_str(&processed).map_err(|e| ProxyError::Config(e.to_string()))?; + let general_table = parsed_toml + .get("general") + .and_then(|value| value.as_table()); + let update_every_is_explicit = general_table + .map(|table| table.contains_key("update_every")) + .unwrap_or(false); + let legacy_secret_is_explicit = general_table + .map(|table| table.contains_key("proxy_secret_auto_reload_secs")) + .unwrap_or(false); + let legacy_config_is_explicit = general_table + .map(|table| table.contains_key("proxy_config_auto_reload_secs")) + .unwrap_or(false); + + let mut config: ProxyConfig = + parsed_toml.try_into().map_err(|e| ProxyError::Config(e.to_string()))?; + + if !update_every_is_explicit && (legacy_secret_is_explicit || legacy_config_is_explicit) { + config.general.update_every = None; + } if let Some(update_every) = config.general.update_every { if update_every == 0 { @@ -437,15 +456,24 @@ mod tests { "#; let cfg: ProxyConfig = toml::from_str(toml).unwrap(); - assert_eq!(cfg.network.ipv6, None); - assert!(!cfg.network.stun_tcp_fallback); - assert_eq!(cfg.general.middle_proxy_warm_standby, 0); - assert_eq!(cfg.general.me_reconnect_max_concurrent_per_dc, 0); - assert_eq!(cfg.general.me_reconnect_fast_retry_count, 0); - assert_eq!(cfg.general.update_every, None); - assert_eq!(cfg.server.listen_addr_ipv4, None); - assert_eq!(cfg.server.listen_addr_ipv6, None); - assert!(cfg.access.users.is_empty()); + assert_eq!(cfg.network.ipv6, default_network_ipv6()); + assert_eq!(cfg.network.stun_tcp_fallback, default_stun_tcp_fallback()); + assert_eq!( + cfg.general.middle_proxy_warm_standby, + default_middle_proxy_warm_standby() + ); + assert_eq!( + cfg.general.me_reconnect_max_concurrent_per_dc, + default_me_reconnect_max_concurrent_per_dc() + ); + assert_eq!( + cfg.general.me_reconnect_fast_retry_count, + default_me_reconnect_fast_retry_count() + ); + assert_eq!(cfg.general.update_every, default_update_every()); + assert_eq!(cfg.server.listen_addr_ipv4, default_listen_addr_ipv4()); + assert_eq!(cfg.server.listen_addr_ipv6, default_listen_addr_ipv6_opt()); + assert_eq!(cfg.access.users, default_access_users()); } #[test] From d7182ae817674b32563a329640256b8b23765396 Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:07:04 +0300 Subject: [PATCH 3/4] Update defaults.rs --- src/config/defaults.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/defaults.rs b/src/config/defaults.rs index a2b2fc7..04755cb 100644 --- a/src/config/defaults.rs +++ b/src/config/defaults.rs @@ -7,7 +7,7 @@ const DEFAULT_NETWORK_IPV6: Option = Some(false); const DEFAULT_STUN_TCP_FALLBACK: bool = true; const DEFAULT_MIDDLE_PROXY_WARM_STANDBY: usize = 16; const DEFAULT_ME_RECONNECT_MAX_CONCURRENT_PER_DC: u32 = 8; -const DEFAULT_ME_RECONNECT_FAST_RETRY_COUNT: u32 = 11; +const DEFAULT_ME_RECONNECT_FAST_RETRY_COUNT: u32 = 16; const DEFAULT_LISTEN_ADDR_IPV6: &str = "::"; const DEFAULT_ACCESS_USER: &str = "default"; const DEFAULT_ACCESS_SECRET: &str = "00000000000000000000000000000000"; From e25b7f5ff8d99e527f9a9553b27fefdcfd258726 Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:10:21 +0300 Subject: [PATCH 4/4] STUN List --- src/config/defaults.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/config/defaults.rs b/src/config/defaults.rs index 04755cb..3fb8c3d 100644 --- a/src/config/defaults.rs +++ b/src/config/defaults.rs @@ -116,8 +116,19 @@ pub(crate) fn default_middle_proxy_nat_stun() -> Option { pub(crate) fn default_middle_proxy_nat_stun_servers() -> Vec { vec![ + "stun.l.google.com:5349".to_string(), + "stun1.l.google.com:3478".to_string(), + "stun.gmx.net:3478".to_string(), + "stun.l.google.com:19302".to_string(), + "stun.1und1.de:3478".to_string(), "stun1.l.google.com:19302".to_string(), "stun2.l.google.com:19302".to_string(), + "stun3.l.google.com:19302".to_string(), + "stun4.l.google.com:19302".to_string(), + "stun.services.mozilla.com:3478".to_string(), + "stun.stunprotocol.org:3478".to_string(), + "stun.nextcloud.com:3478".to_string(), + "stun.voip.eutelia.it:3478".to_string(), ] }