Skip to main content

futu_backend/auth/
conn_points.rs

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