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)
36 HANDLE hStdOutputRead;
37 HANDLE hStdInputWrite;
44 char buffer[16 * 1024];
48static int init_external_addin(Plugin* plugin)
58 saAttr.bInheritHandle = TRUE;
59 saAttr.lpSecurityDescriptor = NULL;
61 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
62 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
65 if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
67 WLog_ERR(TAG,
"stdout CreatePipe");
71 if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
73 WLog_ERR(TAG,
"stdout SetHandleInformation");
77 if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
79 WLog_ERR(TAG,
"stdin CreatePipe");
83 if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
85 WLog_ERR(TAG,
"stdin SetHandleInformation");
91 if (!args || (args->argc < 2))
93 WLog_ERR(TAG,
"missing command line options");
97 plugin->commandline = _strdup(args->argv[1]);
98 if (!CreateProcessA(NULL,
110 WLog_ERR(TAG,
"fork for addin");
114 plugin->hProcess = procInfo.hProcess;
118 (void)CloseHandle(procInfo.hThread);
119 (void)CloseHandle(siStartInfo.hStdOutput);
120 (void)CloseHandle(siStartInfo.hStdInput);
124static DWORD WINAPI copyThread(
void* data)
126 DWORD status = WAIT_OBJECT_0;
127 Plugin* plugin = (Plugin*)data;
128 size_t const bufsize = 16ULL * 1024ULL;
130 WINPR_ASSERT(plugin);
132 while (status == WAIT_OBJECT_0)
134 (void)ResetEvent(plugin->writeComplete);
137 char* buffer = calloc(bufsize,
sizeof(
char));
141 (void)fprintf(stderr,
"rdp2tcp copyThread: malloc failed\n");
147 if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, NULL))
153 if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
154 plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
157 (void)fprintf(stderr,
"rdp2tcp copyThread failed %i\n", (
int)dwRead);
161 HANDLE handles[] = { plugin->writeComplete,
162 freerdp_abort_event(plugin->channelEntryPoints.context) };
163 status = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
171static void closeChannel(Plugin* plugin)
173 WINPR_ASSERT(plugin);
174 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
175 plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
178static void dataReceived(Plugin* plugin,
void* pData, UINT32 dataLength, UINT32 totalLength,
183 WINPR_ASSERT(plugin);
185 if (dataFlags & CHANNEL_FLAG_SUSPEND)
188 if (dataFlags & CHANNEL_FLAG_RESUME)
191 if (dataFlags & CHANNEL_FLAG_FIRST)
193 if (!WriteFile(plugin->hStdInputWrite, &totalLength,
sizeof(totalLength), &dwWritten, NULL))
194 closeChannel(plugin);
197 if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, NULL))
198 closeChannel(plugin);
201static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam,
202 WINPR_ATTR_UNUSED DWORD openHandle, UINT event,
203 LPVOID pData, UINT32 dataLength, UINT32 totalLength,
206 Plugin* plugin = (Plugin*)lpUserParam;
208 WINPR_ASSERT(plugin);
211 case CHANNEL_EVENT_DATA_RECEIVED:
212 dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
215 case CHANNEL_EVENT_WRITE_CANCELLED:
218 case CHANNEL_EVENT_WRITE_COMPLETE:
219 (void)SetEvent(plugin->writeComplete);
227static void channel_terminated(Plugin* plugin)
232 if (plugin->copyThread)
233 (void)CloseHandle(plugin->copyThread);
234 if (plugin->writeComplete)
235 (void)CloseHandle(plugin->writeComplete);
237 (void)CloseHandle(plugin->hStdInputWrite);
238 (void)CloseHandle(plugin->hStdOutputRead);
239 TerminateProcess(plugin->hProcess, 0);
240 (void)CloseHandle(plugin->hProcess);
241 free(plugin->commandline);
245static void channel_initialized(Plugin* plugin)
247 WINPR_ASSERT(plugin);
248 WINPR_ASSERT(!plugin->writeComplete);
249 plugin->writeComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
251 WINPR_ASSERT(!plugin->copyThread);
252 plugin->copyThread = CreateThread(NULL, 0, copyThread, plugin, 0, NULL);
255static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
256 WINPR_ATTR_UNUSED LPVOID pData,
257 WINPR_ATTR_UNUSED UINT dataLength)
259 Plugin* plugin = (Plugin*)lpUserParam;
261 WINPR_ASSERT(plugin);
265 case CHANNEL_EVENT_INITIALIZED:
266 channel_initialized(plugin);
269 case CHANNEL_EVENT_CONNECTED:
270 WINPR_ASSERT(plugin);
271 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
272 if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
273 pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
274 VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
279 case CHANNEL_EVENT_DISCONNECTED:
280 closeChannel(plugin);
283 case CHANNEL_EVENT_TERMINATED:
284 channel_terminated(plugin);
291#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
292FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
297 WINPR_ASSERT(pEntryPointsEx);
299 pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
301 Plugin* plugin = (Plugin*)calloc(1,
sizeof(Plugin));
306 plugin->initHandle = pInitHandle;
307 plugin->channelEntryPoints = *pEntryPointsEx;
309 if (init_external_addin(plugin) < 0)
311 channel_terminated(plugin);
316 strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME,
sizeof(channelDef.name));
318 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
320 if (pEntryPointsEx->pVirtualChannelInitEx(plugin, NULL, pInitHandle, &channelDef, 1,
321 VIRTUAL_CHANNEL_VERSION_WIN2000,
322 VirtualChannelInitEventEx) != CHANNEL_RC_OK)
324 channel_terminated(plugin);