Skip to main content

futu_backend/auth/commconfig/
mod.rs

1//! CommConfig transaction actor and atomically published last-good snapshot.
2//!
3//! C++ `PullCommonConfigV2()` 对齐:动态拉取后台 IP 池 + 配置, 比 DNS / 硬编码
4//! 更权威 (`guaranteed_ip_for_conn` 按 ConnIdentity 分组).
5//!
6//! Network IO, page action policy, raw transaction accumulation, typed
7//! projection, persistence, and publication stay in separate modules so a
8//! partial transaction can never escape as a runtime snapshot.
9//!
10//! Parent auth/mod.rs `pub mod commconfig;` 路径不变 (mod.rs re-export 全 pub 项).
11
12mod accessors;
13mod clock;
14mod fetch_page;
15mod parsers;
16mod projection;
17mod runner;
18mod snapshot;
19mod store;
20mod totp;
21mod transaction;
22mod types;
23mod wire;
24
25#[cfg(test)]
26mod tests;
27
28// hoist for tests.rs (super::* 接 trade_query 类似套路)
29#[cfg(test)]
30pub(super) use super::*;
31
32// ===== 外部 callers 直接用的 (auth/webtcp.rs, auth/broker.rs, auth/mod.rs) =====
33pub use accessors::{
34    broker_auth_webtcp_identity, default_webtcp_identity_for_client_type,
35    forced_ip_for_attribution, ips_for_attribution, ips_for_broker, ips_for_web_identity,
36    webtcp_addrs_for_identity, webtcp_hardcoded_addrs,
37};
38pub use clock::server_now_ts;
39pub use fetch_page::{api_root_for_client, client_version_dotted};
40pub use parsers::is_web_identity;
41pub use runner::{
42    COMMCONFIG_PRE_LOGIN_TIMEOUT, CommConfigBootstrapOutcome, CommConfigPersistence,
43    bootstrap_before_login, spawn_actor,
44};
45pub use snapshot::{SharedCommConfig, empty_snapshot, new_shared_snapshot};
46pub use store::{CommConfigStoreError, load_last_good_snapshot};
47pub use totp::gen_totp_sha1;
48pub use types::{
49    AuthGuaranteedDomainMap, CONN_WEB_AU, CONN_WEB_CA, CONN_WEB_CN, CONN_WEB_HK, CONN_WEB_JP,
50    CONN_WEB_MY, CONN_WEB_SG, CONN_WEB_US, CommConfigSource, CommonConfigSnapshot, ForcedIpEntry,
51    ForcedIpMap, GuaranteedBrokerIpMap, GuaranteedIpMap, GuaranteedWebIpMap,
52};
53
54/// Build the shared Futu common HTTP `auth_token`.
55///
56/// Ref: C++ `NNProtoCenter/NNProtoCenter_Inner_Inline.h`
57/// `NNProto_BuildCommonHttpClientToken()` uses key `PEHMABDNLXIOG65U`,
58/// server time, 30s period, and `GenGoogleOTPCode_SHA1`.
59pub fn common_http_auth_token(unix_ts: i64) -> Option<String> {
60    gen_totp_sha1(types::AUTH_TOKEN_KEY_B32, unix_ts, 30)
61}
62
63/// Build the C++ common HTTP `client_token`.
64///
65/// Ref: `FutuOpenD/Src/NNProtoCenter/Login/NNDataUrl.cpp:222-235`
66/// `NNDataUrl::GetTimeEncryptKey()` writes server time into a 16-byte block,
67/// encrypts it with AES-128 using ASCII key `PEHMABDNLXIOG65U`, then hex encodes
68/// the encrypted block.
69pub fn common_http_client_token(unix_ts: i64) -> Option<String> {
70    if unix_ts <= 0 {
71        return None;
72    }
73
74    let unix_ts = unix_ts.to_string();
75    let mut block = [0u8; 16];
76    let bytes = unix_ts.as_bytes();
77    let len = bytes.len().min(block.len());
78    block[..len].copy_from_slice(&bytes[..len]);
79
80    use aes::cipher::{BlockCipherEncrypt, KeyInit};
81    let cipher = aes::Aes128::new_from_slice(types::AUTH_TOKEN_KEY_B32.as_bytes()).ok()?;
82    let mut cipher_block = aes::cipher::Block::<aes::Aes128>::from(block);
83    cipher.encrypt_block(&mut cipher_block);
84    Some(hex::encode(cipher_block))
85}
86
87// ===== 内部 re-export 给 sibling tests.rs (super::* 接) =====
88#[cfg(test)]
89pub(super) use accessors::delay_until_next_refresh;
90#[cfg(test)]
91pub(super) use clock::server_now_ts_at;
92#[cfg(test)]
93pub(super) use parsers::{
94    is_broker_identity, parse_auth_guaranteed_domain_list, parse_forced_ip, parse_guaranteed_ip,
95    parse_web_tcp_config_identity, value_kind,
96};
97#[cfg(test)]
98pub(super) use std::collections::HashMap;
99#[cfg(test)]
100pub(super) use totp::base32_decode;