Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/channels/rdpdr/client/rdpdr_main.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Device Redirection Virtual Channel
4
 *
5
 * Copyright 2010-2011 Vic Lee
6
 * Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7
 * Copyright 2015-2016 Thincast Technologies GmbH
8
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
9
 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
10
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 *     http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24
25
#include <freerdp/config.h>
26
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
31
#include <winpr/crt.h>
32
#include <winpr/sysinfo.h>
33
#include <winpr/assert.h>
34
#include <winpr/stream.h>
35
36
#include <winpr/print.h>
37
#include <winpr/sspicli.h>
38
39
#include <freerdp/types.h>
40
#include <freerdp/freerdp.h>
41
#include <freerdp/constants.h>
42
#include <freerdp/channels/log.h>
43
#include <freerdp/channels/rdpdr.h>
44
#include <freerdp/utils/rdpdr_utils.h>
45
46
#ifdef _WIN32
47
#include <windows.h>
48
#include <dbt.h>
49
#else
50
#include <sys/types.h>
51
#include <sys/stat.h>
52
#include <fcntl.h>
53
#endif
54
55
#ifdef __MACOSX__
56
#include <CoreFoundation/CoreFoundation.h>
57
#include <stdio.h>
58
#include <dirent.h>
59
#include <sys/types.h>
60
#include <sys/stat.h>
61
#include <unistd.h>
62
#endif
63
64
#include "rdpdr_capabilities.h"
65
66
#include "devman.h"
67
#include "irp.h"
68
69
#include "rdpdr_main.h"
70
71
0
#define TAG CHANNELS_TAG("rdpdr.client")
72
73
/* IMPORTANT: Keep in sync with DRIVE_DEVICE */
74
typedef struct
75
{
76
  DEVICE device;
77
  WCHAR* path;
78
  BOOL automount;
79
} DEVICE_DRIVE_EXT;
80
81
static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state)
82
0
{
83
0
  switch (state)
84
0
  {
85
0
    case RDPDR_CHANNEL_STATE_INITIAL:
86
0
      return "RDPDR_CHANNEL_STATE_INITIAL";
87
0
    case RDPDR_CHANNEL_STATE_ANNOUNCE:
88
0
      return "RDPDR_CHANNEL_STATE_ANNOUNCE";
89
0
    case RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY:
90
0
      return "RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY";
91
0
    case RDPDR_CHANNEL_STATE_NAME_REQUEST:
92
0
      return "RDPDR_CHANNEL_STATE_NAME_REQUEST";
93
0
    case RDPDR_CHANNEL_STATE_SERVER_CAPS:
94
0
      return "RDPDR_CHANNEL_STATE_SERVER_CAPS";
95
0
    case RDPDR_CHANNEL_STATE_CLIENT_CAPS:
96
0
      return "RDPDR_CHANNEL_STATE_CLIENT_CAPS";
97
0
    case RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM:
98
0
      return "RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM";
99
0
    case RDPDR_CHANNEL_STATE_READY:
100
0
      return "RDPDR_CHANNEL_STATE_READY";
101
0
    case RDPDR_CHANNEL_STATE_USER_LOGGEDON:
102
0
      return "RDPDR_CHANNEL_STATE_USER_LOGGEDON";
103
0
    default:
104
0
      return "RDPDR_CHANNEL_STATE_UNKNOWN";
105
0
  }
106
0
}
107
108
static const char* rdpdr_device_type_string(UINT32 type)
109
0
{
110
0
  switch (type)
111
0
  {
112
0
    case RDPDR_DTYP_SERIAL:
113
0
      return "serial";
114
0
    case RDPDR_DTYP_PRINT:
115
0
      return "printer";
116
0
    case RDPDR_DTYP_FILESYSTEM:
117
0
      return "drive";
118
0
    case RDPDR_DTYP_SMARTCARD:
119
0
      return "smartcard";
120
0
    case RDPDR_DTYP_PARALLEL:
121
0
      return "parallel";
122
0
    default:
123
0
      return "UNKNOWN";
124
0
  }
125
0
}
126
127
static const char* support_str(BOOL val)
128
0
{
129
0
  if (val)
130
0
    return "supported";
131
0
  return "not found";
132
0
}
133
134
static const char* rdpdr_caps_pdu_str(UINT32 flag)
135
0
{
136
0
  switch (flag)
137
0
  {
138
0
    case RDPDR_DEVICE_REMOVE_PDUS:
139
0
      return "RDPDR_USER_LOGGEDON_PDU";
140
0
    case RDPDR_CLIENT_DISPLAY_NAME_PDU:
141
0
      return "RDPDR_CLIENT_DISPLAY_NAME_PDU";
142
0
    case RDPDR_USER_LOGGEDON_PDU:
143
0
      return "RDPDR_USER_LOGGEDON_PDU";
144
0
    default:
145
0
      return "RDPDR_UNKNONW";
146
0
  }
147
0
}
148
149
static BOOL rdpdr_check_extended_pdu_flag(rdpdrPlugin* rdpdr, UINT32 flag)
150
0
{
151
0
  WINPR_ASSERT(rdpdr);
152
153
0
  const BOOL client = (rdpdr->clientExtendedPDU & flag) != 0;
154
0
  const BOOL server = (rdpdr->serverExtendedPDU & flag) != 0;
155
156
0
  if (!client || !server)
157
0
  {
158
0
    WLog_Print(rdpdr->log, WLOG_WARN, "Checking ExtendedPDU::%s, client %s, server %s",
159
0
               rdpdr_caps_pdu_str(flag), support_str(client), support_str(server));
160
0
    return FALSE;
161
0
  }
162
0
  return TRUE;
163
0
}
164
165
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next)
166
0
{
167
0
  WINPR_ASSERT(rdpdr);
168
169
0
  if (next != rdpdr->state)
170
0
    WLog_Print(rdpdr->log, WLOG_DEBUG, "[RDPDR] transition from %s to %s",
171
0
               rdpdr_state_str(rdpdr->state), rdpdr_state_str(next));
172
0
  rdpdr->state = next;
173
0
  return TRUE;
174
0
}
175
176
static BOOL device_foreach(rdpdrPlugin* rdpdr, BOOL abortOnFail,
177
                           BOOL (*fkt)(ULONG_PTR key, void* element, void* data), void* data)
178
0
{
179
0
  BOOL rc = TRUE;
180
0
  ULONG_PTR* keys = NULL;
181
182
0
  ListDictionary_Lock(rdpdr->devman->devices);
183
0
  const size_t count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
184
0
  for (size_t x = 0; x < count; x++)
185
0
  {
186
0
    void* element = ListDictionary_GetItemValue(rdpdr->devman->devices, (void*)keys[x]);
187
0
    if (!fkt(keys[x], element, data))
188
0
    {
189
0
      rc = FALSE;
190
0
      if (abortOnFail)
191
0
        break;
192
0
    }
193
0
  }
194
0
  free(keys);
195
0
  ListDictionary_Unlock(rdpdr->devman->devices);
196
0
  return rc;
197
0
}
198
199
/**
200
 * Function description
201
 *
202
 * @return 0 on success, otherwise a Win32 error code
203
 */
204
static UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr);
205
206
static BOOL rdpdr_load_drive(rdpdrPlugin* rdpdr, const char* name, const char* path, BOOL automount)
207
0
{
208
0
  UINT rc = ERROR_INTERNAL_ERROR;
209
0
  union
210
0
  {
211
0
    RDPDR_DRIVE* drive;
212
0
    RDPDR_DEVICE* device;
213
0
  } drive;
214
0
  const char* args[] = { name, path, automount ? NULL : name };
215
216
0
  drive.device = freerdp_device_new(RDPDR_DTYP_FILESYSTEM, ARRAYSIZE(args), args);
217
0
  if (!drive.device)
218
0
    goto fail;
219
220
0
  rc = devman_load_device_service(rdpdr->devman, drive.device, rdpdr->rdpcontext);
221
0
  if (rc != CHANNEL_RC_OK)
222
0
    goto fail;
223
224
0
fail:
225
0
  freerdp_device_free(drive.device);
226
0
  return rc;
227
0
}
228
229
/**
230
 * Function description
231
 *
232
 * @return 0 on success, otherwise a Win32 error code
233
 */
234
static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 count, UINT32 ids[])
235
0
{
236
0
  wStream* s = NULL;
237
238
0
  WINPR_ASSERT(rdpdr);
239
0
  WINPR_ASSERT(ids || (count == 0));
240
241
0
  if (count == 0)
242
0
    return CHANNEL_RC_OK;
243
244
0
  if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_DEVICE_REMOVE_PDUS))
245
0
    return CHANNEL_RC_OK;
246
247
0
  s = StreamPool_Take(rdpdr->pool, count * sizeof(UINT32) + 8);
248
249
0
  if (!s)
250
0
  {
251
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
252
0
    return CHANNEL_RC_NO_MEMORY;
253
0
  }
254
255
0
  Stream_Write_UINT16(s, RDPDR_CTYP_CORE);
256
0
  Stream_Write_UINT16(s, PAKID_CORE_DEVICELIST_REMOVE);
257
0
  Stream_Write_UINT32(s, count);
258
259
0
  for (UINT32 i = 0; i < count; i++)
260
0
    Stream_Write_UINT32(s, ids[i]);
261
262
0
  Stream_SealLength(s);
263
0
  return rdpdr_send(rdpdr, s);
