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