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