264
0
}
265
266
#if defined(_UWP) || defined(__IOS__)
267
268
static void first_hotplug(rdpdrPlugin* rdpdr)
269
{
270
}
271
272
static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
273
{
274
  return CHANNEL_RC_OK;
275
}
276
277
static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
278
{
279
  return CHANNEL_RC_OK;
280
}
281
282
#elif defined(_WIN32)
283
284
static BOOL check_path(const char* path)
285
{
286
  UINT type = GetDriveTypeA(path);
287
288
  if (!(type == DRIVE_FIXED || type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
289
        type == DRIVE_REMOTE))
290
    return FALSE;
291
292
  return GetVolumeInformationA(path, NULL, 0, NULL, NULL, NULL, NULL, 0);
293
}
294
295
static void first_hotplug(rdpdrPlugin* rdpdr)
296
{
297
  DWORD unitmask = GetLogicalDrives();
298
299
  for (size_t i = 0; i < 26; i++)
300
  {
301
    if (unitmask & 0x01)
302
    {
303
      char drive_path[] = { 'c', ':', '\\', '\0' };
304
      char drive_name[] = { 'c', '\0' };
305
      drive_path[0] = 'A' + (char)i;
306
      drive_name[0] = 'A' + (char)i;
307
308
      if (check_path(drive_path))
309
      {
310
        rdpdr_load_drive(rdpdr, drive_name, drive_path, TRUE);
311
      }
312
    }
313
314
    unitmask = unitmask >> 1;
315
  }
316
}
317
318
static LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
319
{
320
  rdpdrPlugin* rdpdr;
321
  PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
322
  UINT error;
323
  rdpdr = (rdpdrPlugin*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
324
325
  switch (Msg)
326
  {
327
    case WM_DEVICECHANGE:
328
      switch (wParam)
329
      {
330
        case DBT_DEVICEARRIVAL:
331
          if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
332
          {
333
            PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
334
            DWORD unitmask = lpdbv->dbcv_unitmask;
335
336
            for (int i = 0; i < 26; i++)
337
            {
338
              if (unitmask & 0x01)
339
              {
340
                char drive_path[] = { 'c', ':', '/', '\0' };
341
                char drive_name[] = { 'c', '\0' };
342
                drive_path[0] = 'A' + (char)i;
343
                drive_name[0] = 'A' + (char)i;
344
345
                if (check_path(drive_path))
346
                {
347
                  rdpdr_load_drive(rdpdr, drive_name, drive_path, TRUE);
348
                  rdpdr_try_send_device_list_announce_request(rdpdr);
349
                }
350
              }
351
352
              unitmask = unitmask >> 1;
353
            }
354
          }
355
356
          break;
357
358
        case DBT_DEVICEREMOVECOMPLETE:
359
          if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
360
          {
361
            PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
362
            DWORD unitmask = lpdbv->dbcv_unitmask;
363
            int count;
364
            char drive_name_upper, drive_name_lower;
365
            ULONG_PTR* keys = NULL;
366
            DEVICE_DRIVE_EXT* device_ext;
367
            UINT32 ids[1];
368
369
            for (int i = 0; i < 26; i++)
370
            {
371
              if (unitmask & 0x01)
372
              {
373
                drive_name_upper = 'A' + i;
374
                drive_name_lower = 'a' + i;
375
                count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
376
377
                for (int j = 0; j < count; j++)
378
                {
379
                  device_ext = (DEVICE_DRIVE_EXT*)ListDictionary_GetItemValue(
380
                      rdpdr->devman->devices, (void*)keys[j]);
381
382
                  if (device_ext->device.type != RDPDR_DTYP_FILESYSTEM)
383
                    continue;
384
385
                  if (device_ext->path[0] == drive_name_upper ||
386
                      device_ext->path[0] == drive_name_lower)
387
                  {
388
                    if (device_ext->automount)
389
                    {
390
                      devman_unregister_device(rdpdr->devman, (void*)keys[j]);
391
                      ids[0] = keys[j];
392
393
                      if ((error = rdpdr_send_device_list_remove_request(
394
                               rdpdr, 1, ids)))
395
                      {
396
                        // dont end on error, just report ?
397
                        WLog_Print(
398
                            rdpdr->log, WLOG_ERROR,
399
                            "rdpdr_send_device_list_remove_request failed "
400
                            "with error %" PRIu32 "!",
401
                            error);
402
                      }
403
404
                      break;
405
                    }
406
                  }
407
                }
408
409
                free(keys);
410
              }
411
412
              unitmask = unitmask >> 1;
413
            }
414
          }
415
416
          break;
417
418
        default:
419
          break;
420
      }
421
422
      break;
423
424
    default:
425
      return DefWindowProc(hWnd, Msg, wParam, lParam);
426
  }
427
428
  return DefWindowProc(hWnd, Msg, wParam, lParam);
429
}
430
431
static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
432
{
433
  rdpdrPlugin* rdpdr;
434
  WNDCLASSEX wnd_cls;
435
  HWND hwnd;
436
  MSG msg;
437
  BOOL bRet;
438
  DEV_BROADCAST_HANDLE NotificationFilter;
439
  HDEVNOTIFY hDevNotify;
440
  rdpdr = (rdpdrPlugin*)arg;
441
  /* init windows class */
442
  wnd_cls.cbSize = sizeof(WNDCLASSEX);
443
  wnd_cls.style = CS_HREDRAW | CS_VREDRAW;
444
  wnd_cls.lpfnWndProc = hotplug_proc;
445
  wnd_cls.cbClsExtra = 0;
446
  wnd_cls.cbWndExtra = 0;
447
  wnd_cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
448
  wnd_cls.hCursor = NULL;
449
  wnd_cls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
450
  wnd_cls.lpszMenuName = NULL;
451
  wnd_cls.lpszClassName = L"DRIVE_HOTPLUG";
452
  wnd_cls.hInstance = NULL;
453
  wnd_cls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
454
  RegisterClassEx(&wnd_cls);
455
  /* create window */
456
  hwnd = CreateWindowEx(0, L"DRIVE_HOTPLUG", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
457
  SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)rdpdr);
458
  rdpdr->hotplug_wnd = hwnd;
459
  /* register device interface to hwnd */
460
  NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
461
  NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
462
  hDevNotify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
463
464
  /* message loop */
465
  while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
466
  {
467
    if (bRet == -1)
468
    {
469
      break;
470
    }
471
    else
472
    {
473
      TranslateMessage(&msg);
474
      DispatchMessage(&msg);
475
    }
476
  }
477
478
  UnregisterDeviceNotification(hDevNotify);
479
  return CHANNEL_RC_OK;
480
}
481
482
/**
483
 * Function description
484
 *
485
 * @return 0 on success, otherwise a Win32 error code
486
 */
487
static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
488
{
489
  UINT error = CHANNEL_RC_OK;
490
491
  if (rdpdr->hotplug_wnd && !PostMessage(rdpdr->hotplug_wnd, WM_QUIT, 0, 0))
492
  {
493
    error = GetLastError();
494
    WLog_Print(rdpdr->log, WLOG_ERROR, "PostMessage failed with error %" PRIu32 "", error);
495
  }
496
497
  return error;
498
}
499
500
#elif defined(__MACOSX__)
501
502
#define MAX_USB_DEVICES 100
503
504
typedef struct
505
{
506
  char* path;
507
  BOOL to_add;
508
} hotplug_dev;
509
510
/**
511
 * Function description
512
 *
513
 * @return 0 on success, otherwise a Win32 error code
514
 */
515
static UINT handle_hotplug(rdpdrPlugin* rdpdr)
516
{
517
  struct dirent* pDirent;
518
  DIR* pDir;
519
  char fullpath[PATH_MAX];
520
  char* szdir = (char*)"/Volumes";
521
  struct stat buf;
522
  hotplug_dev dev_array[MAX_USB_DEVICES];
523
  int count;
524
  DEVICE_DRIVE_EXT* device_ext;
525
  ULONG_PTR* keys = NULL;
526
  int size = 0;
527
  UINT error;
528
  UINT32 ids[1];
529
  pDir = opendir(szdir);
530
531
  if (pDir == NULL)
532
  {
533
    printf("Cannot open directory\n");
534
    return ERROR_OPEN_FAILED;
535
  }
536
537
  while ((pDirent = readdir(pDir)) != NULL)
538
  {
539
    if (pDirent->d_name[0] != '.')
540
    {
541
      sprintf_s(fullpath, ARRAYSIZE(fullpath), "%s/%s", szdir, pDirent->d_name);
542
      if (stat(fullpath, &buf) != 0)
543
        continue;
544
545
      if (S_ISDIR(buf.st_mode))
546
      {
547
        dev_array[size].path = _strdup(fullpath);
548
549
        if (!dev_array[size].path)
550
        {
551
          closedir(pDir);
552
          error = CHANNEL_RC_NO_MEMORY;
553
          goto cleanup;
554
        }
555
556
        dev_array[size++].to_add = TRUE;
557
      }
558
    }
559
  }
560
561
  closedir(pDir);
562
  /* delete removed devices */
563
  count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
564
565
  for (size_t j = 0; j < count; j++)
566
  {
567
    char* path = NULL;
568
    BOOL dev_found = FALSE;
569
    device_ext =
570
        (DEVICE_DRIVE_EXT*)ListDictionary_GetItemValue(rdpdr->devman->devices, (void*)keys[j]);
571
572
    if (!device_ext || !device_ext->automount)
573
      continue;
574
575
    if (device_ext->device.type != RDPDR_DTYP_FILESYSTEM)
576
      continue;
577
578
    if (device_ext->path == NULL)
579
      continue;
580
581
    path = ConvertWCharToUtf8Alloc(device_ext->path, NULL);
582
    if (!path)
583
      continue;
584
585
    /* not plugable device */
586
    if (strstr(path, "/Volumes/") == NULL)
587
    {
588
      free(path);
589
      continue;
590
    }
591
592
    for (size_t i = 0; i < size; i++)
593
    {
594
      if (strstr(path, dev_array[i].path) != NULL)
595
      {
596
        dev_found = TRUE;
597
        dev_array[i].to_add = FALSE;
598
        break;
599
      }
600
    }
601
602
    free(path);
603
604
    if (!dev_found)
605
    {
606
      devman_unregister_device(rdpdr->devman, (void*)keys[j]);
607
      ids[0] = keys[j];
608
609
      if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids)))
610
      {
611
        WLog_Print(rdpdr->log, WLOG_ERROR,
612
                   "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
613
                   error);
614
        goto cleanup;
615
      }
616
    }
617
  }
618
619
  /* add new devices */
620
  for (size_t i = 0; i < size; i++)
621
  {
622
    const hotplug_dev* dev = &dev_array[i];
623
    if (dev->to_add)
624
    {
625
      const char* path = dev->path;
626
      const char* name = strrchr(path, '/') + 1;
627
      error = rdpdr_load_drive(rdpdr, name, path, TRUE);
628
      if (error)
629
        goto cleanup;
630
    }
631
  }
632
633
cleanup:
634
  free(keys);
635
636
  for (size_t i = 0; i < size; i++)
637
    free(dev_array[i].path);
638
639
  return error;
640
}
641
642
static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef,
643
                                           void* clientCallBackInfo, size_t numEvents,
644
                                           void* eventPaths,
645
                                           const FSEventStreamEventFlags eventFlags[],
646
                                           const FSEventStreamEventId eventIds[])
647
{
648
  rdpdrPlugin* rdpdr;
649
  UINT error;
650
  char** paths = (char**)eventPaths;
651
  rdpdr = (rdpdrPlugin*)clientCallBackInfo;
652
653
  for (size_t i = 0; i < numEvents; i++)
654
  {
655
    if (strcmp(paths[i], "/Volumes/") == 0)
656
    {
657
      if ((error = handle_hotplug(rdpdr)))
658
      {
659
        WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
660
                   error);
661
      }
662
      else
663
        rdpdr_try_send_device_list_announce_request(rdpdr);
664
665
      return;
666
    }
667
  }
668
}
669
670
static void first_hotplug(rdpdrPlugin* rdpdr)
671
{
672
  UINT error;
673
674
  if ((error = handle_hotplug(rdpdr)))
675
  {
676
    WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", error);
677
  }
678
}
679
680
static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
681
{
682
  rdpdrPlugin* rdpdr;
683
  FSEventStreamRef fsev;
684
  rdpdr = (rdpdrPlugin*)arg;
685
  CFStringRef path = CFSTR("/Volumes/");
686
  CFArrayRef pathsToWatch = CFArrayCreate(kCFAllocatorMalloc, (const void**)&path, 1, NULL);
687
  FSEventStreamContext ctx = { 0 };
688
689
  ctx.info = arg;
690
691
  WINPR_ASSERT(!rdpdr->stopEvent);
692
  rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
693
  if (!rdpdr->stopEvent)
694
    goto out;
695
696
  fsev =
697
      FSEventStreamCreate(kCFAllocatorMalloc, drive_hotplug_fsevent_callback, &ctx, pathsToWatch,
698
                          kFSEventStreamEventIdSinceNow, 1, kFSEventStreamCreateFlagNone);
699
700
  rdpdr->runLoop = CFRunLoopGetCurrent();
701
  FSEventStreamScheduleWithRunLoop(fsev, rdpdr->runLoop, kCFRunLoopDefaultMode);
702
  FSEventStreamStart(fsev);
703
  CFRunLoopRun();
704
  FSEventStreamStop(fsev);
705
  FSEventStreamRelease(fsev);
706
out:
707
  if (rdpdr->stopEvent)
708
  {
709
    CloseHandle(rdpdr->stopEvent);
710
    rdpdr->stopEvent = NULL;
711
  }
712
  ExitThread(CHANNEL_RC_OK);
713
  return CHANNEL_RC_OK;
714
}
715
716
#else
717
718
static const char* automountLocations[] = { "/run/user/%lu/gvfs", "/run/media/%s", "/media/%s",
719
                                          "/media", "/mnt" };
