Skip to main content

futu_backend/auth/
conn_points.rs

1//! Platform TCP backend IP 池
2use super::UserAttribution;
3
4/// `CONN_PLATFORM_CN` (address.cpp:498-511)
5pub const CN: &[(&str, u16)] = &[
6    ("119.29.48.17", 9595),    // gz  Conn01
7    ("119.29.43.101", 9595),   // gz  Conn02
8    ("115.159.18.59", 9595),   // sh  Conn03
9    ("118.89.98.77", 9595),    // sh  Conn04
10    ("119.28.37.77", 9595),    // hk  Conn05
11    ("119.28.37.206", 9595),   // hk  Conn06
12    ("106.55.67.68", 9595),    // gz  Conn08
13    ("124.156.124.214", 9595), // hk  Conn09
14    ("49.234.241.65", 9595),   // sh  Conn10
15    ("106.55.66.8", 9595),     // gz  Conn11
16    ("124.156.124.226", 9595), // hk  Conn12
17    ("49.234.241.94", 9595),   // sh  Conn13
18];
19
20/// `CONN_PLATFORM_HK` (address.cpp:550-557)
21pub const HK: &[(&str, u16)] = &[
22    ("101.32.217.163", 9595), // hk  Conn01
23    ("43.135.111.64", 9595),  // hk  Conn02
24    ("43.132.195.157", 9595), // hk  Conn03
25    ("43.135.85.38", 9595),   // hk  Conn04
26    ("106.55.216.230", 9595), // gz  Conn05
27    ("134.175.255.64", 9595), // gz  Conn06
28];
29
30/// `CONN_PLATFORM_US` (address.cpp:512-527)
31pub const US: &[(&str, u16)] = &[
32    ("49.51.78.96", 9595),     // us  Conn01
33    ("49.51.77.161", 9595),    // us  Conn02
34    ("134.175.254.224", 9595), // gz  Conn03
35    ("119.28.38.243", 9595),   // hk  Conn04
36    ("170.106.48.244", 9595),  // us  Conn05
37    ("170.106.48.172", 9595),  // us  Conn06
38    ("49.51.78.184", 9595),    // us  Conn07
39    ("49.51.79.131", 9595),    // us  Conn08
40    ("49.51.78.91", 9595),     // us  Conn09
41    ("49.51.79.75", 9595),     // us  Conn10
42    ("49.51.79.238", 9595),    // us  Conn11
43    ("170.106.48.163", 9595),  // us  Conn12
44    ("49.51.78.214", 9595),    // us  Conn13
45    ("170.106.48.193", 9595),  // us  Conn14
46];
47
48/// `CONN_PLATFORM_SG` (address.cpp:528-541)
49pub const SG: &[(&str, u16)] = &[
50    ("129.226.2.218", 9595),  // sg  Conn01
51    ("101.32.143.93", 9595),  // sg  Conn02
52    ("42.194.246.243", 9595), // gz  Conn03
53    ("101.32.70.122", 9595),  // hk  Conn04
54    ("101.32.113.56", 9595),  // sg  Conn05
55    ("101.33.43.108", 9595),  // sg  Conn06
56    ("101.33.43.3", 9595),    // sg  Conn07
57    ("101.32.104.226", 9595), // sg  Conn08
58    ("129.226.1.154", 9595),  // sg  Conn09
59    ("42.193.217.66", 9595),  // gz  Conn10
60    // Conn11 是 anycast IP,中国大陆无法访问,C++ 源里也跳过
61    ("43.135.57.128", 9595), // hk  Conn12
62];
63
64/// `CONN_PLATFORM_AU` (address.cpp:542-545)
65pub const AU: &[(&str, u16)] = &[
66    ("3.24.228.217", 9595),  // au  Conn01
67    ("13.210.218.25", 9595), // au  Conn02
68];
69
70/// `CONN_PLATFORM_JP` (address.cpp:546-549)
71pub const JP: &[(&str, u16)] = &[
72    ("43.153.165.91", 9595),   // jp  Conn01
73    ("124.156.236.226", 9595), // jp  Conn02
74];
75
76/// 按 `UserAttribution` 选对应的 Platform IP 池。
77///
78/// 未知 / 未登录前 fallback 到 CN(历史默认,C++ 同样行为 —— CN 池 IP
79/// 最多、分布最广)。
80pub fn for_attribution(attr: UserAttribution) -> &'static [(&'static str, u16)] {
81    match attr {
82        UserAttribution::Cn => CN,
83        UserAttribution::Hk => HK,
84        UserAttribution::Us => US,
85        UserAttribution::Sg => SG,
86        UserAttribution::Au => AU,
87        UserAttribution::Jp => JP,
88    }
89}
90
91/// 每个 attribution 对应一个**公开可解析**的域名,启动时 DNS 解析出的 IP
92/// 会合并到硬编码池最前面(更新鲜 —— 富途换 IP 我们不用改代码)。
93///
94/// 这 6 个域名 2026-04-17 实测可解析且 9595 通:
95///
96/// | Attribution | 域名                  |
97/// |-------------|-----------------------|
98/// | CN          | `nnconn.futunn.com`   |
99/// | HK          | `hkconn.futunn.com`   |
100/// | US          | `usconn.moomoo.com`   |
101/// | SG          | `sgconn.moomoo.com`   |
102/// | AU          | `auconn.moomoo.com`   |
103/// | JP          | `jpconn.moomoo.com`   |
104///
105/// 注意:C++ FutuOpenD (FTNN/FTMM) 本身**不**走 DNS,它走 CommConfig 服务端
106/// push(见 C++ `address.cpp:159-170`)。我们加 DNS 是对齐"保持 IP 池新鲜"
107/// 的意图,不是盲目照搬 C++。
108pub fn domain_for_attribution(attr: UserAttribution) -> &'static str {
109    match attr {
110        UserAttribution::Cn => "nnconn.futunn.com",
111        UserAttribution::Hk => "hkconn.futunn.com",
112        UserAttribution::Us => "usconn.moomoo.com",
113        UserAttribution::Sg => "sgconn.moomoo.com",
114        UserAttribution::Au => "auconn.moomoo.com",
115        UserAttribution::Jp => "jpconn.moomoo.com",
116    }
117}
118
119/// 异步解析某 attribution 对应域名 → `Vec<(ip_str, 9595)>`。
120/// 带 3 秒超时。解析失败 / 无 IP → 返回空 Vec(调用方 fallback 到硬编码池)。
121pub async fn resolve_domain_ips(attr: UserAttribution) -> Vec<(String, u16)> {
122    use tokio::net::lookup_host;
123    const DNS_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3);
124    let host = format!("{}:9595", domain_for_attribution(attr));
125    match tokio::time::timeout(DNS_TIMEOUT, lookup_host(host.clone())).await {
126        Ok(Ok(addrs)) => addrs
127            .filter_map(|sa| match sa {
128                // 只要 IPv4(我们硬编码池也是 v4,避免 v6 连接测试复杂度)
129                std::net::SocketAddr::V4(v4) => Some((v4.ip().to_string(), v4.port())),
130                std::net::SocketAddr::V6(_) => None,
131            })
132            .collect(),
133        Ok(Err(e)) => {
134            tracing::debug!(host = %host, error = %e, "DNS resolve failed");
135            Vec::new()
136        }
137        Err(_) => {
138            tracing::debug!(host = %host, "DNS resolve timed out after 3s");
139            Vec::new()
140        }
141    }
142}