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