720
721
static BOOL isAutomountLocation(const char* path)
722
0
{
723
0
  const size_t nrLocations = sizeof(automountLocations) / sizeof(automountLocations[0]);
724
0
  char buffer[MAX_PATH] = { 0 };
725
0
  uid_t uid = getuid();
726
0
  char uname[MAX_PATH] = { 0 };
727
0
  ULONG size = sizeof(uname) - 1;
728
729
0
  if (!GetUserNameExA(NameSamCompatible, uname, &size))
730
0
    return FALSE;
731
732
0
  if (!path)
733
0
    return FALSE;
734
735
0
  for (size_t x = 0; x < nrLocations; x++)
736
0
  {
737
0
    const char* location = automountLocations[x];
738
0
    size_t length = 0;
739
740
0
    if (strstr(location, "%lu"))
741
0
      snprintf(buffer, sizeof(buffer), location, (unsigned long)uid);
742
0
    else if (strstr(location, "%s"))
743
0
      snprintf(buffer, sizeof(buffer), location, uname);
744
0
    else
745
0
      snprintf(buffer, sizeof(buffer), "%s", location);
746
747
0
    length = strnlen(buffer, sizeof(buffer));
748
749
0
    if (strncmp(buffer, path, length) == 0)
750
0
    {
751
0
      const char* rest = &path[length];
752
753
      /* Only consider mount locations with max depth of 1 below the
754
       * base path or the base path itself. */
755
0
      if (*rest == '\0')
756
0
        return TRUE;
757
0
      else if (*rest == '/')
758
0
      {
759
0
        const char* token = strstr(&rest[1], "/");
760
761
0
        if (!token || (token[1] == '\0'))
762
0
          return TRUE;
763
0
      }
764
0
    }
765
0
  }
766
767
0
  return FALSE;
768
0
}
769
770
0
#define MAX_USB_DEVICES 100
771
772
typedef struct
773
{
774
  char* path;
775
  BOOL to_add;
776
} hotplug_dev;
777
778
static void handle_mountpoint(hotplug_dev* dev_array, size_t* size, const char* mountpoint)
779
0
{
780
0
  if (!mountpoint)
781
0
    return;
782
  /* copy hotpluged device mount point to the dev_array */
783
0
  if (isAutomountLocation(mountpoint) && (*size < MAX_USB_DEVICES))
784
0
  {
785
0
    dev_array[*size].path = _strdup(mountpoint);
786
0
    dev_array[*size].to_add = TRUE;
787
0
    (*size)++;
788
0
  }
789
0
}
790
791
#ifdef __sun
792
#include <sys/mnttab.h>
793
static UINT handle_platform_mounts_sun(wLog* log, hotplug_dev* dev_array, size_t* size)
794
{
795
  FILE* f;
796
  struct mnttab ent;
797
  f = winpr_fopen("/etc/mnttab", "r");
798
  if (f == NULL)
799
  {
800
    WLog_Print(log, WLOG_ERROR, "fopen failed!");
801
    return ERROR_OPEN_FAILED;
802
  }
803
  while (getmntent(f, &ent) == 0)
804
  {
805
    handle_mountpoint(dev_array, size, ent.mnt_mountp);
806
  }
807
  fclose(f);
808
  return ERROR_SUCCESS;
809
}
810
#endif
811
812
#if defined(__FreeBSD__) || defined(__OpenBSD__)
813
#include <sys/mount.h>
814
static UINT handle_platform_mounts_bsd(wLog* log, hotplug_dev* dev_array, size_t* size)
815
{
816
  int mntsize;
817
  struct statfs* mntbuf = NULL;
818
819
  mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
820
  if (!mntsize)
821
  {
822
    /* TODO: handle 'errno' */
823
    WLog_Print(log, WLOG_ERROR, "getmntinfo failed!");
824
    return ERROR_OPEN_FAILED;
825
  }
826
  for (size_t idx = 0; idx < (size_t)mntsize; idx++)
827
  {
828
    handle_mountpoint(dev_array, size, mntbuf[idx].f_mntonname);
829
  }
830
  free(mntbuf);
831
  return ERROR_SUCCESS;
832
}
833
#endif
834
835
#if defined(__LINUX__) || defined(__linux__)
836
#include <mntent.h>
837
static UINT handle_platform_mounts_linux(wLog* log, hotplug_dev* dev_array, size_t* size)
838
0
{
839
0
  FILE* f = NULL;
840
0
  struct mntent* ent = NULL;
841
0
  f = winpr_fopen("/proc/mounts", "r");
842
0
  if (f == NULL)
843
0
  {
844
0
    WLog_Print(log, WLOG_ERROR, "fopen failed!");
845
0
    return ERROR_OPEN_FAILED;
846
0
  }
847
0
  while ((ent = getmntent(f)) != NULL)
848
0
  {
849
0
    handle_mountpoint(dev_array, size, ent->mnt_dir);
850
0
  }
851
0
  fclose(f);
852
0
  return ERROR_SUCCESS;
853
0
}
854
#endif
855
856
static UINT handle_platform_mounts(wLog* log, hotplug_dev* dev_array, size_t* size)
857
0
{
858
#ifdef __sun
859
  return handle_platform_mounts_sun(log, dev_array, size);
860
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
861
  return handle_platform_mounts_bsd(log, dev_array, size);
862
#elif defined(__LINUX__) || defined(__linux__)
863
  return handle_platform_mounts_linux(log, dev_array, size);
864
0
#endif
865
0
  return ERROR_CALL_NOT_IMPLEMENTED;
866
0
}
867
868
static BOOL device_not_plugged(ULONG_PTR key, void* element, void* data)
869
0
{
870
0
  const WCHAR* path = (const WCHAR*)data;
871
0
  DEVICE_DRIVE_EXT* device_ext = (DEVICE_DRIVE_EXT*)element;
872
873
0
  WINPR_UNUSED(key);
874
0
  WINPR_ASSERT(path);
875
876
0
  if (!device_ext || (device_ext->device.type != RDPDR_DTYP_FILESYSTEM) || !device_ext->path)
877
0
    return TRUE;
878
0
  if (_wcscmp(device_ext->path, path) != 0)
879
0
    return TRUE;
880
0
  return FALSE;
881
0
}
882
883
static BOOL device_already_plugged(rdpdrPlugin* rdpdr, const hotplug_dev* device)
884
0
{
885
0
  BOOL rc = FALSE;
886
0
  WCHAR* path = NULL;
887
888
0
  if (!rdpdr || !device)
889
0
    return TRUE;
890
0
  if (!device->to_add)
891
0
    return TRUE;
892
893
0
  WINPR_ASSERT(rdpdr->devman);
894
0
  WINPR_ASSERT(device->path);
895
896
0
  path = ConvertUtf8ToWCharAlloc(device->path, NULL);
897
0
  if (!path)
898
0
    return TRUE;
899
900
0
  rc = device_foreach(rdpdr, TRUE, device_not_plugged, path);
901
0
  free(path);
902
0
  return !rc;
903
0
}
904
905
struct hotplug_delete_arg
906
{
907
  hotplug_dev* dev_array;
908
  size_t dev_array_size;
909
  rdpdrPlugin* rdpdr;
910
};
911
912
static BOOL hotplug_delete_foreach(ULONG_PTR key, void* element, void* data)
913
0
{
914
0
  char* path = NULL;
915
0
  BOOL dev_found = FALSE;
916
0
  struct hotplug_delete_arg* arg = (struct hotplug_delete_arg*)data;
917
0
  DEVICE_DRIVE_EXT* device_ext = (DEVICE_DRIVE_EXT*)element;
918
919
0
  WINPR_ASSERT(arg);
920
0
  WINPR_ASSERT(arg->rdpdr);
921
0
  WINPR_ASSERT(arg->dev_array || (arg->dev_array_size == 0));
922
923
0
  if (!device_ext || (device_ext->device.type != RDPDR_DTYP_FILESYSTEM) || !device_ext->path ||
924
0
      !device_ext->automount)
925
0
    return TRUE;
926
927
0
  WINPR_ASSERT(device_ext->path);
928
0
  path = ConvertWCharToUtf8Alloc(device_ext->path, NULL);
929
0
  if (!path)
930
0
    return FALSE;
931
932
  /* not plugable device */
933
0
  if (isAutomountLocation(path))
934
0
  {
935
0
    for (size_t i = 0; i < arg->dev_array_size; i++)
936
0
    {
937
0
      hotplug_dev* cur = &arg->dev_array[i];
938
0
      if (cur->path && strstr(path, cur->path) != NULL)
939
0
      {
940
0
        dev_found = TRUE;
941
0
        cur->to_add = FALSE;
942
0
        break;
943
0
      }
944
0
    }
945
0
  }
946
947
0
  free(path);
948
949
0
  if (!dev_found)
950
0
  {
951
0
    UINT error = 0;
952
0
    UINT32 ids[1] = { key };
953
954
0
    WINPR_ASSERT(arg->rdpdr->devman);
955
0
    devman_unregister_device(arg->rdpdr->devman, (void*)key);
956
0
    WINPR_ASSERT(key <= UINT32_MAX);
957
958
0
    error = rdpdr_send_device_list_remove_request(arg->rdpdr, 1, ids);
959
0
    if (error)
960
0
    {
961
0
      WLog_Print(arg->rdpdr->log, WLOG_ERROR,
962
0
                 "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
963
0
                 error);
964
0
      return FALSE;
965
0
    }
966
0
  }
967
968
0
  return TRUE;
969
0
}
970
971
static UINT handle_hotplug(rdpdrPlugin* rdpdr)
972
0
{
973
0
  hotplug_dev dev_array[MAX_USB_DEVICES] = { 0 };
974
0
  size_t size = 0;
975
0
  UINT error = ERROR_SUCCESS;
976
0
  struct hotplug_delete_arg arg = { dev_array, ARRAYSIZE(dev_array), rdpdr };
977
978
0
  WINPR_ASSERT(rdpdr);
979
0
  WINPR_ASSERT(rdpdr->devman);
980
981
0
  error = handle_platform_mounts(rdpdr->log, dev_array, &size);
982
983
  /* delete removed devices */
984
0
  /* Ignore result */ device_foreach(rdpdr, FALSE, hotplug_delete_foreach, &arg);
985
986
  /* add new devices */
987
0
  for (size_t i = 0; i < size; i++)
988
0
  {
989
0
    hotplug_dev* cur = &dev_array[i];
990
0
    if (!device_already_plugged(rdpdr, cur))
991
0
    {
992
0
      const char* path = cur->path;
993
0
      const char* name = strrchr(path, '/') + 1;
994
995
0
      rdpdr_load_drive(rdpdr, name, path, TRUE);
996
0
      error = ERROR_DISK_CHANGE;
997
0
    }
998
0
  }
999
1000
0
  for (size_t i = 0; i < size; i++)
1001
0
    free(dev_array[i].path);
1002
1003
0
  return error;
1004
0
}
1005
1006
static void first_hotplug(rdpdrPlugin* rdpdr)
1007
0
{
1008
0
  UINT error = 0;
1009
1010
0
  WINPR_ASSERT(rdpdr);
1011
0
  if ((error = handle_hotplug(rdpdr)))
1012
0
  {
1013
0
    switch (error)
1014
0
    {
1015
0
      case ERROR_DISK_CHANGE:
1016
0
      case CHANNEL_RC_OK:
1017
0
      case ERROR_OPEN_FAILED:
1018
0
      case ERROR_CALL_NOT_IMPLEMENTED:
1019
0
        break;
1020
0
      default:
1021
0
        WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
1022
0
                   error);
1023
0
        break;
1024
0
    }
1025
0
  }
