1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/meshcore/KVM/Windows/kvm.c
Bryan Roe fd93f62fe4 Added documentation to fs
Addressed minor issues from static analysis
2022-07-21 00:24:10 -07:00

1493 lines
50 KiB
C

/*
Copyright 2006 - 2022 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#if defined(_LINKVM)
#pragma warning(disable: 4996)
#include <stdio.h>
#include "kvm.h"
#include "tile.h"
#include <signal.h>
#include "input.h"
#include <Winuser.h>
#include "meshcore/meshdefines.h"
#include "microstack/ILibParsers.h"
#include "microstack/ILibAsyncSocket.h"
#include "microstack/ILibProcessPipe.h"
#include "microstack/ILibRemoteLogging.h"
#include <sas.h>
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(_MINCORE)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
extern void KVM_WriteLog(ILibKVM_WriteHandler writeHandler, void *user, char *format, ...);
//#define KVMDEBUGENABLED 1
ILibProcessPipe_SpawnTypes gProcessSpawnType = ILibProcessPipe_SpawnTypes_USER;
int gProcessTSID = -1;
extern int gRemoteMouseRenderDefault;
int gRemoteMouseMoved = 0;
extern int gCurrentCursor;
#pragma pack(push, 1)
typedef struct KVMDebugLog
{
unsigned short length;
unsigned short logType;
unsigned short logFlags;
char logData[];
}KVMDebugLog;
#pragma pack(pop)
#ifdef KVMDEBUGENABLED
void KvmCriticalLog(const char* msg, const char* file, int line, int user1, int user2)
{
int len;
HANDLE h;
int DontDestroy = 0;
h = OpenMutex(MUTEX_ALL_ACCESS, FALSE, TEXT("MeshAgentKvmLogLock"));
if (h == NULL)
{
if (GetLastError() != ERROR_FILE_NOT_FOUND) return;
if ((h = CreateMutex(NULL, TRUE, TEXT("MeshAgentKvmLogLock"))) == NULL) return;
DontDestroy = 1;
}
else
{
WaitForSingleObject(h, INFINITE);
}
len = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "\r\n%s:%d (%d,%d) %s", file, line, user1, user2, msg);
if (len > 0 && len < (int)sizeof(ILibScratchPad)) ILibAppendStringToDiskEx("C:\\Temp\\MeshAgentKvm.log", ILibScratchPad, len);
ReleaseMutex(h);
if (DontDestroy == 0) CloseHandle(h);
}
#define KVMDEBUG(m,u) { KvmCriticalLog(m, __FILE__, __LINE__, u, GetLastError()); printf("KVMMSG: %s (%d,%d).\r\n", m, (int)u, (int)GetLastError()); }
#define KVMDEBUG2 ILIBLOGMESSAGEX
#else
#define KVMDEBUG(m, u)
#define KVMDEBUG2(...)
#endif
int TILE_WIDTH = 0;
int TILE_HEIGHT = 0;
int SCREEN_COUNT = -1; // Total number of displays
int SCREEN_SEL = 0; // Currently selected display (0 = all)
int SCREEN_SEL_TARGET = 0; // Desired selected display (0 = all)
int SCREEN_SEL_PROCESS = 0; // In process of changing displays (0 = all)
int SCREEN_X = 0; // Left most of current screen
int SCREEN_Y = 0; // Top most of current screen
int SCREEN_WIDTH = 0; // Width of current screen
int SCREEN_HEIGHT = 0; // Height of current screen
int VSCREEN_X = 0; // Left most of virtual screen
int VSCREEN_Y = 0; // Top most of virtual screen
int VSCREEN_WIDTH = 0; // Width of virtual screen
int VSCREEN_HEIGHT = 0; // Height of virtual screen
int SCALED_WIDTH = 0;
int SCALED_HEIGHT = 0;
int PIXEL_SIZE = 0;
int TILE_WIDTH_COUNT = 0;
int TILE_HEIGHT_COUNT = 0;
int COMPRESSION_RATIO = 0;
int SCALING_FACTOR = 1024; // Scaling factor, 1024 = 100%
int SCALING_FACTOR_NEW = 1024; // Desired scaling factor, 1024 = 100%
int FRAME_RATE_TIMER = 0;
HANDLE kvmthread = NULL;
int g_shutdown = 999;
int g_pause = 0;
int g_remotepause = 1;
int g_restartcount = 0;
struct tileInfo_t **tileInfo = NULL;
int g_slavekvm = 0;
static ILibProcessPipe_Process gChildProcess;
int kvm_relay_restart(int paused, void *pipeMgr, char *exePath, ILibKVM_WriteHandler writeHandler, void *reserved);
HANDLE hStdOut = INVALID_HANDLE_VALUE;
HANDLE hStdIn = INVALID_HANDLE_VALUE;
int ThreadRunning = 0;
int kvmConsoleMode = 0;
ILibQueue gPendingPackets = NULL;
ILibRemoteLogging gKVMRemoteLogging = NULL;
#ifdef _WINSERVICE
void kvm_slave_OnRawForwardLog(ILibRemoteLogging sender, ILibRemoteLogging_Modules module, ILibRemoteLogging_Flags flags, char *buffer, int bufferLen)
{
if (flags <= ILibRemoteLogging_Flags_VerbosityLevel_1)
{
KVMDebugLog *log = (KVMDebugLog*)buffer;
log->length = bufferLen + 1;
log->logType = (unsigned short)module;
log->logFlags = (unsigned short)flags;
buffer[bufferLen] = 0;
WriteFile(GetStdHandle(STD_ERROR_HANDLE), buffer, log->length, &bufferLen, NULL);
}
}
#endif
void kvm_setupSasPermissions()
{
DWORD dw = 3;
HKEY key = NULL;
KVMDEBUG("kvm_setupSasPermissions", 0);
// SoftwareSASGeneration
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
{
RegSetValueEx(key, "SoftwareSASGeneration", 0, REG_DWORD, (BYTE*)&dw, 4);
RegCloseKey(key);
}
}
// Emulate the CTRL-ALT-DEL (Should work on WinXP, not on Vista & Win7)
DWORD WINAPI kvm_ctrlaltdel(LPVOID Param)
{
UNREFERENCED_PARAMETER( Param );
KVMDEBUG("kvm_ctrlaltdel", (int)(uintptr_t)Param);
typedef VOID(WINAPI *SendSas)(BOOL asUser);
SendSas sas;
// Perform new method (Vista & Win7)
HMODULE m = LoadLibraryExA("sas.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
// We need to dynamically load this, becuase it doesn't exist on Windows Core.
// However, LOAD_LIBRARY_SEARCH_SYSTEM32 does not exist on Windows 7 SP1 / Windows Server 2008 R2 without a MSFT Patch,
// but this patch is no longer available from MSFT, so this fallback case will only affect insecure versions of Windows 7 SP1 / Server 2008 R2
if (m == NULL && GetLastError() == ERROR_INVALID_PARAMETER) { m = LoadLibraryA("sas.dll"); }
if (m != NULL)
{
sas = (SendSas)GetProcAddress(m, "SendSAS");
if (sas != NULL)
{
kvm_setupSasPermissions();
sas(FALSE);
}
FreeLibrary(m);
}
return 0;
}
BOOL CALLBACK DisplayInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
int w, h, deviceid = 0;
MONITORINFOEX mi;
DWORD *selection = (DWORD*)dwData;
UNREFERENCED_PARAMETER( hdcMonitor );
UNREFERENCED_PARAMETER( lprcMonitor );
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
// Get the display information
if (!GetMonitorInfo(hMonitor, (LPMONITORINFO)&mi)) return TRUE;
if (sscanf_s(mi.szDevice, "\\\\.\\DISPLAY%d", &deviceid) != 1) return TRUE;
if (--selection[0] > 0) { return TRUE; }
// See if anything changed
w = abs(mi.rcMonitor.left - mi.rcMonitor.right);
h = abs(mi.rcMonitor.top - mi.rcMonitor.bottom);
if (SCREEN_X != mi.rcMonitor.left || SCREEN_Y != mi.rcMonitor.top || SCREEN_WIDTH != w || SCREEN_HEIGHT != h || SCALING_FACTOR != SCALING_FACTOR_NEW)
{
SCREEN_X = mi.rcMonitor.left;
SCREEN_Y = mi.rcMonitor.top;
SCREEN_WIDTH = w;
SCREEN_HEIGHT = h;
SCREEN_SEL_PROCESS |= 1; // Force the new resolution to be sent to the client.
}
if (SCREEN_SEL != SCREEN_SEL_TARGET)
{
SCREEN_SEL = SCREEN_SEL_TARGET;
SCREEN_SEL_PROCESS |= 2; // Force the display list to be sent to the client, includes the new display selection
}
return TRUE;
}
BOOL CALLBACK DisplayInfoEnumProc_Info(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
unsigned short* buffer = (unsigned short*)dwData;
MONITORINFOEX mi;
UNREFERENCED_PARAMETER(hdcMonitor);
UNREFERENCED_PARAMETER(lprcMonitor);
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
// Get the display information
if (!GetMonitorInfo(hMonitor, (LPMONITORINFO)&mi)) return TRUE;
int w = abs(mi.rcMonitor.left - mi.rcMonitor.right);
int h = abs(mi.rcMonitor.top - mi.rcMonitor.bottom);
int i = (int)(buffer[0]++);
int offset = (5 * i) + 4;
if ((((i + 1) * 10) + 4) > buffer[1]) { return(FALSE); }
buffer[offset] = (unsigned short)htons((unsigned short)(i+1)); // ID
buffer[offset+1] = (unsigned short)htons((unsigned short)(mi.rcMonitor.left)); // X
buffer[offset+2] = (unsigned short)htons((unsigned short)(mi.rcMonitor.top)); // Y
buffer[offset+3] = (unsigned short)htons((unsigned short)(w)); // WIDTH
buffer[offset+4] = (unsigned short)htons((unsigned short)(h)); // HEIGHT
return(TRUE);
}
void kvm_send_display_list(ILibKVM_WriteHandler writeHandler, void *reserved)
{
int i;
char dwData[4096];
((unsigned short*)dwData)[0] = (unsigned short)0;
((unsigned short*)dwData)[1] = (unsigned short)sizeof(dwData);
((unsigned short*)dwData)[2] = (unsigned short)htons((unsigned short)MNG_KVM_DISPLAY_INFO); // Write the type
if (EnumDisplayMonitors(NULL, NULL, DisplayInfoEnumProc_Info, (LPARAM)dwData))
{
((unsigned short*)dwData)[3] = (unsigned short)htons((((unsigned short*)dwData)[0]) * 10 + 4); // Length
writeHandler(dwData+4, (((unsigned short*)dwData)[0]) * 10 + 4, reserved);
}
// Not looked at the number of screens yet
if (SCREEN_COUNT == -1) return;
char *buffer = ILibMemory_AllocateA((5 + SCREEN_COUNT) * 2);
memset(buffer, 0xFF, ILibMemory_AllocateA_Size(buffer));
// Send the list of possible displays to remote
if (SCREEN_COUNT == 0 || SCREEN_COUNT == 1)
{
// Only one display, send empty
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_GET_DISPLAYS); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)(8)); // Write the size
((unsigned short*)buffer)[2] = (unsigned short)htons((unsigned short)(0)); // Screen Count
((unsigned short*)buffer)[3] = (unsigned short)htons((unsigned short)(0)); // Selected Screen
writeHandler(buffer, 8, reserved);
}
else
{
// Many displays
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_GET_DISPLAYS); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)(10 + (2 * SCREEN_COUNT))); // Write the size
((unsigned short*)buffer)[2] = (unsigned short)htons((unsigned short)(SCREEN_COUNT + 1)); // Screen Count
((unsigned short*)buffer)[3] = (unsigned short)htons((unsigned short)(-1)); // Possible Screen (ALL)
for (i = 0; i < SCREEN_COUNT; i++) {
((unsigned short*)buffer)[4 + i] = (unsigned short)htons((unsigned short)(i + 1)); // Possible Screen
}
if (SCREEN_SEL == 0) {
((unsigned short*)buffer)[4 + i] = (unsigned short)htons((unsigned short)(-1)); // Selected Screen (All)
} else {
((unsigned short*)buffer)[4 + i] = (unsigned short)htons((unsigned short)(SCREEN_SEL)); // Selected Screen
}
writeHandler(buffer, (10 + (2 * SCREEN_COUNT)), reserved);
}
}
void kvm_server_SetResolution();
int kvm_server_currentDesktopname = 0;
void CheckDesktopSwitch(int checkres, ILibKVM_WriteHandler writeHandler, void *reserved)
{
int x, y, w, h;
HDESK desktop;
HDESK desktop2;
char name[64];
// KVMDEBUG("CheckDesktopSwitch", checkres);
// Check desktop switch
if ((desktop2 = GetThreadDesktop(GetCurrentThreadId())) == NULL) { KVMDEBUG("GetThreadDesktop Error", 0); } // CloseDesktop() is not needed
if ((desktop = OpenInputDesktop(0, TRUE,
DESKTOP_CREATEMENU |
DESKTOP_CREATEWINDOW |
DESKTOP_ENUMERATE |
DESKTOP_HOOKCONTROL |
DESKTOP_WRITEOBJECTS |
DESKTOP_READOBJECTS |
DESKTOP_SWITCHDESKTOP |
GENERIC_WRITE)) == NULL) { KVMDEBUG("OpenInputDesktop Error", 0); }
if (SetThreadDesktop(desktop) == 0)
{
if (CloseDesktop(desktop) == 0) { KVMDEBUG("CloseDesktop1 Error", 0); }
desktop = desktop2;
} else {
CloseDesktop(desktop2);
}
// Check desktop name switch
if (GetUserObjectInformationA(desktop, UOI_NAME, name, 63, 0))
{
//ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: name = %s", name);
// KVMDEBUG(name, 0);
if (kvm_server_currentDesktopname == 0)
{
// This is the first time we come here.
kvm_server_currentDesktopname = ((int*)name)[0];
}
else
{
// If the desktop name has changed, shutdown.
if (kvm_server_currentDesktopname != ((int*)name)[0])
{
KVMDEBUG("DESKTOP NAME CHANGE DETECTED, triggering shutdown", 0);
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: kvm_server_currentDesktop: NAME CHANGE DETECTED...");
g_shutdown = 1;
}
}
}
else
{
KVMDEBUG("GetUserObjectInformation Error", 0);
}
// See if the number of displays has changed
x = GetSystemMetrics(SM_CMONITORS);
if (SCREEN_COUNT != x) { SCREEN_COUNT = x; kvm_send_display_list(writeHandler, reserved); }
// Check resolution change
if (checkres != 0 && g_shutdown == 0)
{
VSCREEN_X = GetSystemMetrics(SM_XVIRTUALSCREEN);
VSCREEN_Y = GetSystemMetrics(SM_YVIRTUALSCREEN);
VSCREEN_WIDTH = GetSystemMetrics(SM_CXVIRTUALSCREEN);
VSCREEN_HEIGHT = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if (SCREEN_SEL_TARGET == 0)
{
if (VSCREEN_WIDTH == 0)
{
// Old style, one display only. Added this just in case VIRTUALSCREEN does not work.
x = 0;
y = 0;
w = GetSystemMetrics(SM_CXSCREEN);
h = GetSystemMetrics(SM_CYSCREEN);
} else {
// New style, entire virtual desktop
x = VSCREEN_X;
y = VSCREEN_Y;
w = VSCREEN_WIDTH;
h = VSCREEN_HEIGHT;
}
if (SCREEN_X != x || SCREEN_Y != y || SCREEN_WIDTH != w || SCREEN_HEIGHT != h || SCALING_FACTOR != SCALING_FACTOR_NEW)
{
//printf("RESOLUTION CHANGED! (supposedly)\n");
SCREEN_X = x;
SCREEN_Y = y;
SCREEN_WIDTH = w;
SCREEN_HEIGHT = h;
kvm_server_SetResolution(writeHandler, reserved);
}
if (SCREEN_SEL_TARGET != SCREEN_SEL) { SCREEN_SEL = SCREEN_SEL_TARGET; kvm_send_display_list(writeHandler, reserved); }
}
else
{
// Get the list of monitors
if (SCREEN_SEL_PROCESS == 0)
{
DWORD selection = SCREEN_SEL_TARGET;
if (EnumDisplayMonitors(NULL, NULL, DisplayInfoEnumProc, (LPARAM)&selection))
{
// Set the resolution
if (SCREEN_SEL_PROCESS & 1) kvm_server_SetResolution(writeHandler, reserved);
if (SCREEN_SEL_PROCESS & 2) kvm_send_display_list(writeHandler, reserved);
}
SCREEN_SEL_PROCESS = 0;
}
}
}
}
unsigned char g_blockinput = 0;
// Feed network data into the KVM. Return the number of bytes consumed.
// This method consumes a single command.
int kvm_server_inputdata(char* block, int blocklen, ILibKVM_WriteHandler writeHandler, void *reserved)
{
unsigned short type, size;
// Decode the block header
if (blocklen < 4) return 0;
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_2, "KVM [SLAVE]: Handle Input [Len = %d]", blocklen);
// KVMDEBUG("kvm_server_inputdata", blocklen);
CheckDesktopSwitch(0, writeHandler, reserved);
type = ntohs(((unsigned short*)(block))[0]);
size = ntohs(((unsigned short*)(block))[1]);
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_2, "KVM [SLAVE]: Handle Input [Len = %d, type = %u, size = %u]", blocklen, type, size);
if (size > blocklen) return 0;
//printf("INPUT: %d, %d\r\n", type, size);
switch (type)
{
case MNG_KVM_INPUT_LOCK:
// 0 = unlock
// 1 = lock
// 2 = query
if (size == 5)
{
switch (block[4])
{
case 0:
g_blockinput = 0;
BlockInput(0);
break;
case 1:
g_blockinput = 1;
BlockInput(1);
break;
case 2:
break;
default:
return(size);
break;
}
unsigned char buffer[5];
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_INPUT_LOCK); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)5); // Write the size
buffer[4] = g_blockinput; // status
writeHandler((char*)buffer, 5, reserved);
}
break;
case MNG_KVM_KEY: // Key
{
if (size != 6) break;
//KVM_WriteLog(writeHandler, reserved, "Key[%u] UP: %d", (unsigned char)(block[5]), block[4]);
KeyAction(block[5], block[4]);
break;
}
case MNG_KVM_KEY_UNICODE: // Unicode key
{
if (size != 7) break;
//KVM_WriteLog(writeHandler, reserved, "UnicodeKey[%u] UP: %d", ((((unsigned char)block[5]) << 8) + ((unsigned char)block[6])), block[4]);
KeyActionUnicode(((((unsigned char)block[5]) << 8) + ((unsigned char)block[6])), block[4]);
break;
}
case MNG_KVM_MOUSE: // Mouse
{
double x, y;
short w = 0;
KVM_MouseCursors curcursor = KVM_MouseCursor_NOCHANGE;
if (size == 10 || size == 12)
{
gRemoteMouseMoved = 1;
// Get positions and scale correctly
x = (double)ntohs(((short*)(block))[3]) * 1024 / SCALING_FACTOR;
y = (double)ntohs(((short*)(block))[4]) * 1024 / SCALING_FACTOR;
// Add relative display position
x += fabs((double)(SCREEN_X - VSCREEN_X));
y += fabs((double)(SCREEN_Y - VSCREEN_Y));
// Scale back to the virtual screen
x = (x * ((double)SCREEN_WIDTH / (double)VSCREEN_WIDTH)) * (double)65535;
y = (y * ((double)SCREEN_HEIGHT / (double)VSCREEN_HEIGHT)) * (double)65535;
// Perform the mouse movement
if (size == 12) w = ((short)ntohs(((short*)(block))[5]));
MouseAction((((double)x / (double)SCREEN_WIDTH)), (((double)y / (double)SCREEN_HEIGHT)), (int)(unsigned char)(block[5]), w);
}
break;
}
case MNG_KVM_COMPRESSION: // Compression
{
if (size >= 10) { int fr = ((int)ntohs(((unsigned short*)(block + 8))[0])); if (fr >= 20 && fr <= 5000) FRAME_RATE_TIMER = fr; }
if (size >= 8) { int ns = ((int)ntohs(((unsigned short*)(block + 6))[0])); if (ns >= 64 && ns <= 4096) SCALING_FACTOR_NEW = ns; }
if (size >= 6) { set_tile_compression((int)block[4], (int)block[5]); }
COMPRESSION_RATIO = 100;
break;
}
case MNG_KVM_REFRESH: // Refresh
{
int row, col;
char buffer[8];
if (size != 4) break;
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_SCREEN); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)8); // Write the size
((unsigned short*)buffer)[2] = (unsigned short)htons((unsigned short)SCALED_WIDTH); // X position
((unsigned short*)buffer)[3] = (unsigned short)htons((unsigned short)SCALED_HEIGHT); // Y position
writeHandler((char*)buffer, 8, reserved);
// Send the list of available displays
kvm_send_display_list(writeHandler, reserved);
// Reset all tile information
if (tileInfo == NULL) {
if ((tileInfo = (struct tileInfo_t **) malloc(TILE_HEIGHT_COUNT * sizeof(struct tileInfo_t *))) == NULL) ILIBCRITICALEXIT(254);
for (row = 0; row < TILE_HEIGHT_COUNT; row++) { if ((tileInfo[row] = (struct tileInfo_t *)malloc(TILE_WIDTH_COUNT * sizeof(struct tileInfo_t))) == NULL) ILIBCRITICALEXIT(254); }
}
for (row = 0; row < TILE_HEIGHT_COUNT; row++) { for (col = 0; col < TILE_WIDTH_COUNT; col++) { tileInfo[row][col].crc = 0xFF; tileInfo[row][col].flags = 0; } }
break;
}
case MNG_KVM_PAUSE: // Pause
{
if (size != 5) break;
g_remotepause = block[4];
break;
}
case MNG_KVM_FRAME_RATE_TIMER:
{
int fr = ((int)ntohs(((unsigned short*)(block))[2]));
if (fr >= 20 && fr <= 5000) FRAME_RATE_TIMER = fr;
break;
}
case MNG_KVM_INIT_TOUCH:
{
// Attempt to initialized touch support
char buffer[6];
unsigned short r = (unsigned short)TouchInit();
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_INIT_TOUCH); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)6); // Write the size
((unsigned short*)buffer)[2] = (unsigned short)htons(r); // Write the return code
writeHandler((char*)buffer, 6, reserved);
break;
}
case MNG_KVM_TOUCH:
{
int r = 0;
if (block[4] == 1) // Version 1 touch structure (Very simple)
{
unsigned int flags = (unsigned int)ntohl(((unsigned int*)(block + 6))[0]);
// Get positions and scale correctly
unsigned short x = (unsigned short)(ntohs(((unsigned short*)(block + 10))[0])) * 1024 / (unsigned short)SCALING_FACTOR;
unsigned short y = (unsigned short)(ntohs(((unsigned short*)(block + 12))[0])) * 1024 / (unsigned short)SCALING_FACTOR;
// Add relative display position
x += (unsigned short)fabs((double)(SCREEN_X - VSCREEN_X));
y += (unsigned short)fabs((double)(SCREEN_Y - VSCREEN_Y));
// Scale back to the virtual screen
x = (unsigned short)(((double)x * ((double)SCREEN_WIDTH / (double)VSCREEN_WIDTH)) * (double)65535);
y = (unsigned short)(((double)y * ((double)SCREEN_HEIGHT / (double)VSCREEN_HEIGHT)) * (double)65535);
r = TouchAction1(block[5], flags, x, y);
}
else if (block[4] == 2) // Version 2 touch structure array
{
r = TouchAction2(block + 5, size - 5, SCALING_FACTOR);
}
if (r == 1) {
// Reset touch
char buffer[4];
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_TOUCH); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)4); // Write the size
writeHandler((char*)buffer, 4, reserved);
}
break;
}
case MNG_KVM_GET_DISPLAYS:
{
kvm_send_display_list(writeHandler, reserved);
break;
}
case MNG_KVM_SET_DISPLAY:
{
// Set the display
int x = 0;
if (size < 6) break;
x = (unsigned short)ntohs(((unsigned short*)(block + 4))[0]);
if (x == 65535) SCREEN_SEL_TARGET = 0; else SCREEN_SEL_TARGET = x;
break;
}
}
return size;
}
typedef struct kvm_data_handler
{
ILibKVM_WriteHandler handler;
void *reserved;
int len;
char buffer[];
}kvm_data_handler;
// Feed network data into the KVM. Return the number of bytes consumed.
// This method consumes as many input commands as it can.
int kvm_relay_feeddata(char* buf, int len, ILibKVM_WriteHandler writeHandler, void *reserved)
{
if (gChildProcess != NULL)
{
if (len >= 2 && ntohs(((unsigned short*)buf)[0]) == MNG_CTRLALTDEL)
{
HANDLE ht = CreateThread(NULL, 0, kvm_ctrlaltdel, 0, 0, 0);
if (ht != NULL) CloseHandle(ht);
}
ILibProcessPipe_Process_WriteStdIn(gChildProcess, buf, len, ILibTransport_MemoryOwnership_USER);
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_2, "KVM [Master]: Write Input [Type = %u]", ntohs(((unsigned short*)buf)[0]));
return len;
}
else
{
int len2 = 0;
int ptr = 0;
//while ((len2 = kvm_server_inputdata(buf + ptr, len - ptr, kvm_relay_feeddata_ex, (void*[]) {writeHandler, reserved})) != 0) { ptr += len2; }
while ((len2 = kvm_server_inputdata(buf + ptr, len - ptr, writeHandler, reserved)) != 0) { ptr += len2; }
return ptr;
}
}
// Set the KVM pause state
void kvm_pause(int pause)
{
// KVMDEBUG("kvm_pause", pause);
if (gChildProcess == NULL)
{
g_pause = pause;
}
else
{
if (pause == 0)
{
KVMDEBUG2("RESUME: KVM");
ILibProcessPipe_Pipe_Resume(ILibProcessPipe_Process_GetStdOut(gChildProcess));
}
else
{
KVMDEBUG2("PAUSE: KVM");
ILibProcessPipe_Pipe_Pause(ILibProcessPipe_Process_GetStdOut(gChildProcess));
}
}
}
void kvm_server_SetResolution(ILibKVM_WriteHandler writeHandler, void *reserved)
{
char buffer[8];
int row, col;
KVMDEBUG("kvm_server_SetResolution", 0);
// Free the tileInfo before you manipulate the TILE_HEIGHT_COUNT
if (tileInfo != NULL) { for (row = 0; row < TILE_HEIGHT_COUNT; row++) { free(tileInfo[row]); } free(tileInfo); tileInfo = NULL; }
// Setup scaling
SCALING_FACTOR = SCALING_FACTOR_NEW;
SCALED_WIDTH = (SCREEN_WIDTH * SCALING_FACTOR) / 1024;
SCALED_HEIGHT = (SCREEN_HEIGHT * SCALING_FACTOR) / 1024;
// Compute the tile count
TILE_WIDTH_COUNT = SCALED_WIDTH / TILE_WIDTH;
TILE_HEIGHT_COUNT = SCALED_HEIGHT / TILE_HEIGHT;
if (SCALED_WIDTH % TILE_WIDTH) TILE_WIDTH_COUNT++;
if (SCALED_HEIGHT % TILE_HEIGHT) TILE_HEIGHT_COUNT++;
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_SCREEN); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)8); // Write the size
((unsigned short*)buffer)[2] = (unsigned short)htons((unsigned short)SCALED_WIDTH); // X position
((unsigned short*)buffer)[3] = (unsigned short)htons((unsigned short)SCALED_HEIGHT); // Y position
writeHandler((char*)buffer, 8, reserved);
if ((tileInfo = (struct tileInfo_t **)malloc(TILE_HEIGHT_COUNT * sizeof(struct tileInfo_t *))) == NULL) ILIBCRITICALEXIT(254);
for (row = 0; row < TILE_HEIGHT_COUNT; row++) {
if ((tileInfo[row] = (struct tileInfo_t *)malloc(TILE_WIDTH_COUNT * sizeof(struct tileInfo_t))) == NULL) ILIBCRITICALEXIT(254);
}
for (row = 0; row < TILE_HEIGHT_COUNT; row++) { for (col = 0; col < TILE_WIDTH_COUNT; col++) { tileInfo[row][col].crc = 0xFF; tileInfo[row][col].flags = 0; } }
}
#define BUFSIZE 65535
#ifdef _WINSERVICE
DWORD WINAPI kvm_mainloopinput_ex(LPVOID Param)
{
int ptr = 0;
int ptr2 = 0;
int len = 0;
char pchRequest2[30000];
BOOL fSuccess = FALSE;
DWORD cbBytesRead = 0;
ILibKVM_WriteHandler writeHandler = (ILibKVM_WriteHandler)((void**)Param)[0];
void *reserved = ((void**)Param)[1];
KVMDEBUG("kvm_mainloopinput / start", (int)GetCurrentThreadId());
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: (mainloopinput) Starting...");
while (!g_shutdown)
{
fSuccess = ReadFile(hStdIn, pchRequest2 + len, 30000 - len, &cbBytesRead, NULL);
if (!fSuccess || cbBytesRead == 0 || g_shutdown)
{
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: fSuccess/%d cbBytesRead/%d g_shutdown/%d", fSuccess, cbBytesRead, g_shutdown);
KVMDEBUG("ReadFile() failed", 0); /*ILIBMESSAGE("KVMBREAK-K1\r\n");*/ g_shutdown = 1; break;
}
len += cbBytesRead;
ptr2 = 0;
while ((ptr2 = kvm_server_inputdata((char*)pchRequest2 + ptr, len - ptr, writeHandler, reserved)) != 0) { ptr += ptr2; }
if (ptr == len) { len = 0; ptr = 0; }
// TODO: else move the reminder.
}
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: (mainloopinput) Exiting...");
ILibRemoteLogging_Destroy(gKVMRemoteLogging);
gKVMRemoteLogging = NULL;
KVMDEBUG("kvm_mainloopinput / end", (int)GetCurrentThreadId());
return 0;
}
DWORD WINAPI kvm_mainloopinput(LPVOID Param)
{
DWORD ret = 0;
if (((int*)&(((void**)Param)[3]))[0] == 1)
{
ILib_DumpEnabledContext winException;
__try
{
ret = kvm_mainloopinput_ex(Param);
}
__except (ILib_WindowsExceptionFilterEx(GetExceptionCode(), GetExceptionInformation(), &winException))
{
ILib_WindowsExceptionDebugEx(&winException);
}
}
else
{
ret = kvm_mainloopinput_ex(Param);
}
return(ret);
}
#endif
// This is the main KVM pooling loop. It will look at the display and see if any changes occur. [Runs as daemon if Windows Service]
DWORD WINAPI kvm_server_mainloop_ex(LPVOID parm)
{
//long cur_timestamp = 0;
//long prev_timestamp = 0;
//long time_diff = 50;
long long tilesize;
int width, height = 0;
void *buf, *desktop;
long long desktopsize;
BITMAPINFO bmpInfo;
int row, col;
ILibKVM_WriteHandler writeHandler = (ILibKVM_WriteHandler)((void**)parm)[0];
void *reserved = ((void**)parm)[1];
char *tmoBuffer;
long mouseMove[3] = { 0,0,0 };
int sentHideCursor = 0;
gPendingPackets = ILibQueue_Create();
KVM_InitMouseCursors(gPendingPackets);
#ifdef _WINSERVICE
if (!kvmConsoleMode)
{
gKVMRemoteLogging = ILibRemoteLogging_Create(NULL);
ILibRemoteLogging_SetRawForward(gKVMRemoteLogging, sizeof(KVMDebugLog), kvm_slave_OnRawForwardLog);
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: Child Processing Running...");
}
#endif
// This basic lock will prevent 2 thread from running at the same time. Gives time for the first one to fully exit.
while (ThreadRunning != 0 && height < 200) { height++; Sleep(50); }
if (height >= 200 && ThreadRunning != 0) return 0;
ThreadRunning = 1;
g_shutdown = 0;
g_pause = 0;
g_remotepause = ((int*)&(((void**)parm)[2]))[0];
KVMDEBUG("kvm_server_mainloop / start1", (int)GetCurrentThreadId());
#ifdef _WINSERVICE
if (!kvmConsoleMode)
{
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
hStdIn = GetStdHandle(STD_INPUT_HANDLE);
}
#endif
KVMDEBUG("kvm_server_mainloop / start2", (int)GetCurrentThreadId());
if (!initialize_gdiplus())
{
#ifdef _WINSERVICE
if (!kvmConsoleMode)
{
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: initialize_gdiplus() failed");
}
#endif
KVMDEBUG("kvm_server_mainloop / initialize_gdiplus failed", (int)GetCurrentThreadId()); return 0;
}
#ifdef _WINSERVICE
if (!kvmConsoleMode)
{
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: initialize_gdiplus() SUCCESS");
}
#endif
kvm_server_SetResolution(writeHandler, reserved);
#ifdef _WINSERVICE
if (!kvmConsoleMode)
{
g_shutdown = 0;
kvmthread = CreateThread(NULL, 0, kvm_mainloopinput, parm, 0, 0);
CloseHandle(kvmthread);
}
#endif
// Set all CRCs to 0xFF
for (row = 0; row < TILE_HEIGHT_COUNT; row++) {
for (col = 0; col < TILE_WIDTH_COUNT; col++) {
tileInfo[row][col].crc = 0xFF;
}
}
// Send the list of displays
kvm_send_display_list(writeHandler, reserved);
Sleep(100); // Pausing here seems to fix connection issues, especially with WebRTC. TODO: Investigate why.
KVMDEBUG("kvm_server_mainloop / start3", (int)GetCurrentThreadId());
// Loop and send only when a tile changes.
while (!g_shutdown)
{
KVMDEBUG("kvm_server_mainloop / loop1", (int)GetCurrentThreadId());
// Reset all the flags to TILE_TODO
for (row = 0; row < TILE_HEIGHT_COUNT; row++)
{
for (col = 0; col < TILE_WIDTH_COUNT; col++)
{
tileInfo[row][col].flags = (char)TILE_TODO;
#ifdef KVM_ALL_TILES
tileInfo[row][col].crc = 0xFF;
#endif
}
}
CheckDesktopSwitch(1, writeHandler, reserved);
if (g_shutdown) break;
// Enter Alertable State, so we can dispatch any packets if necessary.
// We are doing it here, in case we need to merge any data with the bitmaps
SleepEx(0, TRUE);
mouseMove[0] = 0;
while ((tmoBuffer = ILibQueue_DeQueue(gPendingPackets)) != NULL)
{
if (ntohs(((unsigned short*)tmoBuffer)[0]) == MNG_KVM_MOUSE_MOVE)
{
if (SCREEN_SEL_TARGET == 0)
{
mouseMove[0] = 1;
mouseMove[1] = ((long*)tmoBuffer)[1] - VSCREEN_X;
mouseMove[2] = ((long*)tmoBuffer)[2] - VSCREEN_Y;
}
else
{
if (((long*)tmoBuffer)[1] >= SCREEN_X && ((long*)tmoBuffer)[1] <= (SCREEN_X + SCREEN_WIDTH) &&
((long*)tmoBuffer)[2] >= SCREEN_Y && ((long*)tmoBuffer)[2] <= (SCREEN_Y + SCREEN_HEIGHT))
{
mouseMove[0] = 1;
mouseMove[1] = ((long*)tmoBuffer)[1] - SCREEN_X;
mouseMove[2] = ((long*)tmoBuffer)[2] - SCREEN_Y;
}
}
}
else
{
if (ntohs(((unsigned short*)tmoBuffer)[0]) != MNG_KVM_MOUSE_CURSOR || sentHideCursor==0)
{
writeHandler(tmoBuffer, (int)ILibMemory_Size(tmoBuffer), reserved);
}
}
ILibMemory_Free(tmoBuffer);
}
if (mouseMove[0] == 0 && (gRemoteMouseRenderDefault != 0 || gRemoteMouseMoved == 0))
{
mouseMove[0] = 1;
CURSORINFO info = { 0 };
info.cbSize = sizeof(info);
GetCursorInfo(&info);
if (SCREEN_SEL_TARGET == 0)
{
mouseMove[1] = info.ptScreenPos.x - VSCREEN_X;
mouseMove[2] = info.ptScreenPos.y - VSCREEN_Y;
}
else
{
mouseMove[1] = info.ptScreenPos.x - SCREEN_X;
mouseMove[2] = info.ptScreenPos.y - SCREEN_Y;
}
}
if (mouseMove[0] != 0)
{
if (sentHideCursor == 0)
{
sentHideCursor = 1;
char tmpBuffer[5];
((unsigned short*)tmpBuffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_MOUSE_CURSOR); // Write the type
((unsigned short*)tmpBuffer)[1] = (unsigned short)htons((unsigned short)5); // Write the size
tmpBuffer[4] = (char)KVM_MouseCursor_NONE; // Cursor Type
writeHandler(tmpBuffer, 5, reserved);
}
}
else
{
if (sentHideCursor != 0)
{
sentHideCursor = 0;
char tmpBuffer[5];
((unsigned short*)tmpBuffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_MOUSE_CURSOR); // Write the type
((unsigned short*)tmpBuffer)[1] = (unsigned short)htons((unsigned short)5); // Write the size
tmpBuffer[4] = (char)gCurrentCursor; // Cursor Type
writeHandler(tmpBuffer, 5, reserved);
}
}
// Scan the desktop
if (get_desktop_buffer(&desktop, &desktopsize, mouseMove) == 1 || desktop == NULL)
{
#ifdef _WINSERVICE
if (!kvmConsoleMode)
{
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: get_desktop_buffer() failed");
}
#endif
KVMDEBUG("get_desktop_buffer() failed, shutting down", (int)GetCurrentThreadId());
g_shutdown = 1;
}
else
{
bmpInfo = get_bmp_info(TILE_WIDTH, TILE_HEIGHT);
for (row = 0; row < TILE_HEIGHT_COUNT; row++) {
for (col = 0; col < TILE_WIDTH_COUNT; col++) {
height = TILE_HEIGHT * row;
width = TILE_WIDTH * col;
while (!g_shutdown && (g_pause)) { Sleep(50); /*printf(".");*/ } // If the socket is in pause state, wait here. //ToDo: YLIAN!!!!
if (g_shutdown || SCALING_FACTOR != SCALING_FACTOR_NEW) { height = SCALED_HEIGHT; width = SCALED_WIDTH; break; }
// Skip the tile if it has already been sent or if the CRC is same as before
if (tileInfo[row][col].flags == (char)TILE_SENT || tileInfo[row][col].flags == (char)TILE_DONT_SEND) { continue; }
if (get_tile_at(width, height, &buf, &tilesize, desktop, row, col) == 1)
{
// GetTileAt failed, lets not send the tile
continue;
}
if (buf && !g_shutdown)
{
KVMDEBUG2("Writing JPEG: %llu bytes", tilesize);
switch (writeHandler((char*)buf, (int)tilesize, reserved))
{
case ILibTransport_DoneState_INCOMPLETE:
g_pause = 1;
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_2, "Agent KVM: KVM PAUSE");
KVMDEBUG2("JPEG WRITE resulted in PAUSE");
break;
case ILibTransport_DoneState_ERROR:
g_shutdown = 1;
height = SCALED_HEIGHT;
width = SCALED_WIDTH;
KVMDEBUG2("JPEG WRITE resulted in ERROR");
break;
}
free(buf);
}
}
}
KVMDEBUG("kvm_server_mainloop / loop2", (int)GetCurrentThreadId());
if (desktop) free(desktop);
desktop = NULL;
desktopsize = 0;
}
KVMDEBUG("kvm_server_mainloop / loop3", (int)GetCurrentThreadId());
// We can't go full speed here, we need to slow this down.
height = FRAME_RATE_TIMER;
while (!g_shutdown && height > 0) { if (height > 50) { height -= 50; Sleep(50); } else { Sleep(height); height = 0; } SleepEx(0, TRUE); }
}
KVMDEBUG("kvm_server_mainloop / end3", (int)GetCurrentThreadId());
KVMDEBUG("kvm_server_mainloop / end2", (int)GetCurrentThreadId());
if (tileInfo != NULL) {
for (row = 0; row < TILE_HEIGHT_COUNT; row++) free(tileInfo[row]);
free(tileInfo);
tileInfo = NULL;
}
KVMDEBUG("kvm_server_mainloop / end1", (int)GetCurrentThreadId());
teardown_gdiplus();
KVMDEBUG("kvm_server_mainloop / end", (int)GetCurrentThreadId());
KVM_UnInitMouseCursors();
while ((tmoBuffer = ILibQueue_DeQueue(gPendingPackets)) != NULL)
{
ILibMemory_Free(tmoBuffer);
}
ILibQueue_Destroy(gPendingPackets);
ILibRemoteLogging_printf(gKVMRemoteLogging, ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [SLAVE]: Process Exiting...");
ThreadRunning = 0;
free(parm);
return 0;
}
DWORD WINAPI kvm_server_mainloop(LPVOID parm)
{
DWORD ret = 0;
if (((int*)&(((void**)parm)[3]))[0] == 1)
{
// Enable Core Dump in KVM Child
ILib_DumpEnabledContext winException;
WCHAR str[_MAX_PATH];
DWORD strLen;
if ((strLen = GetModuleFileNameW(NULL, str, _MAX_PATH)) > 5)
{
str[strLen - 4] = 0; // We're going to convert .exe to _kvm.dmp
g_ILibCrashDump_path = ILibMemory_Allocate((strLen * 2) + 10, 0, NULL, NULL); // Add enough space to add '.dmp' to the end of the path
swprintf_s((wchar_t*)g_ILibCrashDump_path, strLen + 5, L"%s_kvm.dmp", str);
ILibCriticalLogFilename = "KVMSlave.log";
}
__try
{
ret = kvm_server_mainloop_ex(parm);
}
__except (ILib_WindowsExceptionFilterEx(GetExceptionCode(), GetExceptionInformation(), &winException))
{
ILib_WindowsExceptionDebugEx(&winException);
}
}
else
{
// Core Dump not enabled in KVM Child
ret = kvm_server_mainloop_ex(parm);
}
return(ret);
}
#ifdef _WINSERVICE
void kvm_relay_ExitHandler(ILibProcessPipe_Process sender, int exitCode, void* user)
{
ILibKVM_WriteHandler writeHandler = (ILibKVM_WriteHandler)((void**)user)[0];
void *reserved = ((void**)user)[1];
void *pipeMgr = ((void**)user)[2];
char *exePath = (char*)((void**)user)[3];
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "Agent KVM: KVM Child Process(%d) [EXITED]", g_slavekvm);
UNREFERENCED_PARAMETER(exitCode);
UNREFERENCED_PARAMETER(sender);
if (g_restartcount < 4 && g_shutdown == 0)
{
kvm_relay_restart(1, pipeMgr, exePath, writeHandler, reserved);
}
else
{
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "Agent KVM: g_restartcount = %d, aborting", g_restartcount);
writeHandler(NULL, 0, reserved);
}
}
void kvm_relay_StdOutHandler(ILibProcessPipe_Process sender, char *buffer, size_t bufferLen, size_t* bytesConsumed, void* user)
{
unsigned short size = 0;
UNREFERENCED_PARAMETER(sender);
ILibKVM_WriteHandler writeHandler = (ILibKVM_WriteHandler)((void**)user)[0];
void *reserved = ((void**)user)[1];
if (bufferLen > 4)
{
if (ntohs(((unsigned short*)(buffer))[0]) > 1000)
{
KVMDEBUG2("Invalid KVM Command received: %u", ntohs(((unsigned short*)(buffer))[0]));
}
if (ntohs(((unsigned short*)(buffer))[0]) == (unsigned short)MNG_JUMBO)
{
if (bufferLen > 8)
{
if (bufferLen >= (size_t)(8 + (int)ntohl(((unsigned int*)(buffer))[1])))
{
*bytesConsumed = 8 + (int)ntohl(((unsigned int*)(buffer))[1]);
KVMDEBUG2("Jumbo Packet received of size: %llu (bufferLen=%llu)", *bytesConsumed, bufferLen);
writeHandler(buffer, (int)*bytesConsumed, reserved);
return;
}
}
KVMDEBUG2("Accumulate => JUMBO: [%llu bytes] bufferLen=%llu ", (size_t)(8 + (int)ntohl(((unsigned int*)(buffer))[1])), bufferLen);
}
else
{
size = ntohs(((unsigned short*)(buffer))[1]);
if (size <= bufferLen)
{
KVMDEBUG2("KVM Command: [%u: %llu bytes]", ntohs(((unsigned short*)(buffer))[0]), size);
*bytesConsumed = size;
writeHandler(buffer, size, reserved);
return;
}
KVMDEBUG2("Accumulate => KVM Command: [%u: %d bytes] bufferLen=%llu ", ntohs(((unsigned short*)(buffer))[0]), bufferLen);
}
}
*bytesConsumed = 0;
}
void kvm_relay_StdErrHandler(ILibProcessPipe_Process sender, char *buffer, size_t bufferLen, size_t* bytesConsumed, void* user)
{
KVMDebugLog *log = (KVMDebugLog*)buffer;
UNREFERENCED_PARAMETER(sender);
UNREFERENCED_PARAMETER(user);
if (bufferLen < sizeof(KVMDebugLog) || bufferLen < log->length) { *bytesConsumed = 0; return; }
*bytesConsumed = log->length;
//ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), (ILibRemoteLogging_Modules)log->logType, (ILibRemoteLogging_Flags)log->logFlags, "%s", log->logData);
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, (ILibRemoteLogging_Flags)log->logFlags, "%s", log->logData);
}
int kvm_relay_restart(int paused, void *pipeMgr, char *exePath, ILibKVM_WriteHandler writeHandler, void *reserved)
{
char * parms0[] = { " -kvm0", g_ILibCrashDump_path != NULL ? "-coredump" : NULL, NULL, NULL };
char * parms1[] = { " -kvm1", g_ILibCrashDump_path != NULL ? "-coredump" : NULL, NULL, NULL };
void **user = (void**)ILibMemory_Allocate(4 * sizeof(void*), 0, NULL, NULL);
if (parms0[1] == NULL)
{
parms0[1] = (gRemoteMouseRenderDefault != 0 ? "-remotecursor" : NULL);
parms1[1] = (gRemoteMouseRenderDefault != 0 ? "-remotecursor" : NULL);
}
else
{
parms0[2] = (gRemoteMouseRenderDefault != 0 ? "-remotecursor" : NULL);
parms1[2] = (gRemoteMouseRenderDefault != 0 ? "-remotecursor" : NULL);
}
user[0] = writeHandler;
user[1] = reserved;
user[2] = pipeMgr;
user[3] = exePath;
KVMDEBUG("kvm_relay_restart / start", paused);
// If we are re-launching the child process, wait a bit. The computer may be switching desktop, etc.
if (paused == 0) Sleep(500);
if (gProcessSpawnType == ILibProcessPipe_SpawnTypes_SPECIFIED_USER && gProcessTSID < 0) { gProcessSpawnType = ILibProcessPipe_SpawnTypes_USER; }
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM [Master]: Spawning Slave as %s", gProcessSpawnType == ILibProcessPipe_SpawnTypes_USER ? "USER":"WIN_LOGON");
gChildProcess = ILibProcessPipe_Manager_SpawnProcessEx3(pipeMgr, exePath, paused == 0 ? parms0 : parms1, gProcessSpawnType, (void*)(ULONG_PTR)gProcessTSID, 0);
gProcessSpawnType = (gProcessSpawnType == ILibProcessPipe_SpawnTypes_SPECIFIED_USER || gProcessSpawnType == ILibProcessPipe_SpawnTypes_USER) ? ILibProcessPipe_SpawnTypes_WINLOGON : (gProcessTSID < 0 ? ILibProcessPipe_SpawnTypes_USER : ILibProcessPipe_SpawnTypes_SPECIFIED_USER);
g_slavekvm = ILibProcessPipe_Process_GetPID(gChildProcess);
char tmp[255];
sprintf_s(tmp, sizeof(tmp), "Child KVM (pid: %d)", g_slavekvm);
ILibProcessPipe_Process_ResetMetadata(gChildProcess, tmp);
ILibProcessPipe_Process_AddHandlers(gChildProcess, 65535, &kvm_relay_ExitHandler, &kvm_relay_StdOutHandler, &kvm_relay_StdErrHandler, NULL, user);
KVMDEBUG("kvm_relay_restart() launched child process", g_slavekvm);
// Run the relay
g_shutdown = 0;
KVMDEBUG("kvm_relay_restart / end", (int)(uintptr_t)kvmthread);
return 1;
}
#endif
// Setup the KVM session. Return 1 if ok, 0 if it could not be setup.
int kvm_relay_setup(char *exePath, void *processPipeMgr, ILibKVM_WriteHandler writeHandler, void *reserved, int tsid)
{
if (processPipeMgr != NULL)
{
#ifdef _WINSERVICE
if (ThreadRunning == 1 && g_shutdown == 0) { KVMDEBUG("kvm_relay_setup() session already exists", 0); return 0; }
g_restartcount = 0;
gProcessSpawnType = ILibProcessPipe_SpawnTypes_SPECIFIED_USER;
gProcessTSID = tsid;
KVMDEBUG("kvm_relay_setup() session starting", 0);
return kvm_relay_restart(1, processPipeMgr, exePath, writeHandler, reserved);
#else
return(0);
#endif
}
else
{
// if (kvmthread != NULL && g_shutdown == 0) return 0;
void **parms = (void**)ILibMemory_Allocate((2 * sizeof(void*)) + sizeof(int), 0, NULL, NULL);
parms[0] = writeHandler;
parms[1] = reserved;
((int*)(&parms[2]))[0] = 1;
kvmConsoleMode = 1;
if (ThreadRunning == 1 && g_shutdown == 0) { KVMDEBUG("kvm_relay_setup() session already exists", 0); free(parms); return 0; }
kvmthread = CreateThread(NULL, 0, kvm_server_mainloop, (void*)parms, 0, 0);
if (kvmthread != 0) { CloseHandle(kvmthread); }
return 1;
}
}
// Force a KVM reset & refresh
void kvm_relay_reset(ILibKVM_WriteHandler writeHandler, void *reserved)
{
char buffer[4];
KVMDEBUG("kvm_relay_reset", 0);
((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_REFRESH); // Write the type
((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)4); // Write the size
kvm_relay_feeddata(buffer, 4, writeHandler, reserved);
}
// Clean up the KVM session.
void kvm_cleanup()
{
//ILIBMESSAGE("KVMBREAK-CLEAN\r\n");
KVMDEBUG("kvm_cleanup", 0);
g_shutdown = 1;
if (gChildProcess != NULL)
{
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM.c/kvm_cleanup: Attempting to kill child process");
ILibProcessPipe_Process_SoftKill(gChildProcess);
gChildProcess = NULL;
}
else
{
ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Agent_KVM, ILibRemoteLogging_Flags_VerbosityLevel_1, "KVM.c/kvm_cleanup: gChildProcess = NULL");
}
}
////
//// Desktop Duplication API KVM
////
//#include <d3d11.h>
//#include <dxgi1_2.h>
//
//typedef struct D3D11_Functions
//{
// HRESULT(*D3D11CreateDevice)(
// IDXGIAdapter *pAdapter,
// D3D_DRIVER_TYPE DriverType,
// HMODULE Software,
// UINT Flags,
// const D3D_FEATURE_LEVEL *pFeatureLevels,
// UINT FeatureLevels,
// UINT SDKVersion,
// ID3D11Device **ppDevice,
// D3D_FEATURE_LEVEL *pFeatureLevel,
// ID3D11DeviceContext **ppImmediateContext
// );
//}D3D11_Functions;
//void DD_Init()
//{
//int i;
//HRESULT hr;
//ID3D11Device* m_Device;
//ID3D11DeviceContext* m_DeviceContext;
//IDXGIFactory2* m_Factory;
//DWORD m_OcclusionCookie;
//DXGI_OUTDUPL_DESC lOutputDuplDesc;
//ID3D11Texture2D *lGDIImage;
//ID3D11Texture2D *desktopImage;
//ID3D11Texture2D *destinationImage;
//DXGI_OUTDUPL_FRAME_INFO lFrameInfo;
//IDXGIResource *lDesktopResource;
//D3D11_Functions funcs;
//HMODULE _D3D = NULL;
//if ((_D3D = LoadLibraryExA((LPCSTR)"D3D11.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)) != NULL)
//{
// (FARPROC)funcs.D3D11CreateDevice = GetProcAddress(_D3D, "D3D11CreateDevice");
//}
//D3D_DRIVER_TYPE DriverTypes[] =
//{
// D3D_DRIVER_TYPE_HARDWARE,
// D3D_DRIVER_TYPE_WARP,
// D3D_DRIVER_TYPE_REFERENCE,
//};
//UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
//// Feature levels supported
//D3D_FEATURE_LEVEL FeatureLevels[] =
//{
// D3D_FEATURE_LEVEL_11_0,
// D3D_FEATURE_LEVEL_10_1,
// D3D_FEATURE_LEVEL_10_0,
// D3D_FEATURE_LEVEL_9_1
//};
//UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
//D3D_FEATURE_LEVEL FeatureLevel;
//// Create device
//for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
//{
// hr = funcs.D3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &m_Device, &FeatureLevel, &m_DeviceContext);
// if (SUCCEEDED(hr))
// {
// // Device creation succeeded, no need to loop anymore
// break;
// }
//}
//if (FAILED(hr))
//{
// DebugBreak();
//}
//// Get DXGI factory
//IDXGIDevice* DxgiDevice = NULL;
//hr = m_Device->lpVtbl->QueryInterface(m_Device, &IID_IDXGIDevice, (void**)&DxgiDevice);
//if (FAILED(hr))
//{
// DebugBreak();
//}
//IDXGIAdapter* DxgiAdapter = NULL;
//hr = DxgiDevice->lpVtbl->GetParent(DxgiDevice, &IID_IDXGIAdapter, (void**)&DxgiAdapter);
//DxgiDevice->lpVtbl->Release(DxgiDevice);
//DxgiDevice = NULL;
//if (FAILED(hr))
//{
// DebugBreak();
//}
//hr = DxgiAdapter->lpVtbl->GetParent(DxgiAdapter, &IID_IDXGIFactory2, (void**)&m_Factory);
//DxgiAdapter->lpVtbl->Release(DxgiAdapter);
//DxgiAdapter = NULL;
//if (FAILED(hr))
//{
// DebugBreak();
// //return ProcessFailure(m_Device, L"Failed to get parent DXGI Factory", L"Error", hr, SystemTransitionsExpectedErrors);
//}
//IDXGIOutput1 *DxgiOutput1;
//hr = m_Device->lpVtbl->QueryInterface(m_Device, &IID_IDXGIOutput, (void**)&DxgiOutput1);
//if (FAILED(hr))
//{
// DebugBreak();
//}
//IDXGIOutputDuplication *dupl = NULL;
//DxgiOutput1->lpVtbl->DuplicateOutput(DxgiOutput1, m_Device, &dupl);
//// Create GUI drawing texture
//dupl->lpVtbl->GetDesc(dupl, &lOutputDuplDesc);
//D3D11_TEXTURE2D_DESC desc;
//desc.Width = lOutputDuplDesc.ModeDesc.Width;
//desc.Height = lOutputDuplDesc.ModeDesc.Height;
//desc.Format = lOutputDuplDesc.ModeDesc.Format;
//desc.ArraySize = 1;
//desc.BindFlags = D3D11_BIND_RENDER_TARGET;
//desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
//desc.SampleDesc.Count = 1;
//desc.SampleDesc.Quality = 0;
//desc.MipLevels = 1;
//desc.CPUAccessFlags = 0;
//desc.Usage = D3D11_USAGE_DEFAULT;
//hr = m_Device->lpVtbl->CreateTexture2D(m_Device, &desc, NULL, &lGDIImage);
//hr = m_Device->lpVtbl->CreateTexture2D(m_Device, &desc, NULL, &destinationImage);
//if (FAILED(hr))
//{
// DebugBreak();
//}
//// Get new frame
//for (i = 0; i < 5; ++i)
//{
// hr = dupl->lpVtbl->AcquireNextFrame(dupl, 250, &lFrameInfo, &lDesktopResource);
// if (hr != DXGI_ERROR_WAIT_TIMEOUT) { break; }
// Sleep(100);
//}
//
//hr = lDesktopResource->lpVtbl->QueryInterface(lDesktopResource, &IID_ID3D11Texture2D, &desktopImage);
//// Copy image into GDI drawing texture
//m_DeviceContext->lpVtbl->CopyResource(m_DeviceContext, lGDIImage, desktopImage);
//// Draw cursor image into GDI drawing texture
//IDXGISurface1 *surface;
//hr = lGDIImage->lpVtbl->QueryInterface(lGDIImage, &IID_IDXGISurface1, &surface);
//// Copy from CPU access texture to bitmap buffer
//D3D11_MAPPED_SUBRESOURCE resource;
//UINT subresource = D3D11CalcSubresource(0, 0, 0);
//m_DeviceContext->lpVtbl->Map(m_DeviceContext, destinationImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
//BITMAPINFO lBmpInfo;
//// BMP 32 bpp
//ZeroMemory(&lBmpInfo, sizeof(BITMAPINFO));
//lBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
//lBmpInfo.bmiHeader.biBitCount = 32;
//lBmpInfo.bmiHeader.biCompression = BI_RGB;
//lBmpInfo.bmiHeader.biWidth = lOutputDuplDesc.ModeDesc.Width;
//lBmpInfo.bmiHeader.biHeight = lOutputDuplDesc.ModeDesc.Height;
//lBmpInfo.bmiHeader.biPlanes = 1;
//lBmpInfo.bmiHeader.biSizeImage = lOutputDuplDesc.ModeDesc.Width * lOutputDuplDesc.ModeDesc.Height * 4;
//BYTE* pBuf = (BYTE*)ILibMemory_SmartAllocate(lBmpInfo.bmiHeader.biSizeImage);
//UINT lBmpRowPitch = lOutputDuplDesc.ModeDesc.Width * 4;
//BYTE* sptr = (BYTE*)resource.pData;
//BYTE* dptr = pBuf + lBmpInfo.bmiHeader.biSizeImage - lBmpRowPitch;
//UINT lRowPitch = min(lBmpRowPitch, resource.RowPitch);
//size_t h;
//for (h = 0; h < lOutputDuplDesc.ModeDesc.Height; ++h)
//{
// memcpy_s(dptr, lBmpRowPitch, sptr, lRowPitch);
// sptr += resource.RowPitch;
// dptr -= lBmpRowPitch;
//}
//}
#endif