FreeRDP
Loading...
Searching...
No Matches
shadow_server.c
1
21#include <freerdp/config.h>
22
23#include <errno.h>
24#include <stdint.h>
25
26#include <winpr/assert.h>
27#include <winpr/crt.h>
28#include <winpr/ssl.h>
29#include <winpr/path.h>
30#include <winpr/cmdline.h>
31#include <winpr/winsock.h>
32
33#include <freerdp/log.h>
34#include <freerdp/version.h>
35
36#include <winpr/tools/makecert.h>
37
38#ifndef _WIN32
39#include <sys/select.h>
40#include <signal.h>
41#endif
42
43#include "shadow.h"
44
45#define TAG SERVER_TAG("shadow")
46
47static const char bind_address[] = "bind-address,";
48
49#define fail_at(arg, rc) fail_at_((arg), (rc), __FILE__, __func__, __LINE__)
50static int fail_at_(const COMMAND_LINE_ARGUMENT_A* arg, int rc, const char* file, const char* fkt,
51 size_t line)
52{
53 const DWORD level = WLOG_ERROR;
54 wLog* log = WLog_Get(TAG);
55 if (WLog_IsLevelActive(log, level))
56 WLog_PrintTextMessage(log, level, line, file, fkt,
57 "Command line parsing failed at '%s' value '%s' [%d]", arg->Name,
58 arg->Value, rc);
59 return rc;
60}
61
62WINPR_ATTR_NODISCARD
63static int command_line_compare(const void* pa, const void* pb)
64{
65 const COMMAND_LINE_ARGUMENT_A* a = pa;
66 const COMMAND_LINE_ARGUMENT_A* b = pb;
67
68 if (!a && !b)
69 return 0;
70 if (!a)
71 return -1;
72 if (!b)
73 return 1;
74
75 return strcmp(a->Name, b->Name);
76}
77
78WINPR_ATTR_NODISCARD
79static int shadow_server_print_command_line_help(int argc, char** argv,
80 const COMMAND_LINE_ARGUMENT_A* largs)
81{
82 if ((argc < 1) || !largs || !argv)
83 return -1;
84
85 {
86 char* path = winpr_GetConfigFilePath(TRUE, "SAM");
87 printf("Usage: %s [options]\n", argv[0]);
88 printf("\n");
89 printf("Notes: By default NLA security is active.\n");
90 printf("\tIn this mode a SAM database is required.\n");
91 printf("\tProvide one with /sam-file:<file with path>\n");
92 printf("\telse the default path %s is used.\n", path);
93 printf("\tIf there is no existing SAM file authentication for all users will fail.\n");
94 printf("\n\tIf authentication against PAM is desired, start with -sec-nla (requires "
95 "compiled in "
96 "support for PAM)\n\n");
97 printf("Syntax:\n");
98 printf(" /flag (enables flag)\n");
99 printf(" /option:<value> (specifies option with value)\n");
100 printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
101 printf("\n");
102 free(path);
103 }
104
105 // TODO: Sort arguments
106 size_t nrArgs = 0;
107 {
108 const COMMAND_LINE_ARGUMENT_A* arg = largs;
109 while (arg->Name != nullptr)
110 {
111 nrArgs++;
112 arg++;
113 }
114 nrArgs++;
115 }
116 COMMAND_LINE_ARGUMENT_A* args_copy = calloc(nrArgs, sizeof(COMMAND_LINE_ARGUMENT_A));
117 if (!args_copy)
118 return -1;
119 memcpy(args_copy, largs, nrArgs * sizeof(COMMAND_LINE_ARGUMENT_A));
120 qsort(args_copy, nrArgs - 1, sizeof(COMMAND_LINE_ARGUMENT_A), command_line_compare);
121
122 const COMMAND_LINE_ARGUMENT_A* arg = args_copy;
123
124 int rc = -1;
125 do
126 {
127 if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
128 {
129 printf(" %s", "/");
130 printf("%-20s\n", arg->Name);
131 printf("\t%s\n", arg->Text);
132 }
133 else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
134 (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
135 {
136 printf(" %s", "/");
137
138 if (arg->Format)
139 {
140 const size_t length = (strlen(arg->Name) + strlen(arg->Format) + 2);
141 char* str = (char*)calloc(length + 1, sizeof(char));
142
143 if (!str)
144 goto fail;
145
146 (void)sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
147 (void)printf("%-20s\n", str);
148 free(str);
149 }
150 else
151 {
152 printf("%-20s\n", arg->Name);
153 }
154
155 printf("\t%s\n", arg->Text);
156 }
157 else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
158 {
159 const size_t length = strlen(arg->Name) + 32;
160 char* str = calloc(length + 1, sizeof(char));
161
162 if (!str)
163 goto fail;
164
165 (void)sprintf_s(str, length + 1, "%s (default:%s)", arg->Name,
166 arg->Default ? "on" : "off");
167 (void)printf(" %s", arg->Default ? "-" : "+");
168 (void)printf("%-20s\n", str);
169 (void)printf("\t%s\n", arg->Text);
170
171 free(str);
172 }
173 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
174
175 rc = 1;
176fail:
177 free(args_copy);
178 return rc;
179}
180
181int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
182 int status, const COMMAND_LINE_ARGUMENT_A* cargs)
183{
184 WINPR_UNUSED(server);
185
186 if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
187 {
188 printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
189 return COMMAND_LINE_STATUS_PRINT_VERSION;
190 }
191 else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
192 {
193 printf("%s\n", freerdp_get_build_config());
194 return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
195 }
196 else if (status == COMMAND_LINE_STATUS_PRINT)
197 {
198 return COMMAND_LINE_STATUS_PRINT;
199 }
200 else if (status < 0)
201 {
202 if (shadow_server_print_command_line_help(argc, argv, cargs) < 0)
203 return -1;
204
205 return COMMAND_LINE_STATUS_PRINT_HELP;
206 }
207
208 return 1;
209}
210
211int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv,
213{
214 int status = 0;
215 DWORD flags = 0;
216 const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
217 rdpSettings* settings = server->settings;
218
219 if ((argc < 2) || !argv || !cargs)
220 return 1;
221
222 CommandLineClearArgumentsA(cargs);
223 flags = COMMAND_LINE_SEPARATOR_COLON;
224 flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
225 status = CommandLineParseArgumentsA(argc, argv, cargs, flags, server, nullptr, nullptr);
226
227 if (status < 0)
228 return status;
229
230 arg = cargs;
231 errno = 0;
232
233 do
234 {
235 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
236 continue;
237
238 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
239 {
240 long val = strtol(arg->Value, nullptr, 0);
241
242 if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
243 return fail_at(arg, COMMAND_LINE_ERROR);
244
245 server->port = (DWORD)val;
246 }
247 CommandLineSwitchCase(arg, "ipc-socket")
248 {
249 /* /bind-address is incompatible */
250 if (server->ipcSocket)
251 return fail_at(arg, COMMAND_LINE_ERROR);
252 server->ipcSocket = _strdup(arg->Value);
253
254 if (!server->ipcSocket)
255 return fail_at(arg, COMMAND_LINE_ERROR);
256 }
257 CommandLineSwitchCase(arg, "bind-address")
258 {
259 int rc = 0;
260 size_t len = strlen(arg->Value) + sizeof(bind_address);
261 /* /ipc-socket is incompatible */
262 if (server->ipcSocket)
263 return fail_at(arg, COMMAND_LINE_ERROR);
264 server->ipcSocket = calloc(len, sizeof(CHAR));
265
266 if (!server->ipcSocket)
267 return fail_at(arg, COMMAND_LINE_ERROR);
268
269 rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
270 if ((rc < 0) || ((size_t)rc != len - 1))
271 return fail_at(arg, COMMAND_LINE_ERROR);
272 }
273 CommandLineSwitchCase(arg, "may-view")
274 {
275 server->mayView = arg->Value != nullptr;
276 }
277 CommandLineSwitchCase(arg, "bitmap-compat")
278 {
279 server->SupportMultiRectBitmapUpdates = arg->Value == nullptr;
280 }
281 CommandLineSwitchCase(arg, "may-interact")
282 {
283 server->mayInteract = arg->Value != nullptr;
284 }
285 CommandLineSwitchCase(arg, "server-side-cursor")
286 {
287 server->ShowMouseCursor = arg->Value != nullptr;
288 }
289 CommandLineSwitchCase(arg, "mouse-relative")
290 {
291 const BOOL val = arg->Value != nullptr;
292 if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, val) ||
293 !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, val))
294 return fail_at(arg, COMMAND_LINE_ERROR);
295 }
296 CommandLineSwitchCase(arg, "max-connections")
297 {
298 errno = 0;
299 unsigned long val = strtoul(arg->Value, nullptr, 0);
300
301 if ((errno != 0) || (val > UINT32_MAX))
302 return fail_at(arg, COMMAND_LINE_ERROR);
303 server->maxClientsConnected = val;
304 }
305 CommandLineSwitchCase(arg, "rect")
306 {
307 char* p = nullptr;
308 char* tok[4];
309 long x = -1;
310 long y = -1;
311 long w = -1;
312 long h = -1;
313 char* str = _strdup(arg->Value);
314
315 if (!str)
316 return fail_at(arg, COMMAND_LINE_ERROR);
317
318 tok[0] = p = str;
319 p = strchr(p + 1, ',');
320
321 if (!p)
322 {
323 free(str);
324 return fail_at(arg, COMMAND_LINE_ERROR);
325 }
326
327 *p++ = '\0';
328 tok[1] = p;
329 p = strchr(p + 1, ',');
330
331 if (!p)
332 {
333 free(str);
334 return fail_at(arg, COMMAND_LINE_ERROR);
335 }
336
337 *p++ = '\0';
338 tok[2] = p;
339 p = strchr(p + 1, ',');
340
341 if (!p)
342 {
343 free(str);
344 return fail_at(arg, COMMAND_LINE_ERROR);
345 }
346
347 *p++ = '\0';
348 tok[3] = p;
349 x = strtol(tok[0], nullptr, 0);
350
351 if (errno != 0)
352 goto fail;
353
354 y = strtol(tok[1], nullptr, 0);
355
356 if (errno != 0)
357 goto fail;
358
359 w = strtol(tok[2], nullptr, 0);
360
361 if (errno != 0)
362 goto fail;
363
364 h = strtol(tok[3], nullptr, 0);
365
366 if (errno != 0)
367 goto fail;
368
369 fail:
370 free(str);
371
372 if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
373 return fail_at(arg, COMMAND_LINE_ERROR);
374
375 if ((x > UINT16_MAX) || (y > UINT16_MAX) || (x + w > UINT16_MAX) ||
376 (y + h > UINT16_MAX))
377 return fail_at(arg, COMMAND_LINE_ERROR);
378 server->subRect.left = (UINT16)x;
379 server->subRect.top = (UINT16)y;
380 server->subRect.right = (UINT16)(x + w);
381 server->subRect.bottom = (UINT16)(y + h);
382 server->shareSubRect = TRUE;
383 }
384 CommandLineSwitchCase(arg, "auth")
385 {
386 server->authentication = arg->Value != nullptr;
387 }
388 CommandLineSwitchCase(arg, "remote-guard")
389 {
390 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard,
391 arg->Value != nullptr))
392 return fail_at(arg, COMMAND_LINE_ERROR);
393 }
394 CommandLineSwitchCase(arg, "restricted-admin")
395 {
396 if (!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeSupported,
397 arg->Value != nullptr))
398 return fail_at(arg, COMMAND_LINE_ERROR);
399 }
400 CommandLineSwitchCase(arg, "vmconnect")
401 {
402 if (!freerdp_settings_set_bool(settings, FreeRDP_VmConnectMode, arg->Value != nullptr))
403 return fail_at(arg, COMMAND_LINE_ERROR);
404 }
405 CommandLineSwitchCase(arg, "sec")
406 {
407 if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
408 {
409 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
410 return fail_at(arg, COMMAND_LINE_ERROR);
411 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
412 return fail_at(arg, COMMAND_LINE_ERROR);
413 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
414 return fail_at(arg, COMMAND_LINE_ERROR);
415 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
416 return fail_at(arg, COMMAND_LINE_ERROR);
417 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
418 return fail_at(arg, COMMAND_LINE_ERROR);
419 }
420 else if (strcmp("tls", arg->Value) == 0) /* TLS */
421 {
422 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
423 return fail_at(arg, COMMAND_LINE_ERROR);
424 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
425 return fail_at(arg, COMMAND_LINE_ERROR);
426 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
427 return fail_at(arg, COMMAND_LINE_ERROR);
428 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
429 return fail_at(arg, COMMAND_LINE_ERROR);
430 }
431 else if (strcmp("nla", arg->Value) == 0) /* NLA */
432 {
433 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
434 return fail_at(arg, COMMAND_LINE_ERROR);
435 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
436 return fail_at(arg, COMMAND_LINE_ERROR);
437 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
438 return fail_at(arg, COMMAND_LINE_ERROR);
439 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
440 return fail_at(arg, COMMAND_LINE_ERROR);
441 }
442 else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
443 {
444 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
445 return fail_at(arg, COMMAND_LINE_ERROR);
446 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
447 return fail_at(arg, COMMAND_LINE_ERROR);
448 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
449 return fail_at(arg, COMMAND_LINE_ERROR);
450 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
451 return fail_at(arg, COMMAND_LINE_ERROR);
452 }
453 else
454 {
455 WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
456 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
457 }
458 }
459 CommandLineSwitchCase(arg, "sec-rdp")
460 {
461 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, arg->Value != nullptr))
462 return fail_at(arg, COMMAND_LINE_ERROR);
463 }
464 CommandLineSwitchCase(arg, "sec-tls")
465 {
466 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, arg->Value != nullptr))
467 return fail_at(arg, COMMAND_LINE_ERROR);
468 }
469 CommandLineSwitchCase(arg, "sec-nla")
470 {
471 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, arg->Value != nullptr))
472 return fail_at(arg, COMMAND_LINE_ERROR);
473 }
474 CommandLineSwitchCase(arg, "sec-ext")
475 {
476 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, arg->Value != nullptr))
477 return fail_at(arg, COMMAND_LINE_ERROR);
478 }
479 CommandLineSwitchCase(arg, "sam-file")
480 {
481 if (!freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value))
482 return fail_at(arg, COMMAND_LINE_ERROR);
483 }
484 CommandLineSwitchCase(arg, "log-level")
485 {
486 wLog* root = WLog_GetRoot();
487
488 if (!WLog_SetStringLogLevel(root, arg->Value))
489 return fail_at(arg, COMMAND_LINE_ERROR);
490 }
491 CommandLineSwitchCase(arg, "log-filters")
492 {
493 if (!WLog_AddStringLogFilters(arg->Value))
494 return fail_at(arg, COMMAND_LINE_ERROR);
495 }
496 CommandLineSwitchCase(arg, "nsc")
497 {
498 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, arg->Value != nullptr))
499 return fail_at(arg, COMMAND_LINE_ERROR);
500 }
501 CommandLineSwitchCase(arg, "rfx")
502 {
503 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, arg->Value != nullptr))
504 return fail_at(arg, COMMAND_LINE_ERROR);
505 }
506 CommandLineSwitchCase(arg, "gfx")
507 {
508 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline,
509 arg->Value != nullptr))
510 return fail_at(arg, COMMAND_LINE_ERROR);
511 }
512 CommandLineSwitchCase(arg, "gfx-progressive")
513 {
514 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, arg->Value != nullptr))
515 return fail_at(arg, COMMAND_LINE_ERROR);
516 }
517#if defined(WITH_GFX_AV1)
518 CommandLineSwitchCase(arg, "gfx-av1")
519 {
520 BOOL parseFailed = FALSE;
521 BOOL enabled = TRUE;
522 UINT32 profile = freerdp_settings_get_uint32(settings, FreeRDP_GfxCodecAV1Profile);
523 if (arg->Value)
524 {
525 size_t count = 0;
526 char** args = CommandLineParseCommaSeparatedValues(arg->Value, &count);
527 WINPR_ASSERT(args || (count == 0));
528 for (size_t x = 0; x < count; x++)
529 {
530 const char* cur = args[x];
531 if (strncmp("profile:", cur, 8) == 0)
532 {
533 const char* val = &cur[8];
534 if (strcmp("high", val) == 0)
535 profile = 1;
536 else if (strcmp("low", val) == 0)
537 profile = 0;
538 else
539 {
540 const unsigned long v = strtoul(val, nullptr, 0);
541 if ((errno != 0) || (v >= UINT32_MAX))
542 {
543 parseFailed = TRUE;
544 break;
545 }
546 else
547 profile = WINPR_ASSERTING_INT_CAST(UINT32, v);
548 }
549 }
550 else if (strcmp("off", cur) == 0)
551 {
552 enabled = FALSE;
553 }
554 else
555 {
556 parseFailed = TRUE;
557 break;
558 }
559 }
560 CommandLineParserFree(args);
561 }
562 if (parseFailed)
563 return fail_at(arg, COMMAND_LINE_ERROR);
564 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxCodecAV1, enabled))
565 return fail_at(arg, COMMAND_LINE_ERROR);
566 if (!freerdp_settings_set_uint32(settings, FreeRDP_GfxCodecAV1Profile, profile))
567 return fail_at(arg, COMMAND_LINE_ERROR);
568 }
569#endif
570 CommandLineSwitchCase(arg, "gfx-rfx")
571 {
572 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, arg->Value != nullptr))
573 return fail_at(arg, COMMAND_LINE_ERROR);
574 }
575 CommandLineSwitchCase(arg, "gfx-planar")
576 {
577 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value != nullptr))
578 return fail_at(arg, COMMAND_LINE_ERROR);
579 }
580 CommandLineSwitchCase(arg, "gfx-avc420")
581 {
582 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value != nullptr))
583 return fail_at(arg, COMMAND_LINE_ERROR);
584 }
585 CommandLineSwitchCase(arg, "gfx-avc444")
586 {
587 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, arg->Value != nullptr))
588 return fail_at(arg, COMMAND_LINE_ERROR);
589 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, arg->Value != nullptr))
590 return fail_at(arg, COMMAND_LINE_ERROR);
591 }
592 CommandLineSwitchCase(arg, "keytab")
593 {
594 if (!freerdp_settings_set_string(settings, FreeRDP_KerberosKeytab, arg->Value))
595 return fail_at(arg, COMMAND_LINE_ERROR);
596 }
597 CommandLineSwitchCase(arg, "ccache")
598 {
599 if (!freerdp_settings_set_string(settings, FreeRDP_KerberosCache, arg->Value))
600 return fail_at(arg, COMMAND_LINE_ERROR);
601 }
602 CommandLineSwitchCase(arg, "tls-secrets-file")
603 {
604 if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, arg->Value))
605 return fail_at(arg, COMMAND_LINE_ERROR);
606 }
607 CommandLineSwitchDefault(arg)
608 {
609 }
610 CommandLineSwitchEnd(arg)
611 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
612
613 arg = CommandLineFindArgumentA(cargs, "monitors");
614
615 if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
616 {
617 UINT32 numMonitors = 0;
618 MONITOR_DEF monitors[16] = WINPR_C_ARRAY_INIT;
619 numMonitors = shadow_enum_monitors(monitors, 16);
620
621 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
622 {
623 /* Select monitors */
624 long val = strtol(arg->Value, nullptr, 0);
625
626 if ((val < 0) || (errno != 0) || ((UINT32)val >= numMonitors))
627 status = COMMAND_LINE_STATUS_PRINT;
628
629 server->selectedMonitor = (UINT32)val;
630 }
631 else
632 {
633 /* List monitors */
634
635 for (UINT32 index = 0; index < numMonitors; index++)
636 {
637 const MONITOR_DEF* monitor = &monitors[index];
638 const INT64 width = monitor->right - monitor->left + 1;
639 const INT64 height = monitor->bottom - monitor->top + 1;
640 WLog_INFO(
641 TAG, " %s [%" PRIu32 "] %" PRId64 "x%" PRId64 "\t+%" PRId32 "+%" PRId32 "",
642 (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
643 monitor->top);
644 }
645
646 status = COMMAND_LINE_STATUS_PRINT;
647 }
648 }
649
650 /* If we want to disable authentication we need to ensure that NLA security
651 * is not activated. Only TLS and RDP security allow anonymous login.
652 */
653 if (!server->authentication && !freerdp_settings_get_bool(settings, FreeRDP_VmConnectMode))
654 {
655 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
656 return COMMAND_LINE_ERROR;
657 }
658 return status;
659}
660
661WINPR_ATTR_NODISCARD
662static DWORD WINAPI shadow_server_thread(LPVOID arg)
663{
664 rdpShadowServer* server = (rdpShadowServer*)arg;
665 BOOL running = TRUE;
666 DWORD status = 0;
667 freerdp_listener* listener = server->listener;
668 if (shadow_subsystem_start(server->subsystem) < 0)
669 running = FALSE;
670
671 while (running)
672 {
673 HANDLE events[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
674 DWORD nCount = 0;
675 events[nCount++] = server->StopEvent;
676 nCount += listener->GetEventHandles(listener, &events[nCount], ARRAYSIZE(events) - nCount);
677
678 if (nCount <= 1)
679 {
680 WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
681 break;
682 }
683
684 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
685
686 switch (status)
687 {
688 case WAIT_FAILED:
689 case WAIT_OBJECT_0:
690 running = FALSE;
691 break;
692
693 default:
694 {
695 if (!listener->CheckFileDescriptor(listener))
696 {
697 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
698 running = FALSE;
699 }
700 else
701 {
702#ifdef _WIN32
703 Sleep(100); /* FIXME: listener event handles */
704#endif
705 }
706 }
707 break;
708 }
709 }
710
711 listener->Close(listener);
712 shadow_subsystem_stop(server->subsystem);
713
714 /* Signal to the clients that server is being stopped and wait for them
715 * to disconnect. */
716 if (shadow_client_boardcast_quit(server, 0))
717 {
718 while (ArrayList_Count(server->clients) > 0)
719 {
720 Sleep(100);
721 }
722 }
723
724 ExitThread(0);
725 return 0;
726}
727
728WINPR_ATTR_NODISCARD
729static BOOL open_port(rdpShadowServer* server, char* address)
730{
731 BOOL status = 0;
732 char* modaddr = address;
733
734 if (modaddr)
735 {
736 if (modaddr[0] == '[')
737 {
738 char* end = strchr(address, ']');
739 if (!end)
740 {
741 WLog_ERR(TAG, "Could not parse bind-address %s", address);
742 return -1;
743 }
744 *end++ = '\0';
745 if (strlen(end) > 0)
746 {
747 WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
748 return -1;
749 }
750 modaddr++;
751 }
752 }
753 status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
754
755 if (!status)
756 {
757 WLog_ERR(TAG,
758 "Problem creating TCP listener. (Port already used or insufficient permissions?)");
759 }
760
761 return status;
762}
763
764int shadow_server_start(rdpShadowServer* server)
765{
766 BOOL ipc = 0;
767 BOOL status = 0;
768 WSADATA wsaData;
769
770 if (!server)
771 return -1;
772
773 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
774 return -1;
775
776#ifndef _WIN32
777 (void)signal(SIGPIPE, SIG_IGN);
778#endif
779 server->screen = shadow_screen_new(server);
780
781 if (!server->screen)
782 {
783 WLog_ERR(TAG, "screen_new failed");
784 return -1;
785 }
786
787 server->capture = shadow_capture_new(server);
788
789 if (!server->capture)
790 {
791 WLog_ERR(TAG, "capture_new failed");
792 return -1;
793 }
794
795 /* Bind magic:
796 *
797 * empty ... bind TCP all
798 * <local path> ... bind local (IPC)
799 * bind-socket,<address> ... bind TCP to specified interface
800 */
801 ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
802 strnlen(bind_address, sizeof(bind_address))) != 0);
803 if (!ipc)
804 {
805 size_t count = 0;
806
807 char** ptr = CommandLineParseCommaSeparatedValuesEx(nullptr, server->ipcSocket, &count);
808 if (!ptr || (count <= 1))
809 {
810 if (server->ipcSocket == nullptr)
811 {
812 if (!open_port(server, nullptr))
813 {
814 CommandLineParserFree(ptr);
815 return -1;
816 }
817 }
818 else
819 {
820 CommandLineParserFree(ptr);
821 return -1;
822 }
823 }
824
825 WINPR_ASSERT(ptr || (count == 0));
826 for (size_t x = 1; x < count; x++)
827 {
828 BOOL success = open_port(server, ptr[x]);
829 if (!success)
830 {
831 CommandLineParserFree(ptr);
832 return -1;
833 }
834 }
835 CommandLineParserFree(ptr);
836 }
837 else
838 {
839 status = server->listener->OpenLocal(server->listener, server->ipcSocket);
840
841 if (!status)
842 {
843 WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
844 "insufficient permissions?)");
845 return -1;
846 }
847 }
848
849 if (!(server->thread =
850 CreateThread(nullptr, 0, shadow_server_thread, (void*)server, 0, nullptr)))
851 {
852 return -1;
853 }
854
855 return 0;
856}
857
858int shadow_server_stop(rdpShadowServer* server)
859{
860 if (!server)
861 return -1;
862
863 if (server->thread)
864 {
865 (void)SetEvent(server->StopEvent);
866 (void)WaitForSingleObject(server->thread, INFINITE);
867 (void)CloseHandle(server->thread);
868 server->thread = nullptr;
869 if (server->listener && server->listener->Close)
870 server->listener->Close(server->listener);
871 }
872
873 if (server->screen)
874 {
875 shadow_screen_free(server->screen);
876 server->screen = nullptr;
877 }
878
879 if (server->capture)
880 {
881 shadow_capture_free(server->capture);
882 server->capture = nullptr;
883 }
884
885 return 0;
886}
887
888WINPR_ATTR_NODISCARD
889static int shadow_server_init_config_path(rdpShadowServer* server)
890{
891 if (!server->ConfigPath)
892 {
893 char* configHome = freerdp_settings_get_config_path();
894
895 if (configHome)
896 {
897 if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, nullptr))
898 {
899 WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
900 free(configHome);
901 return -1;
902 }
903
904 server->ConfigPath = configHome;
905 }
906 }
907
908 if (!server->ConfigPath)
909 return -1; /* no usable config path */
910
911 return 1;
912}
913
914WINPR_ATTR_NODISCARD
915static BOOL shadow_server_create_certificate(rdpShadowServer* server, const char* filepath)
916{
917 BOOL rc = FALSE;
918 char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
919
920 WINPR_STATIC_ASSERT(ARRAYSIZE(makecert_argv) <= INT_MAX);
921 const size_t makecert_argc = ARRAYSIZE(makecert_argv);
922
923 MAKECERT_CONTEXT* makecert = makecert_context_new();
924
925 if (!makecert)
926 goto out_fail;
927
928 if (makecert_context_process(makecert, (int)makecert_argc, makecert_argv) < 0)
929 goto out_fail;
930
931 if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
932 goto out_fail;
933
934 WINPR_ASSERT(server);
935 WINPR_ASSERT(filepath);
936 if (!winpr_PathFileExists(server->CertificateFile))
937 {
938 if (makecert_context_output_certificate_file(makecert, filepath) != 1)
939 goto out_fail;
940 }
941
942 if (!winpr_PathFileExists(server->PrivateKeyFile))
943 {
944 if (makecert_context_output_private_key_file(makecert, filepath) != 1)
945 goto out_fail;
946 }
947 rc = TRUE;
948out_fail:
949 makecert_context_free(makecert);
950 return rc;
951}
952
953WINPR_ATTR_NODISCARD
954static BOOL shadow_server_init_certificate(rdpShadowServer* server)
955{
956 char* filepath = nullptr;
957 BOOL ret = FALSE;
958
959 WINPR_ASSERT(server);
960
961 if (!winpr_PathFileExists(server->ConfigPath) &&
962 !winpr_PathMakePath(server->ConfigPath, nullptr))
963 {
964 WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
965 return FALSE;
966 }
967
968 if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
969 return FALSE;
970
971 if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, nullptr))
972 {
973 if (!winpr_PathMakePath(filepath, nullptr))
974 {
975 WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
976 goto out_fail;
977 }
978 }
979
980 server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
981 server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
982
983 if (!server->CertificateFile || !server->PrivateKeyFile)
984 goto out_fail;
985
986 if ((!winpr_PathFileExists(server->CertificateFile)) ||
987 (!winpr_PathFileExists(server->PrivateKeyFile)))
988 {
989 if (!shadow_server_create_certificate(server, filepath))
990 goto out_fail;
991 }
992
993 {
994 rdpSettings* settings = server->settings;
995 WINPR_ASSERT(settings);
996
997 {
998 rdpPrivateKey* key = freerdp_key_new_from_file_enc(server->PrivateKeyFile, nullptr);
999 if (!key)
1000 goto out_fail;
1001 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
1002 goto out_fail;
1003 }
1004 {
1005 rdpCertificate* cert = freerdp_certificate_new_from_file(server->CertificateFile);
1006 if (!cert)
1007 goto out_fail;
1008
1009 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
1010 goto out_fail;
1011
1012 if (!freerdp_certificate_is_rdp_security_compatible(cert))
1013 {
1014 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
1015 goto out_fail;
1016 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
1017 goto out_fail;
1018 }
1019 }
1020 }
1021
1022 ret = TRUE;
1023out_fail:
1024 free(filepath);
1025 return ret;
1026}
1027
1028WINPR_ATTR_NODISCARD
1029static BOOL shadow_server_check_peer_restrictions(freerdp_listener* listener)
1030{
1031 WINPR_ASSERT(listener);
1032
1033 rdpShadowServer* server = (rdpShadowServer*)listener->info;
1034 WINPR_ASSERT(server);
1035
1036 if (server->maxClientsConnected > 0)
1037 {
1038 const size_t count = ArrayList_Count(server->clients);
1039 if (count >= server->maxClientsConnected)
1040 {
1041 WLog_WARN(TAG, "connection limit [%" PRIuz "] reached, discarding client",
1042 server->maxClientsConnected);
1043 return FALSE;
1044 }
1045 }
1046 return TRUE;
1047}
1048
1049int shadow_server_init(rdpShadowServer* server)
1050{
1051 int status = 0;
1052 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
1053 return -1;
1054 if (!WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()))
1055 return -1;
1056
1057 if (!(server->clients = ArrayList_New(TRUE)))
1058 goto fail;
1059
1060 if (!(server->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
1061 goto fail;
1062
1063 if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
1064 goto fail;
1065
1066 status = shadow_server_init_config_path(server);
1067
1068 if (status < 0)
1069 goto fail;
1070
1071 if (!shadow_server_init_certificate(server))
1072 goto fail;
1073
1074 server->listener = freerdp_listener_new();
1075
1076 if (!server->listener)
1077 goto fail;
1078
1079 server->listener->info = (void*)server;
1080 server->listener->CheckPeerAcceptRestrictions = shadow_server_check_peer_restrictions;
1081 server->listener->PeerAccepted = shadow_client_accepted;
1082 server->subsystem = shadow_subsystem_new();
1083
1084 if (!server->subsystem)
1085 goto fail;
1086
1087 status = shadow_subsystem_init(server->subsystem, server);
1088 if (status < 0)
1089 goto fail;
1090
1091 return status;
1092
1093fail:
1094 shadow_server_uninit(server);
1095 WLog_ERR(TAG, "Failed to initialize shadow server");
1096 return -1;
1097}
1098
1099int shadow_server_uninit(rdpShadowServer* server)
1100{
1101 if (!server)
1102 return -1;
1103
1104 shadow_server_stop(server);
1105 shadow_subsystem_uninit(server->subsystem);
1106 shadow_subsystem_free(server->subsystem);
1107 server->subsystem = nullptr;
1108 freerdp_listener_free(server->listener);
1109 server->listener = nullptr;
1110 free(server->CertificateFile);
1111 server->CertificateFile = nullptr;
1112 free(server->PrivateKeyFile);
1113 server->PrivateKeyFile = nullptr;
1114 free(server->ConfigPath);
1115 server->ConfigPath = nullptr;
1116 DeleteCriticalSection(&(server->lock));
1117 (void)CloseHandle(server->StopEvent);
1118 server->StopEvent = nullptr;
1119 ArrayList_Free(server->clients);
1120 server->clients = nullptr;
1121 return 1;
1122}
1123
1124rdpShadowServer* shadow_server_new(void)
1125{
1126 rdpShadowServer* server = nullptr;
1127 server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
1128
1129 if (!server)
1130 return nullptr;
1131
1132 server->SupportMultiRectBitmapUpdates = TRUE;
1133 server->port = 3389;
1134 server->mayView = TRUE;
1135 server->mayInteract = TRUE;
1136 server->h264RateControlMode = H264_RATECONTROL_VBR;
1137 server->h264BitRate = 10000000;
1138 server->h264FrameRate = 30;
1139 server->h264QP = 0;
1140 server->authentication = TRUE;
1141#if defined(WITH_GFX_AV1)
1142 server->AV1BitRate = 500;
1143 server->AV1RateControlMode = FREERDP_AV1_VBR;
1144#endif
1146 return server;
1147}
1148
1149void shadow_server_free(rdpShadowServer* server)
1150{
1151 if (!server)
1152 return;
1153
1154 free(server->ipcSocket);
1155 server->ipcSocket = nullptr;
1156 freerdp_settings_free(server->settings);
1157 server->settings = nullptr;
1158 free(server);
1159}
FREERDP_API rdpSettings * freerdp_settings_new(DWORD flags)
creates a new setting struct
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API char * freerdp_settings_get_config_path(void)
return the configuration directory for the library
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
FREERDP_API void freerdp_settings_free(rdpSettings *settings)
Free a settings struct with all data in it.
#define FREERDP_SETTINGS_SERVER_MODE
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_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.