1026
0
}
1027
1028
static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
1029
0
{
1030
0
  rdpdrPlugin* rdpdr = NULL;
1031
0
  UINT error = 0;
1032
0
  rdpdr = (rdpdrPlugin*)arg;
1033
1034
0
  WINPR_ASSERT(rdpdr);
1035
1036
0
  WINPR_ASSERT(!rdpdr->stopEvent);
1037
0
  rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1038
0
  if (!rdpdr->stopEvent)
1039
0
    goto out;
1040
1041
0
  while (WaitForSingleObject(rdpdr->stopEvent, 1000) == WAIT_TIMEOUT)
1042
0
  {
1043
0
    error = handle_hotplug(rdpdr);
1044
0
    switch (error)
1045
0
    {
1046
0
      case ERROR_DISK_CHANGE:
1047
0
        rdpdr_try_send_device_list_announce_request(rdpdr);
1048
0
        break;
1049
0
      case CHANNEL_RC_OK:
1050
0
      case ERROR_OPEN_FAILED:
1051
0
      case ERROR_CALL_NOT_IMPLEMENTED:
1052
0
        break;
1053
0
      default:
1054
0
        WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
1055
0
                   error);
1056
0
        goto out;
1057
0
    }
1058
0
  }
1059
1060
0
out:
1061
0
  error = GetLastError();
1062
0
  if (error && rdpdr->rdpcontext)
1063
0
    setChannelError(rdpdr->rdpcontext, error, "reported an error");
1064
1065
0
  if (rdpdr->stopEvent)
1066
0
  {
1067
0
    CloseHandle(rdpdr->stopEvent);
1068
0
    rdpdr->stopEvent = NULL;
1069
0
  }
1070
1071
0
  ExitThread(error);
1072
0
  return error;
1073
0
}
1074
1075
#endif
1076
1077
#if !defined(_WIN32) && !defined(__IOS__)
1078
/**
1079
 * Function description
1080
 *
1081
 * @return 0 on success, otherwise a Win32 error code
1082
 */
1083
static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
1084
0
{
1085
0
  UINT error = 0;
1086
1087
0
  WINPR_ASSERT(rdpdr);
1088
1089
0
  if (rdpdr->hotplugThread)
1090
0
  {
1091
0
#if !defined(_WIN32)
1092
0
    if (rdpdr->stopEvent)
1093
0
      SetEvent(rdpdr->stopEvent);
1094
0
#endif
1095
#ifdef __MACOSX__
1096
    CFRunLoopStop(rdpdr->runLoop);
1097
#endif
1098
1099
0
    if (WaitForSingleObject(rdpdr->hotplugThread, INFINITE) == WAIT_FAILED)
1100
0
    {
1101
0
      error = GetLastError();
1102
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
1103
0
                 error);
1104
0
      return error;
1105
0
    }
1106
1107
0
    CloseHandle(rdpdr->hotplugThread);
1108
0
    rdpdr->hotplugThread = NULL;
1109
0
  }
1110
1111
0
  return CHANNEL_RC_OK;
1112
0
}
1113
1114
#endif
1115
1116
/**
1117
 * Function description
1118
 *
1119
 * @return 0 on success, otherwise a Win32 error code
1120
 */
1121
static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
1122
0
{
1123
0
  UINT error = CHANNEL_RC_OK;
1124
1125
0
  WINPR_ASSERT(rdpdr);
1126
1127
0
  rdpdr->devman = devman_new(rdpdr);
1128
1129
0
  if (!rdpdr->devman)
1130
0
  {
1131
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "devman_new failed!");
1132
0
    return CHANNEL_RC_NO_MEMORY;
1133
0
  }
1134
1135
0
  WINPR_ASSERT(rdpdr->rdpcontext);
1136
1137
0
  rdpSettings* settings = rdpdr->rdpcontext->settings;
1138
0
  WINPR_ASSERT(settings);
1139
1140
0
  rdpdr->ignoreInvalidDevices = freerdp_settings_get_bool(settings, FreeRDP_IgnoreInvalidDevices);
1141
1142
0
  const char* name = freerdp_settings_get_string(settings, FreeRDP_ClientHostname);
1143
0
  if (!name)
1144
0
    name = freerdp_settings_get_string(settings, FreeRDP_ComputerName);
1145
0
  strncpy(rdpdr->computerName, name, sizeof(rdpdr->computerName) - 1);
1146
1147
0
  for (UINT32 index = 0; index < freerdp_settings_get_uint32(settings, FreeRDP_DeviceCount);
1148
0
       index++)
1149
0
  {
1150
0
    const RDPDR_DEVICE* device =
1151
0
        freerdp_settings_get_pointer_array(settings, FreeRDP_DeviceArray, index);
1152
1153
0
    if (device->Type == RDPDR_DTYP_FILESYSTEM)
1154
0
    {
1155
0
      const char DynamicDrives[] = "DynamicDrives";
1156
0
      const RDPDR_DRIVE* drive = (const RDPDR_DRIVE*)device;
1157
0
      if (!drive->Path)
1158
0
        continue;
1159
1160
0
      BOOL hotplugAll = strncmp(drive->Path, "*", 2) == 0;
1161
0
      BOOL hotplugLater = strncmp(drive->Path, DynamicDrives, sizeof(DynamicDrives)) == 0;
1162
1163
0
      if (hotplugAll || hotplugLater)
1164
0
      {
1165
0
        if (!rdpdr->async)
1166
0
        {
1167
0
          WLog_Print(rdpdr->log, WLOG_WARN,
1168
0
                     "Drive hotplug is not supported in synchronous mode!");
1169
0
          continue;
1170
0
        }
1171
1172
0
        if (hotplugAll)
1173
0
          first_hotplug(rdpdr);
1174
1175
        /* There might be multiple hotplug related device entries.
1176
         * Ensure the thread is only started once
1177
         */
1178
0
        if (!rdpdr->hotplugThread)
1179
0
        {
1180
0
          rdpdr->hotplugThread =
1181
0
              CreateThread(NULL, 0, drive_hotplug_thread_func, rdpdr, 0, NULL);
1182
0
          if (!rdpdr->hotplugThread)
1183
0
          {
1184
0
            WLog_Print(rdpdr->log, WLOG_ERROR, "CreateThread failed!");
1185
0
            return ERROR_INTERNAL_ERROR;
1186
0
          }
1187
0
        }
1188
1189
0
        continue;
1190
0
      }
1191
0
    }
1192
1193
0
    if ((error = devman_load_device_service(rdpdr->devman, device, rdpdr->rdpcontext)))
1194
0
    {
1195
0
      WLog_Print(rdpdr->log, WLOG_ERROR,
1196
0
                 "devman_load_device_service failed with error %" PRIu32 "!", error);
1197
0
      return error;
1198
0
    }
1199
0
  }
1200
1201
0
  return error;
1202
0
}
1203
1204
static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s)
1205
0
{
1206
0
  WINPR_ASSERT(rdpdr);
1207
0
  WINPR_ASSERT(s);
1208
1209
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 8))
1210
0
    return ERROR_INVALID_DATA;
1211
1212
0
  Stream_Read_UINT16(s, rdpdr->serverVersionMajor);
1213
0
  Stream_Read_UINT16(s, rdpdr->serverVersionMinor);
1214
0
  Stream_Read_UINT32(s, rdpdr->clientID);
1215
0
  rdpdr->sequenceId++;
1216
1217
0
  rdpdr->clientVersionMajor = MIN(RDPDR_VERSION_MAJOR, rdpdr->serverVersionMajor);
1218
0
  rdpdr->clientVersionMinor = MIN(RDPDR_VERSION_MINOR_RDP10X, rdpdr->serverVersionMinor);
1219
0
  WLog_Print(rdpdr->log, WLOG_DEBUG,
1220
0
             "[rdpdr] server announces version %" PRIu32 ".%" PRIu32 ", client uses %" PRIu32
1221
0
             ".%" PRIu32,
1222
0
             rdpdr->serverVersionMajor, rdpdr->serverVersionMinor, rdpdr->clientVersionMajor,
1223
0
             rdpdr->clientVersionMinor);
1224
0
  return CHANNEL_RC_OK;
1225
0
}
1226
1227
/**
1228
 * Function description
1229
 *
1230
 * @return 0 on success, otherwise a Win32 error code
1231
 */
1232
static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr)
1233
0
{
1234
0
  wStream* s = NULL;
1235
1236
0
  WINPR_ASSERT(rdpdr);
1237
0
  WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE);
1238
0
  rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY);
1239
1240
0
  s = StreamPool_Take(rdpdr->pool, 12);
1241
1242
0
  if (!s)
1243
0
  {
1244
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
1245
0
    return CHANNEL_RC_NO_MEMORY;
1246
0
  }
1247
1248
0
  Stream_Write_UINT16(s, RDPDR_CTYP_CORE);             /* Component (2 bytes) */
1249
0
  Stream_Write_UINT16(s, PAKID_CORE_CLIENTID_CONFIRM); /* PacketId (2 bytes) */
1250
0
  Stream_Write_UINT16(s, rdpdr->clientVersionMajor);
1251
0
  Stream_Write_UINT16(s, rdpdr->clientVersionMinor);
1252
0
  Stream_Write_UINT32(s, (UINT32)rdpdr->clientID);
1253
0
  return rdpdr_send(rdpdr, s);
1254
0
}
1255
1256
/**
1257
 * Function description
1258
 *
1259
 * @return 0 on success, otherwise a Win32 error code
1260
 */
1261
static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
1262
0
{
1263
0
  wStream* s = NULL;
1264
0
  WCHAR* computerNameW = NULL;
1265
0
  size_t computerNameLenW = 0;
1266
1267
0
  WINPR_ASSERT(rdpdr);
1268
0
  WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY);
1269
0
  rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_NAME_REQUEST);
1270
1271
0
  if (!rdpdr->computerName[0])
1272
0
  {
1273
0
    DWORD size = sizeof(rdpdr->computerName) - 1;
1274
0
    GetComputerNameA(rdpdr->computerName, &size);
1275
0
  }
1276
1277
0
  WINPR_ASSERT(rdpdr->computerName);
1278
0
  computerNameW = ConvertUtf8ToWCharAlloc(rdpdr->computerName, &computerNameLenW);
