21#include <winpr/assert.h>
23#include <winpr/file.h>
24#include <winpr/pipe.h>
25#include <winpr/thread.h>
27#include <freerdp/freerdp.h>
28#include <freerdp/svc.h>
29#include <freerdp/channels/rdp2tcp.h>
31#include <freerdp/utils/warnings.h>
33#include <freerdp/log.h>
34#define TAG CLIENT_TAG(RDP2TCP_DVC_CHANNEL_NAME)
38 HANDLE hStdOutputRead;
39 HANDLE hStdInputWrite;
46 char buffer[16 * 1024];
50static int init_external_addin(Plugin* plugin)
60 saAttr.bInheritHandle = TRUE;
61 saAttr.lpSecurityDescriptor =
nullptr;
63 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
64 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
67 if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
69 WLog_ERR(TAG,
"stdout CreatePipe");
73 if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
75 WLog_ERR(TAG,
"stdout SetHandleInformation");
79 if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
81 WLog_ERR(TAG,
"stdin CreatePipe");
85 if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
87 WLog_ERR(TAG,
"stdin SetHandleInformation");
93 if (!args || (args->argc < 2))
95 WLog_ERR(TAG,
"missing command line options");
99 plugin->commandline = _strdup(args->argv[1]);
100 if (!CreateProcessA(
nullptr,
112 WLog_ERR(TAG,
"fork for addin");
116 plugin->hProcess = procInfo.hProcess;
120 (void)CloseHandle(procInfo.hThread);
121 (void)CloseHandle(siStartInfo.hStdOutput);
122 (void)CloseHandle(siStartInfo.hStdInput);
126static DWORD WINAPI copyThread(
void* data)
128 DWORD status = WAIT_OBJECT_0;
129 Plugin* plugin = (Plugin*)data;
130 size_t const bufsize = 16ULL * 1024ULL;
132 WINPR_ASSERT(plugin);
134 while (status == WAIT_OBJECT_0)
136 (void)ResetEvent(plugin->writeComplete);
139 char* buffer = calloc(bufsize,
sizeof(
char));
143 (void)fprintf(stderr,
"rdp2tcp copyThread: malloc failed\n");
149 if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead,
nullptr))
155 if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
156 plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
159 (void)fprintf(stderr,
"rdp2tcp copyThread failed %i\n", (
int)dwRead);
163 HANDLE handles[] = { plugin->writeComplete,
164 freerdp_abort_event(plugin->channelEntryPoints.context) };
165 status = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
173static void closeChannel(Plugin* plugin)
175 WINPR_ASSERT(plugin);
176 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
177 plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
180static void dataReceived(Plugin* plugin,
void* pData, UINT32 dataLength, UINT32 totalLength,
185 WINPR_ASSERT(plugin);
187 if (dataFlags & CHANNEL_FLAG_SUSPEND)
190 if (dataFlags & CHANNEL_FLAG_RESUME)
193 if (dataFlags & CHANNEL_FLAG_FIRST)
195 if (!WriteFile(plugin->hStdInputWrite, &totalLength,
sizeof(totalLength), &dwWritten,
197 closeChannel(plugin);
200 if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten,
nullptr))
201 closeChannel(plugin);
204static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam,
205 WINPR_ATTR_UNUSED DWORD openHandle, UINT event,
206 LPVOID pData, UINT32 dataLength, UINT32 totalLength,
209 Plugin* plugin = (Plugin*)lpUserParam;
211 WINPR_ASSERT(plugin);
214 case CHANNEL_EVENT_DATA_RECEIVED:
215 dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
218 case CHANNEL_EVENT_WRITE_CANCELLED:
221 case CHANNEL_EVENT_WRITE_COMPLETE:
222 (void)SetEvent(plugin->writeComplete);
230static void channel_terminated(Plugin* plugin)
235 if (plugin->copyThread)
236 (void)CloseHandle(plugin->copyThread);
237 if (plugin->writeComplete)
238 (void)CloseHandle(plugin->writeComplete);
240 (void)CloseHandle(plugin->hStdInputWrite);
241 (void)CloseHandle(plugin->hStdOutputRead);
242 TerminateProcess(plugin->hProcess, 0);
243 (void)CloseHandle(plugin->hProcess);
244 free(plugin->commandline);
248static void channel_initialized(Plugin* plugin)
250 WINPR_ASSERT(plugin);
251 WINPR_ASSERT(!plugin->writeComplete);
252 plugin->writeComplete = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
254 WINPR_ASSERT(!plugin->copyThread);
255 plugin->copyThread = CreateThread(
nullptr, 0, copyThread, plugin, 0,
nullptr);
258static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
259 WINPR_ATTR_UNUSED LPVOID pData,
260 WINPR_ATTR_UNUSED UINT dataLength)
262 Plugin* plugin = (Plugin*)lpUserParam;
264 WINPR_ASSERT(plugin);
268 case CHANNEL_EVENT_INITIALIZED:
269 channel_initialized(plugin);
272 case CHANNEL_EVENT_CONNECTED:
273 WINPR_ASSERT(plugin);
274 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
275 if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
276 pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
277 VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
282 case CHANNEL_EVENT_DISCONNECTED:
283 closeChannel(plugin);
286 case CHANNEL_EVENT_TERMINATED:
287 channel_terminated(plugin);
294#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
295FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
300 WINPR_ASSERT(pEntryPointsEx);
302 pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
304 freerdp_warn_unmaintained(WLog_Get(TAG),
"CHANNEL_RDP2TCP_CLIENT");
306 Plugin* plugin = (Plugin*)calloc(1,
sizeof(Plugin));
311 plugin->initHandle = pInitHandle;
312 plugin->channelEntryPoints = *pEntryPointsEx;
314 if (init_external_addin(plugin) < 0)
316 channel_terminated(plugin);
321 strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME,
sizeof(channelDef.name));
323 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
325 if (pEntryPointsEx->pVirtualChannelInitEx(plugin,
nullptr, pInitHandle, &channelDef, 1,
326 VIRTUAL_CHANNEL_VERSION_WIN2000,
327 VirtualChannelInitEventEx) != CHANNEL_RC_OK)
329 channel_terminated(plugin);