FreeRDP
Loading...
Searching...
No Matches
rdp2tcp_main.c
1
20#include <stdio.h>
21#include <winpr/assert.h>
22
23#include <winpr/file.h>
24#include <winpr/pipe.h>
25#include <winpr/thread.h>
26
27#include <freerdp/freerdp.h>
28#include <freerdp/svc.h>
29#include <freerdp/channels/rdp2tcp.h>
30
31#include <freerdp/utils/warnings.h>
32
33#include <freerdp/log.h>
34#define TAG CLIENT_TAG(RDP2TCP_DVC_CHANNEL_NAME)
35
36typedef struct
37{
38 HANDLE hStdOutputRead;
39 HANDLE hStdInputWrite;
40 HANDLE hProcess;
41 HANDLE copyThread;
42 HANDLE writeComplete;
43 DWORD openHandle;
44 void* initHandle;
45 CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
46 char buffer[16 * 1024];
47 char* commandline;
48 UINT32 totalLength;
49} Plugin;
50
51static int init_external_addin(Plugin* plugin)
52{
53 int rc = -1;
54 SECURITY_ATTRIBUTES saAttr = WINPR_C_ARRAY_INIT;
55 STARTUPINFOA siStartInfo = WINPR_C_ARRAY_INIT; /* Using ANSI type to match CreateProcessA */
56 PROCESS_INFORMATION procInfo = WINPR_C_ARRAY_INIT;
57
58 WINPR_ASSERT(plugin);
59
60 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
61 saAttr.bInheritHandle = TRUE;
62 saAttr.lpSecurityDescriptor = nullptr;
63 siStartInfo.cb = sizeof(STARTUPINFO);
64 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
65 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
66
67 // Create pipes
68 if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
69 {
70 WLog_ERR(TAG, "stdout CreatePipe");
71 goto fail;
72 }
73
74 if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
75 {
76 WLog_ERR(TAG, "stdout SetHandleInformation");
77 goto fail;
78 }
79
80 if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
81 {
82 WLog_ERR(TAG, "stdin CreatePipe");
83 goto fail;
84 }
85
86 if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
87 {
88 WLog_ERR(TAG, "stdin SetHandleInformation");
89 goto fail;
90 }
91
92 // Execute plugin
93 const ADDIN_ARGV* args = (const ADDIN_ARGV*)plugin->channelEntryPoints.pExtendedData;
94 if (!args || (args->argc < 2))
95 {
96 WLog_ERR(TAG, "missing command line options");
97 goto fail;
98 }
99
100 plugin->commandline = _strdup(args->argv[1]);
101 if (!CreateProcessA(nullptr,
102 plugin->commandline, // command line
103 nullptr, // process security attributes
104 nullptr, // primary thread security attributes
105 TRUE, // handles are inherited
106 0, // creation flags
107 nullptr, // use parent's environment
108 nullptr, // use parent's current directory
109 &siStartInfo, // STARTUPINFO pointer
110 &procInfo // receives PROCESS_INFORMATION
111 ))
112 {
113 WLog_ERR(TAG, "fork for addin");
114 goto fail;
115 }
116
117 plugin->hProcess = procInfo.hProcess;
118
119 rc = 0;
120fail:
121 (void)CloseHandle(procInfo.hThread);
122 (void)CloseHandle(siStartInfo.hStdOutput);
123 (void)CloseHandle(siStartInfo.hStdInput);
124 return rc;
125}
126
127static DWORD WINAPI copyThread(void* data)
128{
129 DWORD status = WAIT_OBJECT_0;
130 Plugin* plugin = (Plugin*)data;
131 size_t const bufsize = 16ULL * 1024ULL;
132
133 WINPR_ASSERT(plugin);
134
135 while (status == WAIT_OBJECT_0)
136 {
137 (void)ResetEvent(plugin->writeComplete);
138
139 DWORD dwRead = 0;
140 char* buffer = calloc(bufsize, sizeof(char));
141
142 if (!buffer)
143 {
144 (void)fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
145 goto fail;
146 }
147
148 // if (!ReadFile(plugin->hStdOutputRead, plugin->buffer, sizeof plugin->buffer, &dwRead,
149 // nullptr))
150 if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, nullptr))
151 {
152 free(buffer);
153 goto fail;
154 }
155
156 if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
157 plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
158 {
159 free(buffer);
160 (void)fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
161 goto fail;
162 }
163
164 HANDLE handles[] = { plugin->writeComplete,
165 freerdp_abort_event(plugin->channelEntryPoints.context) };
166 status = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
167 }
168
169fail:
170 ExitThread(0);
171 return 0;
172}
173
174static void closeChannel(Plugin* plugin)
175{
176 WINPR_ASSERT(plugin);
177 WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
178 plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
179}
180
181static void dataReceived(Plugin* plugin, void* pData, UINT32 dataLength, UINT32 totalLength,
182 UINT32 dataFlags)
183{
184 DWORD dwWritten = 0;
185
186 WINPR_ASSERT(plugin);
187
188 if (dataFlags & CHANNEL_FLAG_SUSPEND)
189 return;
190
191 if (dataFlags & CHANNEL_FLAG_RESUME)
192 return;
193
194 if (dataFlags & CHANNEL_FLAG_FIRST)
195 {
196 plugin->totalLength = totalLength;
197 if (!WriteFile(plugin->hStdInputWrite, &totalLength, sizeof(totalLength), &dwWritten,
198 nullptr))
199 closeChannel(plugin);
200 }
201
202 if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, nullptr))
203 closeChannel(plugin);
204}
205
206static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam,
207 WINPR_ATTR_UNUSED DWORD openHandle, UINT event,
208 LPVOID pData, UINT32 dataLength, UINT32 totalLength,
209 UINT32 dataFlags)
210{
211 Plugin* plugin = (Plugin*)lpUserParam;
212
213 WINPR_ASSERT(plugin);
214 switch (event)
215 {
216 case CHANNEL_EVENT_DATA_RECEIVED:
217 dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
218 break;
219
220 case CHANNEL_EVENT_WRITE_CANCELLED:
221 free(pData);
222 break;
223 case CHANNEL_EVENT_WRITE_COMPLETE:
224 (void)SetEvent(plugin->writeComplete);
225 free(pData);
226 break;
227 default:
228 break;
229 }
230}
231
232static void channel_terminated(Plugin* plugin)
233{
234 if (!plugin)
235 return;
236
237 if (plugin->copyThread)
238 (void)CloseHandle(plugin->copyThread);
239 if (plugin->writeComplete)
240 (void)CloseHandle(plugin->writeComplete);
241
242 (void)CloseHandle(plugin->hStdInputWrite);
243 (void)CloseHandle(plugin->hStdOutputRead);
244 TerminateProcess(plugin->hProcess, 0);
245 (void)CloseHandle(plugin->hProcess);
246 free(plugin->commandline);
247 free(plugin);
248}
249
250static void channel_initialized(Plugin* plugin)
251{
252 WINPR_ASSERT(plugin);
253 WINPR_ASSERT(!plugin->writeComplete);
254 plugin->writeComplete = CreateEvent(nullptr, TRUE, FALSE, nullptr);
255
256 WINPR_ASSERT(!plugin->copyThread);
257 plugin->copyThread = CreateThread(nullptr, 0, copyThread, plugin, 0, nullptr);
258}
259
260static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
261 WINPR_ATTR_UNUSED LPVOID pData,
262 WINPR_ATTR_UNUSED UINT dataLength)
263{
264 Plugin* plugin = (Plugin*)lpUserParam;
265
266 WINPR_ASSERT(plugin);
267
268 switch (event)
269 {
270 case CHANNEL_EVENT_INITIALIZED:
271 channel_initialized(plugin);
272 break;
273
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)
280 return;
281
282 break;
283
284 case CHANNEL_EVENT_DISCONNECTED:
285 closeChannel(plugin);
286 break;
287
288 case CHANNEL_EVENT_TERMINATED:
289 channel_terminated(plugin);
290 break;
291 default:
292 break;
293 }
294}
295
296#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
297FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
298 PVOID pInitHandle))
299{
300 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx =
301 (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
302 WINPR_ASSERT(pEntryPointsEx);
303 WINPR_ASSERT(pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX) &&
304 pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
305
306 freerdp_warn_unmaintained(WLog_Get(TAG), "CHANNEL_RDP2TCP_CLIENT");
307
308 Plugin* plugin = (Plugin*)calloc(1, sizeof(Plugin));
309
310 if (!plugin)
311 return FALSE;
312
313 plugin->initHandle = pInitHandle;
314 plugin->channelEntryPoints = *pEntryPointsEx;
315
316 if (init_external_addin(plugin) < 0)
317 {
318 channel_terminated(plugin);
319 return FALSE;
320 }
321
322 CHANNEL_DEF channelDef = WINPR_C_ARRAY_INIT;
323 strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME, sizeof(channelDef.name));
324 channelDef.options =
325 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
326
327 if (pEntryPointsEx->pVirtualChannelInitEx(plugin, nullptr, pInitHandle, &channelDef, 1,
328 VIRTUAL_CHANNEL_VERSION_WIN2000,
329 VirtualChannelInitEventEx) != CHANNEL_RC_OK)
330 {
331 channel_terminated(plugin);
332 return FALSE;
333 }
334
335 return TRUE;
336}
Definition svc.h:60