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