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