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