2019-05-17 15:32:54 +03:00
|
|
|
/**
|
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
|
|
* FreeRDP Proxy Server
|
|
|
|
*
|
|
|
|
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
|
|
|
|
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
|
|
|
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <freerdp/freerdp.h>
|
|
|
|
#include <freerdp/gdi/gdi.h>
|
|
|
|
#include <freerdp/client/cmdline.h>
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
#include <freerdp/server/proxy/proxy_log.h>
|
|
|
|
#include <freerdp/channels/drdynvc.h>
|
|
|
|
#include <freerdp/channels/encomsp.h>
|
|
|
|
#include <freerdp/channels/rdpdr.h>
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
#include "pf_channels.h"
|
|
|
|
#include "pf_gdi.h"
|
|
|
|
#include "pf_graphics.h"
|
|
|
|
#include "pf_client.h"
|
2021-08-24 13:50:13 +03:00
|
|
|
#include <freerdp/server/proxy/proxy_context.h>
|
2019-05-29 10:00:52 +03:00
|
|
|
#include "pf_update.h"
|
2020-07-05 09:11:56 +03:00
|
|
|
#include "pf_input.h"
|
2021-08-24 13:50:13 +03:00
|
|
|
#include <freerdp/server/proxy/proxy_config.h>
|
|
|
|
#include "proxy_modules.h"
|
|
|
|
#include "pf_utils.h"
|
2019-05-17 15:32:54 +03:00
|
|
|
|
|
|
|
#define TAG PROXY_TAG("client")
|
|
|
|
|
2019-09-08 15:18:45 +03:00
|
|
|
static BOOL proxy_server_reactivate(rdpContext* ps, const rdpContext* pc)
|
2019-05-17 15:32:54 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(ps);
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
|
2019-09-08 15:18:45 +03:00
|
|
|
if (!pf_context_copy_settings(ps->settings, pc->settings))
|
|
|
|
return FALSE;
|
2019-07-16 13:28:14 +03:00
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
/*
|
|
|
|
* DesktopResize causes internal function rdp_server_reactivate to be called,
|
2019-05-17 15:32:54 +03:00
|
|
|
* which causes the reactivation.
|
|
|
|
*/
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(ps->update);
|
2019-09-08 15:18:45 +03:00
|
|
|
if (!ps->update->DesktopResize(ps))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
2019-05-17 15:32:54 +03:00
|
|
|
}
|
|
|
|
|
2019-12-26 11:25:13 +03:00
|
|
|
static void pf_client_on_error_info(void* ctx, ErrorInfoEventArgs* e)
|
2019-05-19 14:35:06 +03:00
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
pClientContext* pc = (pClientContext*)ctx;
|
2021-08-24 13:50:13 +03:00
|
|
|
pServerContext* ps;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
WINPR_ASSERT(e);
|
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
2019-05-19 14:35:06 +03:00
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
if (e->code == ERRINFO_NONE)
|
|
|
|
return;
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_WARN(TAG, pc, "received ErrorInfo PDU. code=0x%08" PRIu32 ", message: %s", e->code,
|
|
|
|
freerdp_get_error_info_string(e->code));
|
2019-09-08 18:10:50 +03:00
|
|
|
|
|
|
|
/* forward error back to client */
|
|
|
|
freerdp_set_error_info(ps->context.rdp, e->code);
|
|
|
|
freerdp_send_error_info(ps->context.rdp);
|
2019-05-19 14:35:06 +03:00
|
|
|
}
|
|
|
|
|
2020-07-05 09:11:56 +03:00
|
|
|
static void pf_client_on_activated(void* ctx, ActivatedEventArgs* e)
|
|
|
|
{
|
|
|
|
pClientContext* pc = (pClientContext*)ctx;
|
2021-08-24 13:50:13 +03:00
|
|
|
pServerContext* ps;
|
|
|
|
freerdp_peer* peer;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
WINPR_ASSERT(e);
|
2020-07-05 09:11:56 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
|
|
|
peer = ps->context.peer;
|
|
|
|
WINPR_ASSERT(peer);
|
|
|
|
|
|
|
|
PROXY_LOG_INFO(TAG, pc, "client activated, registering server input callbacks");
|
2020-07-05 09:11:56 +03:00
|
|
|
|
|
|
|
/* Register server input/update callbacks only after proxy client is fully activated */
|
2021-09-03 12:24:55 +03:00
|
|
|
pf_server_register_input_callbacks(peer->context->input);
|
2020-07-05 09:11:56 +03:00
|
|
|
pf_server_register_update_callbacks(peer->update);
|
|
|
|
}
|
|
|
|
|
2020-05-12 13:30:20 +03:00
|
|
|
static BOOL pf_client_load_rdpsnd(pClientContext* pc)
|
2019-08-13 13:53:36 +03:00
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
rdpContext* context = (rdpContext*)pc;
|
2021-08-24 13:50:13 +03:00
|
|
|
pServerContext* ps;
|
|
|
|
const proxyConfig* config;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
|
|
|
config = pc->pdata->config;
|
|
|
|
WINPR_ASSERT(config);
|
2019-08-13 13:53:36 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if AudioOutput is enabled in proxy and client connected with rdpsnd, use proxy as rdpsnd
|
|
|
|
* backend. Otherwise, use sys:fake.
|
|
|
|
*/
|
2021-08-25 13:10:48 +03:00
|
|
|
if (!freerdp_static_channel_collection_find(context->settings, RDPSND_CHANNEL_NAME))
|
2019-08-13 13:53:36 +03:00
|
|
|
{
|
|
|
|
char* params[2];
|
2021-08-25 13:10:48 +03:00
|
|
|
params[0] = RDPSND_CHANNEL_NAME;
|
2019-08-13 13:53:36 +03:00
|
|
|
|
2021-08-25 11:02:46 +03:00
|
|
|
if (config->AudioOutput &&
|
2021-08-25 13:10:48 +03:00
|
|
|
WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RDPSND_CHANNEL_NAME))
|
2019-08-13 13:53:36 +03:00
|
|
|
params[1] = "sys:proxy";
|
|
|
|
else
|
|
|
|
params[1] = "sys:fake";
|
2019-11-06 17:24:51 +03:00
|
|
|
|
|
|
|
if (!freerdp_client_add_static_channel(context->settings, 2, (char**)params))
|
2019-08-13 13:53:36 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-05-12 13:30:20 +03:00
|
|
|
static BOOL pf_client_use_peer_load_balance_info(pClientContext* pc)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pServerContext* ps;
|
|
|
|
rdpSettings* settings;
|
2020-05-12 13:30:20 +03:00
|
|
|
DWORD lb_info_len;
|
2021-08-24 13:50:13 +03:00
|
|
|
const char* lb_info;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
|
|
|
settings = pc->context.settings;
|
|
|
|
WINPR_ASSERT(settings);
|
|
|
|
|
|
|
|
lb_info = freerdp_nego_get_routing_token(&ps->context, &lb_info_len);
|
2020-05-12 13:30:20 +03:00
|
|
|
if (!lb_info)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
free(settings->LoadBalanceInfo);
|
|
|
|
|
|
|
|
settings->LoadBalanceInfoLength = lb_info_len;
|
|
|
|
settings->LoadBalanceInfo = malloc(settings->LoadBalanceInfoLength);
|
|
|
|
|
|
|
|
if (!settings->LoadBalanceInfo)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
CopyMemory(settings->LoadBalanceInfo, lb_info, settings->LoadBalanceInfoLength);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
static BOOL pf_client_pre_connect(freerdp* instance)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
|
|
|
pServerContext* ps;
|
|
|
|
const proxyConfig* config;
|
|
|
|
rdpSettings* settings;
|
|
|
|
|
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
|
|
|
WINPR_ASSERT(ps->pdata);
|
|
|
|
config = ps->pdata->config;
|
|
|
|
WINPR_ASSERT(config);
|
|
|
|
settings = instance->settings;
|
|
|
|
WINPR_ASSERT(settings);
|
2019-08-06 11:48:26 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* as the client's settings are copied from the server's, GlyphSupportLevel might not be
|
|
|
|
* GLYPH_SUPPORT_NONE. the proxy currently do not support GDI & GLYPH_SUPPORT_CACHE, so
|
|
|
|
* GlyphCacheSupport must be explicitly set to GLYPH_SUPPORT_NONE.
|
2019-09-08 18:10:50 +03:00
|
|
|
*
|
|
|
|
* Also, OrderSupport need to be zeroed, because it is currently not supported.
|
2019-08-06 11:48:26 +03:00
|
|
|
*/
|
|
|
|
settings->GlyphSupportLevel = GLYPH_SUPPORT_NONE;
|
2020-05-19 14:42:26 +03:00
|
|
|
ZeroMemory(settings->OrderSupport, 32);
|
2019-08-13 15:06:45 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, DRDYNVC_SVC_CHANNEL_NAME))
|
|
|
|
settings->SupportDynamicChannels = TRUE;
|
2019-06-13 16:55:56 +03:00
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
/* Multimon */
|
|
|
|
settings->UseMultimon = TRUE;
|
|
|
|
|
|
|
|
/* Sound */
|
2021-08-24 13:50:13 +03:00
|
|
|
settings->AudioPlayback = config->AudioOutput;
|
|
|
|
if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, "rdpdr"))
|
|
|
|
settings->DeviceRedirection = TRUE;
|
2019-09-08 18:10:50 +03:00
|
|
|
|
|
|
|
/* Display control */
|
|
|
|
settings->SupportDisplayControl = config->DisplayControl;
|
|
|
|
settings->DynamicResolutionUpdate = config->DisplayControl;
|
2019-06-04 17:33:48 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, ENCOMSP_SVC_CHANNEL_NAME))
|
|
|
|
settings->EncomspVirtualChannel = TRUE;
|
|
|
|
|
|
|
|
if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, CLIPRDR_SVC_CHANNEL_NAME))
|
|
|
|
settings->RedirectClipboard = config->Clipboard;
|
|
|
|
|
2019-09-25 16:09:15 +03:00
|
|
|
settings->AutoReconnectionEnabled = TRUE;
|
2020-05-12 13:30:20 +03:00
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
/**
|
|
|
|
* Register the channel listeners.
|
|
|
|
* They are required to set up / tear down channels if they are loaded.
|
|
|
|
*/
|
2019-12-26 11:25:13 +03:00
|
|
|
PubSub_SubscribeChannelConnected(instance->context->pubSub,
|
|
|
|
pf_channels_on_client_channel_connect);
|
2019-05-17 15:32:54 +03:00
|
|
|
PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
|
2019-12-26 11:25:13 +03:00
|
|
|
pf_channels_on_client_channel_disconnect);
|
|
|
|
PubSub_SubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
|
2020-07-05 09:11:56 +03:00
|
|
|
PubSub_SubscribeActivated(instance->context->pubSub, pf_client_on_activated);
|
2019-05-17 15:32:54 +03:00
|
|
|
/**
|
|
|
|
* Load all required plugins / channels / libraries specified by current
|
|
|
|
* settings.
|
|
|
|
*/
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_INFO(TAG, pc, "Loading addins");
|
2019-05-17 15:32:54 +03:00
|
|
|
|
2020-09-08 14:03:56 +03:00
|
|
|
if (!pf_client_use_peer_load_balance_info(pc))
|
|
|
|
return FALSE;
|
2020-02-02 16:15:28 +03:00
|
|
|
|
2020-05-12 13:30:20 +03:00
|
|
|
if (!pf_client_load_rdpsnd(pc))
|
2019-08-13 13:53:36 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_ERR(TAG, pc, "Failed to load rdpsnd client");
|
2019-08-13 13:53:36 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-11-06 17:24:51 +03:00
|
|
|
if (!freerdp_client_load_addins(instance->context->channels, instance->settings))
|
2019-05-17 15:32:54 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_ERR(TAG, pc, "Failed to load addins");
|
2019-05-17 15:32:54 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_PRE_CONNECT, pc->pdata, pc);
|
2019-05-17 15:32:54 +03:00
|
|
|
}
|
|
|
|
|
2020-03-09 12:58:02 +03:00
|
|
|
static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channelId,
|
|
|
|
const BYTE* data, size_t size, UINT32 flags,
|
|
|
|
size_t totalSize)
|
2020-02-02 16:15:28 +03:00
|
|
|
{
|
|
|
|
const char* channel_name = freerdp_channels_get_name_by_id(instance, channelId);
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
|
|
|
pServerContext* ps;
|
|
|
|
proxyData* pdata;
|
|
|
|
const proxyConfig* config;
|
|
|
|
int pass;
|
|
|
|
|
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
WINPR_ASSERT(data || (size == 0));
|
|
|
|
|
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
|
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
2020-02-02 16:15:28 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
pdata = ps->pdata;
|
|
|
|
WINPR_ASSERT(pdata);
|
|
|
|
|
|
|
|
config = pdata->config;
|
|
|
|
WINPR_ASSERT(config);
|
|
|
|
|
|
|
|
pass = pf_utils_channel_is_passthrough(config, channel_name);
|
|
|
|
|
|
|
|
switch (pass)
|
2020-02-02 16:15:28 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
case 0:
|
|
|
|
return TRUE; /* Silently drop */
|
|
|
|
case 1:
|
2020-02-02 16:15:28 +03:00
|
|
|
{
|
|
|
|
proxyChannelDataEventInfo ev;
|
2021-08-24 13:50:13 +03:00
|
|
|
UINT16 server_channel_id;
|
2020-02-02 16:15:28 +03:00
|
|
|
|
|
|
|
ev.channel_id = channelId;
|
|
|
|
ev.channel_name = channel_name;
|
2020-03-12 09:42:13 +03:00
|
|
|
ev.data = data;
|
2020-02-02 16:15:28 +03:00
|
|
|
ev.data_len = size;
|
2021-08-24 13:50:13 +03:00
|
|
|
ev.flags = flags;
|
|
|
|
ev.total_size = totalSize;
|
|
|
|
|
|
|
|
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
|
|
|
|
pdata, &ev))
|
|
|
|
return TRUE; /* Silently drop */
|
|
|
|
|
|
|
|
/* Dynamic channels need special treatment
|
|
|
|
*
|
|
|
|
* We need to check every message with CHANNEL_FLAG_FIRST set if it
|
|
|
|
* is a CREATE_REQUEST_PDU (0x01) and extract channelId and name
|
|
|
|
* from it.
|
|
|
|
*
|
|
|
|
* To avoid issues with (misbehaving) clients assume all packets
|
|
|
|
* that do not have at least a length of 1 byte and all incomplete
|
|
|
|
* CREATE_REQUEST_PDU (0x01) packets as invalid.
|
|
|
|
*/
|
|
|
|
if ((flags & CHANNEL_FLAG_FIRST) &&
|
|
|
|
(strncmp(channel_name, DRDYNVC_SVC_CHANNEL_NAME, CHANNEL_NAME_LEN + 1) == 0))
|
|
|
|
{
|
|
|
|
BYTE cmd;
|
|
|
|
if (size < 1)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
cmd = data[0] >> 4;
|
|
|
|
if (cmd == 0x01)
|
|
|
|
{
|
|
|
|
proxyChannelDataEventInfo dev;
|
|
|
|
size_t len, nameLen;
|
|
|
|
const char* name;
|
|
|
|
UINT32 dynChannelId;
|
|
|
|
BYTE cbId = data[0] & 0x03;
|
|
|
|
switch (cbId)
|
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
if (size < 2)
|
|
|
|
return FALSE;
|
|
|
|
dynChannelId = data[1];
|
|
|
|
name = (const char*)&data[2];
|
|
|
|
nameLen = size - 2;
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
if (size < 3)
|
|
|
|
return FALSE;
|
|
|
|
dynChannelId = data[2] << 8 | data[1];
|
|
|
|
name = (const char*)&data[3];
|
|
|
|
nameLen = size - 3;
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
if (size < 5)
|
|
|
|
return FALSE;
|
|
|
|
dynChannelId = data[4] << 24 | data[3] << 16 | data[2] << 8 | data[1];
|
|
|
|
name = (const char*)&data[5];
|
|
|
|
nameLen = size - 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = strnlen(name, nameLen);
|
|
|
|
if ((len == 0) || (len == nameLen))
|
|
|
|
return FALSE;
|
|
|
|
dev.channel_id = dynChannelId;
|
|
|
|
dev.channel_name = name;
|
|
|
|
dev.data = data;
|
|
|
|
dev.data_len = size;
|
|
|
|
dev.flags = flags;
|
|
|
|
dev.total_size = totalSize;
|
|
|
|
|
|
|
|
if (!pf_modules_run_filter(pdata->module,
|
|
|
|
FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
|
|
|
|
pdata, &dev))
|
|
|
|
return TRUE; /* Silently drop */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
server_channel_id = WTSChannelGetId(ps->context.peer, channel_name);
|
|
|
|
|
|
|
|
/* Ignore messages for channels that can not be mapped.
|
|
|
|
* The client might not have enabled support for this specific channel,
|
|
|
|
* so just drop the message. */
|
|
|
|
if (server_channel_id == 0)
|
|
|
|
return TRUE;
|
|
|
|
return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id,
|
|
|
|
totalSize, flags, data, size);
|
2020-02-02 16:15:28 +03:00
|
|
|
}
|
2021-08-24 13:50:13 +03:00
|
|
|
default:
|
|
|
|
WINPR_ASSERT(pc->client_receive_channel_data_original);
|
|
|
|
return pc->client_receive_channel_data_original(instance, channelId, data, size, flags,
|
|
|
|
totalSize);
|
2020-02-02 16:15:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-07 10:16:46 +03:00
|
|
|
static BOOL pf_client_on_server_heartbeat(freerdp* instance, BYTE period, BYTE count1, BYTE count2)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
|
|
|
pServerContext* ps;
|
|
|
|
|
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
ps = pc->pdata->ps;
|
|
|
|
WINPR_ASSERT(ps);
|
|
|
|
|
2020-07-07 10:16:46 +03:00
|
|
|
return freerdp_heartbeat_send_heartbeat_pdu(ps->context.peer, period, count1, count2);
|
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
static BOOL pf_client_send_channel_data(pClientContext* pc, const proxyChannelDataEventInfo* ev)
|
|
|
|
{
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(ev);
|
|
|
|
|
|
|
|
if (!pc->connected)
|
|
|
|
{
|
|
|
|
ArrayList_Append(pc->cached_server_channel_data, ev);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UINT16 channelId;
|
|
|
|
WINPR_ASSERT(pc->context.instance);
|
|
|
|
|
|
|
|
channelId = freerdp_channels_get_id_by_name(pc->context.instance, ev->channel_name);
|
|
|
|
/* Ignore unmappable channels */
|
|
|
|
if ((channelId == 0) || (channelId == UINT16_MAX))
|
|
|
|
return TRUE;
|
|
|
|
WINPR_ASSERT(pc->context.instance->SendChannelPacket);
|
|
|
|
return pc->context.instance->SendChannelPacket(
|
|
|
|
pc->context.instance, channelId, ev->total_size, ev->flags, ev->data, ev->data_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL send_channel_data(void* data, size_t index, va_list ap)
|
|
|
|
{
|
|
|
|
pClientContext* pc = va_arg(ap, pClientContext*);
|
|
|
|
proxyChannelDataEventInfo* ev = data;
|
|
|
|
WINPR_ASSERT(ev);
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_UNUSED(index);
|
|
|
|
|
|
|
|
return pf_client_send_channel_data(pc, ev);
|
|
|
|
}
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
/**
|
|
|
|
* Called after a RDP connection was successfully established.
|
2019-06-13 16:55:56 +03:00
|
|
|
* Settings might have changed during negotiation of client / server feature
|
2019-05-17 15:32:54 +03:00
|
|
|
* support.
|
|
|
|
*
|
|
|
|
* Set up local framebuffers and painting callbacks.
|
|
|
|
* If required, register pointer callbacks to change the local mouse cursor
|
|
|
|
* when hovering over the RDP window
|
|
|
|
*/
|
|
|
|
static BOOL pf_client_post_connect(freerdp* instance)
|
|
|
|
{
|
|
|
|
rdpContext* context;
|
|
|
|
rdpSettings* settings;
|
|
|
|
rdpUpdate* update;
|
|
|
|
rdpContext* ps;
|
2019-10-22 16:06:05 +03:00
|
|
|
pClientContext* pc;
|
2021-08-24 13:50:13 +03:00
|
|
|
const proxyConfig* config;
|
2019-05-17 15:32:54 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(instance);
|
2019-05-17 15:32:54 +03:00
|
|
|
context = instance->context;
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(context);
|
2019-05-17 15:32:54 +03:00
|
|
|
settings = instance->settings;
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(settings);
|
2019-05-17 15:32:54 +03:00
|
|
|
update = instance->update;
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(update);
|
2019-11-06 17:24:51 +03:00
|
|
|
pc = (pClientContext*)context;
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
2019-11-06 17:24:51 +03:00
|
|
|
ps = (rdpContext*)pc->pdata->ps;
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(ps);
|
2019-10-22 16:06:05 +03:00
|
|
|
config = pc->pdata->config;
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(config);
|
2019-10-22 16:06:05 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_CONNECT, pc->pdata, pc))
|
2020-05-19 14:57:15 +03:00
|
|
|
return FALSE;
|
|
|
|
|
2019-10-22 16:01:00 +03:00
|
|
|
if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
|
2019-06-11 18:51:21 +03:00
|
|
|
return FALSE;
|
2019-05-17 15:32:54 +03:00
|
|
|
|
|
|
|
if (!pf_register_pointer(context->graphics))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!settings->SoftwareGdi)
|
|
|
|
{
|
|
|
|
if (!pf_register_graphics(context->graphics))
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_ERR(TAG, pc, "failed to register graphics");
|
2019-05-17 15:32:54 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pf_gdi_register_update_callbacks(update);
|
2019-05-29 10:00:52 +03:00
|
|
|
brush_cache_register_callbacks(update);
|
|
|
|
glyph_cache_register_callbacks(update);
|
|
|
|
bitmap_cache_register_callbacks(update);
|
|
|
|
offscreen_cache_register_callbacks(update);
|
|
|
|
palette_cache_register_callbacks(update);
|
2019-05-17 15:32:54 +03:00
|
|
|
}
|
2019-11-06 17:24:51 +03:00
|
|
|
|
2019-05-29 10:00:52 +03:00
|
|
|
pf_client_register_update_callbacks(update);
|
2019-09-08 18:10:50 +03:00
|
|
|
|
2020-02-02 16:15:28 +03:00
|
|
|
/* virtual channels receive data hook */
|
2021-08-24 13:50:13 +03:00
|
|
|
pc->client_receive_channel_data_original = instance->ReceiveChannelData;
|
2020-02-02 16:15:28 +03:00
|
|
|
instance->ReceiveChannelData = pf_client_receive_channel_data_hook;
|
|
|
|
|
2020-07-07 10:16:46 +03:00
|
|
|
instance->heartbeat->ServerHeartbeat = pf_client_on_server_heartbeat;
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
pc->connected = TRUE;
|
|
|
|
|
|
|
|
/* Send cached channel data */
|
|
|
|
ArrayList_Lock(pc->cached_server_channel_data);
|
|
|
|
ArrayList_ForEach(pc->cached_server_channel_data, send_channel_data, pc);
|
|
|
|
ArrayList_Clear(pc->cached_server_channel_data);
|
|
|
|
ArrayList_Unlock(pc->cached_server_channel_data);
|
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
/*
|
2020-02-02 16:15:28 +03:00
|
|
|
* after the connection fully established and settings were negotiated with target server,
|
|
|
|
* send a reactivation sequence to the client with the negotiated settings. This way,
|
|
|
|
* settings are synchorinized between proxy's peer and and remote target.
|
2019-09-08 18:10:50 +03:00
|
|
|
*/
|
2019-09-08 15:18:45 +03:00
|
|
|
return proxy_server_reactivate(ps, context);
|
2019-05-17 15:32:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This function is called whether a session ends by failure or success.
|
|
|
|
* Clean up everything allocated by pre_connect and post_connect.
|
|
|
|
*/
|
|
|
|
static void pf_client_post_disconnect(freerdp* instance)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
2019-05-17 15:32:54 +03:00
|
|
|
proxyData* pdata;
|
|
|
|
|
|
|
|
if (!instance)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!instance->context)
|
|
|
|
return;
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
pdata = pc->pdata;
|
|
|
|
WINPR_ASSERT(pdata);
|
|
|
|
|
|
|
|
pc->connected = FALSE;
|
|
|
|
pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_CONNECT, pc->pdata, pc);
|
2019-05-19 14:35:06 +03:00
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
|
2019-12-26 11:25:13 +03:00
|
|
|
pf_channels_on_client_channel_connect);
|
2019-05-17 15:32:54 +03:00
|
|
|
PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
|
2019-12-26 11:25:13 +03:00
|
|
|
pf_channels_on_client_channel_disconnect);
|
|
|
|
PubSub_UnsubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
|
2019-05-17 15:32:54 +03:00
|
|
|
gdi_free(instance);
|
|
|
|
|
2019-08-01 17:01:11 +03:00
|
|
|
/* Only close the connection if NLA fallback process is done */
|
2021-08-24 13:50:13 +03:00
|
|
|
if (!pc->allow_next_conn_failure)
|
2019-07-16 16:13:12 +03:00
|
|
|
proxy_data_abort_connect(pdata);
|
2019-05-17 15:32:54 +03:00
|
|
|
}
|
|
|
|
|
2019-09-18 16:08:25 +03:00
|
|
|
/*
|
|
|
|
* pf_client_should_retry_without_nla:
|
|
|
|
*
|
|
|
|
* returns TRUE if in case of connection failure, the client should try again without NLA.
|
|
|
|
* Otherwise, returns FALSE.
|
|
|
|
*/
|
|
|
|
static BOOL pf_client_should_retry_without_nla(pClientContext* pc)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
rdpSettings* settings;
|
|
|
|
const proxyConfig* config;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
settings = pc->context.settings;
|
|
|
|
WINPR_ASSERT(settings);
|
|
|
|
config = pc->pdata->config;
|
|
|
|
WINPR_ASSERT(config);
|
2019-09-18 16:08:25 +03:00
|
|
|
|
2019-12-29 11:55:40 +03:00
|
|
|
if (!config->ClientAllowFallbackToTls || !settings->NlaSecurity)
|
2019-09-18 16:08:25 +03:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return config->ClientTlsSecurity || config->ClientRdpSecurity;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pf_client_set_security_settings(pClientContext* pc)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
rdpSettings* settings;
|
|
|
|
const proxyConfig* config;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
WINPR_ASSERT(pc->pdata);
|
|
|
|
settings = pc->context.settings;
|
|
|
|
WINPR_ASSERT(settings);
|
|
|
|
config = pc->pdata->config;
|
|
|
|
WINPR_ASSERT(config);
|
2019-09-18 16:08:25 +03:00
|
|
|
|
|
|
|
settings->RdpSecurity = config->ClientRdpSecurity;
|
|
|
|
settings->TlsSecurity = config->ClientTlsSecurity;
|
2021-08-24 13:50:13 +03:00
|
|
|
settings->NlaSecurity = config->ClientNlaSecurity;
|
2019-09-18 16:08:25 +03:00
|
|
|
|
|
|
|
if (!config->ClientNlaSecurity)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!settings->Username || !settings->Password)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL pf_client_connect_without_nla(pClientContext* pc)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
freerdp* instance;
|
|
|
|
rdpSettings* settings;
|
|
|
|
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
instance = pc->context.instance;
|
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
settings = pc->context.settings;
|
|
|
|
WINPR_ASSERT(settings);
|
|
|
|
|
|
|
|
/* If already disabled abort early. */
|
|
|
|
if (!settings->NlaSecurity)
|
|
|
|
return FALSE;
|
2019-09-18 16:08:25 +03:00
|
|
|
|
|
|
|
/* disable NLA */
|
|
|
|
settings->NlaSecurity = FALSE;
|
|
|
|
|
|
|
|
/* do not allow next connection failure */
|
|
|
|
pc->allow_next_conn_failure = FALSE;
|
|
|
|
return freerdp_connect(instance);
|
|
|
|
}
|
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
static BOOL pf_client_connect(freerdp* instance)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
|
|
|
rdpSettings* settings;
|
2019-09-18 16:08:25 +03:00
|
|
|
BOOL rc = FALSE;
|
2019-12-29 11:55:40 +03:00
|
|
|
BOOL retry = FALSE;
|
2019-09-08 18:10:50 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
settings = instance->settings;
|
|
|
|
WINPR_ASSERT(settings);
|
|
|
|
|
|
|
|
PROXY_LOG_INFO(TAG, pc, "connecting using client info: Username: %s, Domain: %s",
|
|
|
|
settings->Username, settings->Domain);
|
2020-01-28 16:36:21 +03:00
|
|
|
|
2019-09-18 16:08:25 +03:00
|
|
|
pf_client_set_security_settings(pc);
|
|
|
|
if (pf_client_should_retry_without_nla(pc))
|
2019-12-29 11:55:40 +03:00
|
|
|
retry = pc->allow_next_conn_failure = TRUE;
|
2019-09-08 18:10:50 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_INFO(TAG, pc, "connecting using security settings: rdp=%d, tls=%d, nla=%d",
|
|
|
|
settings->RdpSecurity, settings->TlsSecurity, settings->NlaSecurity);
|
2020-09-21 10:43:47 +03:00
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
if (!freerdp_connect(instance))
|
|
|
|
{
|
2019-12-29 11:55:40 +03:00
|
|
|
if (!retry)
|
|
|
|
goto out;
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_ERR(TAG, pc, "failed to connect with NLA. retrying to connect without NLA");
|
|
|
|
pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata, pc);
|
2019-09-08 18:10:50 +03:00
|
|
|
|
2019-10-31 12:26:13 +03:00
|
|
|
if (!pf_client_connect_without_nla(pc))
|
2019-09-08 18:10:50 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_ERR(TAG, pc, "pf_client_connect_without_nla failed!");
|
2019-09-18 16:08:25 +03:00
|
|
|
goto out;
|
2019-09-08 18:10:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 16:08:25 +03:00
|
|
|
rc = TRUE;
|
|
|
|
out:
|
2019-09-10 10:11:00 +03:00
|
|
|
pc->allow_next_conn_failure = FALSE;
|
2019-09-18 16:08:25 +03:00
|
|
|
return rc;
|
2019-09-08 18:10:50 +03:00
|
|
|
}
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
/**
|
|
|
|
* RDP main loop.
|
|
|
|
* Connects RDP, loops while running and handles event and dispatch, cleans up
|
|
|
|
* after the connection ends.
|
|
|
|
*/
|
|
|
|
static DWORD WINAPI pf_client_thread_proc(LPVOID arg)
|
|
|
|
{
|
|
|
|
freerdp* instance = (freerdp*)arg;
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
|
|
|
proxyData* pdata;
|
|
|
|
DWORD nCount = 0;
|
2019-05-17 15:32:54 +03:00
|
|
|
DWORD status;
|
2021-08-24 13:50:13 +03:00
|
|
|
HANDLE handles[65] = { 0 };
|
2019-08-01 17:01:11 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
pdata = pc->pdata;
|
|
|
|
WINPR_ASSERT(pdata);
|
2019-08-01 17:01:11 +03:00
|
|
|
/*
|
|
|
|
* during redirection, freerdp's abort event might be overriden (reset) by the library, after
|
|
|
|
* the server set it in order to shutdown the connection. it means that the server might signal
|
|
|
|
* the client to abort, but the library code will override the signal and the client will
|
|
|
|
* continue its work instead of exiting. That's why the client must wait on `pdata->abort_event`
|
|
|
|
* too, which will never be modified by the library.
|
|
|
|
*/
|
2021-08-24 13:50:13 +03:00
|
|
|
handles[nCount++] = pdata->abort_event;
|
2019-05-17 15:32:54 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_INIT_CONNECT, pdata, pc))
|
2019-10-22 10:38:25 +03:00
|
|
|
{
|
|
|
|
proxy_data_abort_connect(pdata);
|
2019-09-08 18:10:50 +03:00
|
|
|
return FALSE;
|
2019-10-22 10:38:25 +03:00
|
|
|
}
|
2019-05-17 15:32:54 +03:00
|
|
|
|
2019-09-08 18:10:50 +03:00
|
|
|
if (!pf_client_connect(instance))
|
2019-09-18 16:08:25 +03:00
|
|
|
{
|
|
|
|
proxy_data_abort_connect(pdata);
|
2019-09-08 18:10:50 +03:00
|
|
|
return FALSE;
|
2019-09-18 16:08:25 +03:00
|
|
|
}
|
2019-07-15 10:55:57 +03:00
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
while (!freerdp_shall_disconnect(instance))
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
UINT32 tmp = freerdp_get_event_handles(instance->context, &handles[nCount],
|
|
|
|
ARRAYSIZE(handles) - nCount);
|
2019-05-17 15:32:54 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
if (tmp == 0)
|
2019-05-17 15:32:54 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_ERR(TAG, pc, "freerdp_get_event_handles failed!");
|
2019-05-17 15:32:54 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
status = WaitForMultipleObjects(nCount + tmp, handles, FALSE, INFINITE);
|
2019-05-17 15:32:54 +03:00
|
|
|
|
|
|
|
if (status == WAIT_FAILED)
|
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_ERR(TAG, "%s: WaitForMultipleObjects failed with %" PRIu32 "", __FUNCTION__,
|
2019-05-17 15:32:54 +03:00
|
|
|
status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
/* abort_event triggered */
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
|
|
break;
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
if (freerdp_shall_disconnect(instance))
|
|
|
|
break;
|
|
|
|
|
2019-08-01 17:01:11 +03:00
|
|
|
if (proxy_data_shall_disconnect(pdata))
|
|
|
|
break;
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
if (!freerdp_check_event_handles(instance->context))
|
|
|
|
{
|
|
|
|
if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
|
|
|
|
WLog_ERR(TAG, "Failed to check FreeRDP event handles");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
freerdp_disconnect(instance);
|
2021-08-24 13:50:13 +03:00
|
|
|
|
|
|
|
pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_UNINIT_CONNECT, pdata, pc);
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
|
|
|
|
{
|
|
|
|
const char* str_data = freerdp_get_logon_error_info_data(data);
|
|
|
|
const char* str_type = freerdp_get_logon_error_info_type(type);
|
|
|
|
|
|
|
|
if (!instance || !instance->context)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
static void pf_client_context_free(freerdp* instance, rdpContext* context)
|
2019-05-17 15:32:54 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc = (pClientContext*)context;
|
|
|
|
WINPR_UNUSED(instance);
|
|
|
|
|
|
|
|
if (!pc)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pc->sendChannelData = NULL;
|
|
|
|
ArrayList_Free(pc->cached_server_channel_data);
|
|
|
|
Stream_Free(pc->remote_pem, TRUE);
|
|
|
|
free(pc->remote_hostname);
|
2019-05-17 15:32:54 +03:00
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
static int pf_client_verify_X509_certificate(freerdp* instance, const BYTE* data, size_t length,
|
|
|
|
const char* hostname, UINT16 port, DWORD flags)
|
2019-05-17 15:32:54 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
pClientContext* pc;
|
|
|
|
|
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
WINPR_ASSERT(data);
|
|
|
|
WINPR_ASSERT(length > 0);
|
|
|
|
WINPR_ASSERT(hostname);
|
|
|
|
|
|
|
|
pc = (pClientContext*)instance->context;
|
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
|
|
|
|
if (!Stream_EnsureCapacity(pc->remote_pem, length))
|
|
|
|
return 0;
|
|
|
|
Stream_SetPosition(pc->remote_pem, 0);
|
|
|
|
|
|
|
|
free(pc->remote_hostname);
|
|
|
|
pc->remote_hostname = NULL;
|
|
|
|
|
|
|
|
if (length > 0)
|
|
|
|
Stream_Write(pc->remote_pem, data, length);
|
|
|
|
|
|
|
|
if (hostname)
|
|
|
|
pc->remote_hostname = _strdup(hostname);
|
|
|
|
pc->remote_port = port;
|
|
|
|
pc->remote_flags = flags;
|
|
|
|
|
|
|
|
Stream_SealLength(pc->remote_pem);
|
|
|
|
if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_VERIFY_X509, pc->pdata, pc))
|
|
|
|
return 0;
|
2019-05-17 15:32:54 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
static void* channel_data_copy(const void* obj)
|
2020-02-03 11:44:25 +03:00
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
const proxyChannelDataEventInfo* src = obj;
|
|
|
|
proxyChannelDataEventInfo* dst;
|
2020-02-03 11:44:25 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(src);
|
|
|
|
|
|
|
|
dst = calloc(1, sizeof(proxyChannelDataEventInfo));
|
|
|
|
WINPR_ASSERT(dst);
|
2020-02-03 11:44:25 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
*dst = *src;
|
|
|
|
if (src->channel_name)
|
|
|
|
{
|
|
|
|
dst->channel_name = _strdup(src->channel_name);
|
|
|
|
WINPR_ASSERT(dst->channel_name);
|
|
|
|
}
|
|
|
|
dst->data = malloc(src->data_len);
|
|
|
|
WINPR_ASSERT(dst->data);
|
|
|
|
memcpy((void*)dst->data, src->data, src->data_len);
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void channel_data_free(void* obj)
|
|
|
|
{
|
|
|
|
proxyChannelDataEventInfo* dst = obj;
|
|
|
|
if (dst)
|
|
|
|
{
|
|
|
|
free((void*)dst->data);
|
|
|
|
free((void*)dst->channel_name);
|
|
|
|
free(dst);
|
|
|
|
}
|
2020-02-03 11:44:25 +03:00
|
|
|
}
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
wObject* obj;
|
|
|
|
pClientContext* pc = (pClientContext*)context;
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
if (!instance || !context)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
instance->PreConnect = pf_client_pre_connect;
|
|
|
|
instance->PostConnect = pf_client_post_connect;
|
|
|
|
instance->PostDisconnect = pf_client_post_disconnect;
|
|
|
|
instance->LogonErrorInfo = pf_logon_error_info;
|
2021-08-24 13:50:13 +03:00
|
|
|
instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
|
|
|
|
|
|
|
|
pc->remote_pem = Stream_New(NULL, 4096);
|
|
|
|
if (!pc->remote_pem)
|
|
|
|
return FALSE;
|
2019-10-22 16:06:05 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
pc->sendChannelData = pf_client_send_channel_data;
|
|
|
|
pc->cached_server_channel_data = ArrayList_New(TRUE);
|
|
|
|
if (!pc->cached_server_channel_data)
|
|
|
|
return FALSE;
|
|
|
|
obj = ArrayList_Object(pc->cached_server_channel_data);
|
|
|
|
WINPR_ASSERT(obj);
|
|
|
|
obj->fnObjectNew = channel_data_copy;
|
|
|
|
obj->fnObjectFree = channel_data_free;
|
2019-05-17 15:32:54 +03:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-05-30 11:22:50 +03:00
|
|
|
static int pf_client_client_stop(rdpContext* context)
|
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
pClientContext* pc = (pClientContext*)context;
|
2021-08-24 13:50:13 +03:00
|
|
|
proxyData* pdata;
|
2019-08-01 17:01:11 +03:00
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(pc);
|
|
|
|
pdata = pc->pdata;
|
|
|
|
WINPR_ASSERT(pdata);
|
|
|
|
|
|
|
|
PROXY_LOG_DBG(TAG, pc, "aborting client connection");
|
2019-08-01 17:01:11 +03:00
|
|
|
proxy_data_abort_connect(pdata);
|
2019-05-30 11:22:50 +03:00
|
|
|
freerdp_abort_connect(context->instance);
|
|
|
|
|
2019-07-16 16:13:12 +03:00
|
|
|
if (pdata->client_thread)
|
2019-05-30 11:22:50 +03:00
|
|
|
{
|
2019-08-01 17:01:11 +03:00
|
|
|
/*
|
|
|
|
* Wait for client thread to finish. No need to call CloseHandle() here, as
|
2019-07-16 16:13:12 +03:00
|
|
|
* it is the responsibility of `proxy_data_free`.
|
|
|
|
*/
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_DBG(TAG, pc, "waiting for client thread to finish");
|
2019-07-16 16:13:12 +03:00
|
|
|
WaitForSingleObject(pdata->client_thread, INFINITE);
|
2021-08-24 13:50:13 +03:00
|
|
|
PROXY_LOG_DBG(TAG, pc, "thread finished");
|
2019-05-30 11:22:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
|
|
|
|
{
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(pEntryPoints);
|
|
|
|
|
2019-05-17 15:32:54 +03:00
|
|
|
ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
|
|
|
|
pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
|
|
|
|
pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
|
|
|
|
pEntryPoints->ContextSize = sizeof(pClientContext);
|
|
|
|
/* Client init and finish */
|
|
|
|
pEntryPoints->ClientNew = pf_client_client_new;
|
2021-08-24 13:50:13 +03:00
|
|
|
pEntryPoints->ClientFree = pf_client_context_free;
|
2019-05-30 11:22:50 +03:00
|
|
|
pEntryPoints->ClientStop = pf_client_client_stop;
|
2019-05-17 15:32:54 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts running a client connection towards target server.
|
|
|
|
*/
|
|
|
|
DWORD WINAPI pf_client_start(LPVOID arg)
|
|
|
|
{
|
|
|
|
rdpContext* context = (rdpContext*)arg;
|
|
|
|
|
2021-08-24 13:50:13 +03:00
|
|
|
WINPR_ASSERT(context);
|
2019-05-17 15:32:54 +03:00
|
|
|
if (freerdp_client_start(context) != 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return pf_client_thread_proc(context->instance);
|
|
|
|
}
|