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