futu_backend/auth/commconfig/
accessors.rs1use std::sync::Arc;
6
7use crate::auth::UserAttribution;
8
9use super::types::CommonConfigSnapshot;
10
11use super::fetch::{SharedCommConfig, fetch_all};
12use super::parsers::is_web_identity;
13use super::types::{
14 CONN_WEB_AU, CONN_WEB_CA, CONN_WEB_CN, CONN_WEB_HK, CONN_WEB_JP, CONN_WEB_MY, CONN_WEB_SG,
15 CONN_WEB_US,
16};
17
18pub fn forced_ip_for_attribution(
19 snapshot: &CommonConfigSnapshot,
20 attr: UserAttribution,
21 now_ts: i64,
22) -> Option<(String, u16)> {
23 snapshot.forced_ip.get(&attr).and_then(|entry| {
24 if entry.is_valid(now_ts) {
25 Some((entry.ip.clone(), entry.port))
26 } else {
27 tracing::debug!(
28 ?attr,
29 expire_ts = entry.expire_ts,
30 now_ts,
31 "commconfig: forced_ip expired, ignored"
32 );
33 None
34 }
35 })
36}
37
38pub fn ips_for_attribution(
41 snapshot: &CommonConfigSnapshot,
42 attr: UserAttribution,
43) -> Vec<(String, u16)> {
44 snapshot
45 .guaranteed_ip
46 .get(&attr)
47 .cloned()
48 .unwrap_or_default()
49}
50
51pub fn ips_for_broker(snapshot: &CommonConfigSnapshot, broker_id: u32) -> Vec<(String, u16)> {
60 snapshot
61 .guaranteed_ip_broker
62 .get(&broker_id)
63 .cloned()
64 .unwrap_or_default()
65}
66
67pub fn ips_for_web_identity(snapshot: &CommonConfigSnapshot, identity: u32) -> Vec<(String, u16)> {
73 snapshot
74 .guaranteed_ip_web
75 .get(&identity)
76 .cloned()
77 .unwrap_or_default()
78}
79
80pub fn webtcp_hardcoded_addrs(identity: u32) -> &'static [(&'static str, u16)] {
94 match identity {
95 CONN_WEB_CN => &[
96 ("119.91.245.213", 443),
97 ("119.91.245.125", 443),
98 ("119.91.245.213", 9595),
99 ("119.91.245.125", 9595),
100 ],
101 CONN_WEB_HK => &[
102 ("101.32.198.103", 443),
103 ("43.135.64.109", 443),
104 ("101.32.198.103", 9595),
105 ("43.135.64.109", 9595),
106 ],
107 CONN_WEB_US => &[
108 ("49.51.78.82", 443),
109 ("170.106.62.85", 443),
110 ("49.51.78.82", 9595),
111 ("170.106.62.85", 9595),
112 ],
113 CONN_WEB_SG => &[
114 ("101.32.173.177", 443),
115 ("101.33.49.67", 443),
116 ("101.32.173.177", 9595),
117 ("101.33.49.67", 9595),
118 ],
119 CONN_WEB_AU => &[
120 ("54.206.243.201", 443),
121 ("3.104.68.90", 443),
122 ("54.206.243.201", 9595),
123 ("3.104.68.90", 9595),
124 ],
125 CONN_WEB_JP => &[
126 ("43.163.254.232", 443),
127 ("43.163.252.131", 443),
128 ("43.163.254.232", 9595),
129 ("43.163.252.131", 9595),
130 ],
131 CONN_WEB_MY => &[
132 ("47.254.245.70", 443),
133 ("47.254.254.140", 443),
134 ("47.254.245.70", 9595),
135 ("47.254.254.140", 9595),
136 ],
137 CONN_WEB_CA => &[
138 ("15.157.179.115", 443),
139 ("15.157.83.146", 443),
140 ("15.157.179.115", 9595),
141 ("15.157.83.146", 9595),
142 ],
143 _ => &[],
144 }
145}
146
147pub fn webtcp_addrs_for_identity(
150 snapshot: &CommonConfigSnapshot,
151 identity: u32,
152) -> Vec<(String, u16)> {
153 let dynamic = ips_for_web_identity(snapshot, identity);
154 if !dynamic.is_empty() {
155 return dynamic;
156 }
157 webtcp_hardcoded_addrs(identity)
158 .iter()
159 .map(|(ip, port)| ((*ip).to_string(), *port))
160 .collect()
161}
162
163pub fn default_webtcp_identity_for_client_type(client_type: u8) -> u32 {
169 if client_type == 60 {
170 CONN_WEB_US
171 } else {
172 CONN_WEB_HK
173 }
174}
175
176pub fn broker_auth_webtcp_identity(snapshot: &CommonConfigSnapshot, client_type: u8) -> u32 {
183 snapshot
184 .web_conn_identity
185 .filter(|identity| is_web_identity(*identity))
186 .unwrap_or_else(|| default_webtcp_identity_for_client_type(client_type))
187}
188
189pub fn delay_until_next_refresh(next_refresh_ts: i64, now: i64) -> u64 {
198 const MIN_DELAY: u64 = 300; const MAX_DELAY: u64 = 7200; const DEFAULT_DELAY: u64 = 1800; if next_refresh_ts <= 0 {
202 return DEFAULT_DELAY;
203 }
204 let diff = next_refresh_ts - now;
205 if diff <= 0 {
206 return DEFAULT_DELAY;
207 }
208 (diff as u64).clamp(MIN_DELAY, MAX_DELAY)
209}
210
211pub fn spawn_refresher(
221 snapshot: SharedCommConfig,
222 http: reqwest::Client,
223 client_type: u8,
224 device_id: String,
225 user_id: u64,
226 svr_time_offset: i64,
227) -> tokio::task::JoinHandle<()> {
228 tokio::spawn(async move {
229 loop {
230 let current = snapshot.load();
231 let now = chrono::Utc::now().timestamp();
232 let sleep_secs = delay_until_next_refresh(current.next_refresh_ts, now);
233 drop(current);
234
235 tracing::debug!(
236 sleep_secs,
237 "commconfig: refresher sleeping until next refresh"
238 );
239 tokio::time::sleep(std::time::Duration::from_secs(sleep_secs)).await;
240
241 tracing::info!("commconfig: refreshing...");
242 let new_snapshot =
243 fetch_all(&http, client_type, &device_id, user_id, svr_time_offset).await;
244 if new_snapshot.guaranteed_ip.is_empty()
245 && new_snapshot.guaranteed_ip_broker.is_empty()
246 && new_snapshot.guaranteed_ip_web.is_empty()
247 {
248 tracing::warn!(
249 "commconfig: refresh returned empty guaranteed_ip maps, keeping previous snapshot"
250 );
251 continue;
254 }
255 tracing::info!(
256 platform_pools = new_snapshot.guaranteed_ip.len(),
257 broker_pools = new_snapshot.guaranteed_ip_broker.len(),
258 web_pools = new_snapshot.guaranteed_ip_web.len(),
259 auth_retry_domains = new_snapshot.auth_guaranteed_domains.len(),
260 auth_retry_domain_configured = new_snapshot.auth_guaranteed_domains_configured,
261 next_refresh_ts = new_snapshot.next_refresh_ts,
262 "commconfig: snapshot refreshed"
263 );
264 snapshot.store(Arc::new(new_snapshot));
265 }
266 })
267}