Feature: sub-connection auth (TOKEN_CONN_AUTH) with HMAC + clientID binding
Client first packet on every sub-connection signs (clientID || timestamp || nonce) and waits for server ack. Server verifies signature and pins clientID on the sub-connection ctx, eliminating IP-reverse-lookup unreliability for NAT/localhost scenarios. Sub-conn coverage: Win 12 sites, Linux/macOS 3-4 each. Main connection keeps existing TOKEN_LOGIN flow unchanged. Includes: - Protocol structs sized to 512/256 bytes with reserved space for future extensions (locale, OS info, session token, etc.) - 5-min timestamp tolerance (Kerberos-grade replay window) - 10-sec client wait for cross-pacific / weak-network tolerance - Fix RemoveFromHostList side-effect ordering: MarkDeviceOffline and m_ActiveWndW.erase now only fire when ctx is actually removed from m_HostList, preventing sub-conn disconnects from misreporting main as offline (regression introduced by auth-set clientID on sub ctx) - Fix latent bug: IOCPClient::m_conn was never assigned in ctor, leaving GetConnectionAddress() always NULL and FileManager V2 transfer's srcClientID always 0 Breaking change: new client cannot use sub-features against old server. New server tolerates legacy clients (no auth). Future tightening can reject unauthenticated sub-connections via IsAuthenticated() flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -333,8 +333,60 @@ enum {
|
||||
CMD_EXECUTE_DLL_NEW = 243, // 执行代码
|
||||
CMD_PEER_TO_PEER = 244, // P2P通信
|
||||
TOKEN_CLIENTID = 245,
|
||||
TOKEN_CONN_AUTH = 246, // 子连接身份校验包(客户端首发,服务端回 ConnAuthAck)
|
||||
};
|
||||
|
||||
// 子连接校验:HMAC 签名 (clientID || timestamp || nonce),服务端通过校验后把 clientID
|
||||
// 钉在子连接 ctx 上,后续命令免 IP 反查直接拿到主连接关联。
|
||||
// 主连接(TOKEN_LOGIN 流)不走此校验。
|
||||
//
|
||||
// 兼容性策略:
|
||||
// - 老客户端不发 → 新服务端宽容(保留 IP 反查兜底,行为不变)。
|
||||
// - 新客户端发出 → 等服务端 ConnAuthAck,超时或失败则不继续。
|
||||
// - 因此新客户端只能向新服务端连接(破坏性升级)。
|
||||
// - 未来收紧:服务端可拒绝所有未通过 auth 的子连接。
|
||||
//
|
||||
// 协议固定为 512 / 256 字节(参照 LOGIN_INFOR::szReserved[512] 的做法),
|
||||
// 预留大量字节给未来扩展(如 client locale / OS 标识 / 子连接类型 / 会话 token /
|
||||
// per-conn 能力位等),避免再次破坏性升级。预留区构造时全 0 初始化,未启用字段
|
||||
// 不会进 HMAC 签名输入(签名输入仍只是 clientID || timestamp || nonce 共 32 字节)。
|
||||
#pragma pack(push, 1)
|
||||
struct ConnAuthPacket {
|
||||
uint8_t token; // = TOKEN_CONN_AUTH [1]
|
||||
uint64_t clientID; // 客户端 V2 ID(MachineGuid + 归一化路径算出) [8]
|
||||
uint64_t timestamp; // 客户端发包时的 Unix 秒,防重放第一道 [8]
|
||||
uint8_t nonce[16]; // 随机数,进一步降低重放/碰撞概率 [16]
|
||||
char signature[64]; // signMessage("", clientID||timestamp||nonce, 32) [64]
|
||||
char reserved[415]; // 预留扩展 [415]
|
||||
}; // 总 512
|
||||
|
||||
// 服务端响应:token + status + serverTime + 预留,固定 256 字节。
|
||||
// serverTime 客户端可用来校正本机时钟偏差用于后续协议(可选)。
|
||||
struct ConnAuthAck {
|
||||
uint8_t token; // = TOKEN_CONN_AUTH(回显,方便客户端 dispatch) [1]
|
||||
uint8_t status; // 0=OK, 其它=失败原因(见 ConnAuthStatus) [1]
|
||||
uint64_t serverTime; // 服务端处理时的 Unix 秒 [8]
|
||||
char reserved[246]; // 预留扩展 [246]
|
||||
}; // 总 256
|
||||
#pragma pack(pop)
|
||||
|
||||
// 编译期断言:协议大小不允许被无意改动
|
||||
static_assert(sizeof(ConnAuthPacket) == 512, "ConnAuthPacket must be exactly 512 bytes");
|
||||
static_assert(sizeof(ConnAuthAck) == 256, "ConnAuthAck must be exactly 256 bytes");
|
||||
|
||||
enum ConnAuthStatus {
|
||||
CONN_AUTH_OK = 0,
|
||||
CONN_AUTH_BAD_SIZE = 1, // 包长度不对
|
||||
CONN_AUTH_CLOCK_SKEW = 2, // 时间戳超过容忍范围
|
||||
CONN_AUTH_BAD_SIGNATURE = 3, // HMAC 不匹配
|
||||
CONN_AUTH_INTERNAL_ERROR = 4,
|
||||
};
|
||||
|
||||
#define CONN_AUTH_TIMESTAMP_TOLERANCE_SEC 300 // 客户端/服务端时钟漂移容忍 ±5 分钟
|
||||
#define CONN_AUTH_CLIENT_WAIT_MS 10000 // 客户端等待 ack 的超时
|
||||
// 设为 10 秒留足跨太平洋 + 拥塞 / 卫星链路 / 偏远网络的余量;
|
||||
// 同机几毫秒就回,正常路径用户感知不到。
|
||||
|
||||
enum MachineCommand {
|
||||
MACHINE_LOGOUT,
|
||||
MACHINE_SHUTDOWN,
|
||||
|
||||
Reference in New Issue
Block a user