Feat: Fold hostname into client ID and log clone-ID collisions

Cloned miniPC images share the same MachineGuid, so their client_id
collides and only one host appears in the list. Fold the hostname into
the V2 client-ID hash so hosts with distinct names no longer collide.
Already-persisted client IDs are unchanged: they recompute only after
the settings/client_id registry value is cleared.

Server side, when a login reuses an existing client_id but the hostname,
IP, or username differs, emit a WARN diagnostic before assigning the
connection to the standby list. Observation-only, no behavior change.

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-15 19:43:58 +02:00
1 parent c93f09e9ac
commit 77b688c981
2 files changed
+20 -4

No files matched your search

+4 -4
View File
@@ -412,13 +412,13 @@ LOGIN_INFOR GetLoginInfo(DWORD dwSpeed, CONNECT_ADDRESS& conn, const std::string
sprintf(cpuInfo, "%dMHz", dwCPUMHz); sprintf(cpuInfo, "%dMHz", dwCPUMHz);
std::string clientID = cfg.GetStr("settings", "client_id"); std::string clientID = cfg.GetStr("settings", "client_id");
if (clientID.empty()) { if (clientID.empty()) {
// V2 ID 算法:MachineGuid + 归一化路径 // V2 ID 算法:MachineGuid + 主机名 + 归一化路径
// - 同机同程序路径永远同 ID(不依赖 IP/PCName/OS/CPU 漂移) // - 主机名兜底区分同镜像克隆机:MachineGuid 相同、路径相同仍能靠主机名区分。
// - 局域网多机即便同镜像(sysprep 会让 MachineGuid 各不同)也不撞库 // - 代价:改名会让 client_id 变化,丢该主机的备注/授权/历史;主机名非硬件唯一,重名仍撞。
// MachineGuid 读取失败的极端情况退化到老算法,保兼容。 // MachineGuid 读取失败的极端情况退化到老算法,保兼容。
std::string machineGuid = GetMachineGuidWindows(); std::string machineGuid = GetMachineGuidWindows();
if (!machineGuid.empty()) { if (!machineGuid.empty()) {
conn.clientID = CalcalateIDv2(machineGuid, NormalizeExePathLower(buf), isAuth); conn.clientID = CalcalateIDv2(machineGuid + "|" + szPCName, NormalizeExePathLower(buf), isAuth);
} else { } else {
Mprintf("WARN: MachineGuid 读取失败,回退到老 ID 算法\n"); Mprintf("WARN: MachineGuid 读取失败,回退到老 ID 算法\n");
conn.clientID = CalcalateID({ pubIP, szPCName, LoginInfor.OsVerInfoEx, cpuInfo, buf }); conn.clientID = CalcalateID({ pubIP, szPCName, LoginInfor.OsVerInfoEx, cpuInfo, buf });
+16
View File
@@ -1748,6 +1748,22 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
return; return;
} }
if (!ctx->GetClientData(ONLINELIST_IP).IsEmpty()) { if (!ctx->GetClientData(ONLINELIST_IP).IsEmpty()) {
// 诊断:client_id 相同,但主机名/用户名不同 → 很可能是「同镜像克隆机」
// (MachineGuid 相同 → id 相同,实为不同物理机)。IP 会随 DHCP/公网出口漂移,仅供参考。
// 写日志供管理员核对,作为后续「重复 id 主动下发新 client_id」的依据。
CString newUser = v[RES_USERNAME].c_str();
CString oldUser = ctx->GetAdditionalData(RES_USERNAME);
CString oldName = ctx->GetClientData(ONLINELIST_COMPUTER_NAME);
CString oldIP = ctx->GetClientData(ONLINELIST_IP);
if (strPCName.CompareNoCase(oldName) != 0 ||
data[ONLINELIST_IP].CompareNoCase(oldIP) != 0 ||
newUser.CompareNoCase(oldUser) != 0) {
Mprintf("WARN: client_id 相同但主机信息不同(疑似克隆机撞 ID)id=%llu\n"
" 新上线: 主机名=%s IP=%s 用户名=%s\n"
" 已在线: 主机名=%s IP=%s 用户名=%s\n",
id, strPCName.GetString(), data[ONLINELIST_IP].GetString(), newUser.GetString(),
oldName.GetString(), oldIP.GetString(), oldUser.GetString());
}
Mprintf("上线消息 - 主机重复上线,暂存替补 [2]: %llu. IP= %s. Path= %s\n", id, data[ONLINELIST_IP], path); Mprintf("上线消息 - 主机重复上线,暂存替补 [2]: %llu. IP= %s. Path= %s\n", id, data[ONLINELIST_IP], path);
// 网络抖动导致客户端用新连接重复上线:这里不立即替换/关闭旧连接, // 网络抖动导致客户端用新连接重复上线:这里不立即替换/关闭旧连接,
// 否则旧连接被关会触发客户端反复重连抖动(更新前就一直在"替换旧连接")。 // 否则旧连接被关会触发客户端反复重连抖动(更新前就一直在"替换旧连接")。