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