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