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