1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 *
4 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
5 * Copyright 2017 Armin Novak <armin.novak@thincast.com>
6 * Copyright 2017 Thincast Technologies GmbH
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <errno.h>
26
27 #include <winpr/crt.h>
28 #include <winpr/ssl.h>
29 #include <winpr/wnd.h>
30 #include <winpr/path.h>
31 #include <winpr/cmdline.h>
32 #include <winpr/winsock.h>
33
34 #include <freerdp/log.h>
35 #include <freerdp/version.h>
36
37 #include <winpr/tools/makecert.h>
38
39 #ifndef _WIN32
40 #include <sys/select.h>
41 #include <signal.h>
42 #endif
43
44 #include "shadow.h"
45
46 #define TAG SERVER_TAG("shadow")
47
48 static const char bind_address[] = "bind-address,";
49
50 static const COMMAND_LINE_ARGUMENT_A shadow_args[] = {
51 { "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
52 { "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", NULL, NULL, -1, NULL,
53 "Server IPC socket" },
54 { "bind-address", COMMAND_LINE_VALUE_REQUIRED, "<bind-address>[,<another address>, ...]", NULL,
55 NULL, -1, NULL,
56 "An address to bind to. Use '[<ipv6>]' for IPv6 addresses, e.g. '[::1]' for "
57 "localhost" },
58 { "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL,
59 "Select or list monitors" },
60 { "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", NULL, NULL, -1, NULL,
61 "Select rectangle within monitor to share" },
62 { "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
63 "Clients must authenticate" },
64 { "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
65 "Clients may view without prompt" },
66 { "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
67 "Clients may interact without prompt" },
68 { "sec", COMMAND_LINE_VALUE_REQUIRED, "<rdp|tls|nla|ext>", NULL, NULL, -1, NULL,
69 "force specific protocol security" },
70 { "sec-rdp", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
71 "rdp protocol security" },
72 { "sec-tls", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
73 "tls protocol security" },
74 { "sec-nla", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
75 "nla protocol security" },
76 { "sec-ext", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
77 "nla extended protocol security" },
78 { "sam-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
79 "NTLM SAM file for NLA authentication" },
80 { "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, NULL,
81 "Print version" },
82 { "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, NULL, NULL, NULL, -1,
83 NULL, "Print the build configuration" },
84 { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?",
85 "Print help" },
86 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
87 };
88
shadow_server_print_command_line_help(int argc,char ** argv)89 static int shadow_server_print_command_line_help(int argc, char** argv)
90 {
91 char* str;
92 size_t length;
93 COMMAND_LINE_ARGUMENT_A* arg;
94 COMMAND_LINE_ARGUMENT_A largs[ARRAYSIZE(shadow_args)];
95 memcpy(largs, shadow_args, sizeof(shadow_args));
96 if (argc < 1)
97 return -1;
98
99 WLog_INFO(TAG, "Usage: %s [options]", argv[0]);
100 WLog_INFO(TAG, "");
101 WLog_INFO(TAG, "Syntax:");
102 WLog_INFO(TAG, " /flag (enables flag)");
103 WLog_INFO(TAG, " /option:<value> (specifies option with value)");
104 WLog_INFO(TAG,
105 " +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')");
106 WLog_INFO(TAG, "");
107 arg = largs;
108
109 do
110 {
111 if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
112 {
113 WLog_INFO(TAG, " %s", "/");
114 WLog_INFO(TAG, "%-20s", arg->Name);
115 WLog_INFO(TAG, "\t%s", arg->Text);
116 }
117 else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
118 (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
119 {
120 WLog_INFO(TAG, " %s", "/");
121
122 if (arg->Format)
123 {
124 length = (strlen(arg->Name) + strlen(arg->Format) + 2);
125 str = (char*)malloc(length + 1);
126
127 if (!str)
128 return -1;
129
130 sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
131 WLog_INFO(TAG, "%-20s", str);
132 free(str);
133 }
134 else
135 {
136 WLog_INFO(TAG, "%-20s", arg->Name);
137 }
138
139 WLog_INFO(TAG, "\t%s", arg->Text);
140 }
141 else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
142 {
143 length = strlen(arg->Name) + 32;
144 str = (char*)malloc(length + 1);
145
146 if (!str)
147 return -1;
148
149 sprintf_s(str, length + 1, "%s (default:%s)", arg->Name, arg->Default ? "on" : "off");
150 WLog_INFO(TAG, " %s", arg->Default ? "-" : "+");
151 WLog_INFO(TAG, "%-20s", str);
152 free(str);
153 WLog_INFO(TAG, "\t%s", arg->Text);
154 }
155 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
156
157 return 1;
158 }
159
shadow_server_command_line_status_print(rdpShadowServer * server,int argc,char ** argv,int status)160 int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
161 int status)
162 {
163 WINPR_UNUSED(server);
164
165 if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
166 {
167 WLog_INFO(TAG, "FreeRDP version %s (git %s)", FREERDP_VERSION_FULL, GIT_REVISION);
168 return COMMAND_LINE_STATUS_PRINT_VERSION;
169 }
170 else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
171 {
172 WLog_INFO(TAG, "%s", freerdp_get_build_config());
173 return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
174 }
175 else if (status == COMMAND_LINE_STATUS_PRINT)
176 {
177 return COMMAND_LINE_STATUS_PRINT;
178 }
179 else if (status < 0)
180 {
181 if (shadow_server_print_command_line_help(argc, argv) < 0)
182 return -1;
183
184 return COMMAND_LINE_STATUS_PRINT_HELP;
185 }
186
187 return 1;
188 }
189
shadow_server_parse_command_line(rdpShadowServer * server,int argc,char ** argv)190 int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv)
191 {
192 int status;
193 DWORD flags;
194 COMMAND_LINE_ARGUMENT_A* arg;
195 rdpSettings* settings = server->settings;
196 COMMAND_LINE_ARGUMENT_A largs[ARRAYSIZE(shadow_args)];
197 memcpy(largs, shadow_args, sizeof(shadow_args));
198
199 if (argc < 2)
200 return 1;
201
202 CommandLineClearArgumentsA(largs);
203 flags = COMMAND_LINE_SEPARATOR_COLON;
204 flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
205 status = CommandLineParseArgumentsA(argc, argv, largs, flags, server, NULL, NULL);
206
207 if (status < 0)
208 return status;
209
210 arg = largs;
211 errno = 0;
212
213 do
214 {
215 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
216 continue;
217
218 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
219 {
220 long val = strtol(arg->Value, NULL, 0);
221
222 if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
223 return -1;
224
225 server->port = (DWORD)val;
226 }
227 CommandLineSwitchCase(arg, "ipc-socket")
228 {
229 /* /bind-address is incompatible */
230 if (server->ipcSocket)
231 return -1;
232
233 server->ipcSocket = _strdup(arg->Value);
234
235 if (!server->ipcSocket)
236 return -1;
237 }
238 CommandLineSwitchCase(arg, "bind-address")
239 {
240 int rc;
241 size_t len = strlen(arg->Value) + sizeof(bind_address);
242 /* /ipc-socket is incompatible */
243 if (server->ipcSocket)
244 return -1;
245 server->ipcSocket = calloc(len, sizeof(CHAR));
246
247 if (!server->ipcSocket)
248 return -1;
249
250 rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
251 if ((rc < 0) || ((size_t)rc != len - 1))
252 return -1;
253 }
254 CommandLineSwitchCase(arg, "may-view")
255 {
256 server->mayView = arg->Value ? TRUE : FALSE;
257 }
258 CommandLineSwitchCase(arg, "may-interact")
259 {
260 server->mayInteract = arg->Value ? TRUE : FALSE;
261 }
262 CommandLineSwitchCase(arg, "rect")
263 {
264 char* p;
265 char* tok[4];
266 long x = -1, y = -1, w = -1, h = -1;
267 char* str = _strdup(arg->Value);
268
269 if (!str)
270 return -1;
271
272 tok[0] = p = str;
273 p = strchr(p + 1, ',');
274
275 if (!p)
276 {
277 free(str);
278 return -1;
279 }
280
281 *p++ = '\0';
282 tok[1] = p;
283 p = strchr(p + 1, ',');
284
285 if (!p)
286 {
287 free(str);
288 return -1;
289 }
290
291 *p++ = '\0';
292 tok[2] = p;
293 p = strchr(p + 1, ',');
294
295 if (!p)
296 {
297 free(str);
298 return -1;
299 }
300
301 *p++ = '\0';
302 tok[3] = p;
303 x = strtol(tok[0], NULL, 0);
304
305 if (errno != 0)
306 goto fail;
307
308 y = strtol(tok[1], NULL, 0);
309
310 if (errno != 0)
311 goto fail;
312
313 w = strtol(tok[2], NULL, 0);
314
315 if (errno != 0)
316 goto fail;
317
318 h = strtol(tok[3], NULL, 0);
319
320 if (errno != 0)
321 goto fail;
322
323 fail:
324 free(str);
325
326 if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
327 return -1;
328
329 server->subRect.left = x;
330 server->subRect.top = y;
331 server->subRect.right = x + w;
332 server->subRect.bottom = y + h;
333 server->shareSubRect = TRUE;
334 }
335 CommandLineSwitchCase(arg, "auth")
336 {
337 server->authentication = arg->Value ? TRUE : FALSE;
338 }
339 CommandLineSwitchCase(arg, "sec")
340 {
341 if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
342 {
343 settings->RdpSecurity = TRUE;
344 settings->TlsSecurity = FALSE;
345 settings->NlaSecurity = FALSE;
346 settings->ExtSecurity = FALSE;
347 settings->UseRdpSecurityLayer = TRUE;
348 }
349 else if (strcmp("tls", arg->Value) == 0) /* TLS */
350 {
351 settings->RdpSecurity = FALSE;
352 settings->TlsSecurity = TRUE;
353 settings->NlaSecurity = FALSE;
354 settings->ExtSecurity = FALSE;
355 }
356 else if (strcmp("nla", arg->Value) == 0) /* NLA */
357 {
358 settings->RdpSecurity = FALSE;
359 settings->TlsSecurity = FALSE;
360 settings->NlaSecurity = TRUE;
361 settings->ExtSecurity = FALSE;
362 }
363 else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
364 {
365 settings->RdpSecurity = FALSE;
366 settings->TlsSecurity = FALSE;
367 settings->NlaSecurity = FALSE;
368 settings->ExtSecurity = TRUE;
369 }
370 else
371 {
372 WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
373 }
374 }
375 CommandLineSwitchCase(arg, "sec-rdp")
376 {
377 settings->RdpSecurity = arg->Value ? TRUE : FALSE;
378 }
379 CommandLineSwitchCase(arg, "sec-tls")
380 {
381 settings->TlsSecurity = arg->Value ? TRUE : FALSE;
382 }
383 CommandLineSwitchCase(arg, "sec-nla")
384 {
385 settings->NlaSecurity = arg->Value ? TRUE : FALSE;
386 }
387 CommandLineSwitchCase(arg, "sec-ext")
388 {
389 settings->ExtSecurity = arg->Value ? TRUE : FALSE;
390 }
391 CommandLineSwitchCase(arg, "sam-file")
392 {
393 freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value);
394 }
395 CommandLineSwitchDefault(arg)
396 {
397 }
398 CommandLineSwitchEnd(arg)
399 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
400
401 arg = CommandLineFindArgumentA(largs, "monitors");
402
403 if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
404 {
405 int index;
406 int numMonitors;
407 MONITOR_DEF monitors[16];
408 numMonitors = shadow_enum_monitors(monitors, 16);
409
410 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
411 {
412 /* Select monitors */
413 long val = strtol(arg->Value, NULL, 0);
414
415 if ((val < 0) || (errno != 0) || (val >= numMonitors))
416 status = COMMAND_LINE_STATUS_PRINT;
417
418 server->selectedMonitor = val;
419 }
420 else
421 {
422 int width, height;
423 MONITOR_DEF* monitor;
424
425 /* List monitors */
426
427 for (index = 0; index < numMonitors; index++)
428 {
429 monitor = &monitors[index];
430 width = monitor->right - monitor->left;
431 height = monitor->bottom - monitor->top;
432 WLog_INFO(TAG, " %s [%d] %dx%d\t+%" PRId32 "+%" PRId32 "",
433 (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
434 monitor->top);
435 }
436
437 status = COMMAND_LINE_STATUS_PRINT;
438 }
439 }
440
441 return status;
442 }
443
shadow_server_thread(LPVOID arg)444 static DWORD WINAPI shadow_server_thread(LPVOID arg)
445 {
446 rdpShadowServer* server = (rdpShadowServer*)arg;
447 BOOL running = TRUE;
448 DWORD status;
449 freerdp_listener* listener = server->listener;
450 shadow_subsystem_start(server->subsystem);
451
452 while (running)
453 {
454 HANDLE events[32];
455 DWORD nCount = 0;
456 events[nCount++] = server->StopEvent;
457 nCount += listener->GetEventHandles(listener, &events[nCount], 32 - nCount);
458
459 if (nCount <= 1)
460 {
461 WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
462 break;
463 }
464
465 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
466
467 switch (status)
468 {
469 case WAIT_FAILED:
470 case WAIT_OBJECT_0:
471 running = FALSE;
472 break;
473
474 default:
475 {
476 if (!listener->CheckFileDescriptor(listener))
477 {
478 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
479 running = FALSE;
480 }
481 else
482 {
483 #ifdef _WIN32
484 Sleep(100); /* FIXME: listener event handles */
485 #endif
486 }
487 }
488 break;
489 }
490 }
491
492 listener->Close(listener);
493 shadow_subsystem_stop(server->subsystem);
494
495 /* Signal to the clients that server is being stopped and wait for them
496 * to disconnect. */
497 if (shadow_client_boardcast_quit(server, 0))
498 {
499 while (ArrayList_Count(server->clients) > 0)
500 {
501 Sleep(100);
502 }
503 }
504
505 ExitThread(0);
506 return 0;
507 }
508
open_port(rdpShadowServer * server,char * address)509 static BOOL open_port(rdpShadowServer* server, char* address)
510 {
511 BOOL status;
512 char* modaddr = address;
513
514 if (modaddr)
515 {
516 if (modaddr[0] == '[')
517 {
518 char* end = strchr(address, ']');
519 if (!end)
520 {
521 WLog_ERR(TAG, "Could not parse bind-address %s", address);
522 return -1;
523 }
524 *end++ = '\0';
525 if (strlen(end) > 0)
526 {
527 WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
528 return -1;
529 }
530 modaddr++;
531 }
532 }
533 status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
534
535 if (!status)
536 {
537 WLog_ERR(TAG,
538 "Problem creating TCP listener. (Port already used or insufficient permissions?)");
539 }
540
541 return status;
542 }
543
shadow_server_start(rdpShadowServer * server)544 int shadow_server_start(rdpShadowServer* server)
545 {
546 BOOL ipc;
547 BOOL status;
548 WSADATA wsaData;
549
550 if (!server)
551 return -1;
552
553 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
554 return -1;
555
556 #ifndef _WIN32
557 signal(SIGPIPE, SIG_IGN);
558 #endif
559 server->screen = shadow_screen_new(server);
560
561 if (!server->screen)
562 {
563 WLog_ERR(TAG, "screen_new failed");
564 return -1;
565 }
566
567 server->capture = shadow_capture_new(server);
568
569 if (!server->capture)
570 {
571 WLog_ERR(TAG, "capture_new failed");
572 return -1;
573 }
574
575 /* Bind magic:
576 *
577 * emtpy ... bind TCP all
578 * <local path> ... bind local (IPC)
579 * bind-socket,<address> ... bind TCP to specified interface
580 */
581 ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
582 strnlen(bind_address, sizeof(bind_address))) != 0);
583 if (!ipc)
584 {
585 size_t x, count;
586 char** list = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count);
587 if (!list || (count <= 1))
588 {
589 if (server->ipcSocket == NULL)
590 {
591 if (!open_port(server, NULL))
592 {
593 free(list);
594 return -1;
595 }
596 }
597 else
598 {
599 free(list);
600 return -1;
601 }
602 }
603
604 for (x = 1; x < count; x++)
605 {
606 BOOL success = open_port(server, list[x]);
607 if (!success)
608 {
609 free(list);
610 return -1;
611 }
612 }
613 free(list);
614 }
615 else
616 {
617 status = server->listener->OpenLocal(server->listener, server->ipcSocket);
618 if (!status)
619 {
620 WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
621 "insufficient permissions?)");
622 return -1;
623 }
624 }
625
626 if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL)))
627 {
628 return -1;
629 }
630
631 return 0;
632 }
633
shadow_server_stop(rdpShadowServer * server)634 int shadow_server_stop(rdpShadowServer* server)
635 {
636 if (!server)
637 return -1;
638
639 if (server->thread)
640 {
641 SetEvent(server->StopEvent);
642 WaitForSingleObject(server->thread, INFINITE);
643 CloseHandle(server->thread);
644 server->thread = NULL;
645 server->listener->Close(server->listener);
646 }
647
648 if (server->screen)
649 {
650 shadow_screen_free(server->screen);
651 server->screen = NULL;
652 }
653
654 if (server->capture)
655 {
656 shadow_capture_free(server->capture);
657 server->capture = NULL;
658 }
659
660 return 0;
661 }
662
shadow_server_init_config_path(rdpShadowServer * server)663 static int shadow_server_init_config_path(rdpShadowServer* server)
664 {
665 #ifdef _WIN32
666
667 if (!server->ConfigPath)
668 {
669 server->ConfigPath = GetEnvironmentSubPath("LOCALAPPDATA", "freerdp");
670 }
671
672 #endif
673 #ifdef __APPLE__
674
675 if (!server->ConfigPath)
676 {
677 char* userLibraryPath;
678 char* userApplicationSupportPath;
679 userLibraryPath = GetKnownSubPath(KNOWN_PATH_HOME, "Library");
680
681 if (userLibraryPath)
682 {
683 if (!winpr_PathFileExists(userLibraryPath) && !winpr_PathMakePath(userLibraryPath, 0))
684 {
685 WLog_ERR(TAG, "Failed to create directory '%s'", userLibraryPath);
686 free(userLibraryPath);
687 return -1;
688 }
689
690 userApplicationSupportPath = GetCombinedPath(userLibraryPath, "Application Support");
691
692 if (userApplicationSupportPath)
693 {
694 if (!winpr_PathFileExists(userApplicationSupportPath) &&
695 !winpr_PathMakePath(userApplicationSupportPath, 0))
696 {
697 WLog_ERR(TAG, "Failed to create directory '%s'", userApplicationSupportPath);
698 free(userLibraryPath);
699 free(userApplicationSupportPath);
700 return -1;
701 }
702
703 server->ConfigPath = GetCombinedPath(userApplicationSupportPath, "freerdp");
704 }
705
706 free(userLibraryPath);
707 free(userApplicationSupportPath);
708 }
709 }
710
711 #endif
712
713 if (!server->ConfigPath)
714 {
715 char* configHome;
716 configHome = GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME);
717
718 if (configHome)
719 {
720 if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, 0))
721 {
722 WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
723 free(configHome);
724 return -1;
725 }
726
727 server->ConfigPath = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, "freerdp");
728 free(configHome);
729 }
730 }
731
732 if (!server->ConfigPath)
733 return -1; /* no usable config path */
734
735 return 1;
736 }
737
shadow_server_init_certificate(rdpShadowServer * server)738 static BOOL shadow_server_init_certificate(rdpShadowServer* server)
739 {
740 char* filepath;
741 MAKECERT_CONTEXT* makecert = NULL;
742 BOOL ret = FALSE;
743 const char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
744 int makecert_argc = (sizeof(makecert_argv) / sizeof(char*));
745
746 if (!winpr_PathFileExists(server->ConfigPath) && !winpr_PathMakePath(server->ConfigPath, 0))
747 {
748 WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
749 return FALSE;
750 }
751
752 if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
753 return FALSE;
754
755 if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, 0))
756 {
757 if (!CreateDirectoryA(filepath, 0))
758 {
759 WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
760 goto out_fail;
761 }
762 }
763
764 server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
765 server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
766
767 if (!server->CertificateFile || !server->PrivateKeyFile)
768 goto out_fail;
769
770 if ((!winpr_PathFileExists(server->CertificateFile)) ||
771 (!winpr_PathFileExists(server->PrivateKeyFile)))
772 {
773 makecert = makecert_context_new();
774
775 if (!makecert)
776 goto out_fail;
777
778 if (makecert_context_process(makecert, makecert_argc, (char**)makecert_argv) < 0)
779 goto out_fail;
780
781 if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
782 goto out_fail;
783
784 if (!winpr_PathFileExists(server->CertificateFile))
785 {
786 if (makecert_context_output_certificate_file(makecert, filepath) != 1)
787 goto out_fail;
788 }
789
790 if (!winpr_PathFileExists(server->PrivateKeyFile))
791 {
792 if (makecert_context_output_private_key_file(makecert, filepath) != 1)
793 goto out_fail;
794 }
795 }
796
797 ret = TRUE;
798 out_fail:
799 makecert_context_free(makecert);
800 free(filepath);
801 return ret;
802 }
803
shadow_server_init(rdpShadowServer * server)804 int shadow_server_init(rdpShadowServer* server)
805 {
806 int status;
807 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
808 WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
809
810 if (!(server->clients = ArrayList_New(TRUE)))
811 goto fail_client_array;
812
813 if (!(server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
814 goto fail_stop_event;
815
816 if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
817 goto fail_server_lock;
818
819 status = shadow_server_init_config_path(server);
820
821 if (status < 0)
822 goto fail_config_path;
823
824 status = shadow_server_init_certificate(server);
825
826 if (status < 0)
827 goto fail_certificate;
828
829 server->listener = freerdp_listener_new();
830
831 if (!server->listener)
832 goto fail_listener;
833
834 server->listener->info = (void*)server;
835 server->listener->PeerAccepted = shadow_client_accepted;
836 server->subsystem = shadow_subsystem_new();
837
838 if (!server->subsystem)
839 goto fail_subsystem_new;
840
841 status = shadow_subsystem_init(server->subsystem, server);
842
843 if (status >= 0)
844 return status;
845
846 shadow_subsystem_free(server->subsystem);
847 fail_subsystem_new:
848 freerdp_listener_free(server->listener);
849 server->listener = NULL;
850 fail_listener:
851 free(server->CertificateFile);
852 server->CertificateFile = NULL;
853 free(server->PrivateKeyFile);
854 server->PrivateKeyFile = NULL;
855 fail_certificate:
856 free(server->ConfigPath);
857 server->ConfigPath = NULL;
858 fail_config_path:
859 DeleteCriticalSection(&(server->lock));
860 fail_server_lock:
861 CloseHandle(server->StopEvent);
862 server->StopEvent = NULL;
863 fail_stop_event:
864 ArrayList_Free(server->clients);
865 server->clients = NULL;
866 fail_client_array:
867 WLog_ERR(TAG, "Failed to initialize shadow server");
868 return -1;
869 }
870
shadow_server_uninit(rdpShadowServer * server)871 int shadow_server_uninit(rdpShadowServer* server)
872 {
873 if (!server)
874 return -1;
875
876 shadow_server_stop(server);
877 shadow_subsystem_uninit(server->subsystem);
878 shadow_subsystem_free(server->subsystem);
879 freerdp_listener_free(server->listener);
880 server->listener = NULL;
881 free(server->CertificateFile);
882 server->CertificateFile = NULL;
883 free(server->PrivateKeyFile);
884 server->PrivateKeyFile = NULL;
885 free(server->ConfigPath);
886 server->ConfigPath = NULL;
887 DeleteCriticalSection(&(server->lock));
888 CloseHandle(server->StopEvent);
889 server->StopEvent = NULL;
890 ArrayList_Free(server->clients);
891 server->clients = NULL;
892 return 1;
893 }
894
shadow_server_new(void)895 rdpShadowServer* shadow_server_new(void)
896 {
897 rdpShadowServer* server;
898 server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
899
900 if (!server)
901 return NULL;
902
903 server->port = 3389;
904 server->mayView = TRUE;
905 server->mayInteract = TRUE;
906 server->rfxMode = RLGR3;
907 server->h264RateControlMode = H264_RATECONTROL_VBR;
908 server->h264BitRate = 10000000;
909 server->h264FrameRate = 30;
910 server->h264QP = 0;
911 server->authentication = FALSE;
912 server->settings = freerdp_settings_new(FREERDP_SETTINGS_SERVER_MODE);
913 return server;
914 }
915
shadow_server_free(rdpShadowServer * server)916 void shadow_server_free(rdpShadowServer* server)
917 {
918 if (!server)
919 return;
920
921 free(server->ipcSocket);
922 server->ipcSocket = NULL;
923 freerdp_settings_free(server->settings);
924 server->settings = NULL;
925 free(server);
926 }
927