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];
51static int init_external_addin(Plugin* plugin)
61 saAttr.bInheritHandle = TRUE;
62 saAttr.lpSecurityDescriptor =
nullptr;
64 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
65 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
68 if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
70 WLog_ERR(TAG,
"stdout CreatePipe");
74 if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
76 WLog_ERR(TAG,
"stdout SetHandleInformation");
80 if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
82 WLog_ERR(TAG,
"stdin CreatePipe");
86 if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
88 WLog_ERR(TAG,
"stdin SetHandleInformation");
94 if (!args || (args->argc < 2))
96 WLog_ERR(TAG,
"missing command line options");
100 plugin->commandline = _strdup(args->argv[1]);
101 if (!CreateProcessA(
nullptr,
113 WLog_ERR(TAG,
"fork for addin");
117 plugin->hProcess = procInfo.hProcess;
121 (void)CloseHandle(procInfo.hThread);
122 (void)CloseHandle(siStartInfo.hStdOutput);
123 (void)CloseHandle(siStartInfo.hStdInput);
127static DWORD WINAPI copyThread(
void* data)
129 DWORD status = WAIT_OBJECT_0;
130 Plugin* plugin = (Plugin*)data;
131 size_t const bufsize = 16ULL * 1024ULL;
133 WINPR_ASSERT(plugin);
135 while (status == WAIT_OBJECT_0)
137 (void)ResetEvent(plugin->writeComplete);
140 char* buffer = calloc(bufsize,
sizeof(
char));
144 (void)fprintf(stderr,
"rdp2tcp copyThread: malloc failed\n");
150 if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead,
nullptr))
156 if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
157 plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
160 (void)fprintf(stderr,
"rdp2tcp copyThread failed %i\n", (
int)dwRead);
164 HANDLE handles[] = { plugin->writeComplete,
165 freerdp_abort_event(plugin->channelEntryPoints.context) };
166 status = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
174static void closeChannel(Plugin* plugin)
176 WINPR_ASSERT(plugin);
177 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
178 plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
181static void dataReceived(Plugin* plugin,
void* pData, UINT32 dataLength, UINT32 totalLength,
186 WINPR_ASSERT(plugin);
188 if (dataFlags & CHANNEL_FLAG_SUSPEND)
191 if (dataFlags & CHANNEL_FLAG_RESUME)
194 if (dataFlags & CHANNEL_FLAG_FIRST)
196 plugin->totalLength = totalLength;
197 if (!WriteFile(plugin->hStdInputWrite, &totalLength,
sizeof(totalLength), &dwWritten,
199 closeChannel(plugin);
202 if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten,
nullptr))
203 closeChannel(plugin);
206static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam,
207 WINPR_ATTR_UNUSED DWORD openHandle, UINT event,
208 LPVOID pData, UINT32 dataLength, UINT32 totalLength,
211 Plugin* plugin = (Plugin*)lpUserParam;
213 WINPR_ASSERT(plugin);
216 case CHANNEL_EVENT_DATA_RECEIVED:
217 dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
220 case CHANNEL_EVENT_WRITE_CANCELLED:
223 case CHANNEL_EVENT_WRITE_COMPLETE:
224 (void)SetEvent(plugin->writeComplete);
232static void channel_terminated(Plugin* plugin)
237 if (plugin->copyThread)
238 (void)CloseHandle(plugin->copyThread);
239 if (plugin->writeComplete)
240 (void)CloseHandle(plugin->writeComplete);
242 (void)CloseHandle(plugin->hStdInputWrite);
243 (void)CloseHandle(plugin->hStdOutputRead);
244 TerminateProcess(plugin->hProcess, 0);
245 (void)CloseHandle(plugin->hProcess);
246 free(plugin->commandline);
250static void channel_initialized(Plugin* plugin)
252 WINPR_ASSERT(plugin);
253 WINPR_ASSERT(!plugin->writeComplete);
254 plugin->writeComplete = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
256 WINPR_ASSERT(!plugin->copyThread);
257 plugin->copyThread = CreateThread(
nullptr, 0, copyThread, plugin, 0,
nullptr);
260static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
261 WINPR_ATTR_UNUSED LPVOID pData,
262 WINPR_ATTR_UNUSED UINT dataLength)
264 Plugin* plugin = (Plugin*)lpUserParam;
266 WINPR_ASSERT(plugin);
270 case CHANNEL_EVENT_INITIALIZED:
271 channel_initialized(plugin);
274 case CHANNEL_EVENT_CONNECTED:
275 WINPR_ASSERT(plugin);
276 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
277 if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
278 pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
279 VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
284 case CHANNEL_EVENT_DISCONNECTED:
285 closeChannel(plugin);
288 case CHANNEL_EVENT_TERMINATED:
289 channel_terminated(plugin);
296#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
297FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
302 WINPR_ASSERT(pEntryPointsEx);
304 pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
306 freerdp_warn_unmaintained(WLog_Get(TAG),
"CHANNEL_RDP2TCP_CLIENT");
308 Plugin* plugin = (Plugin*)calloc(1,
sizeof(Plugin));
313 plugin->initHandle = pInitHandle;
314 plugin->channelEntryPoints = *pEntryPointsEx;
316 if (init_external_addin(plugin) < 0)
318 channel_terminated(plugin);
323 strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME,
sizeof(channelDef.name));
325 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
327 if (pEntryPointsEx->pVirtualChannelInitEx(plugin,
nullptr, pInitHandle, &channelDef, 1,
328 VIRTUAL_CHANNEL_VERSION_WIN2000,
329 VirtualChannelInitEventEx) != CHANNEL_RC_OK)
331 channel_terminated(plugin);