From d3302d77d27da853e8c4a0bf34ac5cb8f8858898 Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Wed, 18 Feb 2026 19:49:19 +0300 Subject: [PATCH] Blackmagics... --- src/main.rs | 26 +++++++++++++--------- src/transport/middle_proxy/pool.rs | 4 ++++ src/transport/middle_proxy/pool_nat.rs | 30 ++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 57b993d..46d73c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -140,7 +140,7 @@ fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) { async fn main() -> std::result::Result<(), Box> { let (config_path, cli_silent, cli_log_level) = parse_cli(); - let config = match ProxyConfig::load(&config_path) { + let mut config = match ProxyConfig::load(&config_path) { Ok(c) => c, Err(e) => { if std::path::Path::new(&config_path).exists() { @@ -229,18 +229,9 @@ async fn main() -> std::result::Result<(), Box> { let prefer_ipv6 = decision.prefer_ipv6(); let mut use_middle_proxy = config.general.use_middle_proxy && (decision.ipv4_me || decision.ipv6_me); - let config = Arc::new(config); let stats = Arc::new(Stats::new()); let rng = Arc::new(SecureRandom::new()); - let replay_checker = Arc::new(ReplayChecker::new( - config.access.replay_check_len, - Duration::from_secs(config.access.replay_window_secs), - )); - - let upstream_manager = Arc::new(UpstreamManager::new(config.upstreams.clone())); - let buffer_pool = Arc::new(BufferPool::with_config(16 * 1024, 4096)); - // IP Tracker initialization let ip_tracker = Arc::new(UserIpTracker::new()); ip_tracker.load_limits(&config.access.user_max_unique_ips).await; @@ -389,12 +380,27 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai None }; + // If ME failed to initialize, force direct-only mode. if me_pool.is_some() { info!("Transport: Middle-End Proxy - all DC-over-RPC"); } else { + use_middle_proxy = false; + // Make runtime config reflect direct-only mode for handlers. + config.general.use_middle_proxy = false; info!("Transport: Direct DC - TCP - standard DC-over-TCP"); } + // Freeze config after possible fallback decision + let config = Arc::new(config); + + let replay_checker = Arc::new(ReplayChecker::new( + config.access.replay_check_len, + Duration::from_secs(config.access.replay_window_secs), + )); + + let upstream_manager = Arc::new(UpstreamManager::new(config.upstreams.clone())); + let buffer_pool = Arc::new(BufferPool::with_config(16 * 1024, 4096)); + // Middle-End ping before DC connectivity if let Some(ref pool) = me_pool { let me_results = run_me_ping(pool, &rng).await; diff --git a/src/transport/middle_proxy/pool.rs b/src/transport/middle_proxy/pool.rs index 8510dfd..888cb23 100644 --- a/src/transport/middle_proxy/pool.rs +++ b/src/transport/middle_proxy/pool.rs @@ -48,6 +48,8 @@ pub struct MePool { pub(super) nat_probe: bool, pub(super) nat_stun: Option, pub(super) detected_ipv6: Option, + pub(super) nat_probe_attempts: std::sync::atomic::AtomicU8, + pub(super) nat_probe_disabled: std::sync::atomic::AtomicBool, pub(super) proxy_map_v4: Arc>>>, pub(super) proxy_map_v6: Arc>>>, pub(super) default_dc: AtomicI32, @@ -91,6 +93,8 @@ impl MePool { nat_probe, nat_stun, detected_ipv6, + nat_probe_attempts: std::sync::atomic::AtomicU8::new(0), + nat_probe_disabled: std::sync::atomic::AtomicBool::new(false), pool_size: 2, proxy_map_v4: Arc::new(RwLock::new(proxy_map_v4)), proxy_map_v6: Arc::new(RwLock::new(proxy_map_v6)), diff --git a/src/transport/middle_proxy/pool_nat.rs b/src/transport/middle_proxy/pool_nat.rs index db34e09..1b4f7c7 100644 --- a/src/transport/middle_proxy/pool_nat.rs +++ b/src/transport/middle_proxy/pool_nat.rs @@ -1,7 +1,7 @@ use std::net::{IpAddr, Ipv4Addr}; use std::time::Duration; -use tracing::{info, warn}; +use tracing::{info, warn, debug}; use crate::error::{ProxyError, Result}; use crate::network::probe::is_bogon; @@ -98,6 +98,18 @@ impl MePool { family: IpFamily, ) -> Option { const STUN_CACHE_TTL: Duration = Duration::from_secs(600); + // If STUN probing was disabled after attempts, reuse cached (even stale) or skip. + if self.nat_probe_disabled.load(std::sync::atomic::Ordering::Relaxed) { + if let Ok(cache) = self.nat_reflection_cache.try_lock() { + let slot = match family { + IpFamily::V4 => cache.v4, + IpFamily::V6 => cache.v6, + }; + return slot.map(|(_, addr)| addr); + } + return None; + } + if let Ok(mut cache) = self.nat_reflection_cache.try_lock() { let slot = match family { IpFamily::V4 => &mut cache.v4, @@ -110,6 +122,12 @@ impl MePool { } } + let attempt = self.nat_probe_attempts.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + if attempt >= 2 { + self.nat_probe_disabled.store(true, std::sync::atomic::Ordering::Relaxed); + return None; + } + let stun_addr = self .nat_stun .clone() @@ -135,7 +153,15 @@ impl MePool { } } Err(e) => { - warn!(error = %e, "NAT probe failed"); + let attempts = attempt + 1; + if attempts <= 2 { + warn!(error = %e, attempt = attempts, "NAT probe failed"); + } else { + debug!(error = %e, attempt = attempts, "NAT probe suppressed after max attempts"); + } + if attempts >= 2 { + self.nat_probe_disabled.store(true, std::sync::atomic::Ordering::Relaxed); + } None } }