Coverage Report

Created: 2026-10-15 06:31

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