Coverage Report

Created: 2024-05-20 06:11

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