From 496a4ab0059457d2fbd55335403b35ce73bbe265 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 18 Feb 2026 08:56:42 +0300 Subject: [PATCH] Revert "Skip IPv6 connections when IPv6 is unavailable on host" This reverts commit d122cbfe5a1f8b8a3a32c58b7b9cf3a5ea75071a. --- src/main.rs | 30 +---- src/transport/middle_proxy/health.rs | 7 +- src/transport/upstream.rs | 178 ++++++++++++--------------- src/util/ip.rs | 6 - 4 files changed, 85 insertions(+), 136 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3711265..2dd9a56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ use crate::transport::middle_proxy::{ stun_probe, }; use crate::transport::{ListenOptions, UpstreamManager, create_listener}; -use crate::util::ip::{detect_ip, check_ipv6_available}; +use crate::util::ip::detect_ip; use crate::protocol::constants::{TG_MIDDLE_PROXIES_V4, TG_MIDDLE_PROXIES_V6}; fn parse_cli() -> (String, bool, Option) { @@ -219,19 +219,7 @@ async fn main() -> std::result::Result<(), Box> { warn!("Using default tls_domain. Consider setting a custom domain."); } - let ipv6_available = check_ipv6_available(); - if ipv6_available { - info!("IPv6: available"); - } else { - warn!("IPv6: not available on this host"); - } - - let prefer_ipv6 = if config.general.prefer_ipv6 && !ipv6_available { - warn!("prefer_ipv6 is set but IPv6 is not available, falling back to IPv4"); - false - } else { - config.general.prefer_ipv6 - }; + let prefer_ipv6 = config.general.prefer_ipv6; let mut use_middle_proxy = config.general.use_middle_proxy; let config = Arc::new(config); let stats = Arc::new(Stats::new()); @@ -354,12 +342,6 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai cfg_v6.map = crate::protocol::constants::TG_MIDDLE_PROXIES_V6.clone(); } - let effective_v6_map = if ipv6_available { - cfg_v6.map.clone() - } else { - std::collections::HashMap::new() - }; - let pool = MePool::new( proxy_tag, proxy_secret, @@ -367,7 +349,7 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai config.general.middle_proxy_nat_probe, config.general.middle_proxy_nat_stun.clone(), cfg_v4.map.clone(), - effective_v6_map, + cfg_v6.map.clone(), cfg_v4.default_dc.or(cfg_v6.default_dc), ); @@ -380,7 +362,7 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai let rng_clone = rng.clone(); tokio::spawn(async move { crate::transport::middle_proxy::me_health_monitor( - pool_clone, rng_clone, 2, ipv6_available, + pool_clone, rng_clone, 2, ) .await; }); @@ -500,7 +482,7 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai info!("================= Telegram DC Connectivity ================="); let ping_results = upstream_manager - .ping_all_dcs(prefer_ipv6, &config.dc_overrides, ipv6_available) + .ping_all_dcs(prefer_ipv6, &config.dc_overrides) .await; for upstream_result in &ping_results { @@ -578,7 +560,7 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai // Background tasks let um_clone = upstream_manager.clone(); tokio::spawn(async move { - um_clone.run_health_checks(prefer_ipv6, ipv6_available).await; + um_clone.run_health_checks(prefer_ipv6).await; }); let rc_clone = replay_checker.clone(); diff --git a/src/transport/middle_proxy/health.rs b/src/transport/middle_proxy/health.rs index 745adf8..d2bb51a 100644 --- a/src/transport/middle_proxy/health.rs +++ b/src/transport/middle_proxy/health.rs @@ -10,7 +10,7 @@ use crate::crypto::SecureRandom; use super::MePool; -pub async fn me_health_monitor(pool: Arc, rng: Arc, _min_connections: usize, ipv6_available: bool) { +pub async fn me_health_monitor(pool: Arc, rng: Arc, _min_connections: usize) { let mut backoff: HashMap = HashMap::new(); let mut last_attempt: HashMap = HashMap::new(); loop { @@ -63,10 +63,7 @@ pub async fn me_health_monitor(pool: Arc, rng: Arc, _min_c } } - // IPv6 coverage check (skip if IPv6 not available on host) - if !ipv6_available { - continue; - } + // IPv6 coverage check (if available) let map_v6 = pool.proxy_map_v6.read().await.clone(); let writer_addrs_v6: std::collections::HashSet = pool .writers diff --git a/src/transport/upstream.rs b/src/transport/upstream.rs index 22b5690..db0d366 100644 --- a/src/transport/upstream.rs +++ b/src/transport/upstream.rs @@ -355,7 +355,6 @@ impl UpstreamManager { &self, prefer_ipv6: bool, dc_overrides: &HashMap>, - ipv6_available: bool, ) -> Vec { let upstreams: Vec<(usize, UpstreamConfig)> = { let guard = self.upstreams.read().await; @@ -378,45 +377,43 @@ impl UpstreamManager { let mut v6_results = Vec::new(); let mut v4_results = Vec::new(); - // === Ping IPv6 first (skip if IPv6 not available) === - if ipv6_available { - for dc_zero_idx in 0..NUM_DCS { - let dc_v6 = TG_DATACENTERS_V6[dc_zero_idx]; - let addr_v6 = SocketAddr::new(dc_v6, TG_DATACENTER_PORT); + // === Ping IPv6 first === + for dc_zero_idx in 0..NUM_DCS { + let dc_v6 = TG_DATACENTERS_V6[dc_zero_idx]; + let addr_v6 = SocketAddr::new(dc_v6, TG_DATACENTER_PORT); - let result = tokio::time::timeout( - Duration::from_secs(DC_PING_TIMEOUT_SECS), - self.ping_single_dc(&upstream_config, addr_v6) - ).await; + let result = tokio::time::timeout( + Duration::from_secs(DC_PING_TIMEOUT_SECS), + self.ping_single_dc(&upstream_config, addr_v6) + ).await; - let ping_result = match result { - Ok(Ok(rtt_ms)) => { - let mut guard = self.upstreams.write().await; - if let Some(u) = guard.get_mut(*upstream_idx) { - u.dc_latency[dc_zero_idx].update(rtt_ms); - } - DcPingResult { - dc_idx: dc_zero_idx + 1, - dc_addr: addr_v6, - rtt_ms: Some(rtt_ms), - error: None, - } + let ping_result = match result { + Ok(Ok(rtt_ms)) => { + let mut guard = self.upstreams.write().await; + if let Some(u) = guard.get_mut(*upstream_idx) { + u.dc_latency[dc_zero_idx].update(rtt_ms); } - Ok(Err(e)) => DcPingResult { + DcPingResult { dc_idx: dc_zero_idx + 1, dc_addr: addr_v6, - rtt_ms: None, - error: Some(e.to_string()), - }, - Err(_) => DcPingResult { - dc_idx: dc_zero_idx + 1, - dc_addr: addr_v6, - rtt_ms: None, - error: Some("timeout".to_string()), - }, - }; - v6_results.push(ping_result); - } + rtt_ms: Some(rtt_ms), + error: None, + } + } + Ok(Err(e)) => DcPingResult { + dc_idx: dc_zero_idx + 1, + dc_addr: addr_v6, + rtt_ms: None, + error: Some(e.to_string()), + }, + Err(_) => DcPingResult { + dc_idx: dc_zero_idx + 1, + dc_addr: addr_v6, + rtt_ms: None, + error: Some("timeout".to_string()), + }, + }; + v6_results.push(ping_result); } // === Then ping IPv4 === @@ -520,12 +517,8 @@ impl UpstreamManager { let mut guard = self.upstreams.write().await; if let Some(u) = guard.get_mut(*upstream_idx) { for dc_zero_idx in 0..NUM_DCS { - let v6_ok = v6_results.get(dc_zero_idx) - .map(|r| r.rtt_ms.is_some()) - .unwrap_or(false); - let v4_ok = v4_results.get(dc_zero_idx) - .map(|r| r.rtt_ms.is_some()) - .unwrap_or(false); + let v6_ok = v6_results[dc_zero_idx].rtt_ms.is_some(); + let v4_ok = v4_results[dc_zero_idx].rtt_ms.is_some(); u.dc_ip_pref[dc_zero_idx] = match (v6_ok, v4_ok) { (true, true) => IpPreference::BothWork, @@ -558,7 +551,7 @@ impl UpstreamManager { /// Background health check: rotates through DCs, 30s interval. /// Uses preferred IP version based on config. - pub async fn run_health_checks(&self, prefer_ipv6: bool, ipv6_available: bool) { + pub async fn run_health_checks(&self, prefer_ipv6: bool) { let mut dc_rotation = 0usize; loop { @@ -567,19 +560,16 @@ impl UpstreamManager { let dc_zero_idx = dc_rotation % NUM_DCS; dc_rotation += 1; - let dc_addr = if prefer_ipv6 && ipv6_available { + let dc_addr = if prefer_ipv6 { SocketAddr::new(TG_DATACENTERS_V6[dc_zero_idx], TG_DATACENTER_PORT) } else { SocketAddr::new(TG_DATACENTERS_V4[dc_zero_idx], TG_DATACENTER_PORT) }; - // Skip IPv6 fallback if IPv6 is not available - let fallback_addr = if !ipv6_available { - None - } else if prefer_ipv6 { - Some(SocketAddr::new(TG_DATACENTERS_V4[dc_zero_idx], TG_DATACENTER_PORT)) + let fallback_addr = if prefer_ipv6 { + SocketAddr::new(TG_DATACENTERS_V4[dc_zero_idx], TG_DATACENTER_PORT) } else { - Some(SocketAddr::new(TG_DATACENTERS_V6[dc_zero_idx], TG_DATACENTER_PORT)) + SocketAddr::new(TG_DATACENTERS_V6[dc_zero_idx], TG_DATACENTER_PORT) }; let count = self.upstreams.read().await.len(); @@ -615,67 +605,53 @@ impl UpstreamManager { u.last_check = std::time::Instant::now(); } Ok(Err(_)) | Err(_) => { - // Try fallback (only if fallback address is available) - if let Some(fb_addr) = fallback_addr { - debug!(dc = dc_zero_idx + 1, "Health check failed, trying fallback"); + // Try fallback + debug!(dc = dc_zero_idx + 1, "Health check failed, trying fallback"); - let start2 = Instant::now(); - let result2 = tokio::time::timeout( - Duration::from_secs(10), - self.connect_via_upstream(&config, fb_addr) - ).await; + let start2 = Instant::now(); + let result2 = tokio::time::timeout( + Duration::from_secs(10), + self.connect_via_upstream(&config, fallback_addr) + ).await; - let mut guard = self.upstreams.write().await; - let u = &mut guard[i]; + let mut guard = self.upstreams.write().await; + let u = &mut guard[i]; - match result2 { - Ok(Ok(_stream)) => { - let rtt_ms = start2.elapsed().as_secs_f64() * 1000.0; - u.dc_latency[dc_zero_idx].update(rtt_ms); + match result2 { + Ok(Ok(_stream)) => { + let rtt_ms = start2.elapsed().as_secs_f64() * 1000.0; + u.dc_latency[dc_zero_idx].update(rtt_ms); - if !u.healthy { - info!( - rtt = format!("{:.0} ms", rtt_ms), - dc = dc_zero_idx + 1, - "Upstream recovered (fallback)" - ); - } - u.healthy = true; - u.fails = 0; + if !u.healthy { + info!( + rtt = format!("{:.0} ms", rtt_ms), + dc = dc_zero_idx + 1, + "Upstream recovered (fallback)" + ); } - Ok(Err(e)) => { - u.fails += 1; - debug!(dc = dc_zero_idx + 1, fails = u.fails, - "Health check failed (both): {}", e); - if u.fails > 3 { - u.healthy = false; - warn!("Upstream unhealthy (fails)"); - } - } - Err(_) => { - u.fails += 1; - debug!(dc = dc_zero_idx + 1, fails = u.fails, - "Health check timeout (both)"); - if u.fails > 3 { - u.healthy = false; - warn!("Upstream unhealthy (timeout)"); - } + u.healthy = true; + u.fails = 0; + } + Ok(Err(e)) => { + u.fails += 1; + debug!(dc = dc_zero_idx + 1, fails = u.fails, + "Health check failed (both): {}", e); + if u.fails > 3 { + u.healthy = false; + warn!("Upstream unhealthy (fails)"); } } - u.last_check = std::time::Instant::now(); - } else { - // No fallback available, mark failure directly - let mut guard = self.upstreams.write().await; - let u = &mut guard[i]; - u.fails += 1; - debug!(dc = dc_zero_idx + 1, fails = u.fails, - "Health check failed (no fallback)"); - if u.fails > 3 { - u.healthy = false; - warn!("Upstream unhealthy (fails)"); + Err(_) => { + u.fails += 1; + debug!(dc = dc_zero_idx + 1, fails = u.fails, + "Health check timeout (both)"); + if u.fails > 3 { + u.healthy = false; + warn!("Upstream unhealthy (timeout)"); + } } - u.last_check = std::time::Instant::now(); } + u.last_check = std::time::Instant::now(); } } } diff --git a/src/util/ip.rs b/src/util/ip.rs index 66bd500..9bde513 100644 --- a/src/util/ip.rs +++ b/src/util/ip.rs @@ -54,12 +54,6 @@ fn get_local_ipv6(target: &str) -> Option { socket.local_addr().ok().map(|addr| addr.ip()) } -/// Check if IPv6 connectivity is available on this host. -/// Uses UDP connect to Google DNS IPv6 (no packets sent). -pub fn check_ipv6_available() -> bool { - get_local_ipv6("[2001:4860:4860::8888]:80").is_some() -} - /// Detect public IP addresses pub async fn detect_ip() -> IpInfo { let mut info = IpInfo::default();