Compare commits
3
Commits
8d0f419093
...
c906a46bcc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c906a46bcc | ||
|
|
6c9fef8793 | ||
|
|
bbdb4104bf |
No files matched your search
@@ -0,0 +1,336 @@
|
||||
// ChatManager.cpp: implementation of the CChatManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ChatManager.h"
|
||||
#include "Common.h"
|
||||
#include "resource.h"
|
||||
#include <TlHelp32.h>
|
||||
|
||||
#if ENABLE_MESSAGE
|
||||
|
||||
// 远程交谈(双向聊天)被控端实现,移植自 SimplePlugins/ChatManager。
|
||||
// 服务端 CChat 对话框发起会话后,被控端在此显示聊天窗口,并支持服务端远程锁定屏幕。
|
||||
|
||||
#define SE_DEBUG_PRIVILEGE 20
|
||||
typedef LONG(__stdcall* RtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);
|
||||
typedef LONG(__stdcall* SuspendOrResumeProcess)(HANDLE hProcess);
|
||||
|
||||
struct PIDWNDINFO {
|
||||
DWORD dwProcessId;
|
||||
HWND hWnd;
|
||||
};
|
||||
|
||||
// 锁定屏幕相关的全局状态(同一会话内只有一个 CChatManager 实例)
|
||||
static HANDLE g_hProcess = NULL;
|
||||
static SuspendOrResumeProcess g_lpfnNtResumeProcess = NULL;
|
||||
static HHOOK g_hKeyBoard = NULL;
|
||||
static int g_nLocked = 0;
|
||||
|
||||
static DWORD GetWinlogonPid()
|
||||
{
|
||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (snap == INVALID_HANDLE_VALUE) return 0;
|
||||
PROCESSENTRY32 pEntry = { sizeof(pEntry) };
|
||||
BOOL rtn = Process32First(snap, &pEntry);
|
||||
while (rtn) {
|
||||
if (strcmp(pEntry.szExeFile, "winlogon.exe") == 0) {
|
||||
CloseHandle(snap);
|
||||
return pEntry.th32ProcessID;
|
||||
}
|
||||
memset(pEntry.szExeFile, 0, sizeof(pEntry.szExeFile));
|
||||
rtn = Process32Next(snap, &pEntry);
|
||||
}
|
||||
CloseHandle(snap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 屏蔽 alt+tab / alt+f4 / win 键等系统快捷键
|
||||
static LRESULT CALLBACK KeyBoardProc(int code, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (code == HC_ACTION) {
|
||||
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
|
||||
switch (wParam) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
if ((p->vkCode == VK_TAB && (p->flags & LLKHF_ALTDOWN) != 0) // ALT+TAB
|
||||
|| (p->vkCode == VK_F4 && (p->flags & LLKHF_ALTDOWN) != 0) // ALT+F4
|
||||
|| (p->vkCode == VK_ESCAPE && (p->flags & LLKHF_ALTDOWN) != 0) // Alt+Esc
|
||||
|| (p->vkCode == VK_ESCAPE && (GetKeyState(VK_CONTROL) & 0x8000) != 0) // Ctrl+Esc
|
||||
|| (p->vkCode == VK_LWIN)
|
||||
|| (p->vkCode == VK_RWIN)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ::CallNextHookEx(g_hKeyBoard, code, wParam, lParam);
|
||||
}
|
||||
|
||||
static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
|
||||
{
|
||||
PIDWNDINFO* pInfo = (PIDWNDINFO*)lParam;
|
||||
LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
|
||||
if (pInfo->dwProcessId == 0 && hWnd != pInfo->hWnd &&
|
||||
IsWindowVisible(hWnd) && GetParent(hWnd) == NULL && (lStyle & WS_MINIMIZEBOX)) {
|
||||
ShowWindow(hWnd, SW_MINIMIZE);
|
||||
return TRUE;
|
||||
}
|
||||
DWORD dwProcessId;
|
||||
GetWindowThreadProcessId(hWnd, &dwProcessId);
|
||||
if (dwProcessId == pInfo->dwProcessId && IsWindowVisible(hWnd) && GetParent(hWnd) == NULL) {
|
||||
pInfo->hWnd = hWnd;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 安装键盘钩子并挂起 winlogon(必须在聊天窗口线程调用,确保钩子有消息循环)
|
||||
static void ChatSetHook()
|
||||
{
|
||||
g_hKeyBoard = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardProc, GetModuleHandle(NULL), 0);
|
||||
HMODULE hMod = LoadLibrary("ntdll");
|
||||
if (hMod) {
|
||||
RtlAdjustPrivilege lpfnRtlAdjustPrivilege = (RtlAdjustPrivilege)GetProcAddress(hMod, "RtlAdjustPrivilege");
|
||||
SuspendOrResumeProcess lpfnNtSuspendProcess = (SuspendOrResumeProcess)GetProcAddress(hMod, "NtSuspendProcess");
|
||||
g_lpfnNtResumeProcess = (SuspendOrResumeProcess)GetProcAddress(hMod, "NtResumeProcess");
|
||||
DWORD pid = GetWinlogonPid();
|
||||
BOOLEAN dummy = 0;
|
||||
if (lpfnRtlAdjustPrivilege) lpfnRtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &dummy);
|
||||
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
if (g_hProcess && lpfnNtSuspendProcess) lpfnNtSuspendProcess(g_hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
static void ChatUnSetHook()
|
||||
{
|
||||
if (g_hKeyBoard) {
|
||||
UnhookWindowsHookEx(g_hKeyBoard);
|
||||
g_hKeyBoard = NULL;
|
||||
}
|
||||
if (g_lpfnNtResumeProcess && g_hProcess) {
|
||||
g_lpfnNtResumeProcess(g_hProcess);
|
||||
}
|
||||
if (g_hProcess) {
|
||||
CloseHandle(g_hProcess);
|
||||
g_hProcess = NULL;
|
||||
}
|
||||
g_lpfnNtResumeProcess = NULL;
|
||||
}
|
||||
|
||||
// 解除锁定:恢复鼠标/屏保/任务栏/开始菜单/桌面,并恢复 winlogon
|
||||
static void UnlockScreen()
|
||||
{
|
||||
ChatUnSetHook();
|
||||
ClipCursor(NULL);
|
||||
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, 0, SPIF_UPDATEINIFILE);
|
||||
::ShowWindow(::FindWindow("Shell_TrayWnd", NULL), SW_SHOW);
|
||||
::ShowWindow(::FindWindow("Button", "开始"), SW_SHOW);
|
||||
::ShowWindow(::FindWindow("Progman", NULL), SW_SHOW);
|
||||
g_nLocked = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
CChatManager::CChatManager(IOCPClient* ClientObject, int n, void* user) : CManager(ClientObject)
|
||||
{
|
||||
m_hInstance = HINSTANCE(user);
|
||||
m_hWnd = NULL;
|
||||
m_bRun = TRUE;
|
||||
g_nLocked = 0;
|
||||
|
||||
// 向服务端发起远程交谈,服务端打开 CChat 对话框后会回发 COMMAND_NEXT_CHAT
|
||||
BYTE bToken = TOKEN_CHAT_START;
|
||||
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
|
||||
m_ClientObject->Send2Server((char*)&bToken, 1, &mask);
|
||||
WaitForDialogOpen();
|
||||
|
||||
m_hThread = __CreateThread(NULL, 0, MessageLoopProc, this, 0, NULL);
|
||||
Mprintf("Chat 构造\n");
|
||||
}
|
||||
|
||||
CChatManager::~CChatManager()
|
||||
{
|
||||
m_bRun = FALSE;
|
||||
while (m_hThread)
|
||||
Sleep(100);
|
||||
|
||||
if (g_nLocked == 1) {
|
||||
UnlockScreen();
|
||||
}
|
||||
if (m_hWnd) {
|
||||
SendMessage(m_hWnd, WM_USER + 1, NULL, NULL);
|
||||
}
|
||||
Mprintf("Chat 析构\n");
|
||||
}
|
||||
|
||||
DWORD WINAPI CChatManager::MessageLoopProc(LPVOID lParam)
|
||||
{
|
||||
CChatManager* pThis = (CChatManager*)lParam;
|
||||
HANDLE& hThread(pThis->m_hThread);
|
||||
|
||||
pThis->m_hWnd = CreateDialogParamA(pThis->m_hInstance, MAKEINTRESOURCE(IDD_CHAT), NULL,
|
||||
CChatManager::ChatDialogProc, (LPARAM)pThis);
|
||||
if (pThis->m_hWnd) {
|
||||
SendMessage(GetDlgItem(pThis->m_hWnd, IDC_EDIT_NEWMSG), EM_SETLIMITTEXT, 8158, 0);
|
||||
SetWindowPos(pThis->m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
||||
// 移除最小化/最大化按钮
|
||||
LONG lStyle = GetWindowLong(pThis->m_hWnd, GWL_STYLE);
|
||||
lStyle &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
|
||||
SetWindowLong(pThis->m_hWnd, GWL_STYLE, lStyle);
|
||||
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0) && pThis->m_bRun) {
|
||||
if (!IsDialogMessage(pThis->m_hWnd, &msg)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
hThread = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK CChatManager::ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
CChatManager* pThis = (CChatManager*)GetWindowLongPtr(hDlg, GWLP_USERDATA);
|
||||
switch (uMsg) {
|
||||
case WM_INITDIALOG:
|
||||
SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)lParam);
|
||||
pThis = (CChatManager*)lParam;
|
||||
SetWindowText(hDlg, "远程交谈");
|
||||
SetDlgItemText(hDlg, IDC_BUTTON_SEND, "发送消息");
|
||||
SetDlgItemText(hDlg, IDC_BUTTON_END, "结束交谈");
|
||||
{
|
||||
HICON hIcon = LoadIcon(pThis->m_hInstance, MAKEINTRESOURCE(IDI_ICON_MSG));
|
||||
if (hIcon) {
|
||||
SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
|
||||
SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case WM_COMMAND:
|
||||
if (LOWORD(wParam) == IDC_BUTTON_SEND) {
|
||||
char str[4096] = { 0 };
|
||||
GetDlgItemText(hDlg, IDC_EDIT_NEWMSG, str, sizeof(str));
|
||||
if (str[0] == 0) {
|
||||
SetFocus(GetDlgItem(hDlg, IDC_EDIT_NEWMSG));
|
||||
return TRUE; // 消息为空不发送
|
||||
}
|
||||
pThis->Send((LPBYTE)str, lstrlenA(str) + 1);
|
||||
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
char Text[5120] = { 0 };
|
||||
sprintf_s(Text, sizeof(Text), "%s %d/%d/%d %d:%02d:%02d\r\n %s\r\n\r\n", "自己:",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, str);
|
||||
HWND hEditChatLog = GetDlgItem(hDlg, IDC_EDIT_CHATLOG);
|
||||
int nLen = GetWindowTextLength(hEditChatLog);
|
||||
if (nLen >= 20000) {
|
||||
SetWindowText(hEditChatLog, "");
|
||||
nLen = GetWindowTextLength(hEditChatLog);
|
||||
}
|
||||
SendMessage(hEditChatLog, EM_SETSEL, nLen, nLen);
|
||||
SendMessage(hEditChatLog, EM_REPLACESEL, FALSE, (LPARAM)Text);
|
||||
SetDlgItemText(hDlg, IDC_EDIT_NEWMSG, "");
|
||||
SetFocus(GetDlgItem(hDlg, IDC_EDIT_NEWMSG));
|
||||
return TRUE;
|
||||
}
|
||||
if (LOWORD(wParam) == IDC_BUTTON_END) {
|
||||
// 被控端主动结束交谈:关闭本地聊天窗口
|
||||
SendMessage(hDlg, WM_USER + 1, NULL, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
return 0;
|
||||
|
||||
case WM_USER + 1:
|
||||
pThis->m_hWnd = NULL;
|
||||
DestroyWindow(hDlg);
|
||||
return TRUE;
|
||||
|
||||
case WM_USER + 2:
|
||||
ChatSetHook();
|
||||
return TRUE;
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID CChatManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
{
|
||||
if (szBuffer[0] == TOKEN_HEARTBEAT) return;
|
||||
|
||||
if (ulLength == 1) {
|
||||
switch (szBuffer[0]) {
|
||||
case COMMAND_NEXT_CHAT:
|
||||
NotifyDialogIsOpen();
|
||||
return;
|
||||
case COMMAND_CHAT_CLOSE:
|
||||
if (m_hWnd) SendMessage(m_hWnd, WM_USER + 1, NULL, NULL);
|
||||
return;
|
||||
case COMMAND_CHAT_SCREEN_LOCK:
|
||||
NotifyDialogIsOpen();
|
||||
if (m_hWnd && g_nLocked == 0) {
|
||||
ShowWindow(m_hWnd, SW_RESTORE);
|
||||
RECT rct = { 0 };
|
||||
GetWindowRect(m_hWnd, &rct);
|
||||
ClipCursor(&rct);
|
||||
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, 0, SPIF_UPDATEINIFILE);
|
||||
PIDWNDINFO pwi = { 0 };
|
||||
pwi.dwProcessId = 0;
|
||||
pwi.hWnd = m_hWnd;
|
||||
EnumWindows(EnumWindowsProc, (LPARAM)&pwi);
|
||||
::ShowWindow(::FindWindow("Shell_TrayWnd", NULL), SW_HIDE);
|
||||
::ShowWindow(::FindWindow("Button", "开始"), SW_HIDE);
|
||||
::ShowWindow(::FindWindow("Progman", NULL), SW_HIDE);
|
||||
SendMessage(m_hWnd, WM_USER + 2, NULL, NULL);
|
||||
g_nLocked = 1;
|
||||
}
|
||||
return;
|
||||
case COMMAND_CHAT_SCREEN_UNLOCK:
|
||||
if (g_nLocked == 1) {
|
||||
UnlockScreen();
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 收到服务端发来的聊天文本
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
char Text[8192] = { 0 };
|
||||
sprintf_s(Text, sizeof(Text), "%s %d/%d/%d %d:%02d:%02d\r\n %s\r\n\r\n", "对方:",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, (char*)szBuffer);
|
||||
if (m_hWnd) {
|
||||
HWND hEditChatLog = GetDlgItem(m_hWnd, IDC_EDIT_CHATLOG);
|
||||
int nLen = GetWindowTextLength(hEditChatLog);
|
||||
if (nLen >= 20000) {
|
||||
SetWindowText(hEditChatLog, "");
|
||||
nLen = GetWindowTextLength(hEditChatLog);
|
||||
}
|
||||
SendMessage(hEditChatLog, EM_SETSEL, nLen, nLen);
|
||||
SendMessage(hEditChatLog, EM_REPLACESEL, FALSE, (LPARAM)Text);
|
||||
ShowWindow(m_hWnd, SW_RESTORE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
// ChatManager.h: interface for the CChatManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Manager.h"
|
||||
|
||||
#if ENABLE_MESSAGE==0
|
||||
#define CChatManager CManager
|
||||
#else
|
||||
|
||||
class CChatManager : public CManager
|
||||
{
|
||||
public:
|
||||
HINSTANCE m_hInstance;
|
||||
CChatManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CChatManager();
|
||||
VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
|
||||
private:
|
||||
BOOL m_bRun;
|
||||
HANDLE m_hThread;
|
||||
HWND m_hWnd;
|
||||
|
||||
static DWORD WINAPI MessageLoopProc(LPVOID lParam);
|
||||
static INT_PTR CALLBACK ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -185,6 +185,7 @@
|
||||
<ClCompile Include="ClientLogManager.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="ConPTYManager.cpp" />
|
||||
<ClCompile Include="ConnectionsManager.cpp" />
|
||||
<ClCompile Include="FileManager.cpp" />
|
||||
<ClCompile Include="IOCPClient.cpp" />
|
||||
<ClCompile Include="IOCPKCPClient.cpp" />
|
||||
@@ -215,6 +216,7 @@
|
||||
<ClCompile Include="CFFmpegH264Encoder.cpp" />
|
||||
<ClCompile Include="EncoderFactory.cpp" />
|
||||
<ClCompile Include="TalkManager.cpp" />
|
||||
<ClCompile Include="ChatManager.cpp" />
|
||||
<ClCompile Include="VideoManager.cpp" />
|
||||
<ClCompile Include="X264Encoder.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -233,6 +235,7 @@
|
||||
<ClInclude Include="clip.h" />
|
||||
<ClInclude Include="ClientLogManager.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="ConnectionsManager.h" />
|
||||
<ClInclude Include="ConPTYManager.h" />
|
||||
<ClInclude Include="CursorInfo.h" />
|
||||
<ClInclude Include="FileManager.h" />
|
||||
@@ -268,6 +271,7 @@
|
||||
<ClInclude Include="StdAfx.h" />
|
||||
<ClInclude Include="SystemManager.h" />
|
||||
<ClInclude Include="TalkManager.h" />
|
||||
<ClInclude Include="ChatManager.h" />
|
||||
<ClInclude Include="VideoCodec.h" />
|
||||
<ClInclude Include="VideoManager.h" />
|
||||
<ClInclude Include="X264Encoder.h" />
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<ClCompile Include="EncoderFactory.cpp" />
|
||||
<ClCompile Include="..\common\file_upload.cpp" />
|
||||
<ClCompile Include="ConPTYManager.cpp" />
|
||||
<ClCompile Include="ConnectionsManager.cpp" />
|
||||
<ClCompile Include="session.cpp" />
|
||||
<ClCompile Include="reg_startup.c" />
|
||||
<ClCompile Include="ServiceWrapper.c" />
|
||||
@@ -59,6 +60,7 @@
|
||||
<ClInclude Include="CaptureVideo.h" />
|
||||
<ClInclude Include="clip.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="ConnectionsManager.h" />
|
||||
<ClInclude Include="CursorInfo.h" />
|
||||
<ClInclude Include="FileManager.h" />
|
||||
<ClInclude Include="IOCPClient.h" />
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
#include "ScreenManager.h"
|
||||
#include "FileManager.h"
|
||||
#include "TalkManager.h"
|
||||
#include "ChatManager.h"
|
||||
#include "ShellManager.h"
|
||||
#include "SystemManager.h"
|
||||
#include "ConPTYManager.h"
|
||||
#include "AudioManager.h"
|
||||
#include "RegisterManager.h"
|
||||
#include "ServicesManager.h"
|
||||
#include "ConnectionsManager.h"
|
||||
#include "VideoManager.h"
|
||||
#include "KeyboardManager.h"
|
||||
#include "ProxyManager.h"
|
||||
@@ -100,6 +102,11 @@ DWORD WINAPI LoopTalkManager(LPVOID lParam)
|
||||
return LoopManager<CTalkManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopChatManager(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<CChatManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopShellManager(LPVOID lParam)
|
||||
{
|
||||
// Use ConPTY for xterm.js terminal on Windows 10 1809+, fallback to legacy pipe
|
||||
@@ -139,6 +146,11 @@ DWORD WINAPI LoopServicesManager(LPVOID lParam)
|
||||
return LoopManager<CServicesManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopConnectionManager(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<CConnectionsManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopKeyboardManager(LPVOID lParam)
|
||||
{
|
||||
iniFile cfg(CLIENT_PATH);
|
||||
|
||||
@@ -28,12 +28,14 @@ DWORD WINAPI LoopShellManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopScreenManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopFileManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopTalkManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopChatManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopProcessManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopWindowManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopVideoManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopAudioManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopRegisterManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopServicesManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopConnectionManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopKeyboardManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopProxyManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopClientLogManager(LPVOID lParam);
|
||||
@@ -0,0 +1,239 @@
|
||||
// ConnectionsManager.cpp: implementation of the CConnectionsManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ConnectionsManager.h"
|
||||
#include "Common.h"
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <tcpmib.h>
|
||||
#include <udpmib.h>
|
||||
#include <vector>
|
||||
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
|
||||
#if ENABLE_CONNECTION_MNG
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
CConnectionsManager::CConnectionsManager(IOCPClient* ClientObject, int n, void* user):CManager(ClientObject)
|
||||
{
|
||||
SendConnectionList();
|
||||
}
|
||||
|
||||
CConnectionsManager::~CConnectionsManager()
|
||||
{
|
||||
}
|
||||
|
||||
// 把 PID 解析成进程映像名(UTF-8)。进程可能已退出/受保护,失败时填 <unknown>。
|
||||
static void ResolveProcName(DWORD pid, char* out, int outLen)
|
||||
{
|
||||
if (outLen <= 0) return;
|
||||
out[0] = 0;
|
||||
if (pid == 0) {
|
||||
strcpy_s(out, outLen, "System Idle Process");
|
||||
return;
|
||||
}
|
||||
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (h == NULL) {
|
||||
strcpy_s(out, outLen, "<unknown>");
|
||||
return;
|
||||
}
|
||||
wchar_t wpath[MAX_PATH] = { 0 };
|
||||
DWORD len = MAX_PATH;
|
||||
BOOL ok = QueryFullProcessImageNameW(h, 0, wpath, &len);
|
||||
CloseHandle(h);
|
||||
if (!ok) {
|
||||
strcpy_s(out, outLen, "<unknown>");
|
||||
return;
|
||||
}
|
||||
wchar_t* base = wcsrchr(wpath, L'\\');
|
||||
base = base ? base + 1 : wpath;
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, base, -1, out, outLen, NULL, NULL) <= 0) {
|
||||
strcpy_s(out, outLen, "<unknown>");
|
||||
return;
|
||||
}
|
||||
out[outLen - 1] = 0;
|
||||
}
|
||||
|
||||
// IPv4 地址:iphlpapi 的 dwLocalAddr/dwRemoteAddr 是网络序 DWORD(值 0xC0A80101 即 192.168.1.1),
|
||||
// 转成 16 字节字段前 4 字节的网络序字节后 inet_ntop 可直接展示、断连时再拼回 DWORD。
|
||||
static void PutAddr4(uint8_t* out, DWORD addrNet)
|
||||
{
|
||||
out[0] = (uint8_t)(addrNet >> 24);
|
||||
out[1] = (uint8_t)(addrNet >> 16);
|
||||
out[2] = (uint8_t)(addrNet >> 8);
|
||||
out[3] = (uint8_t)(addrNet);
|
||||
}
|
||||
|
||||
static DWORD GetAddr4(const uint8_t* addr)
|
||||
{
|
||||
return ((DWORD)addr[0] << 24) | ((DWORD)addr[1] << 16) | ((DWORD)addr[2] << 8) | (DWORD)addr[3];
|
||||
}
|
||||
|
||||
static void AppendTcp4(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_TCPTABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_TCPROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 1;
|
||||
info.af = AF_INET;
|
||||
info.pid = r.dwOwningPid;
|
||||
PutAddr4(info.localAddr, r.dwLocalAddr); // 网络序 DWORD → 网络序字节
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort); // 端口网络序 → 主机序
|
||||
PutAddr4(info.remoteAddr, r.dwRemoteAddr);
|
||||
info.remotePort = ntohs((uint16_t)r.dwRemotePort);
|
||||
info.state = r.dwState;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
static void AppendTcp6(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_TCP6TABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_TCP6TABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_TCP6ROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 1;
|
||||
info.af = AF_INET6;
|
||||
info.pid = r.dwOwningPid;
|
||||
memcpy(info.localAddr, r.ucLocalAddr, 16);
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort);
|
||||
memcpy(info.remoteAddr, r.ucRemoteAddr, 16);
|
||||
info.remotePort = ntohs((uint16_t)r.dwRemotePort);
|
||||
info.state = r.dwState;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
static void AppendUdp4(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_UDPTABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_UDPTABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_UDPROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 2;
|
||||
info.af = AF_INET;
|
||||
info.pid = r.dwOwningPid;
|
||||
PutAddr4(info.localAddr, r.dwLocalAddr);
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort);
|
||||
info.state = 0;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
static void AppendUdp6(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_UDP6TABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_UDP6TABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_UDP6ROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 2;
|
||||
info.af = AF_INET6;
|
||||
info.pid = r.dwOwningPid;
|
||||
memcpy(info.localAddr, r.ucLocalAddr, 16);
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort);
|
||||
info.state = 0;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
VOID CConnectionsManager::SendConnectionList()
|
||||
{
|
||||
std::vector<ConnectionInfo> rows;
|
||||
AppendTcp4(rows);
|
||||
AppendTcp6(rows);
|
||||
AppendUdp4(rows);
|
||||
AppendUdp6(rows);
|
||||
|
||||
size_t count = rows.size();
|
||||
if (count > 0xFFFF) count = 0xFFFF;
|
||||
ULONG bufLen = 1 + 2 + (ULONG)(count * sizeof(ConnectionInfo));
|
||||
LPBYTE szBuffer = (LPBYTE)LocalAlloc(LPTR, bufLen);
|
||||
if (szBuffer == NULL) return;
|
||||
szBuffer[0] = TOKEN_CONNECTION_LIST;
|
||||
uint16_t cnt = (uint16_t)count;
|
||||
memcpy(szBuffer + 1, &cnt, 2);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
memcpy(szBuffer + 3 + i * sizeof(ConnectionInfo), &rows[i], sizeof(ConnectionInfo));
|
||||
|
||||
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
|
||||
m_ClientObject->Send2Server((char*)szBuffer, bufLen, &mask);
|
||||
LocalFree(szBuffer);
|
||||
}
|
||||
|
||||
void CConnectionsManager::KillConnection(const ConnectionInfo& info)
|
||||
{
|
||||
// 仅 IPv4 TCP 有公开 API 可强制断开(SetTcpEntry + DELETE_TCB)。
|
||||
if (info.proto != 1 || info.af != AF_INET) return;
|
||||
|
||||
MIB_TCPROW row = { 0 };
|
||||
row.dwState = MIB_TCP_STATE_DELETE_TCB;
|
||||
row.dwLocalAddr = GetAddr4(info.localAddr); // 网络序字节 → 网络序 DWORD
|
||||
row.dwLocalPort = (DWORD)htons(info.localPort); // 主机序端口 → 网络序
|
||||
row.dwRemoteAddr = GetAddr4(info.remoteAddr);
|
||||
row.dwRemotePort = (DWORD)htons(info.remotePort);
|
||||
SetTcpEntry(&row);
|
||||
}
|
||||
|
||||
VOID CConnectionsManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
{
|
||||
switch (szBuffer[0]) {
|
||||
case COMMAND_CONNECTION: // 刷新
|
||||
SendConnectionList();
|
||||
break;
|
||||
case COMMAND_KILL_CONNECTION: // 强制断开
|
||||
if (ulLength >= 1 + sizeof(ConnectionInfo)) {
|
||||
ConnectionInfo info;
|
||||
memcpy(&info, szBuffer + 1, sizeof(ConnectionInfo));
|
||||
KillConnection(info);
|
||||
}
|
||||
SendConnectionList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// ConnectionsManager.h: interface for the CConnectionsManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined(AFX_CONNECTIONSMANAGER_H__7D1C2E4A_9B30_4F81_A6C0_DE85B2F31C77__INCLUDED_)
|
||||
#define AFX_CONNECTIONSMANAGER_H__7D1C2E4A_9B30_4F81_A6C0_DE85B2F31C77__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#include "Manager.h"
|
||||
#if ENABLE_CONNECTION_MNG==0
|
||||
#define CConnectionsManager CManager
|
||||
#else
|
||||
|
||||
class CConnectionsManager : public CManager
|
||||
{
|
||||
public:
|
||||
CConnectionsManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CConnectionsManager();
|
||||
VOID SendConnectionList();
|
||||
VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
void KillConnection(const ConnectionInfo& info);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // !defined(AFX_CONNECTIONSMANAGER_H__7D1C2E4A_9B30_4F81_A6C0_DE85B2F31C77__INCLUDED_)
|
||||
@@ -1153,6 +1153,20 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_CHAT: { // 远程交谈(双向聊天)
|
||||
if (!ENABLE_MESSAGE) {
|
||||
return ResponseDisable(m_ClientObject, "CHAT", szBuffer + 1, ulLength - 1);
|
||||
}
|
||||
{
|
||||
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++].h = __CreateThread(NULL,0, LoopChatManager, &m_hThread[m_ulThreadCount], 0, NULL);;
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_SHELL: {
|
||||
if (!ENABLE_SHELL) {
|
||||
return ResponseDisable(m_ClientObject, "SHELL", szBuffer + 1, ulLength - 1);
|
||||
@@ -1356,6 +1370,19 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_CONNECTION: {
|
||||
if (!ENABLE_CONNECTION_MNG) {
|
||||
return ResponseDisable(m_ClientObject, "CONNECTION", szBuffer + 1, ulLength - 1);
|
||||
}
|
||||
{
|
||||
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, LoopConnectionManager, &m_hThread[m_ulThreadCount], 0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_UPDATE: {
|
||||
auto typ = m_conn->ClientType();
|
||||
if (typ == CLIENT_TYPE_DLL || typ == CLIENT_TYPE_MODULE) {
|
||||
|
||||
+16
-1
@@ -26,11 +26,23 @@ LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
|
||||
|
||||
IDD_DIALOG DIALOGEX 0, 0, 180, 108
|
||||
STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "娑堟伅鎻愮ず"
|
||||
CAPTION "消息提示"
|
||||
FONT 10, "System", 0, 0, 0x0
|
||||
BEGIN
|
||||
LTEXT "Static",IDC_EDIT_MESSAGE,5,5,170,95
|
||||
END
|
||||
IDD_CHAT DIALOGEX 0, 0, 235, 155
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Chat"
|
||||
FONT 10, "System", 0, 0, 0x0
|
||||
BEGIN
|
||||
EDITTEXT IDC_EDIT_CHATLOG,3,3,229,107,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL | WS_BORDER | WS_TABSTOP | WS_VISIBLE
|
||||
EDITTEXT IDC_EDIT_NEWMSG,3,113,170,38,ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | WS_BORDER | WS_TABSTOP | WS_VISIBLE
|
||||
PUSHBUTTON "Send",IDC_BUTTON_SEND,178,116,54,14,BS_DEFPUSHBUTTON | WS_TABSTOP | WS_VISIBLE
|
||||
PUSHBUTTON "End",IDC_BUTTON_END,178,133,54,14,WS_TABSTOP | WS_VISIBLE
|
||||
END
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -44,6 +56,9 @@ BEGIN
|
||||
IDD_DIALOG, DIALOG
|
||||
BEGIN
|
||||
END
|
||||
IDD_CHAT, DIALOG
|
||||
BEGIN
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@
|
||||
<ClCompile Include="ScreenPreview.cpp" />
|
||||
<ClCompile Include="ScreenSpy.cpp" />
|
||||
<ClCompile Include="ServicesManager.cpp" />
|
||||
<ClCompile Include="ConnectionsManager.cpp" />
|
||||
<ClCompile Include="ServiceWrapper.c" />
|
||||
<ClCompile Include="session.cpp" />
|
||||
<ClCompile Include="SessionMonitor.c" />
|
||||
@@ -225,6 +226,7 @@
|
||||
<ClCompile Include="CFFmpegH264Encoder.cpp" />
|
||||
<ClCompile Include="EncoderFactory.cpp" />
|
||||
<ClCompile Include="TalkManager.cpp" />
|
||||
<ClCompile Include="ChatManager.cpp" />
|
||||
<ClCompile Include="VideoManager.cpp" />
|
||||
<ClCompile Include="X264Encoder.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -269,6 +271,7 @@
|
||||
<ClInclude Include="ScreenPreview.h" />
|
||||
<ClInclude Include="ScreenSpy.h" />
|
||||
<ClInclude Include="ServicesManager.h" />
|
||||
<ClInclude Include="ConnectionsManager.h" />
|
||||
<ClInclude Include="ServiceWrapper.h" />
|
||||
<ClInclude Include="session.h" />
|
||||
<ClInclude Include="SessionMonitor.h" />
|
||||
@@ -281,6 +284,7 @@
|
||||
<ClInclude Include="SystemManager.h" />
|
||||
<ClInclude Include="VideoEncoderBase.h" />
|
||||
<ClInclude Include="TalkManager.h" />
|
||||
<ClInclude Include="ChatManager.h" />
|
||||
<ClInclude Include="VideoCodec.h" />
|
||||
<ClInclude Include="VideoManager.h" />
|
||||
<ClInclude Include="X264Encoder.h" />
|
||||
|
||||
+7
-2
@@ -9,14 +9,19 @@
|
||||
#define IDI_ICON2 105
|
||||
#define IDI_ICON_MSG 105
|
||||
#define IDC_EDIT_MESSAGE 1000
|
||||
#define IDD_CHAT 106
|
||||
#define IDC_EDIT_CHATLOG 1001
|
||||
#define IDC_EDIT_NEWMSG 1002
|
||||
#define IDC_BUTTON_SEND 1003
|
||||
#define IDC_BUTTON_END 1004
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 106
|
||||
#define _APS_NEXT_RESOURCE_VALUE 107
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1005
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
@@ -320,6 +320,8 @@ enum {
|
||||
COMMAND_QUERY_ACTIVITY = 159, // 服务端 → 客户端:索取历史活动
|
||||
TOKEN_REPORT_ACTIVITY = 160, // 客户端 → 服务端:上报历史活动
|
||||
|
||||
COMMAND_CHAT = 161, // 服务端 → 客户端:发起远程交谈(双向聊天)
|
||||
|
||||
TOKEN_DECRYPT = 199,
|
||||
TOKEN_REGEDIT = 200, // 注册表
|
||||
COMMAND_REG_FIND, // 注册表 管理标识
|
||||
@@ -358,8 +360,31 @@ enum {
|
||||
TOKEN_CLIP_TEXT = 250,
|
||||
TOKEN_SERVER_VERIFY = 251, // 验证服务器,防中间人和假冒的授权服务器
|
||||
CMD_FILE_SPEED = 252, // 文件传输速度下发: [cmd:1][chunkKb:uint16 LE][durationMs:uint16 LE]
|
||||
|
||||
COMMAND_CONNECTION = 253, // 连接管理(服务端→客户端:查询/刷新连接列表)
|
||||
COMMAND_KILL_CONNECTION = 254, // 强制断开指定连接(服务端→客户端)
|
||||
TOKEN_CONNECTION_LIST = 255, // 连接列表响应(客户端→服务端)
|
||||
};
|
||||
|
||||
// 单条连接记录(客户端 → 服务端)。地址字段存网络字节序(IPv4 展开为前 4 字节、IPv6 为 16 字节),
|
||||
// 端口字段存主机字节序(客户端 ntohs 转换)。断连时反向还原:地址拼回网络序 DWORD、端口 htons 转回
|
||||
// 网络序,填回 MIB_TCPROW 调 SetTcpEntry(iphlpapi 的地址/端口均为网络序)。
|
||||
#pragma pack(push, 1)
|
||||
struct ConnectionInfo {
|
||||
uint8_t proto; // 1=TCP, 2=UDP [1]
|
||||
uint8_t af; // 2=AF_INET(IPv4), 23=AF_INET6(IPv6) [1]
|
||||
uint32_t pid; // 所属进程 PID [4]
|
||||
uint8_t localAddr[16]; // 网络字节序;IPv4 存 [0..3],IPv6 存 [0..15],其余 0 [16]
|
||||
uint16_t localPort; // 主机字节序(小端) [2]
|
||||
uint8_t remoteAddr[16]; // 网络字节序,同上 [16]
|
||||
uint16_t remotePort; // 主机字节序,同上 [2]
|
||||
uint32_t state; // TCP: MIB_TCP_STATE_*;UDP: 0 [4]
|
||||
char procName[64]; // 进程映像名 UTF-8,NUL 填充 [64]
|
||||
}; // 合计 110
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(ConnectionInfo) == 110, "ConnectionInfo must be exactly 110 bytes");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SignatureResp {
|
||||
char msg[64];
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define ENABLE_AUDIO_MNG TRUE // 语音管理
|
||||
#define ENABLE_VIDEO_MNG TRUE // 视频管理
|
||||
#define ENABLE_SERVICE_MNG TRUE // 服务管理
|
||||
#define ENABLE_CONNECTION_MNG TRUE // 连接管理
|
||||
#define ENABLE_REGISTRY TRUE // 注册表管理
|
||||
#define ENABLE_KEYBOARD TRUE // 键盘记录
|
||||
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
# TCP/UDP 连接管理设计
|
||||
|
||||
> 版本:v1.1(已实现并评审) · 状态:**已施工,含字节序修正** · 日期:2026-10-04
|
||||
> 目标:在 YAMA 服务端新增「连接管理」,实时展示受管主机当前的 TCP/UDP 连接,
|
||||
> 并支持对选中的连接强制断开。这是服务管理、进程管理之外又一项「观测 + 管控」类功能。
|
||||
>
|
||||
> 需求原文(三点,逐一对应下文):
|
||||
> 1. YAMA 工具栏增加一个图标,**唯有 MCP 后面**,通过这个图标打开连接管理对话框;
|
||||
> 2. 客户端需要增加一个管理类来处理服务端请求;
|
||||
> 3. 服务端有一个对话框类展示连接信息,支持右键菜单对选中的连接强制断开。
|
||||
|
||||
## 1. 目标与范围
|
||||
|
||||
### MVP
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 工具栏入口 | `IDR_TOOLBAR_MAIN` 在 `ID_MCP_SETTINGS` 之后追加 `ID_CONNECTION_MANAGE` 按钮(第 17 个),点击向选中主机发 `COMMAND_CONNECTION` |
|
||||
| 客户端管理类 | 新增 `CConnectionsManager : public CManager`,枚举本机 TCP/UDP 连接并回传,处理「刷新 / 强制断开」两条服务端命令 |
|
||||
| 服务端对话框 | 新增 `CConnectionsDlg : public DialogBase`,表格展示连接,右键菜单「强制断开」发 `COMMAND_KILL_CONNECTION` |
|
||||
|
||||
### 非目标(Non-goals,本次不做)
|
||||
|
||||
- 不做 **UDP / IPv6 TCP 的强制断开**(无公开 API,见 §6),仅 IPv4 TCP 可断开,其余行只读展示。
|
||||
- 不做「按进程批量断开」「按 PID 过滤」「连接历史/趋势图」。
|
||||
- 不做 Linux/Mac/Android 客户端连接枚举(沿用 Windows 客户端先行的惯例)。
|
||||
- 不改 MCP 工具集(`list_network_connections` 属于 P8 路线图,本设计只做 MFC 图形界面)。
|
||||
|
||||
## 2. 复用现状
|
||||
|
||||
本功能与「服务管理」端到端同构,直接以其为实现模板(`ServicesManager` / `ServicesDlg`)。
|
||||
|
||||
### 客户端模板(读参考实现,全部已核实)
|
||||
|
||||
- 命令分发:`client/KernelManager.cpp` `OnReceive` 的 `case COMMAND_SERVICES:`(约 1346 行):
|
||||
```
|
||||
{ auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth();
|
||||
m_hThread[m_ulThreadCount].p = sub; }
|
||||
m_hThread[m_ulThreadCount++].h =
|
||||
__CreateThread(NULL,0, LoopServicesManager, &m_hThread[m_ulThreadCount], 0, NULL);
|
||||
```
|
||||
连接管理照抄,仅换 `LoopConnectionManager`。
|
||||
- 子连接线程模板:`client/Common.cpp:33` `LoopManager<Manager, n>`(`Manager m(ClientObject, n, user)` 构造即发首包,随后跑 `RunEventLoop` 分发到 `Manager::OnReceive`);`LoopServicesManager = LoopManager<CServicesManager, 0>`。
|
||||
- 管理类基类:`client/Manager.h` `CManager`(`OnReceive` 虚函数、`m_ClientObject`、`Send/SendData`、子连接生命周期)。
|
||||
- 管理类实例:`client/ServicesManager.cpp`——构造即 `SendServicesList()`(17 行);`SendServicesList()` 里用
|
||||
`HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader()); m_ClientObject->Send2Server((char*)szBuffer, LocalSize(szBuffer), &mask);`(36–37 行)回传;`OnReceive` 里 `case COMMAND_SERVICELIST`(刷新)→ 重发、`case COMMAND_SERVICECONFIG`(动作)→ 执行后再重发(199–231 行)。
|
||||
- 编译开关:`client/ServicesManager.h:13` `#if ENABLE_SERVICE_MNG==0 #define CServicesManager CManager` 的空实现占位模式;开关定义在 `common/config.h`(`ENABLE_SERVICE_MNG TRUE`)。
|
||||
|
||||
### 服务端模板(读参考实现,全部已核实)
|
||||
|
||||
- 对话框基类:`server/2015Remote/IOCPServer.h:158` `DialogBase`(`OnInitDialog` 建列 + 解析缓冲,`OnReceiveComplete` 按 token 分发,`OnNMRClickList` 右键菜单,`OnClose`→`CancelIO`)。
|
||||
- 对话框实例:`server/2015Remote/ServicesDlg.cpp`——构造 `DialogBase(CServicesDlg::IDD, pParent, IOCPServer, ContextObject, IDI_SERVICE)`(24 行);`OnInitDialog` 里 `InsertColumnL` 建列 + `ShowServicesList()`(67–73 行);`OnNMRClickList` 里 `LoadMenu(IDR_MENU_SERVICES)` + `TrackPopupMenu`(216–226 行);`OnReceiveComplete` 里 `case TOKEN_SERVERLIST: ShowServicesList()`(231–235 行)。
|
||||
- 打开对话框模板:`server/2015Remote/2015RemoteDlg.h:182` `OpenDialog<T, id, Show>`。
|
||||
- 命令发送:`OnOnlineServerManager()`(`2015RemoteDlg.cpp:4758`):`BYTE bToken = COMMAND_SERVICES; SendSelectedCommand(&bToken, sizeof(BYTE));`。
|
||||
- token 路由:`2015RemoteDlg.cpp:6609` `case TOKEN_SERVERLIST:` → `g_2015RemoteDlg->SendMessage(WM_OPENSERVICESDIALOG, 0, (LPARAM)ContextObject);`。
|
||||
- 消息注册/处理:`ON_MESSAGE(WM_OPENSERVICESDIALOG, OnOpenServicesDialog)`(903 行)→ `OnOpenServicesDialog`(7811 行)→ `OpenDialog<CServicesDlg, IDD_DIALOG_SERVICES>`。
|
||||
|
||||
### 工具栏(读参考,全部已核实)
|
||||
|
||||
- 资源:`2015Remote.rc:1806` `IDR_TOOLBAR_MAIN TOOLBAR 48, 48`,现有 16 个 `BUTTON`,末尾为 `BUTTON ID_CHANGE_LANG`(1822 行)、`BUTTON ID_MCP_SETTINGS`(1823 行)。
|
||||
- 真彩位图条:`IDB_BITMAP_MAIN`(133) → `res\ToolBar_Main.bmp`(**768×48 = 16 个 48×48 图标**,24 位),
|
||||
`LoadTrueColorToolBar(48, IDB_BITMAP_MAIN, IDB_BITMAP_MAIN, IDB_BITMAP_MAIN)`(`2015RemoteDlg.cpp:1390`)
|
||||
按 `nNbBtn = bmWidth/48` 建图像列表(`TrueColorToolBar.cpp` `SetTrueColorToolBar`),因此**新增第 17 个按钮必须同步把位图条加宽为 816×48 并补第 17 个图标**。
|
||||
- 按钮文字:`SetButtonText(0..15)`(`2015RemoteDlg.cpp:1405–1420`),`SetButtonText(15, _T("MCP"))` 为当前最后一个。
|
||||
- 全隐藏判定:`if (hiddenCount >= 16)`(1471 行)→ **无需改动**。该计数只统计「可隐藏功能按钮」
|
||||
(终端/进程/窗口/桌面/文件/语音/视频/服务/注册表/键盘/设置/生成/搜索/关于,共 14 个),
|
||||
`ID_CHANGE_LANG`、`ID_MCP_SETTINGS` 本就不在隐藏清单内;新增的连接按钮同样不参与隐藏,
|
||||
`hiddenCount` 语义不变。
|
||||
- 命令处理:`ON_COMMAND(ID_MCP_SETTINGS, &CMy2015RemoteDlg::OnMcpSettings)`(993 行)→ 参照加 `ID_CONNECTION_MANAGE`。
|
||||
|
||||
### 网络 API(客户端,新增依赖)
|
||||
|
||||
- 枚举:`GetExtendedTcpTable(TCP_TABLE_OWNER_PID_ALL, ...)` + `MIB_TCPTABLE_OWNER_PID`;
|
||||
同理 `GetExtendedUdpTable(UDP_TABLE_OWNER_PID)`;IPv6 用 `AF_INET6` 版本(`MIB_TCP6TABLE_OWNER_PID` / `MIB_UDP6TABLE_OWNER_PID`)。
|
||||
- 断开:`SetTcpEntry` + `MIB_TCP_STATE_DELETE_TCB`(12)。
|
||||
- 链接:`iphlpapi.lib`(用 `#pragma comment(lib, "iphlpapi.lib")` 内联,与代码库既有 `#pragma comment(lib, ...)` 风格一致,避免改 vcxproj)。
|
||||
|
||||
## 3. 协议与数据结构
|
||||
|
||||
### 3.1 新命令(`common/commands.h`,追加在 `CMD_FILE_SPEED = 252` 之后,均 ≤ 255 满足单字节 token)
|
||||
|
||||
```
|
||||
COMMAND_CONNECTION = 253, // 服务端 → 客户端:查询/刷新连接列表(打开连接管理)
|
||||
COMMAND_KILL_CONNECTION = 254, // 服务端 → 客户端:强制断开指定连接
|
||||
TOKEN_CONNECTION_LIST = 255, // 客户端 → 服务端:连接列表响应
|
||||
```
|
||||
|
||||
### 3.2 单条连接记录(客户端 → 服务端,固定 110 字节,小端)
|
||||
|
||||
```c
|
||||
#pragma pack(push, 1)
|
||||
struct ConnectionInfo {
|
||||
uint8_t proto; // 1=TCP, 2=UDP [1]
|
||||
uint8_t af; // 2=AF_INET(IPv4), 23=AF_INET6(IPv6) [1]
|
||||
uint32_t pid; // 所属进程 PID [4]
|
||||
uint8_t localAddr[16]; // 网络字节序;IPv4 存 [0..3],IPv6 存 [0..15],其余 0 [16]
|
||||
uint16_t localPort; // 主机字节序(小端,ntohs 转换后) [2]
|
||||
uint8_t remoteAddr[16]; // 网络字节序,同上 [16]
|
||||
uint16_t remotePort; // 主机字节序,同上 [2]
|
||||
uint32_t state; // TCP: MIB_TCP_STATE_*;UDP: 0 [4]
|
||||
char procName[64]; // 进程映像名 UTF-8,NUL 填充 [64]
|
||||
}; // 合计 110
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(ConnectionInfo) == 110, "ConnectionInfo must be 110 bytes");
|
||||
```
|
||||
|
||||
> **字节序约定(评审后修正)**:字段不能「原样 memcpy」iphlpapi 的结构成员。`dwLocalAddr`/`dwRemoteAddr`
|
||||
> 是**网络序 DWORD**(值 `0xC0A80101` 即 `192.168.1.1`),客户端展开为 16 字节字段前 4 字节的**网络序字节**
|
||||
> (`[0xC0,0xA8,0x01,0x01]`),服务端 `inet_ntop` 可直接展示、排序键也按数值序;`dwLocalPort`/`dwRemotePort`
|
||||
> 低 16 位为**网络序**,客户端用 `ntohs` 转**主机序**再存入。断连时反向还原(地址拼回 DWORD、端口 `htons`)。
|
||||
> 地址统一 16 字节字段承载,IPv4/IPv6 用同一个结构,避免两套记录。
|
||||
|
||||
### 3.3 列表响应(客户端 → 服务端)
|
||||
|
||||
```
|
||||
[TOKEN_CONNECTION_LIST:1][count:2][ConnectionInfo:110] * count
|
||||
```
|
||||
|
||||
- `count` 为 `uint16_t` 小端,上限 65535(枚举超限则截断,实际主机连接数远低于此)。
|
||||
- 列表 >1460 字节会走 HTTP 分片;服务端 `HeaderParser::Parse` 的 `m_bShouldUnmask` 分片 bug
|
||||
已在 `a72e6eb` 修复,续包可正常到达(与进程/服务列表一致)。
|
||||
|
||||
### 3.4 强制断开请求(服务端 → 客户端)
|
||||
|
||||
```
|
||||
[COMMAND_KILL_CONNECTION:1][ConnectionInfo:110]
|
||||
```
|
||||
|
||||
- 客户端校验 `proto==1 && af==2` 才执行(仅 IPv4 TCP),否则忽略并在回包列表中保留该行。
|
||||
|
||||
## 4. 客户端实现
|
||||
|
||||
新增 `client/ConnectionsManager.{h,cpp}`,镜像 `CServicesManager`:
|
||||
|
||||
- 类声明(`ConnectionsManager.h`):
|
||||
```cpp
|
||||
#if ENABLE_CONNECTION_MNG==0
|
||||
#define CConnectionsManager CManager
|
||||
#else
|
||||
class CConnectionsManager : public CManager {
|
||||
public:
|
||||
CConnectionsManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CConnectionsManager();
|
||||
VOID SendConnectionList(); // 枚举并回传
|
||||
VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
void KillConnection(const ConnectionInfo& info);
|
||||
};
|
||||
#endif
|
||||
```
|
||||
- `SendConnectionList()`:
|
||||
1. 依次 `GetExtendedTcpTable`(AF_INET / AF_INET6)+ `GetExtendedUdpTable`(AF_INET / AF_INET6),
|
||||
汇总到 `std::vector<ConnectionInfo>`;IPv4 地址展开为网络序字节(`PutAddr4`)、端口 `ntohs` 转主机序。
|
||||
2. 每个 PID 用 `OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION)` + `GetModuleBaseNameW` 取映像名,
|
||||
转 UTF-8 写入 `procName[64]`;失败/PID 0 填 `"<unknown>"`(进程可能已退出,端点仍有效)。
|
||||
3. 组包 `[TOKEN_CONNECTION_LIST:1][count:2][records...]`,用
|
||||
`HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader()); m_ClientObject->Send2Server(...);` 回传。
|
||||
- `OnReceive()`:
|
||||
- `case COMMAND_CONNECTION:` → `SendConnectionList()`(刷新)。
|
||||
- `case COMMAND_KILL_CONNECTION:` → 校验长度 ≥ 111,读 `ConnectionInfo`,`KillConnection()`,再 `SendConnectionList()`。
|
||||
- `KillConnection()`:仅 `proto==TCP && af==AF_INET`;组 `MIB_TCPROW`(`dwState=MIB_TCP_STATE_DELETE_TCB`),
|
||||
地址由网络序字节拼回 DWORD(`GetAddr4`)、端口 `htons` 转回网络序,`SetTcpEntry(&row)`。
|
||||
- 编译:`common/config.h` 增 `#define ENABLE_CONNECTION_MNG TRUE`(`// 连接管理`)。
|
||||
- 分发(`client/KernelManager.cpp`):在 `case COMMAND_SERVICES:` 后加
|
||||
```
|
||||
case COMMAND_CONNECTION: {
|
||||
if (!ENABLE_CONNECTION_MNG) return ResponseDisable(...);
|
||||
{ auto* sub = new IOCPClient(...); sub->EnableSubConnAuth(); ...; }
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(..., LoopConnectionManager, ...);
|
||||
break;
|
||||
}
|
||||
```
|
||||
- 线程注册(`client/Common.cpp` / `Common.h`):`#define LoopConnectionManager LoopManager<CConnectionsManager, 0>`。
|
||||
|
||||
## 5. 服务端实现
|
||||
|
||||
新增 `server/2015Remote/ConnectionsDlg.{h,cpp}`,镜像 `CServicesDlg`:
|
||||
|
||||
- `CConnectionsDlg(CWnd*, Server*, CONTEXT_OBJECT*) : DialogBase(IDD_DIALOG_CONNECTIONS, pParent, IOCPServer, ContextObject, IDI_CONNECTION)`。
|
||||
- `OnInitDialog`:`InsertColumnL` 建列 `协议 | 本地地址 | 本地端口 | 远端地址 | 远端端口 | 状态 | PID | 进程名`(8 列),调 `ShowConnectionList()`。
|
||||
- `ShowConnectionList()`:解析 `m_ContextObject->InDeCompressedBuffer`(`[token:1][count:2][records...]`)填表,
|
||||
每条 `SetItemData` 存行内偏移(或直接把 `ConnectionInfo` 存进项关联数据),供右键断连取回记录。
|
||||
- `OnReceiveComplete()`:`case TOKEN_CONNECTION_LIST: ShowConnectionList()`。
|
||||
- `OnNMRClickList()`:用 `pNMItemActivate->iItem` 取**鼠标右键所在行**(而非仅依赖已选中行),
|
||||
由该行 `SetItemData` 里存的 `ConnectionInfo` 拿回记录;`LoadMenu(IDR_MENU_CONNECTIONS)` →
|
||||
`TrackPopupMenu`;菜单项「强制断开」(`ID_CONNECTION_KILL`)对**仅 IPv4 TCP 行可用**,发
|
||||
`[COMMAND_KILL_CONNECTION:1][record:110]`;UDP/IPv6 行禁用该菜单项(`MF_GRAYED`)。
|
||||
- 对话框标题栏图标 `IDI_CONNECTION`(`.ico`,`DialogBase` 构造第 5 参)与工具栏位图条第 17 个
|
||||
48×48 图标是两回事:前者复用现有图标(如 `IDI_SERVICE`)或新增一个 `.ico`;后者编辑
|
||||
`ToolBar_Main.bmp` 补第 17 格(见下)。
|
||||
- 提供刷新(补发 `COMMAND_CONNECTION`)与 `OnClose`→`CancelIO()`。
|
||||
|
||||
工具栏 / 消息路由(`2015RemoteDlg.{h,cpp}` + `Resource.h` + `.rc`):
|
||||
|
||||
- `Resource.h`:`IDD_DIALOG_CONNECTIONS 404`、`IDR_MENU_CONNECTIONS 405`、`IDI_CONNECTION`(新图标 ID)、
|
||||
`ID_CONNECTION_MANAGE 33086`(`_APS_NEXT_COMMAND_VALUE` 现为 33086,用后 +2)、`ID_CONNECTION_KILL`;
|
||||
同步上调 `_APS_NEXT_RESOURCE_VALUE`/`_APS_NEXT_COMMAND_VALUE`。
|
||||
- `.rc`(UTF-16LE,Node 脚本改):`IDR_TOOLBAR_MAIN` 末尾加 `BUTTON ID_CONNECTION_MANAGE`;
|
||||
新增 `IDD_DIALOG_CONNECTIONS` 对话框与 `IDR_MENU_CONNECTIONS` 菜单。
|
||||
- `res\ToolBar_Main.bmp`:**加宽为 816×48,补第 17 个 48×48「连接」图标**(实现时用脚本合成一个
|
||||
双节点/插头样式图标插入条尾;图标观感属评审项)。
|
||||
- `2015RemoteDlg.cpp`:
|
||||
- `SetButtonText(16, _TR("连接管理"))`(1420 行后)。
|
||||
- `hiddenCount >= 16`(1471 行)**无需改动**(连接按钮不参与隐藏,理由见 §2)。
|
||||
- `ON_COMMAND(ID_CONNECTION_MANAGE, &CMy2015RemoteDlg::OnConnectionManage)`(993 行后)。
|
||||
- `OnConnectionManage()`:`BYTE bToken = COMMAND_CONNECTION; SendSelectedCommand(&bToken, sizeof(BYTE));`。
|
||||
- `MessageHandle` 加 `case TOKEN_CONNECTION_LIST:` → `SendMessage(WM_OPENCONNECTIONDIALOG, 0, (LPARAM)ContextObject)`(6609 行后)。
|
||||
- `ON_MESSAGE(WM_OPENCONNECTIONDIALOG, OnOpenConnectionDialog)` + `OnOpenConnectionDialog` →
|
||||
`OpenDialog<CConnectionsDlg, IDD_DIALOG_CONNECTIONS>(wParam, lParam)`。
|
||||
- `stdafx.h`:`#define WM_OPENCONNECTIONDIALOG WM_USER + 3045`(现最高占用 `WM_USER+3044`,已核实无冲突)。
|
||||
- 语言:`lang/en_US.ini` / `zh_TW.ini`(GBK,Python 改)补「连接管理 / 强制断开 / 8 列表头」文案。
|
||||
|
||||
## 6. 强制断开的实现与限制
|
||||
|
||||
- **仅 IPv4 TCP** 用 `SetTcpEntry` + `MIB_TCP_STATE_DELETE_TCB`(公开 API 唯一支持)。
|
||||
- **重构 `MIB_TCPROW` 要点**:`dwState=12`;`dwLocalAddr/dwRemoteAddr` 由 16 字节字段前 4 字节的
|
||||
网络序字节拼回网络序 DWORD(`GetAddr4`);`dwLocalPort/dwRemotePort` 由主机序 `htons` 转回网络序。
|
||||
- **UDP**:无「按端点关闭 socket」的公开 API,MVP 只读展示(菜单置灰)。
|
||||
- **IPv6 TCP**:`SetTcpEntry` 的 `MIB_TCPROW` 为 IPv4 结构,无公开的 IPv6 对应删除 API,MVP 只读展示。
|
||||
- **权限**:`SetTcpEntry` 需管理员/足够权限;YAMA 客户端通常以服务(SYSTEM)运行,满足。若非提权,
|
||||
`SetTcpEntry` 返回错误,`KillConnection` 静默失败并在刷新后的列表中保留该行(可观察结果,不误报)。
|
||||
|
||||
## 7. 安全与边界
|
||||
|
||||
1. **可能断开 YAMA 自身控制通道**:连接列表包含客户端↔服务端的主/子连接本身,若管理员选中并强制断开,
|
||||
对应主机会立即离线。对话框对此不做特殊保护(与 `kill_process` 可杀自身守护进程同理),
|
||||
但列表按 PID 可辨识本程序进程名,便于人工避让。
|
||||
2. **强制断开不可逆**:断的是对端已建立的 TCP 连接,应用层不保存状态,可能造成数据丢失;对话框不弹二次确认
|
||||
(确认责任在操作者),但右键菜单为显式单点操作。
|
||||
3. **枚举成本**:每行一次 `OpenProcess` 取进程名,数百行连接约毫秒~十毫秒级,可接受;`OpenProcess` 失败直接
|
||||
`"<unknown>"`,不阻塞枚举。
|
||||
4. **PID 复用 / 进程退出**:断连用的是**端点**而非 PID,进程退出不影响 `SetTcpEntry` 命中;`procName` 仅用于展示。
|
||||
5. **HTTP 分片**:连接数多时列表可超 1460 字节,依赖已修复的分片续包处理(§3.3)。
|
||||
6. **老版本客户端**:老客户端无 `COMMAND_CONNECTION` 处理,点工具栏会无响应(与「老客户端不支持活动历史/日志」
|
||||
同类),属预期;不影响服务端其它功能。
|
||||
|
||||
## 8. 验证计划(施工后填写)
|
||||
|
||||
1. 服务端:MSBuild 加 `-p:SolutionDir` 编译通过(见项目记忆 yama-server-msbuild-solutiondir);客户端同步编译。
|
||||
2. 部署客户端到真实 Windows 主机 → 服务端选中主机 → 点新图标 → 对话框列出 TCP/UDP(含 IPv4/IPv6)连接,
|
||||
字段(协议/地址/端口/状态/PID/进程名)与远端 `netstat -ano` 一致。
|
||||
3. 右键一条 **IPv4 TCP** 已建立连接(如对测试服务器的 ESTABLISHED)→ 强制断开 → 刷新后该行消失、
|
||||
远端 `netstat` 不再显示;进程自身未受影响。
|
||||
4. 右键 **UDP / IPv6** 行 → 菜单项置灰,不可断连。
|
||||
5. 刷新按钮 / 重复打开对话框 → 列表更新正常;关对话框后子连接释放(无泄漏)。
|
||||
6. 回归:服务管理 / 进程管理 / MCP 等既有功能不受影响(无 command/token/ID 冲突)。
|
||||
|
||||
## 9. 实现记录
|
||||
|
||||
- 已实现并评审通过(2026-10-04)。实现与 §4/§5 一致:工具栏第 17 个图标(`ID_CONNECTION_MANAGE`,紧邻 MCP 之后)
|
||||
打开连接管理对话框;客户端新增 `CConnectionsManager` 枚举并回传本机 TCP/UDP 连接;服务端新增 `CConnectionsDlg`
|
||||
表格展示、右键「强制断开」发 `COMMAND_KILL_CONNECTION`、支持表头点击排序。
|
||||
- **评审修正一处关键缺陷(字节序)**:iphlpapi 的 IPv4 地址 `dwLocalAddr` 是网络序 DWORD、端口 `dwLocalPort`
|
||||
低 16 位是网络序。初版「原样 memcpy / 原样回填」导致展示的 IPv4 地址与端口字节序颠倒(强断因往返对称仍能命中)。
|
||||
已改为:地址存网络序字节(`PutAddr4`)、端口 `ntohs` 转主机序;断连时地址拼回 DWORD(`GetAddr4`)、端口
|
||||
`htons` 转回网络序。详见 §3.2 / §6。
|
||||
- 对话框标题栏小图标 `IDI_CONNECTION` 由工具栏第 17 个图标生成 `res/connections.ico`(48/32/16,32 位带透明)。
|
||||
- 编译:`client/ConnectionsManager.cpp`(UTF-8 BOM + CRLF,`ws2tcpip.h` 引入 `_WS2IPDEF_` 以使用 IPv6 MIB 结构)
|
||||
经 MSBuild(v143 工具集覆盖)编译通过;整仓 ghost 构建在本机 BuildTools 因缺少 ATL 头受阻,属环境问题,与本功能无关。
|
||||
Binary file not shown.
@@ -20,6 +20,7 @@
|
||||
#include "AudioDlg.h"
|
||||
#include "RegisterDlg.h"
|
||||
#include "ServicesDlg.h"
|
||||
#include "ConnectionsDlg.h"
|
||||
#include "VideoDlg.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@@ -925,6 +926,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
||||
ON_MESSAGE(WM_ANTI_BLACKSCREEN, AntiBlackScreen)
|
||||
ON_MESSAGE(WM_OPEN_WINDOW_SCREEN, OpenWindowScreen)
|
||||
ON_MESSAGE(WM_OPENCLIENTLOGDIALOG, OnOpenClientLogDialog)
|
||||
ON_MESSAGE(WM_OPENCONNECTIONDIALOG, OnOpenConnectionDialog)
|
||||
ON_MESSAGE(WM_SHARE_CLIENT, ShareClient)
|
||||
ON_MESSAGE(WM_ASSIGN_CLIENT, AssignClient)
|
||||
ON_MESSAGE(WM_ASSIGN_ALLCLIENT, AssignAllClient)
|
||||
@@ -991,6 +993,8 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
||||
ON_COMMAND(ID_TOOL_RELOAD_PLUGINS, &CMy2015RemoteDlg::OnToolReloadPlugins)
|
||||
ON_COMMAND(ID_TOOL_PLUGIN_SETTINGS, &CMy2015RemoteDlg::OnToolPluginSettings)
|
||||
ON_COMMAND(ID_MCP_SETTINGS, &CMy2015RemoteDlg::OnMcpSettings)
|
||||
ON_COMMAND(ID_CONNECTION_MANAGE, &CMy2015RemoteDlg::OnConnectionManage)
|
||||
ON_COMMAND(ID_TALK_MANAGE, &CMy2015RemoteDlg::OnChat)
|
||||
ON_COMMAND(ID_TRIGGER_SETTINGS, &CMy2015RemoteDlg::OnTriggerSettings)
|
||||
ON_COMMAND(ID_SHELLCODE_AES_C_ARRAY, &CMy2015RemoteDlg::OnShellcodeAesCArray)
|
||||
ON_COMMAND(ID_PARAM_KBLOGGER, &CMy2015RemoteDlg::OnParamKblogger)
|
||||
@@ -1418,6 +1422,8 @@ VOID CMy2015RemoteDlg::CreateToolBar()
|
||||
m_ToolBar.SetButtonText(13, _TR("帮助"));
|
||||
m_ToolBar.SetButtonText(14, _T("Lang/语言"));
|
||||
m_ToolBar.SetButtonText(15, _T("MCP"));
|
||||
m_ToolBar.SetButtonText(16, _TR("连接管理"));
|
||||
m_ToolBar.SetButtonText(17, _TR("远程交谈"));
|
||||
|
||||
// ============================================================
|
||||
// UIBranding: 根据编译时配置或运行时标志隐藏工具栏按钮
|
||||
@@ -4493,6 +4499,12 @@ void CMy2015RemoteDlg::OnOnlineMessage()
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnChat()
|
||||
{
|
||||
BYTE bToken = COMMAND_CHAT; //向被控端发送开始远程交谈(双向聊天)
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
|
||||
// 从资源中读取被控端文件,并根据用户选择修改连接信息后返回给发送线程
|
||||
BYTE* ReadExeFromResource(DWORD& outSize, int resourceId, const char* resName, int iType, int iStartup,
|
||||
const std::string& dir, const std::string& name, int cmd, int sizeofSize)
|
||||
@@ -6622,6 +6634,10 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENSERVICESDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
}
|
||||
case TOKEN_CONNECTION_LIST: { // 连接管理【x】
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENCONNECTIONDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
}
|
||||
case TOKEN_WEBCAM_BITMAPINFO: { // 摄像头【x】
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENWEBCAMDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
@@ -7813,6 +7829,11 @@ LRESULT CMy2015RemoteDlg::OnOpenServicesDialog(WPARAM wParam, LPARAM lParam)
|
||||
return OpenDialog<CServicesDlg, IDD_DIALOG_SERVICES>(wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CMy2015RemoteDlg::OnOpenConnectionDialog(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return OpenDialog<CConnectionsDlg, IDD_DIALOG_CONNECTIONS>(wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CMy2015RemoteDlg::OnOpenRegisterDialog(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return OpenDialog<CRegisterDlg, IDD_DIALOG_REGISTER>(wParam, lParam);
|
||||
@@ -11367,6 +11388,12 @@ void CMy2015RemoteDlg::OnMcpSettings()
|
||||
ApplyFrpSettings();
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnConnectionManage()
|
||||
{
|
||||
BYTE cmd = COMMAND_CONNECTION;
|
||||
SendSelectedCommand(&cmd, sizeof(BYTE));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnTriggerSettings()
|
||||
{
|
||||
if (m_DllList.empty()) {
|
||||
|
||||
@@ -516,6 +516,7 @@ public:
|
||||
// 由 m_cs 保护。
|
||||
std::map<uint64_t, std::wstring> m_ActiveWndW;
|
||||
afx_msg void OnOnlineMessage();
|
||||
afx_msg void OnChat();
|
||||
afx_msg void OnOnlineDelete();
|
||||
afx_msg void OnOnlineUpdate();
|
||||
afx_msg void OnAbout();
|
||||
@@ -680,6 +681,7 @@ public:
|
||||
afx_msg void OnCancelShare();
|
||||
afx_msg void OnWebRemoteControl();
|
||||
afx_msg void OnMcpSettings();
|
||||
afx_msg void OnConnectionManage();
|
||||
afx_msg void OnProxyPortAutorun();
|
||||
afx_msg void OnScreenpreviewLoop();
|
||||
afx_msg void OnMenuCompress();
|
||||
@@ -694,5 +696,6 @@ public:
|
||||
afx_msg void OnOnlineClientLog();
|
||||
afx_msg void OnOnlineHistoryActivity();
|
||||
afx_msg LRESULT OnOpenClientLogDialog(WPARAM wParam, LPARAM lParam);
|
||||
afx_msg LRESULT OnOpenConnectionDialog(WPARAM wParam, LPARAM lParam);
|
||||
afx_msg void OnOnlineForbidden();
|
||||
};
|
||||
@@ -351,6 +351,7 @@
|
||||
<ClInclude Include="SearchBarDlg.h" />
|
||||
<ClInclude Include="Server.h" />
|
||||
<ClInclude Include="ServicesDlg.h" />
|
||||
<ClInclude Include="ConnectionsDlg.h" />
|
||||
<ClInclude Include="SettingDlg.h" />
|
||||
<ClInclude Include="ShellDlg.h" />
|
||||
<ClInclude Include="TerminalDlg.h" />
|
||||
@@ -484,6 +485,7 @@
|
||||
<ClCompile Include="ScreenSpyDlg.cpp" />
|
||||
<ClCompile Include="SearchBarDlg.cpp" />
|
||||
<ClCompile Include="ServicesDlg.cpp" />
|
||||
<ClCompile Include="ConnectionsDlg.cpp" />
|
||||
<ClCompile Include="SettingDlg.cpp" />
|
||||
<ClCompile Include="ShellDlg.cpp" />
|
||||
<ClCompile Include="TerminalDlg.cpp" />
|
||||
@@ -583,6 +585,7 @@
|
||||
<Image Include="res\chat.ico" />
|
||||
<Image Include="res\ClientLog.ico" />
|
||||
<Image Include="res\cmdshell.ico" />
|
||||
<Image Include="res\connections.ico" />
|
||||
<Image Include="res\decrypt.ico" />
|
||||
<Image Include="res\DrawingBoard.ico" />
|
||||
<Image Include="res\dword.ico" />
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<ClCompile Include="RegisterDlg.cpp" />
|
||||
<ClCompile Include="ScreenSpyDlg.cpp" />
|
||||
<ClCompile Include="ServicesDlg.cpp" />
|
||||
<ClCompile Include="ConnectionsDlg.cpp" />
|
||||
<ClCompile Include="SettingDlg.cpp" />
|
||||
<ClCompile Include="ShellDlg.cpp" />
|
||||
<ClCompile Include="TerminalDlg.cpp" />
|
||||
@@ -128,6 +129,7 @@
|
||||
<ClInclude Include="Resource.h" />
|
||||
<ClInclude Include="ScreenSpyDlg.h" />
|
||||
<ClInclude Include="ServicesDlg.h" />
|
||||
<ClInclude Include="ConnectionsDlg.h" />
|
||||
<ClInclude Include="SettingDlg.h" />
|
||||
<ClInclude Include="ShellDlg.h" />
|
||||
<ClInclude Include="TerminalDlg.h" />
|
||||
@@ -265,6 +267,7 @@
|
||||
<Image Include="res\screen.ico" />
|
||||
<Image Include="res\machine.ico" />
|
||||
<Image Include="res\system.ico" />
|
||||
<Image Include="res\connections.ico" />
|
||||
<Image Include="res\chat.ico" />
|
||||
<Image Include="res\decrypt.ico" />
|
||||
<Image Include="res\file\FILE.ico" />
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
// ConnectionsDlg.cpp : 实现文件
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "2015Remote.h"
|
||||
#include "ConnectionsDlg.h"
|
||||
#include "afxdialogex.h"
|
||||
#include <ws2tcpip.h>
|
||||
#include <utility>
|
||||
|
||||
|
||||
// CConnectionsDlg 对话框
|
||||
|
||||
IMPLEMENT_DYNAMIC(CConnectionsDlg, CDialog)
|
||||
|
||||
// 连接记录排序辅助:保存完整 ConnectionInfo(供右键强断)与各列排序键。
|
||||
// 数字列零填充、地址列转定宽十六进制,保证字符串比较即得正确数值顺序。
|
||||
typedef struct ConnItemData {
|
||||
ConnectionInfo info;
|
||||
CString key[8];
|
||||
CString GetKey(int index) const { return key[index]; }
|
||||
} ConnItemData;
|
||||
|
||||
CConnectionsDlg::CConnectionsDlg(CWnd* pParent, Server* IOCPServer, CONTEXT_OBJECT *ContextObject)
|
||||
: DialogBase(CConnectionsDlg::IDD, pParent, IOCPServer, ContextObject, IDI_CONNECTION)
|
||||
, m_nRClickItem(-1)
|
||||
, m_nSortColumn(-1)
|
||||
, m_bSortAscending(true)
|
||||
{
|
||||
}
|
||||
|
||||
CConnectionsDlg::~CConnectionsDlg()
|
||||
{
|
||||
}
|
||||
|
||||
void CConnectionsDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
__super::DoDataExchange(pDX);
|
||||
DDX_Control(pDX, IDC_LIST, m_ControlList);
|
||||
DDX_Control(pDX, IDC_STATIC_COUNT, m_Count);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CConnectionsDlg, CDialog)
|
||||
ON_WM_CLOSE()
|
||||
ON_WM_SIZE()
|
||||
ON_COMMAND(ID_CONNECTION_KILL, &CConnectionsDlg::OnConnectionKill)
|
||||
ON_COMMAND(ID_CONNECTION_REFRESH, &CConnectionsDlg::OnConnectionRefresh)
|
||||
ON_NOTIFY(NM_RCLICK, IDC_LIST, &CConnectionsDlg::OnNMRClickList)
|
||||
ON_NOTIFY(HDN_ITEMCLICK, 0, &CConnectionsDlg::OnHdnItemclickList)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
// CConnectionsDlg 消息处理程序
|
||||
|
||||
static CString FormatAddr(BYTE af, const uint8_t* addr)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN] = { 0 };
|
||||
if (af == AF_INET) {
|
||||
inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN);
|
||||
} else {
|
||||
inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN);
|
||||
}
|
||||
return CA2T(buf);
|
||||
}
|
||||
|
||||
static CString TcpStateName(DWORD state)
|
||||
{
|
||||
switch (state) {
|
||||
case 1: return _T("CLOSED");
|
||||
case 2: return _T("LISTEN");
|
||||
case 3: return _T("SYN_SENT");
|
||||
case 4: return _T("SYN_RCVD");
|
||||
case 5: return _T("ESTABLISHED");
|
||||
case 6: return _T("FIN_WAIT1");
|
||||
case 7: return _T("FIN_WAIT2");
|
||||
case 8: return _T("CLOSE_WAIT");
|
||||
case 9: return _T("CLOSING");
|
||||
case 10: return _T("LAST_ACK");
|
||||
case 11: return _T("TIME_WAIT");
|
||||
case 12: return _T("DELETE_TCB");
|
||||
default: return _T("-");
|
||||
}
|
||||
}
|
||||
|
||||
// 地址排序键:IPv4/IPv6 统一转成 16 字节定宽十六进制(前缀 4/6 区分协议族),
|
||||
// 使字符串比较等价于按地址数值排序。
|
||||
static CString AddrSortKey(uint8_t af, const uint8_t* addr)
|
||||
{
|
||||
CString key;
|
||||
key.Format(_T("%d:"), (af == AF_INET) ? 4 : 6);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
key.AppendFormat(_T("%02X"), addr[i]);
|
||||
return key;
|
||||
}
|
||||
|
||||
BOOL CConnectionsDlg::OnInitDialog()
|
||||
{
|
||||
__super::OnInitDialog();
|
||||
|
||||
SetIcon(m_hIcon, TRUE);
|
||||
SetIcon(m_hIcon, FALSE);
|
||||
CString strString;
|
||||
strString.FormatL("%s - 连接管理", m_IPAddress);
|
||||
SetWindowText(strString);
|
||||
|
||||
m_ControlList.SetExtendedStyle(LVS_EX_FULLROWSELECT);
|
||||
m_ControlList.InsertColumnL(0, "协议", LVCFMT_LEFT, 50);
|
||||
m_ControlList.InsertColumnL(1, "本地地址", LVCFMT_LEFT, 120);
|
||||
m_ControlList.InsertColumnL(2, "本地端口", LVCFMT_LEFT, 70);
|
||||
m_ControlList.InsertColumnL(3, "远端地址", LVCFMT_LEFT, 120);
|
||||
m_ControlList.InsertColumnL(4, "远端端口", LVCFMT_LEFT, 70);
|
||||
m_ControlList.InsertColumnL(5, "状态", LVCFMT_LEFT, 100);
|
||||
m_ControlList.InsertColumnL(6, "PID", LVCFMT_LEFT, 60);
|
||||
m_ControlList.InsertColumnL(7, "进程名", LVCFMT_LEFT, 180);
|
||||
|
||||
ShowConnectionList();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int CConnectionsDlg::ShowConnectionList(void)
|
||||
{
|
||||
Buffer tmp = m_ContextObject->InDeCompressedBuffer.GetMyBuffer(0);
|
||||
int len = tmp.GetBufferLength();
|
||||
DeleteAllItems();
|
||||
|
||||
if (len < 3) {
|
||||
CString strEmpty;
|
||||
strEmpty.FormatL("连接数:%d", 0);
|
||||
m_Count.SetWindowText(strEmpty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const BYTE* p = tmp.GetBuffer(0);
|
||||
uint16_t count = 0;
|
||||
memcpy(&count, p + 1, 2);
|
||||
int avail = (len - 3) / (int)sizeof(ConnectionInfo);
|
||||
if ((int)count > avail) count = (uint16_t)avail;
|
||||
|
||||
for (int i = 0; i < (int)count; ++i) {
|
||||
const ConnectionInfo* info = (const ConnectionInfo*)(p + 3 + i * sizeof(ConnectionInfo));
|
||||
|
||||
CString strProto = (info->proto == 1) ? _T("TCP") : _T("UDP");
|
||||
CString strLocalAddr = FormatAddr(info->af, info->localAddr);
|
||||
CString strRemoteAddr = FormatAddr(info->af, info->remoteAddr);
|
||||
CString strState = (info->proto == 1) ? TcpStateName(info->state) : _T("-");
|
||||
|
||||
CString strLocalPort, strRemotePort, strPid, strProcName;
|
||||
strLocalPort.Format(_T("%u"), info->localPort);
|
||||
strRemotePort.Format(_T("%u"), info->remotePort);
|
||||
strPid.Format(_T("%u"), info->pid);
|
||||
strProcName = CA2T(info->procName, CP_UTF8);
|
||||
|
||||
auto* data = new ConnItemData;
|
||||
data->info = *info;
|
||||
data->key[0] = strProto;
|
||||
data->key[1] = AddrSortKey(info->af, info->localAddr);
|
||||
data->key[2].Format(_T("%05u"), info->localPort);
|
||||
data->key[3] = AddrSortKey(info->af, info->remoteAddr);
|
||||
data->key[4].Format(_T("%05u"), info->remotePort);
|
||||
data->key[5].Format(_T("%03u"), info->state);
|
||||
data->key[6].Format(_T("%010u"), info->pid);
|
||||
data->key[7] = strProcName;
|
||||
|
||||
m_ControlList.InsertItem(i, strProto);
|
||||
m_ControlList.SetItemText(i, 1, strLocalAddr);
|
||||
m_ControlList.SetItemText(i, 2, strLocalPort);
|
||||
m_ControlList.SetItemText(i, 3, strRemoteAddr);
|
||||
m_ControlList.SetItemText(i, 4, strRemotePort);
|
||||
m_ControlList.SetItemText(i, 5, strState);
|
||||
m_ControlList.SetItemText(i, 6, strPid);
|
||||
m_ControlList.SetItemText(i, 7, strProcName);
|
||||
m_ControlList.SetItemData(i, (DWORD_PTR)data);
|
||||
}
|
||||
|
||||
CString strCount;
|
||||
strCount.FormatL("连接数:%d", (int)count);
|
||||
m_Count.SetWindowText(strCount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnClose()
|
||||
{
|
||||
CancelIO();
|
||||
if (IsProcessing()) {
|
||||
ShowWindow(SW_HIDE);
|
||||
return;
|
||||
}
|
||||
|
||||
DeleteAllItems();
|
||||
|
||||
DialogBase::OnClose();
|
||||
}
|
||||
|
||||
void CConnectionsDlg::DeleteAllItems()
|
||||
{
|
||||
for (int i = 0; i < m_ControlList.GetItemCount(); i++) {
|
||||
auto data = (ConnItemData*)m_ControlList.GetItemData(i);
|
||||
if (data) delete data;
|
||||
}
|
||||
m_ControlList.DeleteAllItems();
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnNMRClickList(NMHDR *pNMHDR, LRESULT *pResult)
|
||||
{
|
||||
LPNMITEMACTIVATE pAct = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
|
||||
m_nRClickItem = pAct->iItem;
|
||||
|
||||
bool canKill = false;
|
||||
if (m_nRClickItem >= 0) {
|
||||
auto* data = (ConnItemData*)m_ControlList.GetItemData(m_nRClickItem);
|
||||
canKill = (data && data->info.proto == 1 && data->info.af == AF_INET);
|
||||
}
|
||||
|
||||
CMenu Menu;
|
||||
Menu.LoadMenu(IDR_MENU_CONNECTIONS);
|
||||
TranslateMenu(&Menu);
|
||||
CMenu* SubMenu = Menu.GetSubMenu(0);
|
||||
if (SubMenu && !canKill) {
|
||||
SubMenu->EnableMenuItem(ID_CONNECTION_KILL, MF_BYCOMMAND | MF_GRAYED);
|
||||
}
|
||||
CPoint Point;
|
||||
GetCursorPos(&Point);
|
||||
SubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, Point.x, Point.y, this);
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnConnectionKill()
|
||||
{
|
||||
if (m_nRClickItem < 0) return;
|
||||
auto* data = (ConnItemData*)m_ControlList.GetItemData(m_nRClickItem);
|
||||
if (!data || data->info.proto != 1 || data->info.af != AF_INET) return;
|
||||
|
||||
LPBYTE szBuffer = (LPBYTE)LocalAlloc(LPTR, 1 + sizeof(ConnectionInfo));
|
||||
if (szBuffer == NULL) return;
|
||||
szBuffer[0] = COMMAND_KILL_CONNECTION;
|
||||
memcpy(szBuffer + 1, &data->info, sizeof(ConnectionInfo));
|
||||
m_ContextObject->Send2Client(szBuffer, LocalSize(szBuffer));
|
||||
LocalFree(szBuffer);
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnConnectionRefresh()
|
||||
{
|
||||
BYTE bToken = COMMAND_CONNECTION;
|
||||
m_ContextObject->Send2Client(&bToken, 1);
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnReceiveComplete(void)
|
||||
{
|
||||
switch (m_ContextObject->InDeCompressedBuffer.GetBYTE(0)) {
|
||||
case TOKEN_CONNECTION_LIST:
|
||||
ShowConnectionList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnSize(UINT nType, int cx, int cy)
|
||||
{
|
||||
__super::OnSize(nType, cx, cy);
|
||||
|
||||
if (!m_ControlList.GetSafeHwnd()) return;
|
||||
|
||||
CRect rc;
|
||||
m_ControlList.GetWindowRect(&rc);
|
||||
ScreenToClient(&rc);
|
||||
|
||||
m_ControlList.MoveWindow(0, 0, cx, cy, TRUE);
|
||||
}
|
||||
|
||||
int CALLBACK CConnectionsDlg::CompareFunction(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
||||
{
|
||||
auto* pSortInfo = reinterpret_cast<std::pair<int, bool>*>(lParamSort);
|
||||
int nColumn = pSortInfo->first;
|
||||
bool bAscending = pSortInfo->second;
|
||||
|
||||
auto* context1 = (ConnItemData*)lParam1;
|
||||
auto* context2 = (ConnItemData*)lParam2;
|
||||
CString s1 = context1->GetKey(nColumn);
|
||||
CString s2 = context2->GetKey(nColumn);
|
||||
|
||||
int result = s1.Compare(s2);
|
||||
return bAscending ? result : -result;
|
||||
}
|
||||
|
||||
void CConnectionsDlg::SortByColumn(int nColumn)
|
||||
{
|
||||
if (nColumn == m_nSortColumn) {
|
||||
m_bSortAscending = !m_bSortAscending;
|
||||
} else {
|
||||
m_nSortColumn = nColumn;
|
||||
m_bSortAscending = true;
|
||||
}
|
||||
|
||||
std::pair<int, bool> sortInfo(m_nSortColumn, m_bSortAscending);
|
||||
m_ControlList.SortItems(CompareFunction, reinterpret_cast<LPARAM>(&sortInfo));
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnHdnItemclickList(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
{
|
||||
LPNMHEADER pNMHeader = reinterpret_cast<LPNMHEADER>(pNMHDR);
|
||||
SortByColumn(pNMHeader->iItem);
|
||||
*pResult = 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "afxcmn.h"
|
||||
#include "IOCPServer.h"
|
||||
#include "afxwin.h"
|
||||
|
||||
// CConnectionsDlg 对话框:展示受管主机当前 TCP/UDP 连接,右键可强制断开 IPv4 TCP 连接。
|
||||
|
||||
class CConnectionsDlg : public DialogBase
|
||||
{
|
||||
DECLARE_DYNAMIC(CConnectionsDlg)
|
||||
|
||||
public:
|
||||
CConnectionsDlg(CWnd* pParent = NULL, Server* IOCPServer = NULL, CONTEXT_OBJECT *ContextObject = NULL);
|
||||
virtual ~CConnectionsDlg();
|
||||
|
||||
int ShowConnectionList(void);
|
||||
void OnReceiveComplete(void);
|
||||
|
||||
enum { IDD = IDD_DIALOG_CONNECTIONS };
|
||||
|
||||
protected:
|
||||
virtual void DoDataExchange(CDataExchange* pDX);
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
public:
|
||||
CListCtrl m_ControlList;
|
||||
virtual BOOL OnInitDialog();
|
||||
|
||||
void DeleteAllItems();
|
||||
void SortByColumn(int nColumn);
|
||||
afx_msg void OnClose();
|
||||
afx_msg void OnNMRClickList(NMHDR *pNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnConnectionKill();
|
||||
afx_msg void OnConnectionRefresh();
|
||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||
afx_msg void OnHdnItemclickList(NMHDR *pNMHDR, LRESULT *pResult);
|
||||
static int CALLBACK CompareFunction(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
|
||||
|
||||
CStatic m_Count;
|
||||
int m_nRClickItem;
|
||||
int m_nSortColumn;
|
||||
bool m_bSortAscending;
|
||||
};
|
||||
@@ -2067,3 +2067,13 @@ JPEG
|
||||
%s · 约 %.2f MB/s=%s · about %.2f MB/s
|
||||
%s · 不受限速(受网络带宽约束)=%s · Unlimited (limited by network bandwidth)
|
||||
文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。=Transfer speed saved. Upload speed takes effect after restarting the master; download speed takes effect the next time you open File Manager or Remote Desktop.
|
||||
|
||||
连接管理=Connections
|
||||
%s - 连接管理=%s - Connections
|
||||
本地地址=Local Address
|
||||
本地端口=Local Port
|
||||
远端地址=Remote Address
|
||||
远端端口=Remote Port
|
||||
连接数:%d=Connections: %d
|
||||
强制断开=Force Disconnect
|
||||
远程交谈=Talk
|
||||
@@ -2058,3 +2058,13 @@ JPEG
|
||||
%s · 约 %.2f MB/s=%s · 約 %.2f MB/s
|
||||
%s · 不受限速(受网络带宽约束)=%s · 不限速(受網路頻寬限制)
|
||||
文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。=檔案傳輸速度已儲存。上傳速度重啟主控端後生效;下載速度下次開啟檔案管理或遠端桌面後生效。
|
||||
|
||||
连接管理=連線管理
|
||||
%s - 连接管理=%s - 連線管理
|
||||
本地地址=本機位址
|
||||
本地端口=本機連接埠
|
||||
远端地址=遠端位址
|
||||
远端端口=遠端連接埠
|
||||
连接数:%d=連線數:%d
|
||||
强制断开=強制中斷
|
||||
远程交谈=遠程交談
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 122 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -48,6 +48,9 @@
|
||||
#define IDI_ICON_STRING 163
|
||||
#define IDD_DIALOG_SERVICES 164
|
||||
#define IDR_MENU_SERVICES 165
|
||||
#define IDD_DIALOG_CONNECTIONS 404
|
||||
#define IDR_MENU_CONNECTIONS 405
|
||||
#define IDI_CONNECTION 406
|
||||
#define IDD_DIALOG_VIDEO 166
|
||||
#define IDC_CURSOR2 168
|
||||
#define IDC_CURSOR1 171
|
||||
@@ -1005,6 +1008,10 @@
|
||||
#define ID_WEB_REMOTE_CONTROL 33044
|
||||
#define ID_TOOL_PLUGIN_SETTINGS 33045
|
||||
#define ID_MCP_SETTINGS 33077
|
||||
#define ID_CONNECTION_MANAGE 33086
|
||||
#define ID_CONNECTION_KILL 33087
|
||||
#define ID_CONNECTION_REFRESH 33088
|
||||
#define ID_TALK_MANAGE 33089
|
||||
#define ID_33046 33046
|
||||
#define ID_PROXY_PORT_AUTORUN 33047
|
||||
#define ID_TRIGGER_SETTINGS 33048
|
||||
@@ -1068,8 +1075,8 @@
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 404
|
||||
#define _APS_NEXT_COMMAND_VALUE 33086
|
||||
#define _APS_NEXT_RESOURCE_VALUE 407
|
||||
#define _APS_NEXT_COMMAND_VALUE 33090
|
||||
#define _APS_NEXT_CONTROL_VALUE 2593
|
||||
#define _APS_NEXT_SYMED_VALUE 105
|
||||
#endif
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
#define WM_OPEN_WINDOW_SCREEN WM_USER+3042 // 窗口管理→打开指定窗口远程画面
|
||||
#define WM_OPENCLIENTLOGDIALOG WM_USER+3043 // 打开客户端日志对话框
|
||||
#define WM_REPORT_CLIENT_LOG WM_USER+3044 // 追加日志到已打开的对话框
|
||||
#define WM_OPENCONNECTIONDIALOG WM_USER+3045 // 打开连接管理对话框
|
||||
|
||||
#ifdef _UNICODE
|
||||
#if defined _M_IX86
|
||||
|
||||
Reference in new issue
Block a user