Default Cluster Drafts
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
//! Configuration
|
//! Configuration
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::IpAddr;
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -336,6 +336,21 @@ pub struct ProxyConfig {
|
|||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub show_link: Vec<String>,
|
pub show_link: Vec<String>,
|
||||||
|
|
||||||
|
/// DC address overrides for non-standard DCs (CDN, media, test, etc.)
|
||||||
|
/// Keys are DC indices as strings, values are "ip:port" addresses.
|
||||||
|
/// Matches the C implementation's `proxy_for <dc_id> <ip>:<port>` config directive.
|
||||||
|
/// Example in config.toml:
|
||||||
|
/// [dc_overrides]
|
||||||
|
/// "203" = "149.154.175.100:443"
|
||||||
|
#[serde(default)]
|
||||||
|
pub dc_overrides: HashMap<String, String>,
|
||||||
|
|
||||||
|
/// Default DC index (1-5) for unmapped non-standard DCs.
|
||||||
|
/// Matches the C implementation's `default <dc_id>` config directive.
|
||||||
|
/// If not set, defaults to 2 (matching Telegram's official `default 2;` in proxy-multi.conf).
|
||||||
|
#[serde(default)]
|
||||||
|
pub default_dc: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProxyConfig {
|
impl ProxyConfig {
|
||||||
|
|||||||
@@ -298,20 +298,70 @@ impl RunningClientHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve DC index to a target address.
|
||||||
|
///
|
||||||
|
/// Matches the C implementation's behavior exactly:
|
||||||
|
///
|
||||||
|
/// 1. Look up DC in known clusters (standard DCs ±1..±5)
|
||||||
|
/// 2. If not found and `force=1` → fall back to `default_cluster`
|
||||||
|
///
|
||||||
|
/// In the C code:
|
||||||
|
/// - `proxy-multi.conf` is downloaded from Telegram, contains only DC ±1..±5
|
||||||
|
/// - `default 2;` directive sets the default cluster
|
||||||
|
/// - `mf_cluster_lookup(CurConf, target_dc, 1)` returns default_cluster
|
||||||
|
/// for any unknown DC (like CDN DC 203)
|
||||||
|
///
|
||||||
|
/// So DC 203, DC 101, DC -300, etc. all route to the default DC (2).
|
||||||
|
/// There is NO modular arithmetic in the C implementation.
|
||||||
fn get_dc_addr_static(dc_idx: i16, config: &ProxyConfig) -> Result<SocketAddr> {
|
fn get_dc_addr_static(dc_idx: i16, config: &ProxyConfig) -> Result<SocketAddr> {
|
||||||
let idx = (dc_idx.abs() - 1) as usize;
|
|
||||||
|
|
||||||
let datacenters = if config.general.prefer_ipv6 {
|
let datacenters = if config.general.prefer_ipv6 {
|
||||||
&*TG_DATACENTERS_V6
|
&*TG_DATACENTERS_V6
|
||||||
} else {
|
} else {
|
||||||
&*TG_DATACENTERS_V4
|
&*TG_DATACENTERS_V4
|
||||||
};
|
};
|
||||||
|
|
||||||
datacenters.get(idx)
|
let num_dcs = datacenters.len(); // 5
|
||||||
.map(|ip| SocketAddr::new(*ip, TG_DATACENTER_PORT))
|
|
||||||
.ok_or_else(|| ProxyError::InvalidHandshake(
|
// === Step 1: Check dc_overrides (like C's `proxy_for <dc> <ip>:<port>`) ===
|
||||||
format!("Invalid DC index: {}", dc_idx)
|
let dc_key = dc_idx.to_string();
|
||||||
))
|
if let Some(addr_str) = config.dc_overrides.get(&dc_key) {
|
||||||
|
match addr_str.parse::<SocketAddr>() {
|
||||||
|
Ok(addr) => {
|
||||||
|
debug!(dc_idx = dc_idx, addr = %addr, "Using DC override from config");
|
||||||
|
return Ok(addr);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
warn!(dc_idx = dc_idx, addr_str = %addr_str,
|
||||||
|
"Invalid DC override address in config, ignoring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Step 2: Standard DCs ±1..±5 — direct lookup ===
|
||||||
|
let abs_dc = dc_idx.unsigned_abs() as usize;
|
||||||
|
if abs_dc >= 1 && abs_dc <= num_dcs {
|
||||||
|
return Ok(SocketAddr::new(datacenters[abs_dc - 1], TG_DATACENTER_PORT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Step 3: Unknown DC — fall back to default_cluster ===
|
||||||
|
// Exactly like C's `mf_cluster_lookup(CurConf, target_dc, force=1)`
|
||||||
|
// which returns `MC->default_cluster` when the DC is not found.
|
||||||
|
// Telegram's proxy-multi.conf uses `default 2;`
|
||||||
|
let default_dc = config.default_dc.unwrap_or(2) as usize;
|
||||||
|
let fallback_idx = if default_dc >= 1 && default_dc <= num_dcs {
|
||||||
|
default_dc - 1
|
||||||
|
} else {
|
||||||
|
1 // DC 2 (index 1) — matches Telegram's `default 2;`
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
original_dc = dc_idx,
|
||||||
|
fallback_dc = (fallback_idx + 1) as u16,
|
||||||
|
fallback_addr = %datacenters[fallback_idx],
|
||||||
|
"Unknown DC not in ±1..5 range, routing to default cluster (same as C impl: mf_cluster_lookup with force=1 -> default_cluster)"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(SocketAddr::new(datacenters[fallback_idx], TG_DATACENTER_PORT))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_tg_handshake_static(
|
async fn do_tg_handshake_static(
|
||||||
|
|||||||
@@ -66,10 +66,25 @@ impl UpstreamState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert dc_idx (1-based, may be negative) to array index 0..4
|
/// Map DC index to latency array slot (0..NUM_DCS).
|
||||||
|
///
|
||||||
|
/// Matches the C implementation's `mf_cluster_lookup` behavior:
|
||||||
|
/// - Standard DCs ±1..±5 → direct mapping to array index 0..4
|
||||||
|
/// - Unknown DCs (CDN, media, etc.) → default DC slot (index 1 = DC 2)
|
||||||
|
/// This matches Telegram's `default 2;` in proxy-multi.conf.
|
||||||
|
/// - There is NO modular arithmetic in the C implementation.
|
||||||
fn dc_array_idx(dc_idx: i16) -> Option<usize> {
|
fn dc_array_idx(dc_idx: i16) -> Option<usize> {
|
||||||
let idx = (dc_idx.unsigned_abs() as usize).checked_sub(1)?;
|
let abs_dc = dc_idx.unsigned_abs() as usize;
|
||||||
if idx < NUM_DCS { Some(idx) } else { None }
|
if abs_dc == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if abs_dc >= 1 && abs_dc <= NUM_DCS {
|
||||||
|
Some(abs_dc - 1)
|
||||||
|
} else {
|
||||||
|
// Unknown DC → default cluster (DC 2, index 1)
|
||||||
|
// Same as C: mf_cluster_lookup returns default_cluster
|
||||||
|
Some(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get latency for a specific DC, falling back to average across all known DCs
|
/// Get latency for a specific DC, falling back to average across all known DCs
|
||||||
|
|||||||
Reference in New Issue
Block a user