1279
0
  computerNameLenW *= sizeof(WCHAR);
1280
1281
0
  if (computerNameLenW > 0)
1282
0
    computerNameLenW += sizeof(WCHAR); // also write '\0'
1283
1284
0
  s = StreamPool_Take(rdpdr->pool, 16U + computerNameLenW);
1285
1286
0
  if (!s)
1287
0
  {
1288
0
    free(computerNameW);
1289
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
1290
0
    return CHANNEL_RC_NO_MEMORY;
1291
0
  }
1292
1293
0
  Stream_Write_UINT16(s, RDPDR_CTYP_CORE);        /* Component (2 bytes) */
1294
0
  Stream_Write_UINT16(s, PAKID_CORE_CLIENT_NAME); /* PacketId (2 bytes) */
1295
0
  Stream_Write_UINT32(s, 1);                      /* unicodeFlag, 0 for ASCII and 1 for Unicode */
1296
0
  Stream_Write_UINT32(s, 0);                      /* codePage, must be set to zero */
1297
0
  Stream_Write_UINT32(s,
1298
0
                      (UINT32)computerNameLenW); /* computerNameLen, including null terminator */
1299
0
  Stream_Write(s, computerNameW, (size_t)computerNameLenW);
1300
0
  free(computerNameW);
1301
0
  return rdpdr_send(rdpdr, s);
1302
0
}
1303
1304
static UINT rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s)
1305
0
{
1306
0
  UINT16 versionMajor = 0;
1307
0
  UINT16 versionMinor = 0;
1308
0
  UINT32 clientID = 0;
1309
1310
0
  WINPR_ASSERT(rdpdr);
1311
0
  WINPR_ASSERT(s);
1312
1313
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 8))
1314
0
    return ERROR_INVALID_DATA;
1315
1316
0
  Stream_Read_UINT16(s, versionMajor);
1317
0
  Stream_Read_UINT16(s, versionMinor);
1318
0
  Stream_Read_UINT32(s, clientID);
1319
1320
0
  if (versionMajor != rdpdr->clientVersionMajor || versionMinor != rdpdr->clientVersionMinor)
1321
0
  {
1322
0
    WLog_Print(rdpdr->log, WLOG_WARN,
1323
0
               "[rdpdr] server announced version %" PRIu32 ".%" PRIu32 ", client uses %" PRIu32
1324
0
               ".%" PRIu32 " but clientid confirm requests version %" PRIu32 ".%" PRIu32,
1325
0
               rdpdr->serverVersionMajor, rdpdr->serverVersionMinor, rdpdr->clientVersionMajor,
1326
0
               rdpdr->clientVersionMinor, versionMajor, versionMinor);
1327
0
    rdpdr->clientVersionMajor = versionMajor;
1328
0
    rdpdr->clientVersionMinor = versionMinor;
1329
0
  }
1330
1331
0
  if (clientID != rdpdr->clientID)
1332
0
    rdpdr->clientID = clientID;
1333
1334
0
  return CHANNEL_RC_OK;
1335
0
}
1336
1337
struct device_announce_arg
1338
{
1339
  rdpdrPlugin* rdpdr;
1340
  wStream* s;
1341
  BOOL userLoggedOn;
1342
  UINT32 count;
1343
};
1344
1345
static BOOL device_announce(ULONG_PTR key, void* element, void* data)
1346
0
{
1347
0
  struct device_announce_arg* arg = data;
1348
0
  rdpdrPlugin* rdpdr = NULL;
1349
0
  DEVICE* device = (DEVICE*)element;
1350
1351
0
  WINPR_UNUSED(key);
1352
1353
0
  WINPR_ASSERT(arg);
1354
0
  WINPR_ASSERT(device);
1355
0
  WINPR_ASSERT(arg->rdpdr);
1356
0
  WINPR_ASSERT(arg->s);
1357
1358
0
  rdpdr = arg->rdpdr;
1359
1360
  /**
1361
   * 1. versionMinor 0x0005 doesn't send PAKID_CORE_USER_LOGGEDON
1362
   *    so all devices should be sent regardless of user_loggedon
1363
   * 2. smartcard devices should be always sent
1364
   * 3. other devices are sent only after user_loggedon
1365
   */
1366
1367
0
  if ((rdpdr->clientVersionMinor == RDPDR_VERSION_MINOR_RDP51) ||
1368
0
      (device->type == RDPDR_DTYP_SMARTCARD) || arg->userLoggedOn)
1369
0
  {
1370
0
    size_t data_len = (device->data == NULL ? 0 : Stream_GetPosition(device->data));
1371
1372
0
    if (!Stream_EnsureRemainingCapacity(arg->s, 20 + data_len))
1373
0
    {
1374
0
      Stream_Release(arg->s);
1375
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
1376
0
      return FALSE;
1377
0
    }
1378
1379
0
    Stream_Write_UINT32(arg->s, device->type); /* deviceType */
1380
0
    Stream_Write_UINT32(arg->s, device->id);   /* deviceID */
1381
0
    strncpy(Stream_Pointer(arg->s), device->name, 8);
1382
1383
0
    for (size_t i = 0; i < 8; i++)
1384
0
    {
1385
0
      BYTE c = 0;
1386
0
      Stream_Peek_UINT8(arg->s, c);
1387
1388
0
      if (c > 0x7F)
1389
0
        Stream_Write_UINT8(arg->s, '_');
1390
0
      else
1391
0
        Stream_Seek_UINT8(arg->s);
1392
0
    }
1393
1394
0
    WINPR_ASSERT(data_len <= UINT32_MAX);
1395
0
    Stream_Write_UINT32(arg->s, (UINT32)data_len);
1396
1397
0
    if (data_len > 0)
1398
0
      Stream_Write(arg->s, Stream_Buffer(device->data), data_len);
1399
1400
0
    arg->count++;
1401
0
    WLog_Print(rdpdr->log, WLOG_INFO,
1402
0
               "registered [%09s] device #%" PRIu32 ": %s (type=%" PRIu32 " id=%" PRIu32 ")",
1403
0
               rdpdr_device_type_string(device->type), arg->count, device->name, device->type,
1404
0
               device->id);
1405
0
  }
1406
0
  return TRUE;
1407
0
}
1408
1409
static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL userLoggedOn)
1410
0
{
1411
0
  size_t pos = 0;
1412
0
  wStream* s = NULL;
1413
0
  size_t count_pos = 0;
1414
0
  struct device_announce_arg arg = { 0 };
1415
1416
0
  WINPR_ASSERT(rdpdr);
1417
0
  WINPR_ASSERT(rdpdr->devman);
1418
1419
0
  if (userLoggedOn)
1420
0
  {
1421
0
    rdpdr->userLoggedOn = TRUE;
1422
0
  }
1423
1424
0
  s = StreamPool_Take(rdpdr->pool, 256);
1425
1426
0
  if (!s)
1427
0
  {
1428
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
1429
0
    return CHANNEL_RC_NO_MEMORY;
1430
0
  }
1431
1432
0
  Stream_Write_UINT16(s, RDPDR_CTYP_CORE);                /* Component (2 bytes) */
1433
0
  Stream_Write_UINT16(s, PAKID_CORE_DEVICELIST_ANNOUNCE); /* PacketId (2 bytes) */
1434
0
  count_pos = Stream_GetPosition(s);
1435
0
  Stream_Seek_UINT32(s); /* deviceCount */
1436
1437
0
  arg.rdpdr = rdpdr;
1438
0
  arg.userLoggedOn = userLoggedOn;
1439
0
  arg.s = s;
1440
0
  if (!device_foreach(rdpdr, TRUE, device_announce, &arg))
1441
0
    return ERROR_INVALID_DATA;
1442
1443
0
  if (arg.count == 0)
1444
0
  {
1445
0
    Stream_Release(s);
1446
0
    return CHANNEL_RC_OK;
1447
0
  }
1448
0
  pos = Stream_GetPosition(s);
1449
0
  Stream_SetPosition(s, count_pos);
1450
0
  Stream_Write_UINT32(s, arg.count);
1451
0
  Stream_SetPosition(s, pos);
1452
0
  Stream_SealLength(s);
1453
0
  return rdpdr_send(rdpdr, s);
1454
0
}
1455
1456
UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr)
1457
0
{
1458
0
  WINPR_ASSERT(rdpdr);
1459
0
  if (rdpdr->state != RDPDR_CHANNEL_STATE_USER_LOGGEDON)
1460
0
  {
1461
0
    WLog_Print(rdpdr->log, WLOG_DEBUG,
1462
0
               "hotplug event received, but channel [RDPDR] is not ready (state %s), ignoring.",
1463
0
               rdpdr_state_str(rdpdr->state));
1464
0
    return CHANNEL_RC_OK;
1465
0
  }
1466
0
  return rdpdr_send_device_list_announce_request(rdpdr, TRUE);
1467
0
}
1468
1469
static UINT dummy_irp_response(rdpdrPlugin* rdpdr, wStream* s)
1470
0
{
1471
0
  wStream* output = NULL;
1472
0
  UINT32 DeviceId = 0;
1473
0
  UINT32 FileId = 0;
1474
0
  UINT32 CompletionId = 0;
1475
1476
0
  WINPR_ASSERT(rdpdr);
1477
0
  WINPR_ASSERT(s);
1478
1479
0
  output = StreamPool_Take(rdpdr->pool, 256); // RDPDR_DEVICE_IO_RESPONSE_LENGTH
1480
0
  if (!output)
1481
0
  {
1482
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
1483
0
    return CHANNEL_RC_NO_MEMORY;
1484
0
  }
1485
1486
0
  Stream_SetPosition(s, 4); /* see "rdpdr_process_receive" */
1487
1488
0
  Stream_Read_UINT32(s, DeviceId);     /* DeviceId (4 bytes) */
1489
0
  Stream_Read_UINT32(s, FileId);       /* FileId (4 bytes) */
1490
0
  Stream_Read_UINT32(s, CompletionId); /* CompletionId (4 bytes) */
1491
1492
0
  if (!rdpdr_write_iocompletion_header(output, DeviceId, CompletionId,
1493
0
                                       (UINT32)STATUS_UNSUCCESSFUL))
1494
0
    return CHANNEL_RC_NO_MEMORY;
1495
1496
0
  return rdpdr_send(rdpdr, output);
1497
0
}
1498
1499
/**
1500
 * Function description
1501
 *
1502
 * @return 0 on success, otherwise a Win32 error code
1503
 */
1504
static UINT rdpdr_process_irp(rdpdrPlugin* rdpdr, wStream* s)
1505
0
{
1506
0
  IRP* irp = NULL;
1507
0
  UINT error = CHANNEL_RC_OK;
1508
1509
0
  WINPR_ASSERT(rdpdr);
1510
0
  WINPR_ASSERT(s);
1511
1512
0
  irp = irp_new(rdpdr->devman, rdpdr->pool, s, rdpdr->log, &error);
1513
1514
0
  if (!irp)
1515
0
  {
1516
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "irp_new failed with %" PRIu32 "!", error);
1517
1518
0
    if (error == CHANNEL_RC_OK || (error == ERROR_DEV_NOT_EXIST && rdpdr->ignoreInvalidDevices))
1519
0
    {
1520
0
      return dummy_irp_response(rdpdr, s);
1521
0
    }
1522
1523
0
    return error;
1524
0
  }
