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