mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
2166 lines
70 KiB
C
2166 lines
70 KiB
C
/*
|
|
* Copyright (C) 2020-2023 Pascal Nowack
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "grd-session-rdp.h"
|
|
|
|
#include <freerdp/channels/drdynvc.h>
|
|
#include <freerdp/crypto/crypto.h>
|
|
#include <freerdp/freerdp.h>
|
|
#include <freerdp/peer.h>
|
|
#include <gio/gio.h>
|
|
#include <glib-unix.h>
|
|
#include <krb5.h>
|
|
#include <linux/input-event-codes.h>
|
|
#include <pwd.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
#include "grd-clipboard-rdp.h"
|
|
#include "grd-context.h"
|
|
#include "grd-rdp-cursor-renderer.h"
|
|
#include "grd-rdp-dvc-audio-input.h"
|
|
#include "grd-rdp-dvc-audio-playback.h"
|
|
#include "grd-rdp-dvc-camera-enumerator.h"
|
|
#include "grd-rdp-dvc-display-control.h"
|
|
#include "grd-rdp-dvc-graphics-pipeline.h"
|
|
#include "grd-rdp-dvc-handler.h"
|
|
#include "grd-rdp-dvc-input.h"
|
|
#include "grd-rdp-dvc-telemetry.h"
|
|
#include "grd-rdp-event-queue.h"
|
|
#include "grd-rdp-layout-manager.h"
|
|
#include "grd-rdp-network-autodetection.h"
|
|
#include "grd-rdp-private.h"
|
|
#include "grd-rdp-renderer.h"
|
|
#include "grd-rdp-sam.h"
|
|
#include "grd-rdp-server.h"
|
|
#include "grd-rdp-session-metrics.h"
|
|
#include "grd-settings.h"
|
|
#include "grd-utils.h"
|
|
|
|
#define MAX_MONITOR_COUNT_HEADLESS 16
|
|
#define MAX_MONITOR_COUNT_SCREEN_SHARE 1
|
|
#define DISCRETE_SCROLL_STEP 10.0
|
|
#define ELEMENT_TYPE_CERTIFICATE 32
|
|
|
|
enum
|
|
{
|
|
POST_CONNECTED,
|
|
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint signals[N_SIGNALS];
|
|
|
|
typedef enum _RdpPeerFlag
|
|
{
|
|
RDP_PEER_ACTIVATED = 1 << 0,
|
|
} RdpPeerFlag;
|
|
|
|
typedef enum _PauseKeyState
|
|
{
|
|
PAUSE_KEY_STATE_NONE,
|
|
PAUSE_KEY_STATE_CTRL_DOWN,
|
|
PAUSE_KEY_STATE_NUMLOCK_DOWN,
|
|
PAUSE_KEY_STATE_CTRL_UP,
|
|
} PauseKeyState;
|
|
|
|
struct _GrdSessionRdp
|
|
{
|
|
GrdSession parent;
|
|
|
|
GrdRdpServer *server;
|
|
GSocketConnection *connection;
|
|
freerdp_peer *peer;
|
|
GrdRdpSAMFile *sam_file;
|
|
uint32_t rdp_error_info;
|
|
GrdRdpScreenShareMode screen_share_mode;
|
|
gboolean is_view_only;
|
|
gboolean session_should_stop;
|
|
|
|
GrdRdpSessionMetrics *session_metrics;
|
|
|
|
GMutex rdp_flags_mutex;
|
|
RdpPeerFlag rdp_flags;
|
|
|
|
GThread *socket_thread;
|
|
HANDLE stop_event;
|
|
|
|
GrdRdpRenderer *renderer;
|
|
GrdRdpCursorRenderer *cursor_renderer;
|
|
|
|
GHashTable *pressed_keys;
|
|
GHashTable *pressed_unicode_keys;
|
|
PauseKeyState pause_key_state;
|
|
|
|
GrdRdpEventQueue *rdp_event_queue;
|
|
|
|
GrdRdpLayoutManager *layout_manager;
|
|
|
|
GHashTable *stream_table;
|
|
|
|
GMutex close_session_mutex;
|
|
unsigned int close_session_idle_id;
|
|
|
|
GMutex notify_post_connected_mutex;
|
|
unsigned int notify_post_connected_source_id;
|
|
|
|
uint32_t next_stream_id;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdSessionRdp, grd_session_rdp, GRD_TYPE_SESSION)
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (rdpCertificate, freerdp_certificate_free)
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (rdpRedirection, redirection_free)
|
|
|
|
static gboolean
|
|
close_session_idle (gpointer user_data);
|
|
|
|
GrdRdpServer *
|
|
grd_session_rdp_get_server (GrdSessionRdp *session_rdp)
|
|
{
|
|
return session_rdp->server;
|
|
}
|
|
|
|
GrdRdpRenderer *
|
|
grd_session_rdp_get_renderer (GrdSessionRdp *session_rdp)
|
|
{
|
|
return session_rdp->renderer;
|
|
}
|
|
|
|
GrdRdpCursorRenderer *
|
|
grd_session_rdp_get_cursor_renderer (GrdSessionRdp *session_rdp)
|
|
{
|
|
return session_rdp->cursor_renderer;
|
|
}
|
|
|
|
rdpContext *
|
|
grd_session_rdp_get_rdp_context (GrdSessionRdp *session_rdp)
|
|
{
|
|
return session_rdp->peer->context;
|
|
}
|
|
|
|
GrdRdpDvcGraphicsPipeline *
|
|
grd_session_rdp_get_graphics_pipeline (GrdSessionRdp *session_rdp)
|
|
{
|
|
rdpContext *rdp_context = grd_session_rdp_get_rdp_context (session_rdp);
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
|
|
|
|
return rdp_peer_context->graphics_pipeline;
|
|
}
|
|
|
|
GrdRdpScreenShareMode
|
|
grd_session_rdp_get_screen_share_mode (GrdSessionRdp *session_rdp)
|
|
{
|
|
return session_rdp->screen_share_mode;
|
|
}
|
|
|
|
static gboolean
|
|
is_rdp_peer_flag_set (GrdSessionRdp *session_rdp,
|
|
RdpPeerFlag flag)
|
|
{
|
|
gboolean state;
|
|
|
|
g_mutex_lock (&session_rdp->rdp_flags_mutex);
|
|
state = !!(session_rdp->rdp_flags & flag);
|
|
g_mutex_unlock (&session_rdp->rdp_flags_mutex);
|
|
|
|
return state;
|
|
}
|
|
|
|
static void
|
|
set_rdp_peer_flag (GrdSessionRdp *session_rdp,
|
|
RdpPeerFlag flag)
|
|
{
|
|
g_mutex_lock (&session_rdp->rdp_flags_mutex);
|
|
session_rdp->rdp_flags |= flag;
|
|
g_mutex_unlock (&session_rdp->rdp_flags_mutex);
|
|
}
|
|
|
|
static void
|
|
unset_rdp_peer_flag (GrdSessionRdp *session_rdp,
|
|
RdpPeerFlag flag)
|
|
{
|
|
g_mutex_lock (&session_rdp->rdp_flags_mutex);
|
|
session_rdp->rdp_flags &= ~flag;
|
|
g_mutex_unlock (&session_rdp->rdp_flags_mutex);
|
|
}
|
|
|
|
GrdRdpSessionMetrics *
|
|
grd_session_rdp_get_session_metrics (GrdSessionRdp *session_rdp)
|
|
{
|
|
return session_rdp->session_metrics;
|
|
}
|
|
|
|
static uint32_t
|
|
get_next_free_stream_id (GrdSessionRdp *session_rdp)
|
|
{
|
|
uint32_t stream_id = session_rdp->next_stream_id;
|
|
|
|
while (g_hash_table_contains (session_rdp->stream_table,
|
|
GUINT_TO_POINTER (stream_id)))
|
|
++stream_id;
|
|
|
|
session_rdp->next_stream_id = stream_id + 1;
|
|
|
|
return stream_id;
|
|
}
|
|
|
|
uint32_t
|
|
grd_session_rdp_acquire_stream_id (GrdSessionRdp *session_rdp,
|
|
GrdRdpStreamOwner *stream_owner)
|
|
{
|
|
uint32_t stream_id;
|
|
|
|
stream_id = get_next_free_stream_id (session_rdp);
|
|
g_hash_table_insert (session_rdp->stream_table,
|
|
GUINT_TO_POINTER (stream_id), stream_owner);
|
|
|
|
return stream_id;
|
|
}
|
|
|
|
void
|
|
grd_session_rdp_release_stream_id (GrdSessionRdp *session_rdp,
|
|
uint32_t stream_id)
|
|
{
|
|
g_hash_table_remove (session_rdp->stream_table, GUINT_TO_POINTER (stream_id));
|
|
}
|
|
|
|
gboolean
|
|
grd_session_rdp_is_client_mstsc (GrdSessionRdp *session_rdp)
|
|
{
|
|
rdpContext *rdp_context = session_rdp->peer->context;
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
|
|
return freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType) ==
|
|
OSMAJORTYPE_WINDOWS &&
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMinorType) ==
|
|
OSMINORTYPE_WINDOWS_NT;
|
|
}
|
|
|
|
static WCHAR *
|
|
get_utf16_string (const char *str,
|
|
size_t *size)
|
|
{
|
|
WCHAR *utf16_string;
|
|
|
|
*size = 0;
|
|
|
|
utf16_string = ConvertUtf8ToWCharAlloc (str, size);
|
|
if (!utf16_string)
|
|
return NULL;
|
|
|
|
*size = (*size + 1) * sizeof (WCHAR);
|
|
|
|
return utf16_string;
|
|
}
|
|
|
|
static WCHAR *
|
|
generate_encoded_redirection_guid (size_t *size)
|
|
{
|
|
BYTE redirection_guid[16] = {};
|
|
g_autofree char *redirection_guid_base_64 = NULL;
|
|
WCHAR *encoded_redirection_guid;
|
|
|
|
*size = 0;
|
|
|
|
if (winpr_RAND (redirection_guid, 16) == -1)
|
|
return NULL;
|
|
|
|
redirection_guid_base_64 = crypto_base64_encode (redirection_guid, 16);
|
|
if (!redirection_guid_base_64)
|
|
return NULL;
|
|
|
|
encoded_redirection_guid = get_utf16_string (redirection_guid_base_64, size);
|
|
if (!encoded_redirection_guid)
|
|
return NULL;
|
|
|
|
return encoded_redirection_guid;
|
|
}
|
|
|
|
static BYTE *
|
|
get_certificate_container (const char *certificate,
|
|
size_t *size)
|
|
{
|
|
g_autofree BYTE *der_certificate = NULL;
|
|
g_autoptr (rdpCertificate) rdp_certificate = NULL;
|
|
BYTE *certificate_container = NULL;
|
|
size_t der_certificate_len = 0;
|
|
wStream *s;
|
|
|
|
*size = 0;
|
|
|
|
rdp_certificate = freerdp_certificate_new_from_pem (certificate);
|
|
if (!rdp_certificate)
|
|
return NULL;
|
|
|
|
der_certificate = freerdp_certificate_get_der (rdp_certificate,
|
|
&der_certificate_len);
|
|
if (!der_certificate)
|
|
return NULL;
|
|
|
|
s = Stream_New (NULL, 2048);
|
|
g_assert (s);
|
|
|
|
if (!Stream_EnsureRemainingCapacity (s, 12))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write_UINT32 (s, ELEMENT_TYPE_CERTIFICATE);
|
|
Stream_Write_UINT32 (s, ENCODING_TYPE_ASN1_DER);
|
|
Stream_Write_UINT32 (s, der_certificate_len);
|
|
|
|
if (!Stream_EnsureRemainingCapacity (s, der_certificate_len))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write (s, der_certificate, der_certificate_len);
|
|
|
|
*size = Stream_GetPosition (s);
|
|
certificate_container = Stream_Buffer (s);
|
|
|
|
Stream_Free (s, FALSE);
|
|
|
|
return certificate_container;
|
|
}
|
|
|
|
gboolean
|
|
grd_session_rdp_send_server_redirection (GrdSessionRdp *session_rdp,
|
|
const char *routing_token,
|
|
const char *username,
|
|
const char *password,
|
|
const char *certificate)
|
|
{
|
|
freerdp_peer *peer = session_rdp->peer;
|
|
rdpSettings *rdp_settings = peer->context->settings;
|
|
uint32_t os_major_type =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType);
|
|
g_autoptr (rdpRedirection) redirection = NULL;
|
|
g_autofree BYTE *certificate_container = NULL;
|
|
g_autofree WCHAR *utf16_password = NULL;
|
|
g_autofree WCHAR *utf16_encoded_redirection_guid = NULL;
|
|
size_t size = 0;
|
|
uint32_t redirection_flags = 0;
|
|
uint32_t redirection_incorrect_flags = 0;
|
|
|
|
g_assert (routing_token);
|
|
g_assert (username);
|
|
g_assert (password);
|
|
g_assert (certificate);
|
|
|
|
redirection = redirection_new ();
|
|
g_assert (redirection);
|
|
|
|
/* Load Balance Info */
|
|
redirection_flags |= LB_LOAD_BALANCE_INFO;
|
|
redirection_set_byte_option (redirection, LB_LOAD_BALANCE_INFO,
|
|
(BYTE *) routing_token,
|
|
strlen (routing_token));
|
|
|
|
/* Username */
|
|
redirection_flags |= LB_USERNAME;
|
|
redirection_set_string_option (redirection, LB_USERNAME,
|
|
username);
|
|
|
|
/* Password */
|
|
redirection_flags |= LB_PASSWORD;
|
|
if (os_major_type != OSMAJORTYPE_IOS && os_major_type != OSMAJORTYPE_ANDROID)
|
|
redirection_flags |= LB_PASSWORD_IS_PK_ENCRYPTED;
|
|
utf16_password = get_utf16_string (password, &size);
|
|
g_assert (utf16_password);
|
|
redirection_set_byte_option (redirection, LB_PASSWORD,
|
|
(BYTE *) utf16_password,
|
|
size);
|
|
|
|
/* Redirection GUID */
|
|
redirection_flags |= LB_REDIRECTION_GUID;
|
|
utf16_encoded_redirection_guid = generate_encoded_redirection_guid (&size);
|
|
g_assert (utf16_encoded_redirection_guid);
|
|
redirection_set_byte_option (redirection, LB_REDIRECTION_GUID,
|
|
(BYTE *) utf16_encoded_redirection_guid,
|
|
size);
|
|
|
|
/* Target Certificate */
|
|
redirection_flags |= LB_TARGET_CERTIFICATE;
|
|
certificate_container = get_certificate_container (certificate, &size);
|
|
g_assert (certificate_container);
|
|
redirection_set_byte_option (redirection,
|
|
LB_TARGET_CERTIFICATE,
|
|
certificate_container,
|
|
size);
|
|
|
|
redirection_set_flags (redirection, redirection_flags);
|
|
|
|
if (!redirection_settings_are_valid (redirection, &redirection_incorrect_flags))
|
|
{
|
|
g_warning ("[RDP] Something went wrong sending Server Redirection PDU. "
|
|
"Incorrect flag/s: 0x%08x", redirection_incorrect_flags);
|
|
return FALSE;
|
|
}
|
|
|
|
g_message ("[RDP] Sending server redirection");
|
|
if (!peer->SendServerRedirection (peer, redirection))
|
|
{
|
|
g_warning ("[RDP] Error sending server Redirection");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
maybe_queue_close_session_idle (GrdSessionRdp *session_rdp)
|
|
{
|
|
g_mutex_lock (&session_rdp->close_session_mutex);
|
|
if (session_rdp->close_session_idle_id)
|
|
{
|
|
g_mutex_unlock (&session_rdp->close_session_mutex);
|
|
return;
|
|
}
|
|
|
|
session_rdp->close_session_idle_id =
|
|
g_idle_add (close_session_idle, session_rdp);
|
|
g_mutex_unlock (&session_rdp->close_session_mutex);
|
|
|
|
session_rdp->session_should_stop = TRUE;
|
|
SetEvent (session_rdp->stop_event);
|
|
}
|
|
|
|
void
|
|
grd_session_rdp_notify_error (GrdSessionRdp *session_rdp,
|
|
GrdSessionRdpError error_info)
|
|
{
|
|
switch (error_info)
|
|
{
|
|
case GRD_SESSION_RDP_ERROR_NONE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case GRD_SESSION_RDP_ERROR_BAD_CAPS:
|
|
session_rdp->rdp_error_info = ERRINFO_BAD_CAPABILITIES;
|
|
break;
|
|
case GRD_SESSION_RDP_ERROR_BAD_MONITOR_DATA:
|
|
session_rdp->rdp_error_info = ERRINFO_BAD_MONITOR_DATA;
|
|
break;
|
|
case GRD_SESSION_RDP_ERROR_CLOSE_STACK_ON_DRIVER_FAILURE:
|
|
session_rdp->rdp_error_info = ERRINFO_CLOSE_STACK_ON_DRIVER_FAILURE;
|
|
break;
|
|
case GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED:
|
|
session_rdp->rdp_error_info = ERRINFO_GRAPHICS_SUBSYSTEM_FAILED;
|
|
break;
|
|
case GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION:
|
|
session_rdp->rdp_error_info = ERRINFO_CB_CONNECTION_CANCELLED;
|
|
break;
|
|
}
|
|
|
|
unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED);
|
|
maybe_queue_close_session_idle (session_rdp);
|
|
}
|
|
|
|
void
|
|
grd_session_rdp_tear_down_channel (GrdSessionRdp *session_rdp,
|
|
GrdRdpChannel channel)
|
|
{
|
|
freerdp_peer *peer = session_rdp->peer;
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context;
|
|
|
|
g_mutex_lock (&rdp_peer_context->channel_mutex);
|
|
switch (channel)
|
|
{
|
|
case GRD_RDP_CHANNEL_NONE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case GRD_RDP_CHANNEL_AUDIO_INPUT:
|
|
g_clear_object (&rdp_peer_context->audio_input);
|
|
break;
|
|
case GRD_RDP_CHANNEL_AUDIO_PLAYBACK:
|
|
g_clear_object (&rdp_peer_context->audio_playback);
|
|
break;
|
|
case GRD_RDP_CHANNEL_CAMERA:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case GRD_RDP_CHANNEL_CAMERA_ENUMERATOR:
|
|
g_clear_object (&rdp_peer_context->camera_enumerator);
|
|
break;
|
|
case GRD_RDP_CHANNEL_DISPLAY_CONTROL:
|
|
g_clear_object (&rdp_peer_context->display_control);
|
|
break;
|
|
case GRD_RDP_CHANNEL_GRAPHICS_PIPELINE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case GRD_RDP_CHANNEL_INPUT:
|
|
g_clear_object (&rdp_peer_context->input);
|
|
break;
|
|
case GRD_RDP_CHANNEL_TELEMETRY:
|
|
g_clear_object (&rdp_peer_context->telemetry);
|
|
break;
|
|
}
|
|
g_mutex_unlock (&rdp_peer_context->channel_mutex);
|
|
}
|
|
|
|
static void
|
|
handle_client_gone (GrdSessionRdp *session_rdp)
|
|
{
|
|
g_debug ("RDP client gone");
|
|
|
|
unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED);
|
|
maybe_queue_close_session_idle (session_rdp);
|
|
}
|
|
|
|
static gboolean
|
|
notify_keycode_released (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
GrdSessionRdp *session_rdp = user_data;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
uint32_t keycode = GPOINTER_TO_UINT (key);
|
|
|
|
grd_rdp_event_queue_add_input_event_keyboard_keycode (rdp_event_queue,
|
|
keycode,
|
|
GRD_KEY_STATE_RELEASED);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
notify_keysym_released (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
GrdSessionRdp *session_rdp = user_data;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
xkb_keysym_t keysym = GPOINTER_TO_UINT (key);
|
|
|
|
grd_rdp_event_queue_add_input_event_keyboard_keysym (rdp_event_queue,
|
|
keysym,
|
|
GRD_KEY_STATE_RELEASED);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_input_synchronize_event (rdpInput *rdp_input,
|
|
uint32_t flags)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
|
|
g_debug ("[RDP] Received Synchronize event with flags 0x%08X", flags);
|
|
|
|
if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) ||
|
|
session_rdp->is_view_only)
|
|
return TRUE;
|
|
|
|
g_hash_table_foreach_remove (session_rdp->pressed_keys,
|
|
notify_keycode_released,
|
|
session_rdp);
|
|
|
|
g_hash_table_foreach_remove (session_rdp->pressed_unicode_keys,
|
|
notify_keysym_released,
|
|
session_rdp);
|
|
|
|
grd_rdp_event_queue_add_synchronization_event (rdp_event_queue,
|
|
!!(flags & KBD_SYNC_CAPS_LOCK),
|
|
!!(flags & KBD_SYNC_NUM_LOCK));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_input_mouse_event (rdpInput *rdp_input,
|
|
uint16_t flags,
|
|
uint16_t x,
|
|
uint16_t y)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
GrdEventMotionAbs motion_abs = {};
|
|
GrdStream *stream = NULL;
|
|
GrdButtonState button_state;
|
|
int32_t button = 0;
|
|
uint16_t axis_value;
|
|
double axis_step;
|
|
|
|
if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) ||
|
|
session_rdp->is_view_only)
|
|
return TRUE;
|
|
|
|
if (!(flags & PTR_FLAGS_WHEEL) && !(flags & PTR_FLAGS_HWHEEL) &&
|
|
grd_rdp_layout_manager_transform_position (session_rdp->layout_manager,
|
|
x, y,
|
|
&stream, &motion_abs))
|
|
{
|
|
grd_rdp_event_queue_add_input_event_pointer_motion_abs (rdp_event_queue,
|
|
stream,
|
|
&motion_abs);
|
|
}
|
|
|
|
button_state = flags & PTR_FLAGS_DOWN ? GRD_BUTTON_STATE_PRESSED
|
|
: GRD_BUTTON_STATE_RELEASED;
|
|
|
|
if (flags & PTR_FLAGS_BUTTON1)
|
|
button = BTN_LEFT;
|
|
else if (flags & PTR_FLAGS_BUTTON2)
|
|
button = BTN_RIGHT;
|
|
else if (flags & PTR_FLAGS_BUTTON3)
|
|
button = BTN_MIDDLE;
|
|
|
|
if (button)
|
|
{
|
|
grd_rdp_event_queue_add_input_event_pointer_button (rdp_event_queue,
|
|
button, button_state);
|
|
}
|
|
|
|
if (!(flags & PTR_FLAGS_WHEEL) && !(flags & PTR_FLAGS_HWHEEL))
|
|
return TRUE;
|
|
|
|
axis_value = flags & WheelRotationMask;
|
|
if (axis_value & PTR_FLAGS_WHEEL_NEGATIVE)
|
|
{
|
|
axis_value = ~axis_value & WheelRotationMask;
|
|
++axis_value;
|
|
}
|
|
|
|
axis_step = -axis_value / 120.0;
|
|
if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
|
|
axis_step = -axis_step;
|
|
|
|
if (flags & PTR_FLAGS_WHEEL)
|
|
{
|
|
grd_rdp_event_queue_add_input_event_pointer_axis (
|
|
rdp_event_queue, 0, axis_step * DISCRETE_SCROLL_STEP,
|
|
GRD_POINTER_AXIS_FLAGS_SOURCE_WHEEL);
|
|
}
|
|
if (flags & PTR_FLAGS_HWHEEL)
|
|
{
|
|
grd_rdp_event_queue_add_input_event_pointer_axis (
|
|
rdp_event_queue, -axis_step * DISCRETE_SCROLL_STEP, 0,
|
|
GRD_POINTER_AXIS_FLAGS_SOURCE_WHEEL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_input_extended_mouse_event (rdpInput *rdp_input,
|
|
uint16_t flags,
|
|
uint16_t x,
|
|
uint16_t y)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
GrdButtonState button_state;
|
|
int32_t button = 0;
|
|
|
|
if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) ||
|
|
session_rdp->is_view_only)
|
|
return TRUE;
|
|
|
|
rdp_input_mouse_event (rdp_input, PTR_FLAGS_MOVE, x, y);
|
|
|
|
button_state = flags & PTR_XFLAGS_DOWN ? GRD_BUTTON_STATE_PRESSED
|
|
: GRD_BUTTON_STATE_RELEASED;
|
|
|
|
if (flags & PTR_XFLAGS_BUTTON1)
|
|
button = BTN_SIDE;
|
|
else if (flags & PTR_XFLAGS_BUTTON2)
|
|
button = BTN_EXTRA;
|
|
|
|
if (button)
|
|
{
|
|
grd_rdp_event_queue_add_input_event_pointer_button (rdp_event_queue,
|
|
button, button_state);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_input_rel_mouse_event (rdpInput *rdp_input,
|
|
uint16_t flags,
|
|
int16_t dx,
|
|
int16_t dy)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
GrdButtonState button_state;
|
|
int32_t button = 0;
|
|
|
|
grd_rdp_event_queue_add_input_event_pointer_motion (rdp_event_queue, dx, dy);
|
|
|
|
button_state = flags & PTR_FLAGS_DOWN ? GRD_BUTTON_STATE_PRESSED
|
|
: GRD_BUTTON_STATE_RELEASED;
|
|
|
|
if (flags & PTR_FLAGS_BUTTON1)
|
|
button = BTN_LEFT;
|
|
else if (flags & PTR_FLAGS_BUTTON2)
|
|
button = BTN_RIGHT;
|
|
else if (flags & PTR_FLAGS_BUTTON3)
|
|
button = BTN_MIDDLE;
|
|
else if (flags & PTR_XFLAGS_BUTTON1)
|
|
button = BTN_SIDE;
|
|
else if (flags & PTR_XFLAGS_BUTTON2)
|
|
button = BTN_EXTRA;
|
|
|
|
if (button)
|
|
{
|
|
grd_rdp_event_queue_add_input_event_pointer_button (rdp_event_queue,
|
|
button, button_state);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
is_pause_key_sequence (GrdSessionRdp *session_rdp,
|
|
uint16_t vkcode,
|
|
uint16_t flags)
|
|
{
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
|
|
switch (session_rdp->pause_key_state)
|
|
{
|
|
case PAUSE_KEY_STATE_NONE:
|
|
if (vkcode == VK_LCONTROL &&
|
|
!(flags & KBD_FLAGS_RELEASE) &&
|
|
flags & KBD_FLAGS_EXTENDED1)
|
|
{
|
|
session_rdp->pause_key_state = PAUSE_KEY_STATE_CTRL_DOWN;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
case PAUSE_KEY_STATE_CTRL_DOWN:
|
|
if (vkcode == VK_NUMLOCK &&
|
|
!(flags & KBD_FLAGS_RELEASE))
|
|
{
|
|
session_rdp->pause_key_state = PAUSE_KEY_STATE_NUMLOCK_DOWN;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case PAUSE_KEY_STATE_NUMLOCK_DOWN:
|
|
if (vkcode == VK_LCONTROL &&
|
|
flags & KBD_FLAGS_RELEASE &&
|
|
flags & KBD_FLAGS_EXTENDED1)
|
|
{
|
|
session_rdp->pause_key_state = PAUSE_KEY_STATE_CTRL_UP;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case PAUSE_KEY_STATE_CTRL_UP:
|
|
if (vkcode == VK_NUMLOCK &&
|
|
flags & KBD_FLAGS_RELEASE)
|
|
{
|
|
session_rdp->pause_key_state = PAUSE_KEY_STATE_NONE;
|
|
grd_rdp_event_queue_add_input_event_keyboard_keysym (
|
|
rdp_event_queue, XKB_KEY_Pause, GRD_KEY_STATE_PRESSED);
|
|
grd_rdp_event_queue_add_input_event_keyboard_keysym (
|
|
rdp_event_queue, XKB_KEY_Pause, GRD_KEY_STATE_RELEASED);
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
g_warning ("Received invalid pause key sequence");
|
|
session_rdp->pause_key_state = PAUSE_KEY_STATE_NONE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_input_keyboard_event (rdpInput *rdp_input,
|
|
uint16_t flags,
|
|
uint8_t code)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
rdpSettings *rdp_settings = rdp_input->context->settings;
|
|
uint32_t keyboard_type =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_KeyboardType);
|
|
GrdKeyState key_state;
|
|
uint16_t scancode;
|
|
uint16_t fullcode;
|
|
uint16_t vkcode;
|
|
uint16_t keycode;
|
|
|
|
if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) ||
|
|
session_rdp->is_view_only)
|
|
return TRUE;
|
|
|
|
scancode = code;
|
|
fullcode = flags & KBD_FLAGS_EXTENDED ? scancode | KBDEXT : scancode;
|
|
vkcode = GetVirtualKeyCodeFromVirtualScanCode (fullcode, keyboard_type);
|
|
vkcode = flags & KBD_FLAGS_EXTENDED ? vkcode | KBDEXT : vkcode;
|
|
keycode = GetKeycodeFromVirtualKeyCode (vkcode, WINPR_KEYCODE_TYPE_EVDEV);
|
|
|
|
key_state = flags & KBD_FLAGS_RELEASE ? GRD_KEY_STATE_RELEASED
|
|
: GRD_KEY_STATE_PRESSED;
|
|
|
|
if (is_pause_key_sequence (session_rdp, vkcode, flags))
|
|
return TRUE;
|
|
|
|
if (key_state == GRD_KEY_STATE_PRESSED)
|
|
{
|
|
if (!g_hash_table_add (session_rdp->pressed_keys,
|
|
GUINT_TO_POINTER (keycode)))
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!g_hash_table_remove (session_rdp->pressed_keys,
|
|
GUINT_TO_POINTER (keycode)))
|
|
return TRUE;
|
|
}
|
|
|
|
grd_rdp_event_queue_add_input_event_keyboard_keycode (rdp_event_queue,
|
|
keycode, key_state);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_input_unicode_keyboard_event (rdpInput *rdp_input,
|
|
uint16_t flags,
|
|
uint16_t code_utf16)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_input->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
uint32_t *code_utf32;
|
|
xkb_keysym_t keysym;
|
|
GrdKeyState key_state;
|
|
|
|
if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED) ||
|
|
session_rdp->is_view_only)
|
|
return TRUE;
|
|
|
|
code_utf32 = g_utf16_to_ucs4 (&code_utf16, 1, NULL, NULL, NULL);
|
|
if (!code_utf32)
|
|
return TRUE;
|
|
|
|
keysym = xkb_utf32_to_keysym (*code_utf32);
|
|
g_free (code_utf32);
|
|
|
|
key_state = flags & KBD_FLAGS_RELEASE ? GRD_KEY_STATE_RELEASED
|
|
: GRD_KEY_STATE_PRESSED;
|
|
|
|
if (key_state == GRD_KEY_STATE_PRESSED)
|
|
{
|
|
if (!g_hash_table_add (session_rdp->pressed_unicode_keys,
|
|
GUINT_TO_POINTER (keysym)))
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!g_hash_table_remove (session_rdp->pressed_unicode_keys,
|
|
GUINT_TO_POINTER (keysym)))
|
|
return TRUE;
|
|
}
|
|
|
|
grd_rdp_event_queue_add_input_event_keyboard_keysym (rdp_event_queue,
|
|
keysym, key_state);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_suppress_output (rdpContext *rdp_context,
|
|
uint8_t allow,
|
|
const RECTANGLE_16 *area)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
|
|
if (!is_rdp_peer_flag_set (session_rdp, RDP_PEER_ACTIVATED))
|
|
return TRUE;
|
|
|
|
grd_rdp_renderer_update_output_suppression_state (session_rdp->renderer,
|
|
!allow);
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) &&
|
|
rdp_peer_context->network_autodetection)
|
|
{
|
|
if (allow)
|
|
{
|
|
grd_rdp_network_autodetection_ensure_rtt_consumer (
|
|
rdp_peer_context->network_autodetection,
|
|
GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX);
|
|
}
|
|
else
|
|
{
|
|
grd_rdp_network_autodetection_remove_rtt_consumer (
|
|
rdp_peer_context->network_autodetection,
|
|
GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static FREERDP_AUTODETECT_STATE
|
|
rdp_autodetect_on_connect_time_autodetect_begin (rdpAutoDetect *rdp_autodetect)
|
|
{
|
|
rdpContext *rdp_context = rdp_autodetect->context;
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
GrdRdpNetworkAutodetection *network_autodetection;
|
|
|
|
g_assert (freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect));
|
|
g_assert (!rdp_peer_context->network_autodetection);
|
|
|
|
network_autodetection = grd_rdp_network_autodetection_new (rdp_context);
|
|
rdp_peer_context->network_autodetection = network_autodetection;
|
|
|
|
grd_rdp_network_autodetection_start_connect_time_autodetection (network_autodetection);
|
|
|
|
return FREERDP_AUTODETECT_STATE_REQUEST;
|
|
}
|
|
|
|
static gboolean
|
|
is_using_remote_login (GrdSessionRdp *session_rdp)
|
|
{
|
|
GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp));
|
|
|
|
switch (grd_context_get_runtime_mode (context))
|
|
{
|
|
case GRD_RUNTIME_MODE_SCREEN_SHARE:
|
|
case GRD_RUNTIME_MODE_HEADLESS:
|
|
return FALSE;
|
|
case GRD_RUNTIME_MODE_SYSTEM:
|
|
case GRD_RUNTIME_MODE_HANDOVER:
|
|
return TRUE;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static GrdRdpAuthMethods
|
|
get_effective_auth_method (rdpContext *rdp_context)
|
|
{
|
|
GrdRdpAuthMethods auth_method = -1;
|
|
SECURITY_STATUS status;
|
|
SecPkgContext_PackageInfo package_info = {};
|
|
|
|
status = freerdp_nla_QueryContextAttributes (rdp_context,
|
|
SECPKG_ATTR_PACKAGE_INFO,
|
|
&package_info);
|
|
if (status != SEC_E_OK)
|
|
{
|
|
g_warning ("Failed to query NLA context package info: %s",
|
|
GetSecurityStatusString (status));
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_strcmp0 (package_info.PackageInfo->Name, KERBEROS_SSP_NAME) == 0)
|
|
auth_method = GRD_RDP_AUTH_METHOD_KERBEROS;
|
|
else if (g_strcmp0 (package_info.PackageInfo->Name, NTLM_SSP_NAME) == 0)
|
|
auth_method = GRD_RDP_AUTH_METHOD_CREDENTIALS;
|
|
else
|
|
g_warning ("Unknown NLA context package '%s'", package_info.PackageInfo->Name);
|
|
|
|
freerdp_nla_FreeContextBuffer (rdp_context, package_info.PackageInfo);
|
|
return auth_method;
|
|
}
|
|
|
|
static gboolean
|
|
is_auth_identity_current_user (const SecPkgContext_AuthIdentity *auth_identity)
|
|
{
|
|
krb5_error_code kret;
|
|
g_autofree char *principal_string = NULL;
|
|
krb5_context krb5_context = NULL;
|
|
krb5_principal principal = NULL;
|
|
char local_name[256 + 1];
|
|
g_autofree struct passwd *pwd = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
uid_t current_uid;
|
|
|
|
kret = krb5_init_context (&krb5_context);
|
|
if (kret)
|
|
{
|
|
g_critical ("Failed to initialize krb5 context");
|
|
return FALSE;
|
|
}
|
|
|
|
principal_string = g_strdup_printf ("%s@%s",
|
|
auth_identity->User,
|
|
auth_identity->Domain);
|
|
|
|
kret = krb5_parse_name (krb5_context, principal_string, &principal);
|
|
if (kret)
|
|
{
|
|
g_critical ("Failed to parse principal string %s", principal_string);
|
|
goto err;
|
|
}
|
|
|
|
kret = krb5_aname_to_localname (krb5_context, principal,
|
|
sizeof (local_name), local_name);
|
|
if (kret)
|
|
{
|
|
g_critical ("Failed to map principal '%s' name to local name",
|
|
principal_string);
|
|
return FALSE;
|
|
goto err;
|
|
}
|
|
|
|
pwd = g_unix_get_passwd_entry (local_name, &error);
|
|
if (error)
|
|
{
|
|
g_critical ("Failed to get passwd field for %s: %s",
|
|
local_name, error->message);
|
|
goto err;
|
|
}
|
|
|
|
if (!pwd)
|
|
{
|
|
g_warning ("Tried to authenticate with invalid user %s", local_name);
|
|
goto err;
|
|
}
|
|
|
|
krb5_free_principal (krb5_context, principal);
|
|
krb5_free_context (krb5_context);
|
|
|
|
current_uid = getuid ();
|
|
if (pwd->pw_uid == current_uid)
|
|
{
|
|
g_debug ("[RDP] Kerberos principal %s match current user %s (%u), "
|
|
"accepting.",
|
|
principal_string,
|
|
local_name,
|
|
current_uid);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
err:
|
|
if (principal)
|
|
krb5_free_principal (krb5_context, principal);
|
|
if (krb5_context)
|
|
krb5_free_context (krb5_context);
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_peer_logon (freerdp_peer *peer,
|
|
const SEC_WINNT_AUTH_IDENTITY *identity,
|
|
BOOL automatic)
|
|
{
|
|
rdpContext *rdp_context = peer->context;
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp));
|
|
GrdRdpAuthMethods auth_method;
|
|
SECURITY_STATUS status;
|
|
SecPkgContext_AuthIdentity auth_identity = {};
|
|
|
|
if (is_using_remote_login (session_rdp))
|
|
return TRUE;
|
|
|
|
auth_method = get_effective_auth_method (rdp_context);
|
|
|
|
if (auth_method == -1)
|
|
return FALSE;
|
|
|
|
switch (auth_method)
|
|
{
|
|
case GRD_RDP_AUTH_METHOD_CREDENTIALS:
|
|
g_debug ("[RDP] Authenticated using NTLM, "
|
|
"not applying any additional policy.");
|
|
return TRUE;
|
|
case GRD_RDP_AUTH_METHOD_KERBEROS:
|
|
break;
|
|
}
|
|
|
|
status = freerdp_nla_QueryContextAttributes (rdp_context,
|
|
SECPKG_ATTR_AUTH_IDENTITY,
|
|
&auth_identity);
|
|
|
|
if (status != SEC_E_OK)
|
|
{
|
|
g_warning ("Failed to authenticate Kerberos principal: %s",
|
|
GetSecurityStatusString (status));
|
|
return FALSE;
|
|
}
|
|
|
|
switch (grd_context_get_runtime_mode (context))
|
|
{
|
|
case GRD_RUNTIME_MODE_HEADLESS:
|
|
case GRD_RUNTIME_MODE_SCREEN_SHARE:
|
|
if (is_auth_identity_current_user (&auth_identity))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_debug ("[RDP] Kerberos principal doesn't match current user, "
|
|
"disconnecting");
|
|
return FALSE;
|
|
}
|
|
case GRD_RUNTIME_MODE_SYSTEM:
|
|
case GRD_RUNTIME_MODE_HANDOVER:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static uint32_t
|
|
get_max_monitor_count (GrdSessionRdp *session_rdp)
|
|
{
|
|
GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp));
|
|
|
|
switch (grd_context_get_runtime_mode (context))
|
|
{
|
|
case GRD_RUNTIME_MODE_HEADLESS:
|
|
case GRD_RUNTIME_MODE_SYSTEM:
|
|
case GRD_RUNTIME_MODE_HANDOVER:
|
|
return MAX_MONITOR_COUNT_HEADLESS;
|
|
case GRD_RUNTIME_MODE_SCREEN_SHARE:
|
|
return MAX_MONITOR_COUNT_SCREEN_SHARE;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static BOOL
|
|
rdp_peer_capabilities (freerdp_peer *peer)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
rdpSettings *rdp_settings = peer->context->settings;
|
|
HANDLE vcm = rdp_peer_context->vcm;
|
|
GrdRdpMonitorConfig *monitor_config;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline))
|
|
{
|
|
g_warning ("[RDP] Client did not advertise support for the Graphics "
|
|
"Pipeline, closing connection");
|
|
return FALSE;
|
|
}
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) ||
|
|
freerdp_settings_get_bool (rdp_settings, FreeRDP_RemoteFxCodec) ||
|
|
freerdp_settings_get_bool (rdp_settings, FreeRDP_RemoteFxImageCodec) ||
|
|
freerdp_settings_get_bool (rdp_settings, FreeRDP_NSCodec))
|
|
{
|
|
uint16_t supported_color_depths =
|
|
freerdp_settings_get_uint16 (rdp_settings, FreeRDP_SupportedColorDepths);
|
|
|
|
if (!(supported_color_depths & RNS_UD_32BPP_SUPPORT))
|
|
{
|
|
g_warning ("[RDP] Protocol violation: Client advertised support for "
|
|
"codecs or the Graphics Pipeline, but does not support "
|
|
"32-bit colour depth");
|
|
return FALSE;
|
|
}
|
|
if (freerdp_settings_get_uint32 (rdp_settings, FreeRDP_ColorDepth) != 32)
|
|
{
|
|
g_debug ("[RDP] Client prefers colour depth %u, invalidated by support "
|
|
"of codecs or graphics pipeline",
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_ColorDepth));
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_ColorDepth, 32);
|
|
}
|
|
}
|
|
|
|
if (!freerdp_settings_get_bool (rdp_settings, FreeRDP_DesktopResize))
|
|
{
|
|
g_warning ("Client doesn't support desktop resizing, closing connection");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!WTSVirtualChannelManagerIsChannelJoined (vcm, DRDYNVC_SVC_CHANNEL_NAME))
|
|
{
|
|
g_warning ("[RDP] Client doesn't support the DRDYNVC SVC, "
|
|
"closing connection");
|
|
return FALSE;
|
|
}
|
|
|
|
if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND)
|
|
{
|
|
uint32_t max_monitor_count;
|
|
|
|
max_monitor_count = get_max_monitor_count (session_rdp);
|
|
monitor_config =
|
|
grd_rdp_monitor_config_new_from_client_data (rdp_settings,
|
|
max_monitor_count, &error);
|
|
}
|
|
else
|
|
{
|
|
g_autoptr (GStrvBuilder) connector_builder = NULL;
|
|
char **connectors;
|
|
|
|
monitor_config = g_new0 (GrdRdpMonitorConfig, 1);
|
|
|
|
connector_builder = g_strv_builder_new ();
|
|
g_strv_builder_add (connector_builder, "");
|
|
connectors = g_strv_builder_end (connector_builder);
|
|
|
|
monitor_config->connectors = connectors;
|
|
monitor_config->monitor_count = 1;
|
|
}
|
|
if (!monitor_config)
|
|
{
|
|
g_warning ("[RDP] Received invalid monitor layout from client: %s, "
|
|
"closing connection", error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND)
|
|
{
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_DesktopWidth,
|
|
monitor_config->desktop_width);
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_DesktopHeight,
|
|
monitor_config->desktop_height);
|
|
}
|
|
|
|
if (monitor_config->is_virtual)
|
|
g_debug ("[RDP] Remote Desktop session will use virtual monitors");
|
|
else
|
|
g_debug ("[RDP] Remote Desktop session will mirror the primary monitor");
|
|
|
|
grd_rdp_layout_manager_submit_new_monitor_config (session_rdp->layout_manager,
|
|
monitor_config);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
notify_post_connected (gpointer user_data)
|
|
{
|
|
GrdSessionRdp *session_rdp = user_data;
|
|
GrdContext *grd_context = grd_session_get_context (GRD_SESSION (session_rdp));
|
|
|
|
g_mutex_lock (&session_rdp->notify_post_connected_mutex);
|
|
session_rdp->notify_post_connected_source_id = 0;
|
|
g_mutex_unlock (&session_rdp->notify_post_connected_mutex);
|
|
|
|
grd_rdp_session_metrics_notify_phase_completion (session_rdp->session_metrics,
|
|
GRD_RDP_PHASE_POST_CONNECT);
|
|
if (grd_context_get_runtime_mode (grd_context) != GRD_RUNTIME_MODE_SYSTEM)
|
|
grd_session_start (GRD_SESSION (session_rdp));
|
|
|
|
g_signal_emit (session_rdp, signals[POST_CONNECTED], 0);
|
|
}
|
|
|
|
static BOOL
|
|
rdp_peer_post_connect (freerdp_peer *peer)
|
|
{
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context;
|
|
GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
|
|
rdpSettings *rdp_settings = peer->context->settings;
|
|
uint32_t rdp_version =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_RdpVersion);
|
|
uint32_t keyboard_type =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_KeyboardType);
|
|
uint32_t os_major_type =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType);
|
|
|
|
if (freerdp_settings_get_uint32 (rdp_settings, FreeRDP_PointerCacheSize) <= 0)
|
|
{
|
|
g_warning ("Client doesn't have a pointer cache, closing connection");
|
|
return FALSE;
|
|
}
|
|
if (!freerdp_settings_get_bool (rdp_settings, FreeRDP_FastPathOutput))
|
|
{
|
|
g_warning ("Client does not support fastpath output, closing connection");
|
|
return FALSE;
|
|
}
|
|
|
|
g_debug ("New RDP client: [OS major type, OS minor type]: [%s, %s]",
|
|
freerdp_peer_os_major_type_string (peer),
|
|
freerdp_peer_os_minor_type_string (peer));
|
|
g_debug ("[RDP] Maximum common RDP version 0x%08X", rdp_version);
|
|
g_debug ("[RDP] Client uses keyboard type %u", keyboard_type);
|
|
|
|
g_debug ("[RDP] Virtual Channels: compression flags: %u, "
|
|
"compression level: %u, chunk size: %u",
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_VCFlags),
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_CompressionLevel),
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_VCChunkSize));
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) &&
|
|
!freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect))
|
|
{
|
|
g_warning ("Client does not support autodetecting network characteristics "
|
|
"(RTT detection, Bandwidth measurement). "
|
|
"High latency connections will suffer!");
|
|
}
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioPlayback) &&
|
|
!freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect))
|
|
{
|
|
g_warning ("[RDP] Client does not support autodetecting network "
|
|
"characteristics. Disabling audio output redirection");
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioPlayback, FALSE);
|
|
}
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioPlayback) &&
|
|
(os_major_type == OSMAJORTYPE_IOS ||
|
|
os_major_type == OSMAJORTYPE_ANDROID))
|
|
{
|
|
g_warning ("[RDP] Client cannot handle graphics and audio "
|
|
"simultaneously. Disabling audio output redirection");
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioPlayback, FALSE);
|
|
}
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline))
|
|
grd_rdp_renderer_notify_graphics_pipeline_reset (session_rdp->renderer);
|
|
|
|
g_clear_pointer (&session_rdp->sam_file, grd_rdp_sam_free_sam_file);
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) &&
|
|
rdp_peer_context->network_autodetection)
|
|
{
|
|
grd_rdp_network_autodetection_ensure_rtt_consumer (
|
|
rdp_peer_context->network_autodetection,
|
|
GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX);
|
|
}
|
|
|
|
set_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED);
|
|
|
|
g_mutex_lock (&session_rdp->notify_post_connected_mutex);
|
|
session_rdp->notify_post_connected_source_id =
|
|
g_idle_add_once (notify_post_connected, session_rdp);
|
|
g_mutex_unlock (&session_rdp->notify_post_connected_mutex);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
rdp_peer_activate (freerdp_peer *peer)
|
|
{
|
|
g_debug ("Activating client");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
rdp_peer_context_free (freerdp_peer *peer,
|
|
RdpPeerContext *rdp_peer_context)
|
|
{
|
|
if (!rdp_peer_context)
|
|
return;
|
|
|
|
g_clear_object (&rdp_peer_context->dvc_handler);
|
|
|
|
if (rdp_peer_context->vcm != INVALID_HANDLE_VALUE)
|
|
g_clear_pointer (&rdp_peer_context->vcm, WTSCloseServer);
|
|
|
|
if (rdp_peer_context->encode_stream)
|
|
{
|
|
Stream_Free (rdp_peer_context->encode_stream, TRUE);
|
|
rdp_peer_context->encode_stream = NULL;
|
|
}
|
|
|
|
g_clear_pointer (&rdp_peer_context->rfx_context, rfx_context_free);
|
|
|
|
g_mutex_clear (&rdp_peer_context->channel_mutex);
|
|
}
|
|
|
|
static BOOL
|
|
rdp_peer_context_new (freerdp_peer *peer,
|
|
RdpPeerContext *rdp_peer_context)
|
|
{
|
|
g_mutex_init (&rdp_peer_context->channel_mutex);
|
|
|
|
rdp_peer_context->rfx_context = rfx_context_new (TRUE);
|
|
if (!rdp_peer_context->rfx_context)
|
|
{
|
|
g_warning ("[RDP] Failed to create RFX context");
|
|
return FALSE;
|
|
}
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
rfx_context_set_pixel_format (rdp_peer_context->rfx_context,
|
|
PIXEL_FORMAT_BGRX32);
|
|
#else
|
|
rfx_context_set_pixel_format (rdp_peer_context->rfx_context,
|
|
PIXEL_FORMAT_XRGB32);
|
|
#endif
|
|
|
|
rdp_peer_context->encode_stream = Stream_New (NULL, 64 * 64 * 4);
|
|
if (!rdp_peer_context->encode_stream)
|
|
{
|
|
g_warning ("[RDP] Failed to create encode stream");
|
|
return FALSE;
|
|
}
|
|
|
|
rdp_peer_context->vcm = WTSOpenServerA ((LPSTR) peer->context);
|
|
if (!rdp_peer_context->vcm || rdp_peer_context->vcm == INVALID_HANDLE_VALUE)
|
|
{
|
|
g_warning ("[RDP] Failed to create virtual channel manager");
|
|
return FALSE;
|
|
}
|
|
|
|
rdp_peer_context->dvc_handler =
|
|
grd_rdp_dvc_handler_new (rdp_peer_context->vcm);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
init_rdp_session (GrdSessionRdp *session_rdp,
|
|
const char *username,
|
|
const char *password,
|
|
GError **error)
|
|
{
|
|
GrdContext *context = grd_session_get_context (GRD_SESSION (session_rdp));
|
|
GrdSettings *settings = grd_context_get_settings (context);
|
|
GSocket *socket = g_socket_connection_get_socket (session_rdp->connection);
|
|
GrdRdpAuthMethods auth_methods = 0;
|
|
g_autofree char *server_cert = NULL;
|
|
g_autofree char *server_key = NULL;
|
|
gboolean use_client_configs;
|
|
freerdp_peer *peer;
|
|
rdpInput *rdp_input;
|
|
rdpAutoDetect *rdp_autodetect;
|
|
RdpPeerContext *rdp_peer_context;
|
|
rdpSettings *rdp_settings;
|
|
rdpCertificate *rdp_certificate = NULL;
|
|
rdpPrivateKey *rdp_private_key = NULL;
|
|
|
|
use_client_configs = session_rdp->screen_share_mode ==
|
|
GRD_RDP_SCREEN_SHARE_MODE_EXTEND;
|
|
|
|
g_debug ("Initialize RDP session");
|
|
|
|
peer = freerdp_peer_new (g_socket_get_fd (socket));
|
|
if (!peer)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create peer");
|
|
return FALSE;
|
|
}
|
|
|
|
peer->ContextSize = sizeof (RdpPeerContext);
|
|
peer->ContextFree = (psPeerContextFree) rdp_peer_context_free;
|
|
peer->ContextNew = (psPeerContextNew) rdp_peer_context_new;
|
|
|
|
if (!freerdp_peer_context_new (peer))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create peer context");
|
|
freerdp_peer_free (peer);
|
|
return FALSE;
|
|
}
|
|
session_rdp->peer = peer;
|
|
|
|
rdp_peer_context = (RdpPeerContext *) peer->context;
|
|
rdp_peer_context->session_rdp = session_rdp;
|
|
|
|
rdp_settings = peer->context->settings;
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-auth-methods", &auth_methods,
|
|
NULL);
|
|
g_assert (auth_methods);
|
|
|
|
if (auth_methods & GRD_RDP_AUTH_METHOD_CREDENTIALS)
|
|
{
|
|
session_rdp->sam_file = grd_rdp_sam_create_sam_file (username, password);
|
|
if (!session_rdp->sam_file)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create SAM database");
|
|
return FALSE;
|
|
}
|
|
freerdp_settings_set_string (rdp_settings, FreeRDP_NtlmSamFile,
|
|
session_rdp->sam_file->filename);
|
|
}
|
|
|
|
if (auth_methods & GRD_RDP_AUTH_METHOD_KERBEROS &&
|
|
!is_using_remote_login (session_rdp))
|
|
{
|
|
g_autofree char *kerberos_keytab = NULL;
|
|
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-kerberos-keytab", &kerberos_keytab,
|
|
NULL);
|
|
|
|
g_debug ("[RDP] Enabling Kerberos authentication using %s", kerberos_keytab);
|
|
|
|
freerdp_settings_set_string (rdp_settings, FreeRDP_KerberosKeytab,
|
|
kerberos_keytab);
|
|
}
|
|
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-server-cert", &server_cert,
|
|
"rdp-server-key", &server_key,
|
|
NULL);
|
|
|
|
if (server_cert)
|
|
rdp_certificate = freerdp_certificate_new_from_pem (server_cert);
|
|
if (!rdp_certificate)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create certificate from file");
|
|
return FALSE;
|
|
}
|
|
freerdp_settings_set_pointer_len (rdp_settings,
|
|
FreeRDP_RdpServerCertificate,
|
|
rdp_certificate, 1);
|
|
|
|
if (server_key)
|
|
rdp_private_key = freerdp_key_new_from_pem (server_key);
|
|
if (!rdp_private_key)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create private key from file");
|
|
return FALSE;
|
|
}
|
|
freerdp_settings_set_pointer_len (rdp_settings,
|
|
FreeRDP_RdpServerRsaKey,
|
|
rdp_private_key, 1);
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_RdpSecurity, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_TlsSecurity, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_NlaSecurity, TRUE);
|
|
|
|
if (grd_context_get_runtime_mode (context) == GRD_RUNTIME_MODE_HANDOVER)
|
|
{
|
|
freerdp_settings_set_string (rdp_settings, FreeRDP_Username, username);
|
|
freerdp_settings_set_string (rdp_settings, FreeRDP_Password, password);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_RdstlsSecurity, TRUE);
|
|
}
|
|
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_OsMajorType,
|
|
OSMAJORTYPE_UNIX);
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_OsMinorType,
|
|
OSMINORTYPE_PSEUDO_XSERVER);
|
|
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_ColorDepth, 32);
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxAVC444v2, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxAVC444, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxH264, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxSmallCache, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxThinClient, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline, TRUE);
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_RemoteFxCodec, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_RemoteFxImageCodec, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_NSCodec, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_FrameMarkerCommandEnabled, TRUE);
|
|
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_PointerCacheSize, 100);
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_FastPathOutput, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_NetworkAutoDetect, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_RefreshRect, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_SupportMonitorLayoutPdu,
|
|
use_client_configs);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_SupportMultitransport, FALSE);
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_VCFlags, VCCAPS_COMPR_SC);
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_VCChunkSize, 16256);
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_HasExtendedMouseEvent, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_HasHorizontalWheel, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_HasRelativeMouseEvent, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_HasQoeEvent, FALSE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_UnicodeInput, TRUE);
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioCapture, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_AudioPlayback, TRUE);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_RemoteConsoleAudio, TRUE);
|
|
|
|
peer->Logon = rdp_peer_logon;
|
|
peer->Capabilities = rdp_peer_capabilities;
|
|
peer->PostConnect = rdp_peer_post_connect;
|
|
peer->Activate = rdp_peer_activate;
|
|
|
|
peer->context->update->SuppressOutput = rdp_suppress_output;
|
|
|
|
rdp_input = peer->context->input;
|
|
rdp_input->SynchronizeEvent = rdp_input_synchronize_event;
|
|
rdp_input->MouseEvent = rdp_input_mouse_event;
|
|
rdp_input->ExtendedMouseEvent = rdp_input_extended_mouse_event;
|
|
rdp_input->RelMouseEvent = rdp_input_rel_mouse_event;
|
|
rdp_input->KeyboardEvent = rdp_input_keyboard_event;
|
|
rdp_input->UnicodeKeyboardEvent = rdp_input_unicode_keyboard_event;
|
|
|
|
rdp_autodetect = peer->context->autodetect;
|
|
rdp_autodetect->OnConnectTimeAutoDetectBegin =
|
|
rdp_autodetect_on_connect_time_autodetect_begin;
|
|
|
|
if (!peer->Initialize (peer))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to initialize peer");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gpointer
|
|
socket_thread_func (gpointer data)
|
|
{
|
|
GrdSessionRdp *session_rdp = data;
|
|
freerdp_peer *peer;
|
|
RdpPeerContext *rdp_peer_context;
|
|
HANDLE vcm;
|
|
HANDLE channel_event;
|
|
HANDLE events[32] = {};
|
|
uint32_t n_events;
|
|
uint32_t n_freerdp_handles;
|
|
|
|
peer = session_rdp->peer;
|
|
rdp_peer_context = (RdpPeerContext *) peer->context;
|
|
vcm = rdp_peer_context->vcm;
|
|
channel_event = WTSVirtualChannelManagerGetEventHandle (vcm);
|
|
|
|
while (TRUE)
|
|
{
|
|
GrdRdpNetworkAutodetection *network_autodetection =
|
|
rdp_peer_context->network_autodetection;
|
|
gboolean pending_bw_measure_stop = FALSE;
|
|
HANDLE bw_measure_stop_event = NULL;
|
|
|
|
n_events = 0;
|
|
|
|
events[n_events++] = session_rdp->stop_event;
|
|
if (network_autodetection)
|
|
{
|
|
bw_measure_stop_event =
|
|
grd_rdp_network_autodetection_get_bw_measure_stop_event_handle (network_autodetection);
|
|
events[n_events++] = bw_measure_stop_event;
|
|
}
|
|
|
|
events[n_events++] = channel_event;
|
|
|
|
n_freerdp_handles = peer->GetEventHandles (peer, &events[n_events],
|
|
32 - n_events);
|
|
if (!n_freerdp_handles)
|
|
{
|
|
g_warning ("Failed to get FreeRDP transport event handles");
|
|
handle_client_gone (session_rdp);
|
|
break;
|
|
}
|
|
n_events += n_freerdp_handles;
|
|
|
|
WaitForMultipleObjects (n_events, events, FALSE, INFINITE);
|
|
|
|
if (session_rdp->session_should_stop)
|
|
break;
|
|
|
|
if (!peer->CheckFileDescriptor (peer))
|
|
{
|
|
g_message ("[RDP] Network or intentional disconnect, stopping session");
|
|
handle_client_gone (session_rdp);
|
|
break;
|
|
}
|
|
|
|
if (peer->connected &&
|
|
WTSVirtualChannelManagerIsChannelJoined (vcm, DRDYNVC_SVC_CHANNEL_NAME))
|
|
{
|
|
GrdRdpDvcTelemetry *telemetry;
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline;
|
|
GrdRdpDvcInput *input;
|
|
GrdRdpDvcAudioPlayback *audio_playback;
|
|
GrdRdpDvcDisplayControl *display_control;
|
|
GrdRdpDvcAudioInput *audio_input;
|
|
GrdRdpDvcCameraEnumerator *camera_enumerator;
|
|
|
|
switch (WTSVirtualChannelManagerGetDrdynvcState (vcm))
|
|
{
|
|
case DRDYNVC_STATE_NONE:
|
|
/*
|
|
* This ensures that WTSVirtualChannelManagerCheckFileDescriptor()
|
|
* will be called, which initializes the drdynvc channel
|
|
*/
|
|
SetEvent (channel_event);
|
|
break;
|
|
case DRDYNVC_STATE_READY:
|
|
g_mutex_lock (&rdp_peer_context->channel_mutex);
|
|
telemetry = rdp_peer_context->telemetry;
|
|
graphics_pipeline = rdp_peer_context->graphics_pipeline;
|
|
input = rdp_peer_context->input;
|
|
audio_playback = rdp_peer_context->audio_playback;
|
|
display_control = rdp_peer_context->display_control;
|
|
audio_input = rdp_peer_context->audio_input;
|
|
camera_enumerator = rdp_peer_context->camera_enumerator;
|
|
|
|
if (telemetry && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (telemetry));
|
|
if (graphics_pipeline && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (graphics_pipeline));
|
|
if (input && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (input));
|
|
if (audio_playback && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (audio_playback));
|
|
if (display_control && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (display_control));
|
|
if (audio_input && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (audio_input));
|
|
if (camera_enumerator && !session_rdp->session_should_stop)
|
|
grd_rdp_dvc_maybe_init (GRD_RDP_DVC (camera_enumerator));
|
|
g_mutex_unlock (&rdp_peer_context->channel_mutex);
|
|
break;
|
|
}
|
|
|
|
if (session_rdp->session_should_stop)
|
|
break;
|
|
}
|
|
|
|
if (bw_measure_stop_event)
|
|
{
|
|
pending_bw_measure_stop = WaitForSingleObject (bw_measure_stop_event,
|
|
0) == WAIT_OBJECT_0;
|
|
}
|
|
|
|
if (WaitForSingleObject (channel_event, 0) == WAIT_OBJECT_0 &&
|
|
!WTSVirtualChannelManagerCheckFileDescriptor (vcm))
|
|
{
|
|
g_message ("Unable to check VCM file descriptor, closing connection");
|
|
handle_client_gone (session_rdp);
|
|
break;
|
|
}
|
|
|
|
if (pending_bw_measure_stop)
|
|
grd_rdp_network_autodetection_bw_measure_stop (network_autodetection);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
on_view_only_changed (GrdSettings *settings,
|
|
GParamSpec *pspec,
|
|
GrdSessionRdp *session_rdp)
|
|
{
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-view-only", &session_rdp->is_view_only,
|
|
NULL);
|
|
}
|
|
|
|
GrdSessionRdp *
|
|
grd_session_rdp_new (GrdRdpServer *rdp_server,
|
|
GSocketConnection *connection)
|
|
{
|
|
g_autoptr (GrdSessionRdp) session_rdp = NULL;
|
|
GrdContext *context;
|
|
GrdSettings *settings;
|
|
char *username;
|
|
char *password;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
context = grd_rdp_server_get_context (rdp_server);
|
|
settings = grd_context_get_settings (context);
|
|
if (!grd_settings_get_rdp_credentials (settings,
|
|
&username, &password,
|
|
&error))
|
|
{
|
|
if (error)
|
|
g_warning ("[RDP] Couldn't retrieve RDP credentials: %s", error->message);
|
|
else
|
|
g_message ("[RDP] Credentials are not set, denying client");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
session_rdp = g_object_new (GRD_TYPE_SESSION_RDP,
|
|
"context", context,
|
|
NULL);
|
|
|
|
session_rdp->server = rdp_server;
|
|
session_rdp->connection = g_object_ref (connection);
|
|
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-screen-share-mode", &session_rdp->screen_share_mode,
|
|
"rdp-view-only", &session_rdp->is_view_only,
|
|
NULL);
|
|
|
|
g_signal_connect (settings, "notify::rdp-view-only",
|
|
G_CALLBACK (on_view_only_changed),
|
|
session_rdp);
|
|
|
|
session_rdp->renderer = grd_rdp_renderer_new (session_rdp);
|
|
session_rdp->layout_manager = grd_rdp_layout_manager_new (session_rdp);
|
|
|
|
if (!init_rdp_session (session_rdp, username, password, &error))
|
|
{
|
|
g_warning ("[RDP] Couldn't initialize session: %s", error->message);
|
|
g_free (password);
|
|
g_free (username);
|
|
return NULL;
|
|
}
|
|
|
|
session_rdp->socket_thread = g_thread_new ("RDP socket thread",
|
|
socket_thread_func,
|
|
session_rdp);
|
|
|
|
g_free (password);
|
|
g_free (username);
|
|
|
|
return g_steal_pointer (&session_rdp);
|
|
}
|
|
|
|
static gboolean
|
|
has_session_close_queued (GrdSessionRdp *session_rdp)
|
|
{
|
|
gboolean pending_session_closing;
|
|
|
|
g_mutex_lock (&session_rdp->close_session_mutex);
|
|
pending_session_closing = session_rdp->close_session_idle_id != 0;
|
|
g_mutex_unlock (&session_rdp->close_session_mutex);
|
|
|
|
return pending_session_closing;
|
|
}
|
|
|
|
static void
|
|
clear_rdp_peer (GrdSessionRdp *session_rdp)
|
|
{
|
|
g_clear_pointer (&session_rdp->sam_file, grd_rdp_sam_free_sam_file);
|
|
|
|
if (session_rdp->peer)
|
|
{
|
|
freerdp_peer_context_free (session_rdp->peer);
|
|
g_clear_pointer (&session_rdp->peer, freerdp_peer_free);
|
|
}
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_stop (GrdSession *session)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session);
|
|
freerdp_peer *peer = session_rdp->peer;
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context;
|
|
|
|
g_debug ("Stopping RDP session");
|
|
|
|
unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED);
|
|
session_rdp->session_should_stop = TRUE;
|
|
SetEvent (session_rdp->stop_event);
|
|
|
|
if (!has_session_close_queued (session_rdp))
|
|
{
|
|
freerdp_set_error_info (peer->context->rdp,
|
|
ERRINFO_RPC_INITIATED_DISCONNECT);
|
|
}
|
|
else if (session_rdp->rdp_error_info)
|
|
{
|
|
freerdp_set_error_info (peer->context->rdp, session_rdp->rdp_error_info);
|
|
}
|
|
|
|
if (rdp_peer_context->network_autodetection)
|
|
{
|
|
grd_rdp_network_autodetection_invoke_shutdown (
|
|
rdp_peer_context->network_autodetection);
|
|
}
|
|
|
|
grd_rdp_renderer_invoke_shutdown (session_rdp->renderer);
|
|
|
|
g_mutex_lock (&rdp_peer_context->channel_mutex);
|
|
g_clear_object (&rdp_peer_context->camera_enumerator);
|
|
g_clear_object (&rdp_peer_context->audio_input);
|
|
g_clear_object (&rdp_peer_context->clipboard_rdp);
|
|
g_clear_object (&rdp_peer_context->audio_playback);
|
|
g_clear_object (&rdp_peer_context->display_control);
|
|
g_clear_object (&rdp_peer_context->input);
|
|
g_clear_object (&rdp_peer_context->graphics_pipeline);
|
|
g_clear_object (&rdp_peer_context->telemetry);
|
|
g_mutex_unlock (&rdp_peer_context->channel_mutex);
|
|
|
|
g_clear_pointer (&session_rdp->socket_thread, g_thread_join);
|
|
g_clear_handle_id (&session_rdp->notify_post_connected_source_id,
|
|
g_source_remove);
|
|
|
|
g_hash_table_foreach_remove (session_rdp->pressed_keys,
|
|
notify_keycode_released,
|
|
session_rdp);
|
|
g_hash_table_foreach_remove (session_rdp->pressed_unicode_keys,
|
|
notify_keysym_released,
|
|
session_rdp);
|
|
grd_rdp_event_queue_flush (session_rdp->rdp_event_queue);
|
|
|
|
g_clear_object (&session_rdp->layout_manager);
|
|
|
|
g_clear_object (&session_rdp->cursor_renderer);
|
|
g_clear_object (&session_rdp->renderer);
|
|
|
|
peer->Close (peer);
|
|
grd_close_connection_and_notify (session_rdp->connection);
|
|
g_clear_object (&session_rdp->connection);
|
|
|
|
g_clear_object (&rdp_peer_context->network_autodetection);
|
|
|
|
peer->Disconnect (peer);
|
|
clear_rdp_peer (session_rdp);
|
|
|
|
g_clear_handle_id (&session_rdp->close_session_idle_id, g_source_remove);
|
|
}
|
|
|
|
static gboolean
|
|
close_session_idle (gpointer user_data)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (user_data);
|
|
|
|
grd_session_stop (GRD_SESSION (session_rdp));
|
|
|
|
session_rdp->close_session_idle_id = 0;
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
initialize_graphics_pipeline (GrdSessionRdp *session_rdp)
|
|
{
|
|
rdpContext *rdp_context = session_rdp->peer->context;
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
|
|
GrdHwAccelNvidia *hwaccel_nvidia =
|
|
grd_rdp_server_get_hwaccel_nvidia (session_rdp->server);
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline;
|
|
GrdRdpDvcTelemetry *telemetry;
|
|
|
|
g_assert (!rdp_peer_context->telemetry);
|
|
g_assert (!rdp_peer_context->graphics_pipeline);
|
|
|
|
telemetry = grd_rdp_dvc_telemetry_new (session_rdp,
|
|
rdp_peer_context->dvc_handler,
|
|
rdp_peer_context->vcm,
|
|
rdp_context);
|
|
rdp_peer_context->telemetry = telemetry;
|
|
|
|
graphics_pipeline =
|
|
grd_rdp_dvc_graphics_pipeline_new (session_rdp,
|
|
session_rdp->renderer,
|
|
rdp_peer_context->dvc_handler,
|
|
rdp_peer_context->vcm,
|
|
rdp_context,
|
|
rdp_peer_context->network_autodetection,
|
|
rdp_peer_context->encode_stream,
|
|
rdp_peer_context->rfx_context);
|
|
grd_rdp_dvc_graphics_pipeline_set_hwaccel_nvidia (graphics_pipeline,
|
|
hwaccel_nvidia);
|
|
rdp_peer_context->graphics_pipeline = graphics_pipeline;
|
|
}
|
|
|
|
static void
|
|
initialize_remaining_virtual_channels (GrdSessionRdp *session_rdp)
|
|
{
|
|
rdpContext *rdp_context = session_rdp->peer->context;
|
|
RdpPeerContext *rdp_peer_context = (RdpPeerContext *) rdp_context;
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
GrdRdpDvcHandler *dvc_handler = rdp_peer_context->dvc_handler;
|
|
HANDLE vcm = rdp_peer_context->vcm;
|
|
|
|
rdp_peer_context->input =
|
|
grd_rdp_dvc_input_new (session_rdp->layout_manager,
|
|
session_rdp, dvc_handler, vcm);
|
|
|
|
if (session_rdp->screen_share_mode == GRD_RDP_SCREEN_SHARE_MODE_EXTEND)
|
|
{
|
|
rdp_peer_context->display_control =
|
|
grd_rdp_dvc_display_control_new (session_rdp->layout_manager,
|
|
session_rdp,
|
|
dvc_handler,
|
|
rdp_peer_context->vcm,
|
|
get_max_monitor_count (session_rdp));
|
|
}
|
|
if (WTSVirtualChannelManagerIsChannelJoined (vcm, CLIPRDR_SVC_CHANNEL_NAME))
|
|
{
|
|
uint32_t os_major_type =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType);
|
|
gboolean peer_is_on_ms_windows;
|
|
|
|
peer_is_on_ms_windows = os_major_type == OSMAJORTYPE_WINDOWS;
|
|
rdp_peer_context->clipboard_rdp =
|
|
grd_clipboard_rdp_new (session_rdp, vcm, !peer_is_on_ms_windows);
|
|
}
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioPlayback) &&
|
|
!freerdp_settings_get_bool (rdp_settings, FreeRDP_RemoteConsoleAudio))
|
|
{
|
|
rdp_peer_context->audio_playback =
|
|
grd_rdp_dvc_audio_playback_new (session_rdp, dvc_handler, vcm,
|
|
rdp_context);
|
|
}
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_AudioCapture))
|
|
{
|
|
rdp_peer_context->audio_input =
|
|
grd_rdp_dvc_audio_input_new (session_rdp, dvc_handler, vcm,
|
|
rdp_context);
|
|
}
|
|
|
|
rdp_peer_context->camera_enumerator =
|
|
grd_rdp_dvc_camera_enumerator_new (session_rdp, dvc_handler, vcm,
|
|
rdp_context);
|
|
}
|
|
|
|
static void
|
|
on_remote_desktop_session_ready (GrdSession *session)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session);
|
|
|
|
grd_rdp_session_metrics_notify_phase_completion (session_rdp->session_metrics,
|
|
GRD_RDP_PHASE_SESSION_READY);
|
|
|
|
session_rdp->cursor_renderer =
|
|
grd_rdp_cursor_renderer_new (session_rdp->renderer,
|
|
session_rdp->peer->context);
|
|
grd_rdp_cursor_renderer_notify_session_ready (session_rdp->cursor_renderer);
|
|
|
|
initialize_graphics_pipeline (session_rdp);
|
|
initialize_remaining_virtual_channels (session_rdp);
|
|
}
|
|
|
|
static void
|
|
on_remote_desktop_session_started (GrdSession *session)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session);
|
|
|
|
if (!grd_rdp_renderer_start (session_rdp->renderer))
|
|
{
|
|
grd_session_rdp_notify_error (session_rdp,
|
|
GRD_SESSION_RDP_ERROR_CLOSE_STACK_ON_DRIVER_FAILURE);
|
|
return;
|
|
}
|
|
|
|
grd_rdp_session_metrics_notify_phase_completion (session_rdp->session_metrics,
|
|
GRD_RDP_PHASE_SESSION_STARTED);
|
|
grd_rdp_layout_manager_notify_session_started (session_rdp->layout_manager);
|
|
grd_rdp_event_queue_flush_synchronization (session_rdp->rdp_event_queue);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_on_stream_created (GrdSession *session,
|
|
uint32_t stream_id,
|
|
GrdStream *stream)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session);
|
|
GrdRdpStreamOwner *stream_owner = NULL;
|
|
|
|
if (!g_hash_table_lookup_extended (session_rdp->stream_table,
|
|
GUINT_TO_POINTER (stream_id),
|
|
NULL, (gpointer) &stream_owner))
|
|
g_assert_not_reached ();
|
|
|
|
grd_rdp_stream_owner_notify_stream_created (stream_owner, stream_id, stream);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_on_caps_lock_state_changed (GrdSession *session,
|
|
gboolean state)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session);
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
|
|
grd_rdp_event_queue_update_caps_lock_state (rdp_event_queue, state);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_on_num_lock_state_changed (GrdSession *session,
|
|
gboolean state)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (session);
|
|
GrdRdpEventQueue *rdp_event_queue = session_rdp->rdp_event_queue;
|
|
|
|
grd_rdp_event_queue_update_num_lock_state (rdp_event_queue, state);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_dispose (GObject *object)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (object);
|
|
|
|
g_assert (!session_rdp->notify_post_connected_source_id);
|
|
g_assert (!session_rdp->cursor_renderer);
|
|
|
|
g_clear_object (&session_rdp->layout_manager);
|
|
clear_rdp_peer (session_rdp);
|
|
|
|
if (session_rdp->connection)
|
|
grd_close_connection_and_notify (session_rdp->connection);
|
|
g_clear_object (&session_rdp->connection);
|
|
|
|
g_clear_object (&session_rdp->renderer);
|
|
|
|
g_clear_object (&session_rdp->rdp_event_queue);
|
|
g_clear_object (&session_rdp->session_metrics);
|
|
|
|
g_clear_pointer (&session_rdp->stream_table, g_hash_table_unref);
|
|
g_clear_pointer (&session_rdp->pressed_unicode_keys, g_hash_table_unref);
|
|
g_clear_pointer (&session_rdp->pressed_keys, g_hash_table_unref);
|
|
|
|
g_clear_pointer (&session_rdp->stop_event, CloseHandle);
|
|
|
|
G_OBJECT_CLASS (grd_session_rdp_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_finalize (GObject *object)
|
|
{
|
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (object);
|
|
|
|
g_mutex_clear (&session_rdp->notify_post_connected_mutex);
|
|
g_mutex_clear (&session_rdp->close_session_mutex);
|
|
g_mutex_clear (&session_rdp->rdp_flags_mutex);
|
|
|
|
G_OBJECT_CLASS (grd_session_rdp_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_init (GrdSessionRdp *session_rdp)
|
|
{
|
|
session_rdp->stop_event = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
|
|
session_rdp->pressed_keys = g_hash_table_new (NULL, NULL);
|
|
session_rdp->pressed_unicode_keys = g_hash_table_new (NULL, NULL);
|
|
session_rdp->stream_table = g_hash_table_new (NULL, NULL);
|
|
|
|
g_mutex_init (&session_rdp->rdp_flags_mutex);
|
|
g_mutex_init (&session_rdp->close_session_mutex);
|
|
g_mutex_init (&session_rdp->notify_post_connected_mutex);
|
|
|
|
session_rdp->session_metrics = grd_rdp_session_metrics_new ();
|
|
session_rdp->rdp_event_queue = grd_rdp_event_queue_new (session_rdp);
|
|
|
|
g_signal_connect (session_rdp, "ready",
|
|
G_CALLBACK (on_remote_desktop_session_ready), NULL);
|
|
g_signal_connect (session_rdp, "started",
|
|
G_CALLBACK (on_remote_desktop_session_started), NULL);
|
|
}
|
|
|
|
static void
|
|
grd_session_rdp_class_init (GrdSessionRdpClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GrdSessionClass *session_class = GRD_SESSION_CLASS (klass);
|
|
|
|
object_class->dispose = grd_session_rdp_dispose;
|
|
object_class->finalize = grd_session_rdp_finalize;
|
|
|
|
session_class->stop = grd_session_rdp_stop;
|
|
session_class->on_stream_created = grd_session_rdp_on_stream_created;
|
|
session_class->on_caps_lock_state_changed =
|
|
grd_session_rdp_on_caps_lock_state_changed;
|
|
session_class->on_num_lock_state_changed =
|
|
grd_session_rdp_on_num_lock_state_changed;
|
|
|
|
signals[POST_CONNECTED] = g_signal_new ("post-connected",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|