1525
1526
0
  if (irp->device->IRPRequest)
1527
0
    IFCALLRET(irp->device->IRPRequest, error, irp->device, irp);
1528
0
  else
1529
0
    irp->Discard(irp);
1530
1531
0
  if (error != CHANNEL_RC_OK)
1532
0
  {
1533
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "device->IRPRequest failed with error %" PRIu32 "",
1534
0
               error);
1535
0
    irp->Discard(irp);
1536
0
  }
1537
1538
0
  return error;
1539
0
}
1540
1541
static UINT rdpdr_process_component(rdpdrPlugin* rdpdr, UINT16 component, UINT16 packetId,
1542
                                    wStream* s)
1543
0
{
1544
0
  UINT32 type = 0;
1545
0
  DEVICE* device = NULL;
1546
1547
0
  WINPR_ASSERT(rdpdr);
1548
0
  WINPR_ASSERT(s);
1549
1550
0
  switch (component)
1551
0
  {
1552
0
    case RDPDR_CTYP_PRN:
1553
0
      type = RDPDR_DTYP_PRINT;
1554
0
      break;
1555
1556
0
    default:
1557
0
      return ERROR_INVALID_DATA;
1558
0
  }
1559
1560
0
  device = devman_get_device_by_type(rdpdr->devman, type);
1561
1562
0
  if (!device)
1563
0
    return ERROR_DEV_NOT_EXIST;
1564
1565
0
  return IFCALLRESULT(ERROR_INVALID_PARAMETER, device->CustomComponentRequest, device, component,
1566
0
                      packetId, s);
1567
0
}
1568
1569
/**
1570
 * Function description
1571
 *
1572
 * @return 0 on success, otherwise a Win32 error code
1573
 */
1574
static BOOL device_init(ULONG_PTR key, void* element, void* data)
1575
0
{
1576
0
  wLog* log = data;
1577
0
  UINT error = CHANNEL_RC_OK;
1578
0
  DEVICE* device = element;
1579
1580
0
  WINPR_UNUSED(key);
1581
0
  WINPR_UNUSED(data);
1582
1583
0
  IFCALLRET(device->Init, error, device);
1584
1585
0
  if (error != CHANNEL_RC_OK)
1586
0
  {
1587
0
    WLog_Print(log, WLOG_ERROR, "Device init failed with %s", WTSErrorToString(error));
1588
0
    return FALSE;
1589
0
  }
1590
0
  return TRUE;
1591
0
}
1592
1593
static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
1594
0
{
1595
0
  WINPR_ASSERT(rdpdr);
1596
0
  WINPR_ASSERT(rdpdr->devman);
1597
1598
0
  rdpdr->userLoggedOn = FALSE; /* reset possible received state */
1599
0
  if (!device_foreach(rdpdr, TRUE, device_init, rdpdr->log))
1600
0
    return ERROR_INTERNAL_ERROR;
1601
0
  return CHANNEL_RC_OK;
1602
0
}
1603
1604
static BOOL state_match(enum RDPDR_CHANNEL_STATE state, size_t count, va_list ap)
1605
0
{
1606
0
  for (size_t x = 0; x < count; x++)
1607
0
  {
1608
0
    enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE);
1609
0
    if (state == cur)
1610
0
      return TRUE;
1611
0
  }
1612
0
  return FALSE;
1613
0
}
1614
1615
static const char* state_str(size_t count, va_list ap, char* buffer, size_t size)
1616
0
{
1617
0
  for (size_t x = 0; x < count; x++)
1618
0
  {
1619
0
    enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE);
1620
0
    const char* curstr = rdpdr_state_str(cur);
1621
0
    winpr_str_append(curstr, buffer, size, "|");
1622
0
  }
1623
0
  return buffer;
1624
0
}
1625
1626
static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid, enum RDPDR_CHANNEL_STATE next,
1627
                              size_t count, ...)
1628
0
{
1629
0
  va_list ap;
1630
0
  WINPR_ASSERT(rdpdr);
1631
1632
0
  va_start(ap, count);
1633
0
  BOOL rc = state_match(rdpdr->state, count, ap);
1634
0
  va_end(ap);
1635
1636
0
  if (!rc)
1637
0
  {
1638
0
    const char* strstate = rdpdr_state_str(rdpdr->state);
1639
0
    char buffer[256] = { 0 };
1640
1641
0
    va_start(ap, count);
1642
0
    state_str(count, ap, buffer, sizeof(buffer));
1643
0
    va_end(ap);
1644
1645
0
    WLog_Print(rdpdr->log, WLOG_ERROR,
1646
0
               "channel [RDPDR] received %s, expected states [%s] but have state %s, aborting.",
1647
0
               rdpdr_packetid_string(packetid), buffer, strstate);
1648
1649
0
    rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
1650
0
    return FALSE;
1651
0
  }
1652
0
  return rdpdr_state_advance(rdpdr, next);
1653
0
}
1654
1655
static BOOL rdpdr_check_channel_state(rdpdrPlugin* rdpdr, UINT16 packetid)
1656
0
{
1657
0
  WINPR_ASSERT(rdpdr);
1658
1659
0
  switch (packetid)
1660
0
  {
1661
0
    case PAKID_CORE_SERVER_ANNOUNCE:
1662
      /* windows servers sometimes send this message.
1663
       * it seems related to session login (e.g. first initialization for RDP/TLS style login,
1664
       * then reinitialize the channel after login successful
1665
       */
1666
0
      rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
1667
0
      return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_ANNOUNCE, 1,
1668
0
                               RDPDR_CHANNEL_STATE_INITIAL);
1669
0
    case PAKID_CORE_SERVER_CAPABILITY:
1670
0
      return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_SERVER_CAPS, 6,
1671
0
                               RDPDR_CHANNEL_STATE_NAME_REQUEST,
1672
0
                               RDPDR_CHANNEL_STATE_SERVER_CAPS, RDPDR_CHANNEL_STATE_READY,
1673
0
                               RDPDR_CHANNEL_STATE_CLIENT_CAPS, PAKID_CORE_CLIENTID_CONFIRM,
1674
0
                               PAKID_CORE_USER_LOGGEDON);
1675
0
    case PAKID_CORE_CLIENTID_CONFIRM:
1676
0
      return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, 3,
1677
0
                               RDPDR_CHANNEL_STATE_CLIENT_CAPS, RDPDR_CHANNEL_STATE_READY,
1678
0
                               RDPDR_CHANNEL_STATE_USER_LOGGEDON);
1679
0
    case PAKID_CORE_USER_LOGGEDON:
1680
0
      if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_USER_LOGGEDON_PDU))
1681
0
        return FALSE;
1682
1683
0
      return rdpdr_state_check(
1684
0
          rdpdr, packetid, RDPDR_CHANNEL_STATE_USER_LOGGEDON, 4,
1685
0
          RDPDR_CHANNEL_STATE_NAME_REQUEST, RDPDR_CHANNEL_STATE_CLIENT_CAPS,
1686
0
          RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, RDPDR_CHANNEL_STATE_READY);
1687
0
    default:
1688
0
    {
1689
0
      enum RDPDR_CHANNEL_STATE state = RDPDR_CHANNEL_STATE_READY;
1690
0
      return rdpdr_state_check(rdpdr, packetid, state, 1, state);
1691
0
    }
1692
0
  }
1693
0
}
1694
1695
/**
1696
 * Function description
1697
 *
1698
 * @return 0 on success, otherwise a Win32 error code
1699
 */
1700
static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
1701
0
{
1702
0
  UINT16 component = 0;
1703
0
  UINT16 packetId = 0;
1704
0
  UINT32 deviceId = 0;
1705
0
  UINT32 status = 0;
1706
0
  UINT error = ERROR_INVALID_DATA;
1707
1708
0
  if (!rdpdr || !s)
1709
0
    return CHANNEL_RC_NULL_DATA;
1710
1711
0
  rdpdr_dump_received_packet(rdpdr->log, WLOG_TRACE, s, "[rdpdr-channel] receive");
1712
0
  if (Stream_GetRemainingLength(s) >= 4)
1713
0
  {
1714
0
    Stream_Read_UINT16(s, component); /* Component (2 bytes) */
1715
0
    Stream_Read_UINT16(s, packetId);  /* PacketId (2 bytes) */
1716
1717
0
    if (component == RDPDR_CTYP_CORE)
1718
0
    {
1719
0
      if (!rdpdr_check_channel_state(rdpdr, packetId))
1720
0
        return CHANNEL_RC_OK;
1721
1722
0
      switch (packetId)
1723
0
      {
1724
0
        case PAKID_CORE_SERVER_ANNOUNCE:
1725
0
          if ((error = rdpdr_process_server_announce_request(rdpdr, s)))
1726
0
          {
1727
0
          }
1728
0
          else if ((error = rdpdr_send_client_announce_reply(rdpdr)))
1729
0
          {
1730
0
            WLog_Print(rdpdr->log, WLOG_ERROR,
1731
0
                       "rdpdr_send_client_announce_reply failed with error %" PRIu32 "",
1732
0
                       error);
1733
0
          }
1734
0
          else if ((error = rdpdr_send_client_name_request(rdpdr)))
1735
0
          {
1736
0
            WLog_Print(rdpdr->log, WLOG_ERROR,
1737
0
                       "rdpdr_send_client_name_request failed with error %" PRIu32 "",
1738
0
                       error);
1739
0
          }
1740
0
          else if ((error = rdpdr_process_init(rdpdr)))
1741
0
          {
1742
0
            WLog_Print(rdpdr->log, WLOG_ERROR,
1743
0
                       "rdpdr_process_init failed with error %" PRIu32 "", error);
1744
0
          }
1745
1746
0
          break;
1747
1748
0
        case PAKID_CORE_SERVER_CAPABILITY:
1749
0
          if ((error = rdpdr_process_capability_request(rdpdr, s)))
1750
0
          {
1751
0
          }
1752
0
          else if ((error = rdpdr_send_capability_response(rdpdr)))
1753
0
          {
1754
0
            WLog_Print(rdpdr->log, WLOG_ERROR,
1755
0
                       "rdpdr_send_capability_response failed with error %" PRIu32 "",
1756
0
                       error);
1757
0
          }
1758
1759
0
          break;
1760
1761
0
        case PAKID_CORE_CLIENTID_CONFIRM:
1762
0
          if ((error = rdpdr_process_server_clientid_confirm(rdpdr, s)))
1763
0
          {
1764
0
          }
1765
0
          else if ((error = rdpdr_send_device_list_announce_request(rdpdr, FALSE)))
1766
0
          {
1767
0
            WLog_Print(
1768
0
                rdpdr->log, WLOG_ERROR,
1769
0
                "rdpdr_send_device_list_announce_request failed with error %" PRIu32 "",
1770
0
                error);
1771
0
          }
1772
0
          else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY))
1773
0
          {
1774
0
            error = ERROR_INTERNAL_ERROR;
1775
0
          }
1776
0
          break;
1777
1778
0
        case PAKID_CORE_USER_LOGGEDON:
1779
0
          if ((error = rdpdr_send_device_list_announce_request(rdpdr, TRUE)))
1780
0
          {
1781
0
            WLog_Print(
1782
0
                rdpdr->log, WLOG_ERROR,
1783
0
                "rdpdr_send_device_list_announce_request failed with error %" PRIu32 "",
1784
0
                error);
1785
0
          }
1786
0
          else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY))
