Coverage Report

Created: 2026-10-15 06:30

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