FreeRDP
Loading...
Searching...
No Matches
pf_server.c
1
24#include <freerdp/config.h>
25
26#include <winpr/crt.h>
27#include <winpr/ssl.h>
28#include <winpr/path.h>
29#include <winpr/synch.h>
30#include <winpr/string.h>
31#include <winpr/winsock.h>
32#include <winpr/thread.h>
33#include <errno.h>
34
35#include <freerdp/freerdp.h>
36#include <freerdp/streamdump.h>
37#include <freerdp/channels/wtsvc.h>
38#include <freerdp/channels/channels.h>
39#include <freerdp/channels/drdynvc.h>
40#include <freerdp/build-config.h>
41
42#include <freerdp/channels/rdpdr.h>
43
44#include <freerdp/server/proxy/proxy_server.h>
45#include <freerdp/server/proxy/proxy_log.h>
46
47#include "pf_server.h"
48#include "pf_input.h"
49#include "pf_channel.h"
50#include <freerdp/server/proxy/proxy_config.h>
51#include "pf_client.h"
52#include <freerdp/server/proxy/proxy_context.h>
53#include "pf_update.h"
54#include "proxy_modules.h"
55#include "pf_utils.h"
56#include "channels/pf_channel_drdynvc.h"
57#include "channels/pf_channel_rdpdr.h"
58
59#define TAG PROXY_TAG("server")
60
61typedef struct
62{
63 HANDLE thread;
64 freerdp_peer* client;
65} peer_thread_args;
66
67WINPR_ATTR_NODISCARD
68static BOOL pf_server_parse_target_from_routing_token(rdpContext* context, rdpSettings* settings,
69 FreeRDP_Settings_Keys_String targetID,
70 FreeRDP_Settings_Keys_UInt32 portID)
71{
72#define TARGET_MAX (100)
73#define ROUTING_TOKEN_PREFIX "Cookie: msts="
74 char* colon = nullptr;
75 size_t len = 0;
76 DWORD routing_token_length = 0;
77 const size_t prefix_len = strnlen(ROUTING_TOKEN_PREFIX, sizeof(ROUTING_TOKEN_PREFIX));
78 const char* routing_token = freerdp_nego_get_routing_token(context, &routing_token_length);
79 pServerContext* ps = (pServerContext*)context;
80
81 if (!routing_token)
82 return FALSE;
83
84 if ((routing_token_length <= prefix_len) || (routing_token_length >= TARGET_MAX))
85 {
86 PROXY_LOG_ERR(TAG, ps, "invalid routing token length: %" PRIu32 "", routing_token_length);
87 return FALSE;
88 }
89
90 len = routing_token_length - prefix_len;
91
92 if (!freerdp_settings_set_string_len(settings, targetID, routing_token + prefix_len, len))
93 return FALSE;
94
95 const char* target = freerdp_settings_get_string(settings, targetID);
96 colon = strchr(target, ':');
97
98 if (colon)
99 {
100 /* port is specified */
101 unsigned long p = strtoul(colon + 1, nullptr, 10);
102
103 if (p > USHRT_MAX)
104 return FALSE;
105
106 if (!freerdp_settings_set_uint32(settings, portID, (USHORT)p))
107 return FALSE;
108 }
109
110 return TRUE;
111}
112
113WINPR_ATTR_NODISCARD
114static BOOL pf_server_get_target_info(rdpContext* context, rdpSettings* settings,
115 const proxyConfig* config)
116{
117 pServerContext* ps = (pServerContext*)context;
118 proxyFetchTargetEventInfo ev = WINPR_C_ARRAY_INIT;
119
120 WINPR_ASSERT(settings);
121 WINPR_ASSERT(ps);
122 WINPR_ASSERT(ps->pdata);
123
124 ev.fetch_method = config->FixedTarget ? PROXY_FETCH_TARGET_METHOD_CONFIG
125 : PROXY_FETCH_TARGET_METHOD_LOAD_BALANCE_INFO;
126
127 if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_SERVER_FETCH_TARGET_ADDR, ps->pdata,
128 &ev))
129 return FALSE;
130
131 switch (ev.fetch_method)
132 {
133 case PROXY_FETCH_TARGET_METHOD_DEFAULT:
134 case PROXY_FETCH_TARGET_METHOD_LOAD_BALANCE_INFO:
135 return pf_server_parse_target_from_routing_token(
136 context, settings, FreeRDP_ServerHostname, FreeRDP_ServerPort);
137
138 case PROXY_FETCH_TARGET_METHOD_CONFIG:
139 {
140 WINPR_ASSERT(config);
141
142 if (config->TargetPort > 0)
143 {
144 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, config->TargetPort))
145 return FALSE;
146 }
147 else
148 {
149 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, 3389))
150 return FALSE;
151 }
152
153 if (!freerdp_settings_set_uint32(settings, FreeRDP_TlsSecLevel,
154 config->TargetTlsSecLevel))
155 return FALSE;
156
157 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, config->TargetHost))
158 {
159 PROXY_LOG_ERR(TAG, ps, "strdup failed!");
160 return FALSE;
161 }
162
163 if (config->TargetUser)
164 {
165 if (!freerdp_settings_set_string(settings, FreeRDP_Username, config->TargetUser))
166 return FALSE;
167 }
168
169 if (config->TargetDomain)
170 {
171 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, config->TargetDomain))
172 return FALSE;
173 }
174
175 if (config->TargetPassword)
176 {
177 if (!freerdp_settings_set_string(settings, FreeRDP_Password,
178 config->TargetPassword))
179 return FALSE;
180 }
181
182 return TRUE;
183 }
184 case PROXY_FETCH_TARGET_USE_CUSTOM_ADDR:
185 {
186 if (!ev.target_address)
187 {
188 PROXY_LOG_ERR(
189 TAG, ps,
190 "router: using CUSTOM_ADDR fetch method, but target_address == nullptr");
191 return FALSE;
192 }
193
194 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, ev.target_address))
195 {
196 PROXY_LOG_ERR(TAG, ps, "strdup failed!");
197 return FALSE;
198 }
199
200 free(ev.target_address);
201 return freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, ev.target_port);
202 }
203 default:
204 PROXY_LOG_ERR(TAG, ps, "unknown target fetch method: %u", ev.fetch_method);
205 return FALSE;
206 }
207
208 return TRUE;
209}
210
211WINPR_ATTR_NODISCARD
212static BOOL pf_server_setup_channels(freerdp_peer* peer)
213{
214 BOOL rc = FALSE;
215 char** accepted_channels = nullptr;
216 size_t accepted_channels_count = 0;
217 pServerContext* ps = (pServerContext*)peer->context;
218
219 accepted_channels = WTSGetAcceptedChannelNames(peer, &accepted_channels_count);
220 if (!accepted_channels)
221 return TRUE;
222
223 for (size_t i = 0; i < accepted_channels_count; i++)
224 {
225 pServerStaticChannelContext* channelContext = nullptr;
226 const char* cname = accepted_channels[i];
227 UINT16 channelId = WTSChannelGetId(peer, cname);
228
229 PROXY_LOG_INFO(TAG, ps, "Accepted channel: %s (%" PRIu16 ")", cname, channelId);
230 channelContext = StaticChannelContext_new(ps, cname, channelId);
231 if (!channelContext)
232 {
233 PROXY_LOG_ERR(TAG, ps, "error setting up channelContext for '%s'", cname);
234 goto fail;
235 }
236
237 if ((strcmp(cname, DRDYNVC_SVC_CHANNEL_NAME) == 0) &&
238 (channelContext->channelMode == PF_UTILS_CHANNEL_INTERCEPT))
239 {
240 if (!pf_channel_setup_drdynvc(ps->pdata, channelContext))
241 {
242 PROXY_LOG_ERR(TAG, ps, "error while setting up dynamic channel");
243 StaticChannelContext_free(channelContext);
244 goto fail;
245 }
246 }
247 else if (strcmp(cname, RDPDR_SVC_CHANNEL_NAME) == 0 &&
248 (channelContext->channelMode == PF_UTILS_CHANNEL_INTERCEPT))
249 {
250 if (!pf_channel_setup_rdpdr(ps, channelContext))
251 {
252 PROXY_LOG_ERR(TAG, ps, "error while setting up redirection channel");
253 StaticChannelContext_free(channelContext);
254 goto fail;
255 }
256 }
257 else
258 {
259 if (!pf_channel_setup_generic(channelContext))
260 {
261 PROXY_LOG_ERR(TAG, ps, "error while setting up generic channel");
262 StaticChannelContext_free(channelContext);
263 goto fail;
264 }
265 }
266
267 if (!HashTable_Insert(ps->channelsByFrontId, &channelContext->front_channel_id,
268 channelContext))
269 {
270 StaticChannelContext_free(channelContext);
271 PROXY_LOG_ERR(TAG, ps, "error inserting channelContext in byId table for '%s'", cname);
272 goto fail;
273 }
274 }
275
276 rc = TRUE;
277fail:
278 free((void*)accepted_channels);
279 return rc;
280}
281
282/* Event callbacks */
283
291WINPR_ATTR_NODISCARD
292static BOOL pf_server_post_connect(freerdp_peer* peer)
293{
294 pServerContext* ps = nullptr;
295 pClientContext* pc = nullptr;
296 rdpSettings* client_settings = nullptr;
297 proxyData* pdata = nullptr;
298 rdpSettings* frontSettings = nullptr;
299
300 WINPR_ASSERT(peer);
301
302 ps = (pServerContext*)peer->context;
303 WINPR_ASSERT(ps);
304
305 frontSettings = peer->context->settings;
306 WINPR_ASSERT(frontSettings);
307
308 pdata = ps->pdata;
309 WINPR_ASSERT(pdata);
310
311 const char* ClientHostname = freerdp_settings_get_string(frontSettings, FreeRDP_ClientHostname);
312 PROXY_LOG_INFO(TAG, ps, "Accepted client: %s", ClientHostname);
313 if (!pf_server_setup_channels(peer))
314 {
315 PROXY_LOG_ERR(TAG, ps, "error setting up channels");
316 return FALSE;
317 }
318
319 pc = pf_context_create_client_context(frontSettings);
320 if (pc == nullptr)
321 {
322 PROXY_LOG_ERR(TAG, ps, "failed to create client context!");
323 return FALSE;
324 }
325
326 client_settings = pc->context.settings;
327
328 /* keep both sides of the connection in pdata */
329 proxy_data_set_client_context(pdata, pc);
330
331 if (!pf_server_get_target_info(peer->context, client_settings, pdata->config))
332 {
333 PROXY_LOG_INFO(TAG, ps, "pf_server_get_target_info failed!");
334 return FALSE;
335 }
336
337 PROXY_LOG_INFO(TAG, ps, "remote target is %s:%" PRIu32 "",
338 freerdp_settings_get_string(client_settings, FreeRDP_ServerHostname),
339 freerdp_settings_get_uint32(client_settings, FreeRDP_ServerPort));
340
341 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_POST_CONNECT, pdata, peer))
342 return FALSE;
343
344 /* Start a proxy's client in it's own thread */
345 if (!(pdata->client_thread = CreateThread(nullptr, 0, pf_client_start, pc, 0, nullptr)))
346 {
347 PROXY_LOG_ERR(TAG, ps, "failed to create client thread");
348 return FALSE;
349 }
350
351 return TRUE;
352}
353
354WINPR_ATTR_NODISCARD
355static BOOL pf_server_activate(freerdp_peer* peer)
356{
357 pServerContext* ps = nullptr;
358 proxyData* pdata = nullptr;
359 rdpSettings* settings = nullptr;
360
361 WINPR_ASSERT(peer);
362
363 ps = (pServerContext*)peer->context;
364 WINPR_ASSERT(ps);
365
366 pdata = ps->pdata;
367 WINPR_ASSERT(pdata);
368
369 settings = peer->context->settings;
370
371 if (!freerdp_settings_set_uint32(settings, FreeRDP_CompressionLevel, PACKET_COMPR_TYPE_RDP8))
372 return FALSE;
373 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_ACTIVATE, pdata, peer))
374 return FALSE;
375
376 return TRUE;
377}
378
379WINPR_ATTR_NODISCARD
380static BOOL pf_server_logon(freerdp_peer* peer, const SEC_WINNT_AUTH_IDENTITY* identity,
381 BOOL automatic)
382{
383 pServerContext* ps = nullptr;
384 proxyData* pdata = nullptr;
385 proxyServerPeerLogon info = WINPR_C_ARRAY_INIT;
386
387 WINPR_ASSERT(peer);
388
389 ps = (pServerContext*)peer->context;
390 WINPR_ASSERT(ps);
391
392 pdata = ps->pdata;
393 WINPR_ASSERT(pdata);
394 WINPR_ASSERT(identity);
395
396 info.identity = identity;
397 info.automatic = automatic;
398 return (pf_modules_run_filter(pdata->module, FILTER_TYPE_SERVER_PEER_LOGON, pdata, &info));
399}
400
401WINPR_ATTR_NODISCARD
402static BOOL pf_server_adjust_monitor_layout(WINPR_ATTR_UNUSED freerdp_peer* peer)
403{
404 WINPR_ASSERT(peer);
405 /* proxy as is, there's no need to do anything here */
406 return TRUE;
407}
408
409WINPR_ATTR_NODISCARD
410static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 channelId,
411 const BYTE* data, size_t size, UINT32 flags,
412 size_t totalSize)
413{
414 UINT64 channelId64 = channelId;
415
416 WINPR_ASSERT(peer);
417
418 pServerContext* ps = (pServerContext*)peer->context;
419 WINPR_ASSERT(ps);
420
421 proxyData* pdata = ps->pdata;
422 WINPR_ASSERT(pdata);
423
424 pClientContext* pc = pdata->pc;
425
426 /*
427 * client side is not initialized yet, call original callback.
428 * this is probably a drdynvc message between peer and proxy server,
429 * which doesn't need to be proxied.
430 */
431 if (!pc)
432 goto original_cb;
433
434 {
435 const pServerStaticChannelContext* channel =
436 HashTable_GetItemValue(ps->channelsByFrontId, &channelId64);
437 if (!channel)
438 {
439 PROXY_LOG_ERR(TAG, ps, "channel id=%" PRIu64 " not registered here, dropping",
440 channelId64);
441 return TRUE;
442 }
443
444 WINPR_ASSERT(channel->onFrontData);
445 switch (channel->onFrontData(pdata, channel, data, size, flags, totalSize))
446 {
447 case PF_CHANNEL_RESULT_PASS:
448 {
449 proxyChannelDataEventInfo ev = WINPR_C_ARRAY_INIT;
450
451 ev.channel_id = channelId;
452 ev.channel_name = channel->channel_name;
453 ev.data = data;
454 ev.data_len = size;
455 ev.flags = flags;
456 ev.total_size = totalSize;
457 return IFCALLRESULT(TRUE, pc->sendChannelData, pc, &ev);
458 }
459 case PF_CHANNEL_RESULT_DROP:
460 return TRUE;
461 case PF_CHANNEL_RESULT_ERROR:
462 default:
463 return FALSE;
464 }
465 }
466original_cb:
467 WINPR_ASSERT(pdata->server_receive_channel_data_original);
468 return pdata->server_receive_channel_data_original(peer, channelId, data, size, flags,
469 totalSize);
470}
471
472WINPR_ATTR_NODISCARD
473static BOOL pf_server_initialize_peer_connection(freerdp_peer* peer)
474{
475 WINPR_ASSERT(peer);
476
477 pServerContext* ps = (pServerContext*)peer->context;
478 if (!ps)
479 return FALSE;
480
481 rdpSettings* settings = peer->context->settings;
482 WINPR_ASSERT(settings);
483
484 proxyData* pdata = proxy_data_new();
485 if (!pdata)
486 return FALSE;
487 proxyServer* server = (proxyServer*)peer->ContextExtra;
488 WINPR_ASSERT(server);
489 proxy_data_set_server_context(pdata, ps);
490
491 pdata->module = server->module;
492 const proxyConfig* config = pdata->config = server->config;
493
494 rdpPrivateKey* key = freerdp_key_new_from_pem_enc(config->PrivateKeyPEM, nullptr);
495 if (!key)
496 return FALSE;
497
498 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
499 return FALSE;
500
501 rdpCertificate* cert = freerdp_certificate_new_from_pem(config->CertificatePEM);
502 if (!cert)
503 return FALSE;
504
505 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
506 return FALSE;
507
508 /* currently not supporting GDI orders */
509 {
510 void* OrderSupport = freerdp_settings_get_pointer_writable(settings, FreeRDP_OrderSupport);
511 ZeroMemory(OrderSupport, 32);
512 }
513
514 WINPR_ASSERT(peer->context->update);
515 peer->context->update->autoCalculateBitmapData = FALSE;
516
517 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMonitorLayoutPdu, TRUE))
518 return FALSE;
519 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, config->GFX))
520 return FALSE;
521
522 if (pf_utils_is_passthrough(config))
523 {
524 if (!freerdp_settings_set_bool(settings, FreeRDP_DeactivateClientDecoding, TRUE))
525 return FALSE;
526 }
527
528 if (config->RemoteApp)
529 {
530 const UINT32 mask =
531 RAIL_LEVEL_SUPPORTED | RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED |
532 RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED | RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED |
533 RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED |
534 RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED | RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED |
535 RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED;
536 if (!freerdp_settings_set_uint32(settings, FreeRDP_RemoteApplicationSupportLevel, mask))
537 return FALSE;
538 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAppLanguageBarSupported, TRUE))
539 return FALSE;
540 }
541
542 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, config->ServerRdpSecurity))
543 return FALSE;
544 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, config->ServerTlsSecurity))
545 return FALSE;
546 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, config->ServerNlaSecurity))
547 return FALSE;
548
549 if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
550 ENCRYPTION_LEVEL_CLIENT_COMPATIBLE))
551 return FALSE;
552 if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32))
553 return FALSE;
554 if (!freerdp_settings_set_bool(settings, FreeRDP_SuppressOutput, TRUE))
555 return FALSE;
556 if (!freerdp_settings_set_bool(settings, FreeRDP_RefreshRect, TRUE))
557 return FALSE;
558 if (!freerdp_settings_set_bool(settings, FreeRDP_DesktopResize, TRUE))
559 return FALSE;
560
561 if (!freerdp_settings_set_uint32(settings, FreeRDP_MultifragMaxRequestSize,
562 0xFFFFFF)) /* FIXME */
563 return FALSE;
564
565 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, config->RFX))
566 return FALSE;
567
568 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, config->NSC))
569 return FALSE;
570
571 peer->PostConnect = pf_server_post_connect;
572 peer->Activate = pf_server_activate;
573 peer->Logon = pf_server_logon;
574 peer->AdjustMonitorsLayout = pf_server_adjust_monitor_layout;
575
576 /* virtual channels receive data hook */
577 pdata->server_receive_channel_data_original = peer->ReceiveChannelData;
578 peer->ReceiveChannelData = pf_server_receive_channel_data_hook;
579
580 /* Register server input/update callbacks only after proxy client is fully activated */
581 pf_server_register_input_callbacks(peer->context->input);
582 pf_server_register_update_callbacks(peer->context->update);
583
584 return (stream_dump_register_handlers(peer->context, CONNECTION_STATE_NEGO, TRUE));
585}
586
592WINPR_ATTR_NODISCARD
593static DWORD WINAPI pf_server_handle_peer(LPVOID arg)
594{
595 HANDLE eventHandles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
596 pServerContext* ps = nullptr;
597 proxyData* pdata = nullptr;
598 peer_thread_args* args = arg;
599
600 WINPR_ASSERT(args);
601
602 freerdp_peer* client = args->client;
603 WINPR_ASSERT(client);
604
605 proxyServer* server = (proxyServer*)client->ContextExtra;
606 WINPR_ASSERT(server);
607
608 ArrayList_Lock(server->peer_list);
609 size_t count = ArrayList_Count(server->peer_list);
610 ArrayList_Unlock(server->peer_list);
611
612 if (!pf_context_init_server_context(client))
613 goto out_free_peer;
614
615 if (!pf_server_initialize_peer_connection(client))
616 goto out_free_peer;
617
618 ps = (pServerContext*)client->context;
619 WINPR_ASSERT(ps);
620 PROXY_LOG_DBG(TAG, ps, "Added peer, %" PRIuz " connected", count);
621
622 pdata = ps->pdata;
623 WINPR_ASSERT(pdata);
624
625 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_SESSION_INITIALIZE, pdata, client))
626 goto out_free_peer;
627
628 WINPR_ASSERT(client->Initialize);
629 if (!client->Initialize(client))
630 goto out_free_peer;
631
632 PROXY_LOG_INFO(TAG, ps, "new connection: proxy address: %s, client address: %s",
633 pdata->config->Host, client->hostname);
634
635 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_SESSION_STARTED, pdata, client))
636 goto out_free_peer;
637
638 while (1)
639 {
640 HANDLE ChannelEvent = INVALID_HANDLE_VALUE;
641 DWORD eventCount = 0;
642 {
643 WINPR_ASSERT(client->GetEventHandles);
644 const DWORD tmp = client->GetEventHandles(client, &eventHandles[eventCount],
645 ARRAYSIZE(eventHandles) - eventCount);
646
647 if (tmp == 0)
648 {
649 PROXY_LOG_ERR(TAG, ps, "Failed to get FreeRDP transport event handles");
650 break;
651 }
652
653 eventCount += tmp;
654 }
655 /* Main client event handling loop */
656 ChannelEvent = WTSVirtualChannelManagerGetEventHandle(ps->vcm);
657
658 WINPR_ASSERT(ChannelEvent && (ChannelEvent != INVALID_HANDLE_VALUE));
659 WINPR_ASSERT(pdata->abort_event && (pdata->abort_event != INVALID_HANDLE_VALUE));
660 eventHandles[eventCount++] = ChannelEvent;
661 eventHandles[eventCount++] = pdata->abort_event;
662 eventHandles[eventCount++] = server->stopEvent;
663
664 const DWORD status = WaitForMultipleObjects(
665 eventCount, eventHandles, FALSE, 1000); /* Do periodic polling to avoid client hang */
666
667 if (status == WAIT_FAILED)
668 {
669 PROXY_LOG_ERR(TAG, ps, "WaitForMultipleObjects failed (status: %" PRIu32 ")", status);
670 break;
671 }
672
673 WINPR_ASSERT(client->CheckFileDescriptor);
674 if (client->CheckFileDescriptor(client) != TRUE)
675 break;
676
677 if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0)
678 {
679 if (!WTSVirtualChannelManagerCheckFileDescriptor(ps->vcm))
680 {
681 PROXY_LOG_ERR(TAG, ps, "WTSVirtualChannelManagerCheckFileDescriptor failure");
682 goto fail;
683 }
684 }
685
686 /* only disconnect after checking client's and vcm's file descriptors */
687 if (proxy_data_shall_disconnect(pdata))
688 {
689 PROXY_LOG_INFO(TAG, ps, "abort event is set, closing connection with peer %s",
690 client->hostname);
691 break;
692 }
693
694 if (WaitForSingleObject(server->stopEvent, 0) == WAIT_OBJECT_0)
695 {
696 PROXY_LOG_INFO(TAG, ps, "Server shutting down, terminating peer");
697 break;
698 }
699
700 switch (WTSVirtualChannelManagerGetDrdynvcState(ps->vcm))
701 {
702 /* Dynamic channel status may have been changed after processing */
703 case DRDYNVC_STATE_NONE:
704
705 /* Initialize drdynvc channel */
706 if (!WTSVirtualChannelManagerCheckFileDescriptor(ps->vcm))
707 {
708 PROXY_LOG_ERR(TAG, ps, "Failed to initialize drdynvc channel");
709 goto fail;
710 }
711
712 break;
713
714 case DRDYNVC_STATE_READY:
715 if (WaitForSingleObject(ps->dynvcReady, 0) == WAIT_TIMEOUT)
716 {
717 (void)SetEvent(ps->dynvcReady);
718 }
719
720 break;
721
722 default:
723 break;
724 }
725 }
726
727fail:
728
729 PROXY_LOG_INFO(TAG, ps, "starting shutdown of connection");
730 PROXY_LOG_INFO(TAG, ps, "stopping proxy's client");
731
732 /* Abort the client. */
733 proxy_data_abort_connect(pdata);
734
735 (void)pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_SESSION_END, pdata, client);
736
737 PROXY_LOG_INFO(TAG, ps, "freeing server's channels");
738
739 WINPR_ASSERT(client->Close);
740 client->Close(client);
741
742 WINPR_ASSERT(client->Disconnect);
743 client->Disconnect(client);
744
745out_free_peer:
746 PROXY_LOG_INFO(TAG, ps, "freeing proxy data");
747
748 if (pdata && pdata->client_thread)
749 {
750 proxy_data_abort_connect(pdata);
751 (void)WaitForSingleObject(pdata->client_thread, INFINITE);
752 }
753
754 {
755 ArrayList_Lock(server->peer_list);
756 ArrayList_Remove(server->peer_list, args->thread);
757 count = ArrayList_Count(server->peer_list);
758 ArrayList_Unlock(server->peer_list);
759 }
760 PROXY_LOG_DBG(TAG, ps, "Removed peer, %" PRIuz " connected", count);
761 freerdp_peer_context_free(client);
762 freerdp_peer_free(client);
763 proxy_data_free(pdata);
764
765#if defined(WITH_DEBUG_EVENTS)
766 DumpEventHandles();
767#endif
768 free(args);
769 ExitThread(0);
770 return 0;
771}
772
773WINPR_ATTR_NODISCARD
774static BOOL pf_server_start_peer(freerdp_peer* client)
775{
776 HANDLE hThread = nullptr;
777 proxyServer* server = nullptr;
778 peer_thread_args* args = calloc(1, sizeof(peer_thread_args));
779 if (!args)
780 return FALSE;
781
782 WINPR_ASSERT(client);
783 args->client = client;
784
785 server = (proxyServer*)client->ContextExtra;
786 WINPR_ASSERT(server);
787
788 hThread = CreateThread(nullptr, 0, pf_server_handle_peer, args, CREATE_SUSPENDED, nullptr);
789 if (!hThread)
790 {
791 free(args);
792 return FALSE;
793 }
794
795 args->thread = hThread;
796 ArrayList_Lock(server->peer_list);
797 const BOOL appended = ArrayList_Append(server->peer_list, hThread);
798 ArrayList_Unlock(server->peer_list);
799 if (!appended)
800 {
801 (void)CloseHandle(hThread);
802 free(args);
803 return FALSE;
804 }
805
806 return ResumeThread(hThread) != (DWORD)-1;
807}
808
809WINPR_ATTR_NODISCARD
810static BOOL pf_server_peer_accepted(freerdp_listener* listener, freerdp_peer* client)
811{
812 WINPR_ASSERT(listener);
813 WINPR_ASSERT(client);
814
815 client->ContextExtra = listener->info;
816
817 return pf_server_start_peer(client);
818}
819
820BOOL pf_server_start(proxyServer* server)
821{
822 WSADATA wsaData;
823
824 WINPR_ASSERT(server);
825
826 if (!WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()))
827 goto error;
828
829 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
830 goto error;
831
832 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
833 goto error;
834
835 WINPR_ASSERT(server->config);
836 WINPR_ASSERT(server->listener);
837 WINPR_ASSERT(server->listener->Open);
838 if (!server->listener->Open(server->listener, server->config->Host, server->config->Port))
839 {
840 switch (errno)
841 {
842 case EADDRINUSE:
843 WLog_ERR(TAG, "failed to start listener: address already in use!");
844 break;
845 case EACCES:
846 WLog_ERR(TAG, "failed to start listener: insufficient permissions!");
847 break;
848 default:
849 WLog_ERR(TAG, "failed to start listener: errno=%d", errno);
850 break;
851 }
852
853 goto error;
854 }
855
856 return TRUE;
857
858error:
859 WSACleanup();
860 return FALSE;
861}
862
863BOOL pf_server_start_from_socket(proxyServer* server, int socket)
864{
865 WSADATA wsaData;
866
867 WINPR_ASSERT(server);
868
869 if (!WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()))
870 goto error;
871
872 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
873 goto error;
874
875 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
876 goto error;
877
878 WINPR_ASSERT(server->listener);
879 WINPR_ASSERT(server->listener->OpenFromSocket);
880 if (!server->listener->OpenFromSocket(server->listener, socket))
881 {
882 switch (errno)
883 {
884 case EADDRINUSE:
885 WLog_ERR(TAG, "failed to start listener: address already in use!");
886 break;
887 case EACCES:
888 WLog_ERR(TAG, "failed to start listener: insufficient permissions!");
889 break;
890 default:
891 WLog_ERR(TAG, "failed to start listener: errno=%d", errno);
892 break;
893 }
894
895 goto error;
896 }
897
898 return TRUE;
899
900error:
901 WSACleanup();
902 return FALSE;
903}
904
905BOOL pf_server_start_with_peer_socket(proxyServer* server, int socket)
906{
907 struct sockaddr_storage peer_addr;
908 socklen_t len = sizeof(peer_addr);
909 freerdp_peer* client = nullptr;
910
911 WINPR_ASSERT(server);
912
913 if (WaitForSingleObject(server->stopEvent, 0) == WAIT_OBJECT_0)
914 goto fail;
915
916 client = freerdp_peer_new(socket);
917 if (!client)
918 goto fail;
919
920 if (getpeername(socket, (struct sockaddr*)&peer_addr, &len) != 0)
921 goto fail;
922
923 if (!freerdp_peer_set_local_and_hostname(client, &peer_addr))
924 goto fail;
925
926 client->ContextExtra = server;
927
928 if (!pf_server_start_peer(client))
929 goto fail;
930
931 return TRUE;
932
933fail:
934 WLog_ERR(TAG, "PeerAccepted callback failed");
935 freerdp_peer_free(client);
936 return FALSE;
937}
938
939WINPR_ATTR_NODISCARD
940static BOOL are_all_required_modules_loaded(proxyModule* module, const proxyConfig* config)
941{
942 for (size_t i = 0; i < pf_config_required_plugins_count(config); i++)
943 {
944 const char* plugin_name = pf_config_required_plugin(config, i);
945
946 if (!pf_modules_is_plugin_loaded(module, plugin_name))
947 {
948 WLog_ERR(TAG, "Required plugin '%s' is not loaded. stopping.", plugin_name);
949 return FALSE;
950 }
951 }
952
953 return TRUE;
954}
955
956static void peer_free(void* obj)
957{
958 HANDLE hdl = (HANDLE)obj;
959 (void)CloseHandle(hdl);
960}
961
962proxyServer* pf_server_new(const proxyConfig* config)
963{
964 wObject* obj = nullptr;
965 proxyServer* server = nullptr;
966
967 WINPR_ASSERT(config);
968
969 server = calloc(1, sizeof(proxyServer));
970 if (!server)
971 return nullptr;
972
973 if (!pf_config_clone(&server->config, config))
974 goto out;
975
976 server->module = pf_modules_new(FREERDP_PROXY_PLUGINDIR, pf_config_modules(server->config),
977 pf_config_modules_count(server->config));
978 if (!server->module)
979 {
980 WLog_ERR(TAG, "failed to initialize proxy modules!");
981 goto out;
982 }
983
984 if (!pf_modules_list_loaded_plugins(server->module))
985 goto out;
986
987 if (!are_all_required_modules_loaded(server->module, server->config))
988 goto out;
989
990 server->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
991 if (!server->stopEvent)
992 goto out;
993
994 server->listener = freerdp_listener_new();
995 if (!server->listener)
996 goto out;
997
998 server->peer_list = ArrayList_New(FALSE);
999 if (!server->peer_list)
1000 goto out;
1001
1002 obj = ArrayList_Object(server->peer_list);
1003 WINPR_ASSERT(obj);
1004
1005 obj->fnObjectFree = peer_free;
1006
1007 server->listener->info = server;
1008 server->listener->PeerAccepted = pf_server_peer_accepted;
1009
1010 if (!pf_modules_add(server->module, pf_config_plugin, (void*)server->config))
1011 goto out;
1012
1013 return server;
1014
1015out:
1016 WINPR_PRAGMA_DIAG_PUSH
1017 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1018 pf_server_free(server);
1019 WINPR_PRAGMA_DIAG_POP
1020 return nullptr;
1021}
1022
1023BOOL pf_server_run(proxyServer* server)
1024{
1025 BOOL rc = TRUE;
1026 HANDLE eventHandles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
1027 DWORD eventCount = 0;
1028 DWORD status = 0;
1029 freerdp_listener* listener = nullptr;
1030
1031 WINPR_ASSERT(server);
1032
1033 listener = server->listener;
1034 WINPR_ASSERT(listener);
1035
1036 while (1)
1037 {
1038 WINPR_ASSERT(listener->GetEventHandles);
1039 eventCount = listener->GetEventHandles(listener, eventHandles, ARRAYSIZE(eventHandles));
1040
1041 if ((0 == eventCount) || (eventCount >= ARRAYSIZE(eventHandles)))
1042 {
1043 WLog_ERR(TAG, "Failed to get FreeRDP event handles");
1044 break;
1045 }
1046
1047 WINPR_ASSERT(server->stopEvent);
1048 eventHandles[eventCount++] = server->stopEvent;
1049 status = WaitForMultipleObjects(eventCount, eventHandles, FALSE, 1000);
1050
1051 if (WAIT_FAILED == status)
1052 break;
1053
1054 if (WaitForSingleObject(server->stopEvent, 0) == WAIT_OBJECT_0)
1055 break;
1056
1057 if (WAIT_FAILED == status)
1058 {
1059 WLog_ERR(TAG, "select failed");
1060 rc = FALSE;
1061 break;
1062 }
1063
1064 WINPR_ASSERT(listener->CheckFileDescriptor);
1065 if (listener->CheckFileDescriptor(listener) != TRUE)
1066 {
1067 WLog_ERR(TAG, "Failed to accept new peer");
1068 // TODO: Set out of resource error
1069 continue;
1070 }
1071 }
1072
1073 WINPR_ASSERT(listener->Close);
1074 listener->Close(listener);
1075 return rc;
1076}
1077
1078void pf_server_stop(proxyServer* server)
1079{
1080
1081 if (!server)
1082 return;
1083
1084 /* signal main thread to stop and wait for the thread to exit */
1085 (void)SetEvent(server->stopEvent);
1086}
1087
1088void pf_server_free(proxyServer* server)
1089{
1090 if (!server)
1091 return;
1092
1093 pf_server_stop(server);
1094
1095 if (server->peer_list)
1096 {
1097 while (ArrayList_Count(server->peer_list) > 0)
1098 {
1099 /* pf_server_stop triggers the threads to shut down.
1100 * loop here until all of them stopped.
1101 *
1102 * This must be done before ArrayList_Free otherwise the thread removal
1103 * in pf_server_handle_peer will deadlock due to both threads trying to
1104 * lock the list.
1105 */
1106 Sleep(100);
1107 }
1108 }
1109 ArrayList_Free(server->peer_list);
1110 freerdp_listener_free(server->listener);
1111
1112 if (server->stopEvent)
1113 (void)CloseHandle(server->stopEvent);
1114
1115 pf_server_config_free(server->config);
1116 pf_modules_free(server->module);
1117 free(server);
1118
1119#if defined(WITH_DEBUG_EVENTS)
1120 DumpEventHandles();
1121#endif
1122}
1123
1124BOOL pf_server_add_module(proxyServer* server, proxyModuleEntryPoint ep, void* userdata)
1125{
1126 WINPR_ASSERT(server);
1127 WINPR_ASSERT(ep);
1128
1129 return pf_modules_add(server->module, ep, userdata);
1130}
FREERDP_API void pf_server_config_free(proxyConfig *config)
pf_server_config_free Releases all resources associated with proxyConfig
Definition pf_config.c:900
WINPR_ATTR_NODISCARD FREERDP_API const char ** pf_config_modules(const proxyConfig *config)
pf_config_modules
Definition pf_config.c:947
WINPR_ATTR_NODISCARD FREERDP_API const char * pf_config_required_plugin(const proxyConfig *config, size_t index)
pf_config_required_plugin
Definition pf_config.c:932
WINPR_ATTR_NODISCARD FREERDP_API size_t pf_config_modules_count(const proxyConfig *config)
pf_config_modules_count
Definition pf_config.c:941
WINPR_ATTR_NODISCARD FREERDP_API BOOL pf_config_clone(proxyConfig **dst, const proxyConfig *config)
pf_config_clone Create a copy of the configuration
Definition pf_config.c:1009
WINPR_ATTR_NODISCARD FREERDP_API size_t pf_config_required_plugins_count(const proxyConfig *config)
pf_config_required_plugins_count
Definition pf_config.c:926
WINPR_ATTR_NODISCARD FREERDP_API BOOL pf_config_plugin(proxyPluginsManager *plugins_manager, void *userdata)
pf_config_plugin Register a proxy plugin handling event filtering defined in the configuration.
Definition pf_config.c:1349
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 BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string_len(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val, size_t len)
Sets a string settings value. The val is copied.
WINPR_ATTR_NODISCARD FREERDP_API void * freerdp_settings_get_pointer_writable(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a mutable pointer settings value.
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.
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.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59