Skip to main content

futu_backend/auth/commconfig/
mod.rs

1//! auth/commconfig — 拆 commconfig.rs 1123 → commconfig/{mod, 5 子}.rs
2//! (v1.4.110 CC Batch M)
3//!
4//! C++ `PullCommonConfigV2()` 对齐:动态拉取后台 IP 池 + 配置, 比 DNS / 硬编码
5//! 更权威 (`guaranteed_ip_for_conn` 按 ConnIdentity 分组).
6//!
7//! 子模块:
8//! - `types`      : 6 type alias + 9 const (CONN_WEB_*) + 2 struct (ForcedIpEntry, CommonConfigSnapshot)
9//! - `totp`       : base32_decode + gen_totp_sha1 (Google Authenticator 风格 6 digit)
10//! - `parsers`    : api_root + fetch_page + 4 parse_* + value_kind + is_*_identity
11//! - `fetch`      : fetch_all (主入口) + SharedCommConfig + empty_snapshot + spawn_refresher
12//! - `accessors`  : forced_ip_for_attribution + 3 ips_for_* + webtcp_hardcoded_addrs + 3 identity 派生
13//! - `tests`      : 单元测试 (内容不变, 已 split sibling)
14//!
15//! Parent auth/mod.rs `pub mod commconfig;` 路径不变 (mod.rs re-export 全 pub 项).
16
17mod accessors;
18mod fetch;
19mod parsers;
20mod totp;
21mod types;
22
23#[cfg(test)]
24mod tests;
25
26// hoist for tests.rs (super::* 接 trade_query 类似套路)
27#[cfg(test)]
28pub(super) use super::*;
29
30// ===== 外部 callers 直接用的 (auth/webtcp.rs, auth/broker.rs, auth/mod.rs) =====
31pub use accessors::{
32    broker_auth_webtcp_identity, default_webtcp_identity_for_client_type,
33    forced_ip_for_attribution, ips_for_attribution, ips_for_broker, ips_for_web_identity,
34    spawn_refresher, webtcp_addrs_for_identity, webtcp_hardcoded_addrs,
35};
36pub use fetch::server_now_ts;
37pub use fetch::{SharedCommConfig, empty_snapshot, fetch_all};
38pub use parsers::{api_root_for_client, client_version_dotted, is_web_identity};
39pub use totp::gen_totp_sha1;
40pub use types::{
41    AuthGuaranteedDomainMap, CONN_WEB_AU, CONN_WEB_CA, CONN_WEB_CN, CONN_WEB_HK, CONN_WEB_JP,
42    CONN_WEB_MY, CONN_WEB_SG, CONN_WEB_US, CommonConfigSnapshot, ForcedIpEntry, ForcedIpMap,
43    GuaranteedBrokerIpMap, GuaranteedIpMap, GuaranteedWebIpMap,
44};
45
46/// Build the shared Futu common HTTP `auth_token`.
47///
48/// Ref: C++ `NNProtoCenter/NNProtoCenter_Inner_Inline.h`
49/// `NNProto_BuildCommonHttpClientToken()` uses key `PEHMABDNLXIOG65U`,
50/// server time, 30s period, and `GenGoogleOTPCode_SHA1`.
51pub fn common_http_auth_token(unix_ts: i64) -> Option<String> {
52    gen_totp_sha1(types::AUTH_TOKEN_KEY_B32, unix_ts, 30)
53}
54
55/// Build the C++ common HTTP `client_token`.
56///
57/// Ref: `FutuOpenD/Src/NNProtoCenter/Login/NNDataUrl.cpp:222-235`
58/// `NNDataUrl::GetTimeEncryptKey()` writes server time into a 16-byte block,
59/// encrypts it with AES-128 using ASCII key `PEHMABDNLXIOG65U`, then hex encodes
60/// the encrypted block.
61pub fn common_http_client_token(unix_ts: i64) -> Option<String> {
62    if unix_ts <= 0 {
63        return None;
64    }
65
66    let unix_ts = unix_ts.to_string();
67    let mut block = [0u8; 16];
68    let bytes = unix_ts.as_bytes();
69    let len = bytes.len().min(block.len());
70    block[..len].copy_from_slice(&bytes[..len]);
71
72    use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
73    let cipher = aes::Aes128::new_from_slice(types::AUTH_TOKEN_KEY_B32.as_bytes()).ok()?;
74    let mut block = GenericArray::clone_from_slice(&block);
75    cipher.encrypt_block(&mut block);
76    Some(hex::encode(block))
77}
78
79// ===== 内部 re-export 给 sibling tests.rs (super::* 接) =====
80#[cfg(test)]
81pub(super) use accessors::delay_until_next_refresh;
82#[cfg(test)]
83pub(super) use fetch::server_now_ts_at;
84#[cfg(test)]
85pub(super) use parsers::{
86    is_broker_identity, parse_auth_guaranteed_domain_list, parse_forced_ip, parse_guaranteed_ip,
87    parse_web_tcp_config_identity, value_kind,
88};
89#[cfg(test)]
90pub(super) use std::collections::HashMap;
91#[cfg(test)]
92pub(super) use totp::base32_decode;