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