1787
0
          {
1788
0
            error = ERROR_INTERNAL_ERROR;
1789
0
          }
1790
1791
0
          break;
1792
1793
0
        case PAKID_CORE_DEVICE_REPLY:
1794
1795
          /* connect to a specific resource */
1796
0
          if (Stream_GetRemainingLength(s) >= 8)
1797
0
          {
1798
0
            Stream_Read_UINT32(s, deviceId);
1799
0
            Stream_Read_UINT32(s, status);
1800
1801
0
            if (status != 0)
1802
0
              devman_unregister_device(rdpdr->devman, (void*)((size_t)deviceId));
1803
0
            error = CHANNEL_RC_OK;
1804
0
          }
1805
1806
0
          break;
1807
1808
0
        case PAKID_CORE_DEVICE_IOREQUEST:
1809
0
          if ((error = rdpdr_process_irp(rdpdr, s)))
1810
0
          {
1811
0
            WLog_Print(rdpdr->log, WLOG_ERROR,
1812
0
                       "rdpdr_process_irp failed with error %" PRIu32 "", error);
1813
0
            return error;
1814
0
          }
1815
0
          else
1816
0
            s = NULL;
1817
1818
0
          break;
1819
1820
0
        default:
1821
0
          WLog_Print(rdpdr->log, WLOG_ERROR,
1822
0
                     "RDPDR_CTYP_CORE unknown PacketId: 0x%04" PRIX16 "", packetId);
1823
0
          error = ERROR_INVALID_DATA;
1824
0
          break;
1825
0
      }
1826
0
    }
1827
0
    else
1828
0
    {
1829
0
      error = rdpdr_process_component(rdpdr, component, packetId, s);
1830
1831
0
      if (error != CHANNEL_RC_OK)
1832
0
      {
1833
0
        DWORD level = WLOG_ERROR;
1834
0
        if (rdpdr->ignoreInvalidDevices)
1835
0
        {
1836
0
          if (error == ERROR_DEV_NOT_EXIST)
1837
0
          {
1838
0
            level = WLOG_WARN;
1839
0
            error = CHANNEL_RC_OK;
1840
0
          }
1841
0
        }
1842
0
        WLog_Print(rdpdr->log, level,
1843
0
                   "Unknown message: Component: %s [0x%04" PRIX16
1844
0
                   "] PacketId: %s [0x%04" PRIX16 "]",
1845
0
                   rdpdr_component_string(component), component,
1846
0
                   rdpdr_packetid_string(packetId), packetId);
1847
0
      }
1848
0
    }
1849
0
  }
1850
1851
0
  return error;
1852
0
}
1853
1854
/**
1855
 * Function description
1856
 *
1857
 * @return 0 on success, otherwise a Win32 error code
1858
 */
1859
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
1860
0
{
1861
0
  UINT status = 0;
1862
0
  rdpdrPlugin* plugin = (rdpdrPlugin*)rdpdr;
1863
1864
0
  if (!s)
1865
0
  {
1866
0
    Stream_Release(s);
1867
0
    return CHANNEL_RC_NULL_DATA;
1868
0
  }
1869
1870
0
  if (!plugin)
1871
0
  {
1872
0
    Stream_Release(s);
1873
0
    return CHANNEL_RC_BAD_INIT_HANDLE;
1874
0
  }
1875
1876
0
  const size_t pos = Stream_GetPosition(s);
1877
0
  rdpdr_dump_send_packet(rdpdr->log, WLOG_TRACE, s, "[rdpdr-channel] send");
1878
0
  status = plugin->channelEntryPoints.pVirtualChannelWriteEx(
1879
0
      plugin->InitHandle, plugin->OpenHandle, Stream_Buffer(s), pos, s);
1880
1881
0
  if (status != CHANNEL_RC_OK)
1882
0
  {
1883
0
    Stream_Release(s);
1884
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1885
0
               WTSErrorToString(status), status);
1886
0
  }
1887
1888
0
  return status;
1889
0
}
1890
1891
/**
1892
 * Function description
1893
 *
1894
 * @return 0 on success, otherwise a Win32 error code
1895
 */
1896
static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void* pData,
1897
                                                      UINT32 dataLength, UINT32 totalLength,
1898
                                                      UINT32 dataFlags)
1899
0
{
1900
0
  wStream* data_in = NULL;
1901
1902
0
  WINPR_ASSERT(rdpdr);
1903
0
  WINPR_ASSERT(pData || (dataLength == 0));
1904
1905
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1906
0
  {
1907
    /*
1908
     * According to MS-RDPBCGR 2.2.6.1, "All virtual channel traffic MUST be suspended.
1909
     * This flag is only valid in server-to-client virtual channel traffic. It MUST be
1910
     * ignored in client-to-server data." Thus it would be best practice to cease data
1911
     * transmission. However, simply returning here avoids a crash.
1912
     */
1913
0
    return CHANNEL_RC_OK;
1914
0
  }
1915
1916
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
1917
0
  {
1918
0
    if (rdpdr->data_in != NULL)
1919
0
      Stream_Release(rdpdr->data_in);
1920
1921
0
    rdpdr->data_in = StreamPool_Take(rdpdr->pool, totalLength);
1922
1923
0
    if (!rdpdr->data_in)
1924
0
    {
1925
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
1926
0
      return CHANNEL_RC_NO_MEMORY;
1927
0
    }
1928
0
  }
1929
1930
0
  data_in = rdpdr->data_in;
1931
1932
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
1933
0
  {
1934
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
1935
0
    return ERROR_INVALID_DATA;
1936
0
  }
1937
1938
0
  Stream_Write(data_in, pData, dataLength);
1939
1940
0
  if (dataFlags & CHANNEL_FLAG_LAST)
1941
0
  {
1942
0
    const size_t pos = Stream_GetPosition(data_in);
1943
0
    const size_t cap = Stream_Capacity(data_in);
1944
0
    if (cap < pos)
1945
0
    {
1946
0
      WLog_Print(rdpdr->log, WLOG_ERROR,
1947
0
                 "rdpdr_virtual_channel_event_data_received: read error");
1948
0
      return ERROR_INTERNAL_ERROR;
1949
0
    }
1950
1951
0
    Stream_SealLength(data_in);
1952
0
    Stream_SetPosition(data_in, 0);
1953
1954
0
    if (rdpdr->async)
1955
0
    {
1956
0
      if (!MessageQueue_Post(rdpdr->queue, NULL, 0, (void*)data_in, NULL))
1957
0
      {
1958
0
        WLog_Print(rdpdr->log, WLOG_ERROR, "MessageQueue_Post failed!");
1959
0
        return ERROR_INTERNAL_ERROR;
1960
0
      }
1961
0
      rdpdr->data_in = NULL;
1962
0
    }
1963
0
    else
1964
0
    {
1965
0
      UINT error = rdpdr_process_receive(rdpdr, data_in);
1966
0
      Stream_Release(data_in);
1967
0
      rdpdr->data_in = NULL;
1968
0
      if (error)
1969
0
        return error;
1970
0
    }
1971
0
  }
1972
1973
0
  return CHANNEL_RC_OK;
1974
0
}
1975
1976
static VOID VCAPITYPE rdpdr_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1977
                                                          UINT event, LPVOID pData,
1978
                                                          UINT32 dataLength, UINT32 totalLength,
1979
                                                          UINT32 dataFlags)
1980
0
{
1981
0
  UINT error = CHANNEL_RC_OK;
1982
0
  rdpdrPlugin* rdpdr = (rdpdrPlugin*)lpUserParam;
1983
1984
0
  WINPR_ASSERT(rdpdr);
1985
0
  switch (event)
1986
0
  {
1987
0
    case CHANNEL_EVENT_DATA_RECEIVED:
1988
0
      if (!rdpdr || !pData || (rdpdr->OpenHandle != openHandle))
1989
0
      {
1990
0
        WLog_Print(rdpdr->log, WLOG_ERROR, "error no match");
1991
0
        return;
1992
0
      }
1993
0
      if ((error = rdpdr_virtual_channel_event_data_received(rdpdr, pData, dataLength,
1994
0
                                                             totalLength, dataFlags)))
1995
0
        WLog_Print(rdpdr->log, WLOG_ERROR,
1996
0
                   "rdpdr_virtual_channel_event_data_received failed with error %" PRIu32
1997
0
                   "!",
1998
0
                   error);
1999
2000
0
      break;
2001
2002
0
    case CHANNEL_EVENT_WRITE_CANCELLED:
2003
0
    case CHANNEL_EVENT_WRITE_COMPLETE:
2004
0
    {
2005
0
      wStream* s = (wStream*)pData;
2006
0
      Stream_Release(s);
2007
0
    }
2008
0
    break;
2009
2010
0
    case CHANNEL_EVENT_USER:
2011
0
      break;
2012
0
  }
2013
2014
0
  if (error && rdpdr && rdpdr->rdpcontext)
2015
0
    setChannelError(rdpdr->rdpcontext, error,
2016
0
                    "rdpdr_virtual_channel_open_event_ex reported an error");
2017
2018
0
  return;
2019
0
}
2020
2021
static DWORD WINAPI rdpdr_virtual_channel_client_thread(LPVOID arg)
2022
0
{
2023
0
  rdpdrPlugin* rdpdr = (rdpdrPlugin*)arg;
2024
0
  UINT error = 0;
2025
2026
0
  if (!rdpdr)
2027
0
  {
2028
0
    ExitThread((DWORD)CHANNEL_RC_NULL_DATA);
2029
0
    return CHANNEL_RC_NULL_DATA;
2030
0
  }
2031
2032
0
  if ((error = rdpdr_process_connect(rdpdr)))
2033
0
  {
2034
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "rdpdr_process_connect failed with error %" PRIu32 "!",
2035
0
               error);
2036
2037
0
    if (rdpdr->rdpcontext)
2038
0
      setChannelError(rdpdr->rdpcontext, error,
2039
0
                      "rdpdr_virtual_channel_client_thread reported an error");
2040
2041
0
    ExitThread(error);
2042
0
    return error;
2043
0
  }
2044
2045
0
  while (1)
2046
0
  {
2047
0
    wMessage message = { 0 };
2048
0
    WINPR_ASSERT(rdpdr);
2049
2050
0
    if (!MessageQueue_Wait(rdpdr->queue))
2051
0
      break;
2052
2053
0
    if (MessageQueue_Peek(rdpdr->queue, &message, TRUE))
2054
0
    {
2055
0
      if (message.id == WMQ_QUIT)
2056
0
        break;
2057
2058
0
      if (message.id == 0)
2059
0
      {
2060
0
        wStream* data = (wStream*)message.wParam;
2061
2062
0
        error = rdpdr_process_receive(rdpdr, data);
2063
2064
0
        Stream_Release(data);
2065
0
        if (error)
2066
0
        {
2067
0
          WLog_Print(rdpdr->log, WLOG_ERROR,
2068
0
                     "rdpdr_process_receive failed with error %" PRIu32 "!", error);
2069
2070
0
          if (rdpdr->rdpcontext)
2071
0
            setChannelError(rdpdr->rdpcontext, error,
2072
0
                            "rdpdr_virtual_channel_client_thread reported an error");
2073
2074
0
          goto fail;
2075
0
        }
2076
0
      }
2077
0
    }
2078
0
  }
