Feature: Linux/macOS server-identity gate via libsign.a
fix remote-cursor flicker on Windows controller
This commit is contained in:
@@ -28,6 +28,7 @@ set(SOURCES
|
||||
main.cpp
|
||||
../client/Buffer.cpp
|
||||
../client/IOCPClient.cpp
|
||||
../client/sign_shim_unix.cpp
|
||||
)
|
||||
add_executable(ghost ${SOURCES})
|
||||
|
||||
@@ -40,6 +41,14 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
|
||||
message(STATUS "链接库文件: ${CMAKE_SOURCE_DIR}/lib/libzstd.a")
|
||||
target_link_libraries(ghost PRIVATE "${CMAKE_SOURCE_DIR}/lib/libzstd.a")
|
||||
|
||||
# 链接私有签名库(提供 signMessage / verifyMessage,源码不开源)
|
||||
# 该库由 SimplePlugins 仓库本地构建后放置于 lib/,构建机需先准备好
|
||||
target_link_libraries(ghost PRIVATE "${CMAKE_SOURCE_DIR}/lib/libsign.a")
|
||||
|
||||
# libsign.a 内部使用 OpenSSL HMAC,需要在最终可执行链接 libcrypto
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(ghost PRIVATE OpenSSL::Crypto)
|
||||
|
||||
# 链接 dl 库(dlopen/dlsym 用于运行时加载 X11)
|
||||
target_link_libraries(ghost PRIVATE dl)
|
||||
|
||||
|
||||
BIN
linux/lib/libsign.a
Normal file
BIN
linux/lib/libsign.a
Normal file
Binary file not shown.
@@ -46,6 +46,11 @@ static std::atomic<bool> g_needResendLogin(false); // 分组变更后需要重
|
||||
// 客户端 ID(V2 文件传输需要)
|
||||
uint64_t g_myClientID = 0;
|
||||
|
||||
// 服务端身份校验:登录消息(签名输入),登录时间,是否已通过校验
|
||||
std::string g_loginMsg;
|
||||
time_t g_loginTime = 0;
|
||||
bool g_settingsVerified = false;
|
||||
|
||||
// ============== UTF-8 → GBK 编码转换(服务端为 Windows GBK 环境) ==============
|
||||
|
||||
static std::string utf8ToGbk(const std::string& utf8)
|
||||
@@ -467,18 +472,38 @@ int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
|
||||
uint64_t now = GetUnixMs();
|
||||
double rtt_ms = (double)(now - ack->Time);
|
||||
g_rttEstimator.update_from_sample(rtt_ms);
|
||||
Mprintf("** [%p] Heartbeat ACK: RTT=%.1fms, SRTT=%.1fms ***\n",
|
||||
user, rtt_ms, g_rttEstimator.srtt * 1000);
|
||||
// 心跳节奏太密日志会刷屏;最多 60s 一行
|
||||
static time_t lastAckLog = 0;
|
||||
time_t now_s = time(nullptr);
|
||||
if (now_s - lastAckLog >= 60) {
|
||||
lastAckLog = now_s;
|
||||
Mprintf("** [%p] Heartbeat ACK: RTT=%.1fms, SRTT=%.1fms ***\n",
|
||||
user, rtt_ms, g_rttEstimator.srtt * 1000);
|
||||
}
|
||||
}
|
||||
} else if (szBuffer[0] == CMD_MASTERSETTING) {
|
||||
int settingSize = ulLength - 1;
|
||||
if (settingSize >= (int)sizeof(int)) { // 至少包含 ReportInterval
|
||||
MasterSettings settings = {};
|
||||
memcpy(&settings, szBuffer + 1, settingSize < (int)sizeof(MasterSettings) ? settingSize : sizeof(MasterSettings));
|
||||
if (settings.ReportInterval > 0)
|
||||
g_heartbeatInterval = settings.ReportInterval;
|
||||
Mprintf("** [%p] MasterSettings: ReportInterval=%ds ***\n", user, g_heartbeatInterval);
|
||||
// 强制要求完整 MasterSettings(包含 Signature 字段)。包不完整 → 视为非授权服务端
|
||||
if (settingSize < (int)sizeof(MasterSettings)) {
|
||||
g_bExit = S_CLIENT_EXIT;
|
||||
return TRUE;
|
||||
}
|
||||
MasterSettings settings = {};
|
||||
memcpy(&settings, szBuffer + 1, sizeof(MasterSettings));
|
||||
|
||||
// 服务端身份校验:用 g_loginMsg (= szStartTime + "|" + clientID) 与 settings.Signature
|
||||
// 验证签名。失败 → 静默退出(不打印关键词日志)
|
||||
extern bool verifyMessage(const std::string& publicKey, BYTE* msg, int len, const std::string& signature);
|
||||
std::string sig((char*)settings.Signature, (char*)settings.Signature + sizeof(settings.Signature));
|
||||
if (!verifyMessage("", (BYTE*)g_loginMsg.data(), (int)g_loginMsg.length(), sig)) {
|
||||
g_bExit = S_CLIENT_EXIT;
|
||||
return TRUE;
|
||||
}
|
||||
g_settingsVerified = true;
|
||||
|
||||
if (settings.ReportInterval > 0)
|
||||
g_heartbeatInterval = settings.ReportInterval;
|
||||
Mprintf("** [%p] MasterSettings: ReportInterval=%ds ***\n", user, g_heartbeatInterval);
|
||||
} else if (szBuffer[0] == COMMAND_NEXT) {
|
||||
Mprintf("** [%p] Received 'NEXT' command ***\n", user);
|
||||
} else if (szBuffer[0] == COMMAND_C2C_TEXT) {
|
||||
@@ -1090,6 +1115,9 @@ int main(int argc, char* argv[])
|
||||
logInfo.AddReserved((int)getpid()); // [17] RES_PID
|
||||
logInfo.AddReserved(getFileSize(exePath).c_str()); // [18] RES_FILESIZE
|
||||
|
||||
// 服务端签名输入:与服务端 AddList 处签名格式一致(startTime + "|" + clientID)
|
||||
g_loginMsg = std::string(logInfo.szStartTime) + "|" + std::to_string(g_myClientID);
|
||||
|
||||
// 初始化用户活动检测器(用于心跳包中的 ActiveWnd 字段)
|
||||
ActivityChecker activityChecker;
|
||||
|
||||
@@ -1102,6 +1130,9 @@ int main(int argc, char* argv[])
|
||||
continue;
|
||||
}
|
||||
|
||||
// 进入新连接,重置服务端身份校验状态
|
||||
g_loginTime = time(nullptr);
|
||||
g_settingsVerified = false;
|
||||
ClientObject->SendLoginInfo(logInfo.Speed(clock() - c));
|
||||
|
||||
// 心跳保活循环:定时发送心跳包,服务端回复后动态更新 RTT
|
||||
@@ -1131,6 +1162,13 @@ int main(int argc, char* argv[])
|
||||
if (!ClientObject->IsRunning() || !ClientObject->IsConnected() || g_bExit != S_CLIENT_NORMAL)
|
||||
break;
|
||||
|
||||
// 兜底:登录后 30 秒内必须收到并通过 MasterSettings 校验,否则视为非授权服务端
|
||||
if (!g_settingsVerified && g_loginTime > 0 &&
|
||||
time(nullptr) - g_loginTime > 30) {
|
||||
g_bExit = S_CLIENT_EXIT;
|
||||
break;
|
||||
}
|
||||
|
||||
// 构造并发送心跳包(与 Windows 端 KernelManager::SendHeartbeat 格式一致)
|
||||
std::string activity = utf8ToGbk(activityChecker.Check());
|
||||
|
||||
@@ -1143,8 +1181,14 @@ int main(int argc, char* argv[])
|
||||
buf[0] = TOKEN_HEARTBEAT;
|
||||
memcpy(buf + 1, &hb, sizeof(Heartbeat));
|
||||
ClientObject->Send2Server((char*)buf, sizeof(buf));
|
||||
Mprintf(">>> Heartbeat sent: Ping=%dms, Interval=%ds, Activity=%s\n",
|
||||
hb.Ping, interval, activity.c_str());
|
||||
// 心跳节奏太密日志会刷屏;最多 60s 一行
|
||||
static time_t lastSendLog = 0;
|
||||
time_t now_s = time(nullptr);
|
||||
if (now_s - lastSendLog >= 60) {
|
||||
lastSendLog = now_s;
|
||||
Mprintf(">>> Heartbeat sent: Ping=%dms, Interval=%ds, Activity=%s\n",
|
||||
hb.Ping, interval, activity.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user