FreeRDP/channels/rdp2tcp/client/rdp2tcp_main.c

345 lines
8.5 KiB
C
Raw Normal View History

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* rdp2tcp Virtual Channel Extension
*
* Copyright 2017 Artur Zaprzala
*
* 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.
*/
#include <stdio.h>
#include <assert.h>
#include <winpr/file.h>
#include <winpr/pipe.h>
#include <winpr/thread.h>
#include <freerdp/svc.h>
#define RDP2TCP_CHAN_NAME "rdp2tcp"
2019-06-25 11:52:22 +03:00
#include <freerdp/log.h>
#define TAG CLIENT_TAG(RDP2TCP_CHAN_NAME)
static int const debug = 0;
2019-05-15 20:12:33 +03:00
typedef struct
{
HANDLE hStdOutputRead;
HANDLE hStdInputWrite;
HANDLE hProcess;
HANDLE copyThread;
HANDLE writeComplete;
DWORD openHandle;
2019-05-15 20:12:33 +03:00
void* initHandle;
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
2019-05-15 20:12:33 +03:00
char buffer[16 * 1024];
2020-11-18 09:51:45 +03:00
char* commandline;
} Plugin;
2019-05-15 20:12:33 +03:00
static int init_external_addin(Plugin* plugin)
{
2019-05-15 22:33:35 +03:00
SECURITY_ATTRIBUTES saAttr;
STARTUPINFO siStartInfo;
PROCESS_INFORMATION procInfo;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartInfo.dwFlags = STARTF_USESTDHANDLES;
// Create pipes
2019-05-15 20:12:33 +03:00
if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
{
2019-06-25 11:52:22 +03:00
WLog_ERR(TAG, "stdout CreatePipe");
return -1;
}
2019-05-15 20:12:33 +03:00
if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
{
2019-06-25 11:52:22 +03:00
WLog_ERR(TAG, "stdout SetHandleInformation");
return -1;
}
2019-05-15 20:12:33 +03:00
if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
{
2019-06-25 11:52:22 +03:00
WLog_ERR(TAG, "stdin CreatePipe");
return -1;
}
2019-05-15 20:12:33 +03:00
if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
{
2019-06-25 11:52:22 +03:00
WLog_ERR(TAG, "stdin SetHandleInformation");
return -1;
}
// Execute plugin
2020-11-18 09:51:45 +03:00
plugin->commandline = _strdup(plugin->channelEntryPoints.pExtendedData);
if (!CreateProcessA(NULL,
plugin->commandline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&procInfo // receives PROCESS_INFORMATION
))
2019-05-15 20:12:33 +03:00
{
2019-06-25 11:52:22 +03:00
WLog_ERR(TAG, "fork for addin");
return -1;
}
plugin->hProcess = procInfo.hProcess;
CloseHandle(procInfo.hThread);
CloseHandle(siStartInfo.hStdOutput);
CloseHandle(siStartInfo.hStdInput);
return 0;
}
2019-05-15 20:12:33 +03:00
static void dumpData(char* data, unsigned length)
{
unsigned const limit = 98;
2019-05-15 20:12:33 +03:00
unsigned l = length > limit ? limit / 2 : length;
unsigned i;
for (i = 0; i < l; ++i)
{
printf("%02hhx", data[i]);
}
2019-05-15 20:12:33 +03:00
if (length > limit)
{
printf("...");
2019-05-15 20:12:33 +03:00
for (i = length - l; i < length; ++i)
printf("%02hhx", data[i]);
}
2019-05-15 20:12:33 +03:00
puts("");
}
2019-05-15 22:33:35 +03:00
static DWORD WINAPI copyThread(void* data)
2019-05-15 20:12:33 +03:00
{
Plugin* plugin = (Plugin*)data;
size_t const bufsize = 16 * 1024;
while (1)
{
DWORD dwRead;
2019-05-15 20:12:33 +03:00
char* buffer = malloc(bufsize);
if (!buffer)
{
fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
goto fail;
}
2019-05-15 20:12:33 +03:00
2019-11-06 17:24:51 +03:00
// if (!ReadFile(plugin->hStdOutputRead, plugin->buffer, sizeof plugin->buffer, &dwRead,
// NULL))
if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, NULL))
{
free(buffer);
goto fail;
}
2019-05-15 20:12:33 +03:00
if (debug > 1)
{
printf(">%8u ", (unsigned)dwRead);
dumpData(buffer, dwRead);
}
2019-05-15 20:12:33 +03:00
2019-11-06 17:24:51 +03:00
if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
2019-05-15 20:12:33 +03:00
{
free(buffer);
fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
goto fail;
}
2019-05-15 20:12:33 +03:00
WaitForSingleObject(plugin->writeComplete, INFINITE);
ResetEvent(plugin->writeComplete);
}
2019-05-15 20:12:33 +03:00
fail:
ExitThread(0);
return 0;
}
2019-05-15 20:12:33 +03:00
static void closeChannel(Plugin* plugin)
{
if (debug)
puts("rdp2tcp closing channel");
2019-05-15 20:12:33 +03:00
plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
}
2019-05-15 20:12:33 +03:00
static void dataReceived(Plugin* plugin, void* pData, UINT32 dataLength, UINT32 totalLength,
UINT32 dataFlags)
{
DWORD dwWritten;
2019-05-15 20:12:33 +03:00
if (dataFlags & CHANNEL_FLAG_SUSPEND)
{
if (debug)
puts("rdp2tcp Channel Suspend");
2019-05-15 20:12:33 +03:00
return;
}
2019-05-15 20:12:33 +03:00
if (dataFlags & CHANNEL_FLAG_RESUME)
{
if (debug)
puts("rdp2tcp Channel Resume");
2019-05-15 20:12:33 +03:00
return;
}
2019-05-15 20:12:33 +03:00
if (debug > 1)
{
printf("<%c%3u/%3u ", dataFlags & CHANNEL_FLAG_FIRST ? ' ' : '+', totalLength, dataLength);
dumpData(pData, dataLength);
}
2019-05-15 20:12:33 +03:00
if (dataFlags & CHANNEL_FLAG_FIRST)
{
if (!WriteFile(plugin->hStdInputWrite, &totalLength, sizeof(totalLength), &dwWritten, NULL))
closeChannel(plugin);
}
2019-05-15 20:12:33 +03:00
if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, NULL))
closeChannel(plugin);
}
2019-05-15 20:12:33 +03:00
static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam, DWORD openHandle, UINT event,
2019-11-06 17:24:51 +03:00
LPVOID pData, UINT32 dataLength, UINT32 totalLength,
UINT32 dataFlags)
2019-05-15 20:12:33 +03:00
{
Plugin* plugin = (Plugin*)lpUserParam;
switch (event)
{
case CHANNEL_EVENT_DATA_RECEIVED:
dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
break;
2019-05-15 20:12:33 +03:00
case CHANNEL_EVENT_WRITE_CANCELLED:
free(pData);
break;
case CHANNEL_EVENT_WRITE_COMPLETE:
SetEvent(plugin->writeComplete);
free(pData);
break;
}
}
2020-11-18 09:51:45 +03:00
static void channel_terminated(Plugin* plugin)
{
if (debug)
puts("rdp2tcp terminated");
if (!plugin)
return;
if (plugin->copyThread)
TerminateThread(plugin->copyThread, 0);
if (plugin->writeComplete)
CloseHandle(plugin->writeComplete);
CloseHandle(plugin->hStdInputWrite);
CloseHandle(plugin->hStdOutputRead);
TerminateProcess(plugin->hProcess, 0);
CloseHandle(plugin->hProcess);
free(plugin->commandline);
free(plugin);
}
static void channel_initialized(Plugin* plugin)
{
plugin->writeComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
plugin->copyThread = CreateThread(NULL, 0, copyThread, plugin, 0, NULL);
}
2019-05-15 20:12:33 +03:00
static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
2019-11-06 17:24:51 +03:00
LPVOID pData, UINT dataLength)
2019-05-15 20:12:33 +03:00
{
Plugin* plugin = (Plugin*)lpUserParam;
switch (event)
{
2020-11-18 09:51:45 +03:00
case CHANNEL_EVENT_INITIALIZED:
channel_initialized(plugin);
break;
case CHANNEL_EVENT_CONNECTED:
if (debug)
puts("rdp2tcp connected");
2019-05-15 20:12:33 +03:00
2019-11-06 17:24:51 +03:00
if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
pInitHandle, &plugin->openHandle, RDP2TCP_CHAN_NAME,
VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
return;
2019-05-15 20:12:33 +03:00
break;
2019-05-15 20:12:33 +03:00
case CHANNEL_EVENT_DISCONNECTED:
if (debug)
puts("rdp2tcp disconnected");
2019-05-15 20:12:33 +03:00
break;
2019-05-15 20:12:33 +03:00
case CHANNEL_EVENT_TERMINATED:
2020-11-18 09:51:45 +03:00
channel_terminated(plugin);
break;
}
}
#if 1
#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
#else
#define VirtualChannelEntryEx FREERDP_API VirtualChannelEntryEx
#endif
2019-05-15 20:12:33 +03:00
BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
{
2019-05-15 22:33:35 +03:00
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
CHANNEL_DEF channelDef;
2019-05-15 20:12:33 +03:00
Plugin* plugin = (Plugin*)calloc(1, sizeof(Plugin));
if (!plugin)
return FALSE;
2019-05-15 22:33:35 +03:00
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
2019-05-15 20:12:33 +03:00
assert(pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX) &&
pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
plugin->initHandle = pInitHandle;
plugin->channelEntryPoints = *pEntryPointsEx;
2019-05-15 20:12:33 +03:00
if (init_external_addin(plugin) < 0)
return FALSE;
2019-10-29 12:23:48 +03:00
strncpy(channelDef.name, RDP2TCP_CHAN_NAME, sizeof(channelDef.name));
2019-05-15 22:33:35 +03:00
channelDef.options =
2019-11-06 17:24:51 +03:00
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
2019-05-15 20:12:33 +03:00
if (pEntryPointsEx->pVirtualChannelInitEx(plugin, NULL, pInitHandle, &channelDef, 1,
2019-11-06 17:24:51 +03:00
VIRTUAL_CHANNEL_VERSION_WIN2000,
VirtualChannelInitEventEx) != CHANNEL_RC_OK)
return FALSE;
2019-05-15 20:12:33 +03:00
return TRUE;
}
// vim:ts=4