2079
2080
0
fail:
2081
0
  if ((error = drive_hotplug_thread_terminate(rdpdr)))
2082
0
    WLog_Print(rdpdr->log, WLOG_ERROR,
2083
0
               "drive_hotplug_thread_terminate failed with error %" PRIu32 "!", error);
2084
2085
0
  ExitThread(error);
2086
0
  return error;
2087
0
}
2088
2089
static void queue_free(void* obj)
2090
0
{
2091
0
  wStream* s = NULL;
2092
0
  wMessage* msg = (wMessage*)obj;
2093
2094
0
  if (!msg || (msg->id != 0))
2095
0
    return;
2096
2097
0
  s = (wStream*)msg->wParam;
2098
0
  WINPR_ASSERT(s);
2099
0
  Stream_Release(s);
2100
0
}
2101
2102
/**
2103
 * Function description
2104
 *
2105
 * @return 0 on success, otherwise a Win32 error code
2106
 */
2107
static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pData,
2108
                                                  UINT32 dataLength)
2109
0
{
2110
0
  wObject* obj = NULL;
2111
2112
0
  WINPR_ASSERT(rdpdr);
2113
0
  WINPR_UNUSED(pData);
2114
0
  WINPR_UNUSED(dataLength);
2115
2116
0
  if (rdpdr->async)
2117
0
  {
2118
0
    rdpdr->queue = MessageQueue_New(NULL);
2119
2120
0
    if (!rdpdr->queue)
2121
0
    {
2122
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "MessageQueue_New failed!");
2123
0
      return CHANNEL_RC_NO_MEMORY;
2124
0
    }
2125
2126
0
    obj = MessageQueue_Object(rdpdr->queue);
2127
0
    obj->fnObjectFree = queue_free;
2128
2129
0
    if (!(rdpdr->thread = CreateThread(NULL, 0, rdpdr_virtual_channel_client_thread,
2130
0
                                       (void*)rdpdr, 0, NULL)))
2131
0
    {
2132
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "CreateThread failed!");
2133
0
      return ERROR_INTERNAL_ERROR;
2134
0
    }
2135
0
  }
2136
0
  else
2137
0
  {
2138
0
    UINT error = rdpdr_process_connect(rdpdr);
2139
0
    if (error)
2140
0
    {
2141
0
      WLog_Print(rdpdr->log, WLOG_ERROR,
2142
0
                 "rdpdr_process_connect failed with error %" PRIu32 "!", error);
2143
0
      return error;
2144
0
    }
2145
0
  }
2146
2147
0
  return rdpdr->channelEntryPoints.pVirtualChannelOpenEx(rdpdr->InitHandle, &rdpdr->OpenHandle,
2148
0
                                                         rdpdr->channelDef.name,
2149
0
                                                         rdpdr_virtual_channel_open_event_ex);
2150
0
}
2151
2152
/**
2153
 * Function description
2154
 *
2155
 * @return 0 on success, otherwise a Win32 error code
2156
 */
2157
static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
2158
0
{
2159
0
  UINT error = 0;
2160
2161
0
  WINPR_ASSERT(rdpdr);
2162
2163
0
  if (rdpdr->OpenHandle == 0)
2164
0
    return CHANNEL_RC_OK;
2165
2166
0
  if (rdpdr->queue && rdpdr->thread)
2167
0
  {
2168
0
    if (MessageQueue_PostQuit(rdpdr->queue, 0) &&
2169
0
        (WaitForSingleObject(rdpdr->thread, INFINITE) == WAIT_FAILED))
2170
0
    {
2171
0
      error = GetLastError();
2172
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
2173
0
                 error);
2174
0
      return error;
2175
0
    }
2176
0
  }
2177
2178
0
  if (rdpdr->thread)
2179
0
    CloseHandle(rdpdr->thread);
2180
0
  MessageQueue_Free(rdpdr->queue);
2181
0
  rdpdr->queue = NULL;
2182
0
  rdpdr->thread = NULL;
2183
2184
0
  WINPR_ASSERT(rdpdr->channelEntryPoints.pVirtualChannelCloseEx);
2185
0
  error = rdpdr->channelEntryPoints.pVirtualChannelCloseEx(rdpdr->InitHandle, rdpdr->OpenHandle);
2186
2187
0
  if (CHANNEL_RC_OK != error)
2188
0
  {
2189
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
2190
0
               WTSErrorToString(error), error);
2191
0
  }
2192
2193
0
  rdpdr->OpenHandle = 0;
2194
2195
0
  if (rdpdr->data_in)
2196
0
  {
2197
0
    Stream_Release(rdpdr->data_in);
2198
0
    rdpdr->data_in = NULL;
2199
0
  }
2200
2201
0
  if (rdpdr->devman)
2202
0
  {
2203
0
    devman_free(rdpdr->devman);
2204
0
    rdpdr->devman = NULL;
2205
0
  }
2206
2207
0
  return error;
2208
0
}
2209
2210
static void rdpdr_virtual_channel_event_terminated(rdpdrPlugin* rdpdr)
2211
0
{
2212
0
  WINPR_ASSERT(rdpdr);
2213
0
  rdpdr->InitHandle = 0;
2214
0
  StreamPool_Free(rdpdr->pool);
2215
0
  free(rdpdr);
2216
0
}
2217
2218
static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
2219
                                                          UINT event, LPVOID pData, UINT dataLength)
2220
0
{
2221
0
  UINT error = CHANNEL_RC_OK;
2222
0
  rdpdrPlugin* rdpdr = (rdpdrPlugin*)lpUserParam;
2223
2224
0
  if (!rdpdr || (rdpdr->InitHandle != pInitHandle))
2225
0
  {
2226
0
    WLog_ERR(TAG, "error no match");
2227
0
    return;
2228
0
  }
2229
2230
0
  WINPR_ASSERT(pData || (dataLength == 0));
2231
2232
0
  switch (event)
2233
0
  {
2234
0
    case CHANNEL_EVENT_INITIALIZED:
2235
0
      break;
2236
2237
0
    case CHANNEL_EVENT_CONNECTED:
2238
0
      if ((error = rdpdr_virtual_channel_event_connected(rdpdr, pData, dataLength)))
2239
0
        WLog_Print(rdpdr->log, WLOG_ERROR,
2240
0
                   "rdpdr_virtual_channel_event_connected failed with error %" PRIu32 "!",
2241
0
                   error);
2242
2243
0
      break;
2244
2245
0
    case CHANNEL_EVENT_DISCONNECTED:
2246
0
      if ((error = rdpdr_virtual_channel_event_disconnected(rdpdr)))
2247
0
        WLog_Print(rdpdr->log, WLOG_ERROR,
2248
0
                   "rdpdr_virtual_channel_event_disconnected failed with error %" PRIu32
2249
0
                   "!",
2250
0
                   error);
2251
2252
0
      break;
2253
2254
0
    case CHANNEL_EVENT_TERMINATED:
2255
0
      rdpdr_virtual_channel_event_terminated(rdpdr);
2256
0
      rdpdr = NULL;
2257
0
      break;
2258
2259
0
    case CHANNEL_EVENT_ATTACHED:
2260
0
    case CHANNEL_EVENT_DETACHED:
2261
0
    default:
2262
0
      WLog_Print(rdpdr->log, WLOG_ERROR, "unknown event %" PRIu32 "!", event);
2263
0
      break;
2264
0
  }
2265
2266
0
  if (error && rdpdr && rdpdr->rdpcontext)
2267
0
    setChannelError(rdpdr->rdpcontext, error,
2268
0
                    "rdpdr_virtual_channel_init_event_ex reported an error");
2269
0
}
2270
2271
/* rdpdr is always built-in */
2272
#define VirtualChannelEntryEx rdpdr_VirtualChannelEntryEx
2273
2274
FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
2275
                                                         PVOID pInitHandle))
2276
0
{
2277
0
  UINT rc = 0;
2278
0
  rdpdrPlugin* rdpdr = NULL;
2279
0
  CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
2280
2281
0
  WINPR_ASSERT(pEntryPoints);
2282
0
  WINPR_ASSERT(pInitHandle);
2283
2284
0
  rdpdr = (rdpdrPlugin*)calloc(1, sizeof(rdpdrPlugin));
2285
2286
0
  if (!rdpdr)
2287
0
  {
2288
0
    WLog_ERR(TAG, "calloc failed!");
2289
0
    return FALSE;
2290
0
  }
2291
0
  rdpdr->log = WLog_Get(TAG);
2292
2293
0
  rdpdr->clientExtendedPDU =
2294
0
      RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | RDPDR_USER_LOGGEDON_PDU;
2295
0
  rdpdr->clientIOCode1 =
2296
0
      RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ |
2297
0
      RDPDR_IRP_MJ_WRITE | RDPDR_IRP_MJ_FLUSH_BUFFERS | RDPDR_IRP_MJ_SHUTDOWN |
2298
0
      RDPDR_IRP_MJ_DEVICE_CONTROL | RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION |
2299
0
      RDPDR_IRP_MJ_SET_VOLUME_INFORMATION | RDPDR_IRP_MJ_QUERY_INFORMATION |
2300
0
      RDPDR_IRP_MJ_SET_INFORMATION | RDPDR_IRP_MJ_DIRECTORY_CONTROL | RDPDR_IRP_MJ_LOCK_CONTROL |
2301
0
      RDPDR_IRP_MJ_QUERY_SECURITY | RDPDR_IRP_MJ_SET_SECURITY;
2302
2303
0
  rdpdr->clientExtraFlags1 = ENABLE_ASYNCIO;
2304
2305
0
  rdpdr->pool = StreamPool_New(TRUE, 1024);
2306
0
  if (!rdpdr->pool)
2307
0
  {
2308
0
    free(rdpdr);
2309
0
    return FALSE;
2310
0
  }
2311
2312
0
  rdpdr->channelDef.options =
2313
0
      CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
2314
0
  sprintf_s(rdpdr->channelDef.name, ARRAYSIZE(rdpdr->channelDef.name), RDPDR_SVC_CHANNEL_NAME);
2315
0
  rdpdr->sequenceId = 0;
2316
0
  pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
2317
2318
0
  if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
2319
0
      (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
2320
0
  {
2321
0
    rdpdr->rdpcontext = pEntryPointsEx->context;
2322
0
    if (!freerdp_settings_get_bool(rdpdr->rdpcontext->settings,
2323
0
                                   FreeRDP_SynchronousStaticChannels))
2324
0
      rdpdr->async = TRUE;
2325
0
  }
2326
2327
0
  CopyMemory(&(rdpdr->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
2328
0
  rdpdr->InitHandle = pInitHandle;
2329
0
  rc = rdpdr->channelEntryPoints.pVirtualChannelInitEx(
2330
0
      rdpdr, NULL, pInitHandle, &rdpdr->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
2331
0
      rdpdr_virtual_channel_init_event_ex);
2332
2333
0
  if (CHANNEL_RC_OK != rc)
2334
0
  {
2335
0
    WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]",
2336
0
               WTSErrorToString(rc), rc);
2337
0
    free(rdpdr);
2338
0
    return FALSE;
2339
0
  }
2340
2341
0
  return TRUE;
2342
0
}