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 =
nullptr;
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(
nullptr,
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,
nullptr))
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,
195 closeChannel(plugin);
198 if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten,
nullptr))
199 closeChannel(plugin);
202static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam,
203 WINPR_ATTR_UNUSED DWORD openHandle, UINT event,
204 LPVOID pData, UINT32 dataLength, UINT32 totalLength,
207 Plugin* plugin = (Plugin*)lpUserParam;
209 WINPR_ASSERT(plugin);
212 case CHANNEL_EVENT_DATA_RECEIVED:
213 dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
216 case CHANNEL_EVENT_WRITE_CANCELLED:
219 case CHANNEL_EVENT_WRITE_COMPLETE:
220 (void)SetEvent(plugin->writeComplete);
228static void channel_terminated(Plugin* plugin)
233 if (plugin->copyThread)
234 (void)CloseHandle(plugin->copyThread);
235 if (plugin->writeComplete)
236 (void)CloseHandle(plugin->writeComplete);
238 (void)CloseHandle(plugin->hStdInputWrite);
239 (void)CloseHandle(plugin->hStdOutputRead);
240 TerminateProcess(plugin->hProcess, 0);
241 (void)CloseHandle(plugin->hProcess);
242 free(plugin->commandline);
246static void channel_initialized(Plugin* plugin)
248 WINPR_ASSERT(plugin);
249 WINPR_ASSERT(!plugin->writeComplete);
250 plugin->writeComplete = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
252 WINPR_ASSERT(!plugin->copyThread);
253 plugin->copyThread = CreateThread(
nullptr, 0, copyThread, plugin, 0,
nullptr);
256static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
257 WINPR_ATTR_UNUSED LPVOID pData,
258 WINPR_ATTR_UNUSED UINT dataLength)
260 Plugin* plugin = (Plugin*)lpUserParam;
262 WINPR_ASSERT(plugin);
266 case CHANNEL_EVENT_INITIALIZED:
267 channel_initialized(plugin);
270 case CHANNEL_EVENT_CONNECTED:
271 WINPR_ASSERT(plugin);
272 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
273 if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
274 pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
275 VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
280 case CHANNEL_EVENT_DISCONNECTED:
281 closeChannel(plugin);
284 case CHANNEL_EVENT_TERMINATED:
285 channel_terminated(plugin);
292#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
293FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
298 WINPR_ASSERT(pEntryPointsEx);
300 pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
302 Plugin* plugin = (Plugin*)calloc(1,
sizeof(Plugin));
307 plugin->initHandle = pInitHandle;
308 plugin->channelEntryPoints = *pEntryPointsEx;
310 if (init_external_addin(plugin) < 0)
312 channel_terminated(plugin);
317 strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME,
sizeof(channelDef.name));
319 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
321 if (pEntryPointsEx->pVirtualChannelInitEx(plugin,
nullptr, pInitHandle, &channelDef, 1,
322 VIRTUAL_CHANNEL_VERSION_WIN2000,
323 VirtualChannelInitEventEx) != CHANNEL_RC_OK)
325 channel_terminated(plugin);