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/log.h>
32 #define TAG CLIENT_TAG(RDP2TCP_DVC_CHANNEL_NAME)
34 static int const debug = 0;
38 HANDLE hStdOutputRead;
39 HANDLE hStdInputWrite;
46 char buffer[16 * 1024];
50 static int init_external_addin(Plugin* plugin)
52 SECURITY_ATTRIBUTES saAttr;
54 PROCESS_INFORMATION procInfo;
55 saAttr.nLength =
sizeof(SECURITY_ATTRIBUTES);
56 saAttr.bInheritHandle = TRUE;
57 saAttr.lpSecurityDescriptor = NULL;
59 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
60 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
63 if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
65 WLog_ERR(TAG,
"stdout CreatePipe");
69 if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
71 WLog_ERR(TAG,
"stdout SetHandleInformation");
75 if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
77 WLog_ERR(TAG,
"stdin CreatePipe");
81 if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
83 WLog_ERR(TAG,
"stdin SetHandleInformation");
88 plugin->commandline = _strdup(plugin->channelEntryPoints.pExtendedData);
89 if (!CreateProcessA(NULL,
101 WLog_ERR(TAG,
"fork for addin");
105 plugin->hProcess = procInfo.hProcess;
106 (void)CloseHandle(procInfo.hThread);
107 (void)CloseHandle(siStartInfo.hStdOutput);
108 (void)CloseHandle(siStartInfo.hStdInput);
112 static void dumpData(
char* data,
unsigned length)
114 unsigned const limit = 98;
115 unsigned l = length > limit ? limit / 2 : length;
117 for (
unsigned i = 0; i < l; ++i)
119 printf(
"%02hhx", data[i]);
126 for (
unsigned i = length - l; i < length; ++i)
127 printf(
"%02hhx", data[i]);
133 static DWORD WINAPI copyThread(
void* data)
135 DWORD status = WAIT_OBJECT_0;
136 Plugin* plugin = (Plugin*)data;
137 size_t const bufsize = 16ULL * 1024ULL;
139 while (status == WAIT_OBJECT_0)
142 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
144 char* buffer = malloc(bufsize);
148 (void)fprintf(stderr,
"rdp2tcp copyThread: malloc failed\n");
154 if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, NULL))
162 printf(
">%8u ", (
unsigned)dwRead);
163 dumpData(buffer, dwRead);
166 if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
167 plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
170 (void)fprintf(stderr,
"rdp2tcp copyThread failed %i\n", (
int)dwRead);
174 handles[0] = plugin->writeComplete;
175 handles[1] = freerdp_abort_event(plugin->channelEntryPoints.context);
176 status = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
177 if (status == WAIT_OBJECT_0)
178 (void)ResetEvent(plugin->writeComplete);
186 static void closeChannel(Plugin* plugin)
189 puts(
"rdp2tcp closing channel");
191 WINPR_ASSERT(plugin);
192 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
193 plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
196 static void dataReceived(Plugin* plugin,
void* pData, UINT32 dataLength, UINT32 totalLength,
201 if (dataFlags & CHANNEL_FLAG_SUSPEND)
204 puts(
"rdp2tcp Channel Suspend");
209 if (dataFlags & CHANNEL_FLAG_RESUME)
212 puts(
"rdp2tcp Channel Resume");
219 printf(
"<%c%3u/%3u ", dataFlags & CHANNEL_FLAG_FIRST ?
' ' :
'+', totalLength, dataLength);
220 dumpData(pData, dataLength);
223 if (dataFlags & CHANNEL_FLAG_FIRST)
225 if (!WriteFile(plugin->hStdInputWrite, &totalLength,
sizeof(totalLength), &dwWritten, NULL))
226 closeChannel(plugin);
229 if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, NULL))
230 closeChannel(plugin);
233 static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam, DWORD openHandle, UINT event,
234 LPVOID pData, UINT32 dataLength, UINT32 totalLength,
237 Plugin* plugin = (Plugin*)lpUserParam;
241 case CHANNEL_EVENT_DATA_RECEIVED:
242 dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
245 case CHANNEL_EVENT_WRITE_CANCELLED:
248 case CHANNEL_EVENT_WRITE_COMPLETE:
249 (void)SetEvent(plugin->writeComplete);
257 static void channel_terminated(Plugin* plugin)
260 puts(
"rdp2tcp terminated");
265 if (plugin->copyThread)
266 (void)TerminateThread(plugin->copyThread, 0);
267 if (plugin->writeComplete)
268 (void)CloseHandle(plugin->writeComplete);
270 (void)CloseHandle(plugin->hStdInputWrite);
271 (void)CloseHandle(plugin->hStdOutputRead);
272 TerminateProcess(plugin->hProcess, 0);
273 (void)CloseHandle(plugin->hProcess);
274 free(plugin->commandline);
278 static void channel_initialized(Plugin* plugin)
280 plugin->writeComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
281 plugin->copyThread = CreateThread(NULL, 0, copyThread, plugin, 0, NULL);
284 static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
285 LPVOID pData, UINT dataLength)
287 Plugin* plugin = (Plugin*)lpUserParam;
291 case CHANNEL_EVENT_INITIALIZED:
292 channel_initialized(plugin);
295 case CHANNEL_EVENT_CONNECTED:
297 puts(
"rdp2tcp connected");
299 WINPR_ASSERT(plugin);
300 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
301 if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
302 pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
303 VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
308 case CHANNEL_EVENT_DISCONNECTED:
310 puts(
"rdp2tcp disconnected");
314 case CHANNEL_EVENT_TERMINATED:
315 channel_terminated(plugin);
323 #define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
325 #define VirtualChannelEntryEx FREERDP_API VirtualChannelEntryEx
327 FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
332 Plugin* plugin = (Plugin*)calloc(1,
sizeof(Plugin));
339 pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
340 plugin->initHandle = pInitHandle;
341 plugin->channelEntryPoints = *pEntryPointsEx;
343 if (init_external_addin(plugin) < 0)
349 strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME,
sizeof(channelDef.name));
351 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
353 if (pEntryPointsEx->pVirtualChannelInitEx(plugin, NULL, pInitHandle, &channelDef, 1,
354 VIRTUAL_CHANNEL_VERSION_WIN2000,
355 VirtualChannelInitEventEx) != CHANNEL_RC_OK)