GUACAMOLE-249: Migrate SVC support to FreeRDP 2.0.0 plugin API.
This commit is contained in:
parent
fa0ad267b8
commit
233c0555c3
@ -60,6 +60,7 @@ libguac_client_rdp_la_SOURCES = \
|
||||
rdp_settings.c \
|
||||
rdp_stream.c \
|
||||
resolution.c \
|
||||
svc.c \
|
||||
unicode.c \
|
||||
user.c
|
||||
|
||||
@ -76,7 +77,6 @@ noinst_HEADERS = \
|
||||
guac_rdpdr/rdpdr_service.h \
|
||||
guac_rdpsnd/rdpsnd_messages.h \
|
||||
guac_rdpsnd/rdpsnd_service.h \
|
||||
guac_svc/svc_service.h \
|
||||
audio_input.h \
|
||||
client.h \
|
||||
clipboard.h \
|
||||
@ -101,6 +101,7 @@ noinst_HEADERS = \
|
||||
rdp_status.h \
|
||||
rdp_stream.h \
|
||||
resolution.h \
|
||||
svc.h \
|
||||
unicode.h \
|
||||
user.h
|
||||
|
||||
@ -125,11 +126,11 @@ libguac_client_rdp_la_LIBADD = \
|
||||
# Plugins for FreeRDP
|
||||
#
|
||||
|
||||
freerdp_LTLIBRARIES = \
|
||||
libguacai-client.la
|
||||
# libguacdr-client.la \
|
||||
# libguacsnd-client.la \
|
||||
# libguacsvc-client.la
|
||||
freerdp_LTLIBRARIES = \
|
||||
libguacai-client.la \
|
||||
libguacsvc-client.la
|
||||
# libguacdr-client.la
|
||||
# libguacsnd-client.la
|
||||
|
||||
freerdpdir = ${libdir}/freerdp2
|
||||
|
||||
@ -221,25 +222,25 @@ libguacai_client_la_LIBADD = \
|
||||
# Static Virtual Channels
|
||||
#
|
||||
|
||||
#libguacsvc_client_la_SOURCES = \
|
||||
# guac_svc/svc_service.c \
|
||||
# rdp_svc.c
|
||||
#
|
||||
#libguacsvc_client_la_CFLAGS = \
|
||||
# -Werror -Wall -Iinclude \
|
||||
# @COMMON_INCLUDE@ \
|
||||
# @COMMON_SSH_INCLUDE@ \
|
||||
# @LIBGUAC_INCLUDE@ \
|
||||
# @RDP_CFLAGS@
|
||||
#
|
||||
#libguacsvc_client_la_LDFLAGS = \
|
||||
# -module -avoid-version -shared \
|
||||
# @PTHREAD_LIBS@ \
|
||||
# @RDP_LIBS@
|
||||
#
|
||||
#libguacsvc_client_la_LIBADD = \
|
||||
# @COMMON_LTLIB@ \
|
||||
# @LIBGUAC_LTLIB@
|
||||
libguacsvc_client_la_SOURCES = \
|
||||
guac_svc/svc_service.c \
|
||||
svc.c
|
||||
|
||||
libguacsvc_client_la_CFLAGS = \
|
||||
-Werror -Wall -Iinclude \
|
||||
@COMMON_INCLUDE@ \
|
||||
@COMMON_SSH_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@ \
|
||||
@RDP_CFLAGS@
|
||||
|
||||
libguacsvc_client_la_LDFLAGS = \
|
||||
-module -avoid-version -shared \
|
||||
@PTHREAD_LIBS@ \
|
||||
@RDP_LIBS@
|
||||
|
||||
libguacsvc_client_la_LIBADD = \
|
||||
@COMMON_LTLIB@ \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
#
|
||||
# Optional SFTP support
|
||||
|
@ -19,13 +19,12 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "svc_service.h"
|
||||
#include "svc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
@ -33,60 +32,142 @@
|
||||
#include <winpr/stream.h>
|
||||
|
||||
/**
|
||||
* Entry point for arbitrary SVC.
|
||||
* Processes data received along an SVC via a CHANNEL_EVENT_DATA_RECEIVED
|
||||
* event, forwarding the data along an established, outbound pipe stream to the
|
||||
* Guacamole client.
|
||||
*
|
||||
* @param svc
|
||||
* The guac_rdp_svc structure representing the SVC that received the data.
|
||||
*
|
||||
* @param data
|
||||
* The data that was received.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes received.
|
||||
*/
|
||||
int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) {
|
||||
static void guac_rdp_svc_process_receive(guac_rdp_svc* svc,
|
||||
void* data, int length) {
|
||||
|
||||
/* Gain access to plugin data */
|
||||
CHANNEL_ENTRY_POINTS_FREERDP* entry_points_ex =
|
||||
(CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
|
||||
/* Fail if output not created */
|
||||
if (svc->output_pipe == NULL) {
|
||||
guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
|
||||
"received from within the remote desktop session for SVC "
|
||||
"\"%s\" are being dropped because the outbound pipe stream "
|
||||
"for that SVC is not yet open. This should NOT happen.",
|
||||
length, svc->channel_def.name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate plugin */
|
||||
guac_svcPlugin* svc_plugin =
|
||||
(guac_svcPlugin*) calloc(1, sizeof(guac_svcPlugin));
|
||||
|
||||
/* Get SVC descriptor from plugin parameters */
|
||||
guac_rdp_svc* svc = (guac_rdp_svc*) entry_points_ex->pExtendedData;
|
||||
|
||||
/* Init channel def */
|
||||
guac_strlcpy(svc_plugin->plugin.channel_def.name, svc->name,
|
||||
GUAC_RDP_SVC_MAX_LENGTH);
|
||||
svc_plugin->plugin.channel_def.options =
|
||||
CHANNEL_OPTION_INITIALIZED
|
||||
| CHANNEL_OPTION_ENCRYPT_RDP
|
||||
| CHANNEL_OPTION_COMPRESS_RDP;
|
||||
|
||||
/* Init plugin */
|
||||
svc_plugin->svc = svc;
|
||||
|
||||
/* Set callbacks */
|
||||
svc_plugin->plugin.connect_callback = guac_svc_process_connect;
|
||||
svc_plugin->plugin.receive_callback = guac_svc_process_receive;
|
||||
svc_plugin->plugin.event_callback = guac_svc_process_event;
|
||||
svc_plugin->plugin.terminate_callback = guac_svc_process_terminate;
|
||||
|
||||
/* Store plugin reference in SVC */
|
||||
svc->plugin = (rdpSvcPlugin*) svc_plugin;
|
||||
|
||||
/* Finish init */
|
||||
svc_plugin_init((rdpSvcPlugin*) svc_plugin, pEntryPoints);
|
||||
return 1;
|
||||
/* Send blob */
|
||||
guac_protocol_send_blob(svc->client->socket, svc->output_pipe, data, length);
|
||||
guac_socket_flush(svc->client->socket);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Service Handlers
|
||||
/**
|
||||
* Event handler for events which deal with data transmitted over an open SVC.
|
||||
* This specific implementation of the event handler currently handles only the
|
||||
* CHANNEL_EVENT_DATA_RECEIVED event, delegating actual handling of that event
|
||||
* to guac_rdp_svc_process_receive().
|
||||
*
|
||||
* The FreeRDP requirements for this function follow those of the
|
||||
* VirtualChannelOpenEventEx callback defined within Microsoft's RDP API:
|
||||
*
|
||||
* https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa514754%28v%3dmsdn.10%29
|
||||
*
|
||||
* @param user_param
|
||||
* The pointer to arbitrary data originally passed via the first parameter
|
||||
* of the pVirtualChannelInitEx() function call when the associated channel
|
||||
* was initialized. The pVirtualChannelInitEx() function is exposed within
|
||||
* the channel entry points structure.
|
||||
*
|
||||
* @param open_handle
|
||||
* The handle which identifies the channel itself, typically referred to
|
||||
* within the FreeRDP source as OpenHandle.
|
||||
*
|
||||
* @param event
|
||||
* An integer representing the event that should be handled. This will be
|
||||
* either CHANNEL_EVENT_DATA_RECEIVED, CHANNEL_EVENT_WRITE_CANCELLED, or
|
||||
* CHANNEL_EVENT_WRITE_COMPLETE.
|
||||
*
|
||||
* @param data
|
||||
* The data received, for CHANNEL_EVENT_DATA_RECEIVED events, and the value
|
||||
* passed as user data to pVirtualChannelWriteEx() for
|
||||
* CHANNEL_EVENT_WRITE_* events (note that user data for
|
||||
* pVirtualChannelWriteEx() as implemented by FreeRDP MUST either be NULL
|
||||
* or a wStream containing the data written).
|
||||
*
|
||||
* @param data_length
|
||||
* The number of bytes of event-specific data.
|
||||
*
|
||||
* @param total_length
|
||||
* The total number of bytes written to the RDP server in a single write
|
||||
* operation.
|
||||
*
|
||||
* NOTE: The meaning of total_length is unclear. The above description was
|
||||
* written mainly through referencing the documentation in MSDN. Real-world
|
||||
* use will need to be consulted, likely within the FreeRDP source, before
|
||||
* this value can be reliably used. The current implementation of this
|
||||
* handler ignores this parameter.
|
||||
*
|
||||
* @param data_flags
|
||||
* The result of a bitwise OR of the CHANNEL_FLAG_* flags which apply to
|
||||
* the data received. This value is relevant only to
|
||||
* CHANNEL_EVENT_DATA_RECEIVED events. Valid flags are CHANNEL_FLAG_FIRST,
|
||||
* CHANNEL_FLAG_LAST, and CHANNEL_FLAG_ONLY. The flag CHANNEL_FLAG_MIDDLE
|
||||
* is not itself a flag, but the absence of both CHANNEL_FLAG_FIRST and
|
||||
* CHANNEL_FLAG_LAST.
|
||||
*/
|
||||
static VOID guac_rdp_svc_handle_open_event(LPVOID user_param,
|
||||
DWORD open_handle, UINT event, LPVOID data, UINT32 data_length,
|
||||
UINT32 total_length, UINT32 data_flags) {
|
||||
|
||||
void guac_svc_process_connect(rdpSvcPlugin* plugin) {
|
||||
/* Ignore all events except for received data */
|
||||
if (event != CHANNEL_EVENT_DATA_RECEIVED)
|
||||
return;
|
||||
|
||||
/* Get corresponding guac_rdp_svc */
|
||||
guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
|
||||
guac_rdp_svc* svc = svc_plugin->svc;
|
||||
guac_rdp_svc* svc = (guac_rdp_svc*) user_param;
|
||||
|
||||
/* NULL out pExtendedData so we don't lose our guac_rdp_svc due to an
|
||||
* automatic free() within libfreerdp */
|
||||
plugin->channel_entry_points.pExtendedData = NULL;
|
||||
/* Validate relevant handle matches that of SVC */
|
||||
if (open_handle != svc->open_handle) {
|
||||
guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
|
||||
"received from within the remote desktop session for SVC "
|
||||
"\"%s\" are being dropped because the relevant open handle "
|
||||
"(0x%X) does not match the open handle of the SVC (0x%X).",
|
||||
data_length, svc->channel_def.name, open_handle,
|
||||
svc->open_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
guac_rdp_svc_process_receive(svc, data, data_length);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a CHANNEL_EVENT_CONNECTED event, completing the
|
||||
* connection/initialization process of the SVC.
|
||||
*
|
||||
* @param svc
|
||||
* The guac_rdp_svc structure representing the SVC that is now connected.
|
||||
*/
|
||||
static void guac_rdp_svc_process_connect(guac_rdp_svc* svc) {
|
||||
|
||||
/* Open FreeRDP side of connected channel */
|
||||
UINT32 open_status =
|
||||
svc->entry_points.pVirtualChannelOpenEx(svc->init_handle,
|
||||
&svc->open_handle, svc->channel_def.name,
|
||||
guac_rdp_svc_handle_open_event);
|
||||
|
||||
/* Warn if the channel cannot be opened after all */
|
||||
if (open_status != CHANNEL_RC_OK) {
|
||||
guac_client_log(svc->client, GUAC_LOG_WARNING, "SVC \"%s\" could not "
|
||||
"be opened: %s (error %i)", svc->channel_def.name,
|
||||
WTSErrorToString(open_status), open_status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* SVC may now receive data from client */
|
||||
guac_rdp_svc_add(svc->client, svc);
|
||||
|
||||
/* Create pipe */
|
||||
svc->output_pipe = guac_client_alloc_stream(svc->client);
|
||||
@ -96,50 +177,135 @@ void guac_svc_process_connect(rdpSvcPlugin* plugin) {
|
||||
|
||||
/* Log connection to static channel */
|
||||
guac_client_log(svc->client, GUAC_LOG_INFO,
|
||||
"Static channel \"%s\" connected.", svc->name);
|
||||
"Static channel \"%s\" connected.", svc->channel_def.name);
|
||||
|
||||
}
|
||||
|
||||
void guac_svc_process_terminate(rdpSvcPlugin* plugin) {
|
||||
|
||||
/* Get corresponding guac_rdp_svc */
|
||||
guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
|
||||
guac_rdp_svc* svc = svc_plugin->svc;
|
||||
/**
|
||||
* Processes a CHANNEL_EVENT_TERMINATED event, freeing all resources associated
|
||||
* with the SVC.
|
||||
*
|
||||
* @param svc
|
||||
* The guac_rdp_svc structure representing the SVC that has been
|
||||
* terminated.
|
||||
*/
|
||||
static void guac_rdp_svc_process_terminate(guac_rdp_svc* svc) {
|
||||
|
||||
/* Remove and free SVC */
|
||||
guac_client_log(svc->client, GUAC_LOG_INFO, "Closing channel \"%s\"...", svc->name);
|
||||
guac_rdp_remove_svc(svc->client, svc->name);
|
||||
guac_client_log(svc->client, GUAC_LOG_INFO, "Closing channel \"%s\"...", svc->channel_def.name);
|
||||
guac_rdp_svc_remove(svc->client, svc->channel_def.name);
|
||||
free(svc);
|
||||
|
||||
free(plugin);
|
||||
|
||||
}
|
||||
|
||||
void guac_svc_process_event(rdpSvcPlugin* plugin, wMessage* event) {
|
||||
freerdp_event_free(event);
|
||||
}
|
||||
/**
|
||||
* Event handler for events which deal with the overall lifecycle of an SVC.
|
||||
* This specific implementation of the event handler currently handles only
|
||||
* CHANNEL_EVENT_CONNECTED and CHANNEL_EVENT_TERMINATED events, delegating
|
||||
* actual handling of those events to guac_rdp_svc_process_connect() and
|
||||
* guac_rdp_svc_process_terminate() respectively.
|
||||
*
|
||||
* The FreeRDP requirements for this function follow those of the
|
||||
* VirtualChannelInitEventEx callback defined within Microsoft's RDP API:
|
||||
*
|
||||
* https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa514727%28v%3dmsdn.10%29
|
||||
*
|
||||
* @param user_param
|
||||
* The pointer to arbitrary data originally passed via the first parameter
|
||||
* of the pVirtualChannelInitEx() function call when the associated channel
|
||||
* was initialized. The pVirtualChannelInitEx() function is exposed within
|
||||
* the channel entry points structure.
|
||||
*
|
||||
* @param init_handle
|
||||
* The handle which identifies the client connection, typically referred to
|
||||
* within the FreeRDP source as pInitHandle.
|
||||
*
|
||||
* @param event
|
||||
* An integer representing the event that should be handled. This will be
|
||||
* either CHANNEL_EVENT_CONNECTED, CHANNEL_EVENT_DISCONNECTED,
|
||||
* CHANNEL_EVENT_INITIALIZED, CHANNEL_EVENT_TERMINATED, or
|
||||
* CHANNEL_EVENT_V1_CONNECTED.
|
||||
*
|
||||
* @param data
|
||||
* NULL in all cases except the CHANNEL_EVENT_CONNECTED event, in which
|
||||
* case this is a null-terminated string containing the name of the server.
|
||||
*
|
||||
* @param data_length
|
||||
* The number of bytes of data, if any.
|
||||
*/
|
||||
static VOID guac_rdp_svc_handle_init_event(LPVOID user_param,
|
||||
LPVOID init_handle, UINT event, LPVOID data, UINT data_length) {
|
||||
|
||||
void guac_svc_process_receive(rdpSvcPlugin* plugin,
|
||||
wStream* input_stream) {
|
||||
guac_rdp_svc* svc = (guac_rdp_svc*) user_param;
|
||||
|
||||
/* Get corresponding guac_rdp_svc */
|
||||
guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
|
||||
guac_rdp_svc* svc = svc_plugin->svc;
|
||||
|
||||
/* Fail if output not created */
|
||||
if (svc->output_pipe == NULL) {
|
||||
guac_client_log(svc->client, GUAC_LOG_ERROR,
|
||||
"Output for channel \"%s\" dropped.",
|
||||
svc->name);
|
||||
/* Validate relevant handle matches that of SVC */
|
||||
if (init_handle != svc->init_handle) {
|
||||
guac_client_log(svc->client, GUAC_LOG_WARNING, "An init event (#%i) "
|
||||
"for SVC \"%s\" has been dropped because the relevant init "
|
||||
"handle (0x%X) does not match the init handle of the SVC "
|
||||
"(0x%X).", event, svc->channel_def.name, init_handle,
|
||||
svc->init_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send blob */
|
||||
guac_protocol_send_blob(svc->client->socket, svc->output_pipe,
|
||||
Stream_Buffer(input_stream),
|
||||
Stream_Length(input_stream));
|
||||
switch (event) {
|
||||
|
||||
guac_socket_flush(svc->client->socket);
|
||||
/* The remote desktop side of the SVC has been connected */
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
guac_rdp_svc_process_connect(svc);
|
||||
break;
|
||||
|
||||
/* The channel has disconnected and now must be cleaned up */
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
guac_rdp_svc_process_terminate(svc);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for FreeRDP plugins. This function is automatically invoked when
|
||||
* the plugin is loaded.
|
||||
*
|
||||
* @param entry_points
|
||||
* Functions and data specific to the FreeRDP side of the virtual channel
|
||||
* and plugin. This structure must be copied within implementation-specific
|
||||
* storage such that the functions it references can be invoked when
|
||||
* needed.
|
||||
*
|
||||
* @param init_handle
|
||||
* The handle which identifies the client connection, typically referred to
|
||||
* within the FreeRDP source as pInitHandle. This handle is also provided
|
||||
* to the channel init event handler. The handle must eventually be used
|
||||
* within the channel open event handler to obtain a handle to the channel
|
||||
* itself.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the plugin has initialized successfully, FALSE otherwise.
|
||||
*/
|
||||
BOOL VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS entry_points,
|
||||
PVOID init_handle) {
|
||||
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* entry_points_ex =
|
||||
(CHANNEL_ENTRY_POINTS_FREERDP_EX*) entry_points;
|
||||
|
||||
/* Get structure representing the Guacamole side of the SVC from plugin
|
||||
* parameters */
|
||||
guac_rdp_svc* svc = (guac_rdp_svc*) entry_points_ex->pExtendedData;
|
||||
|
||||
/* Copy FreeRDP data into SVC structure for future reference */
|
||||
svc->entry_points = *entry_points_ex;
|
||||
svc->init_handle = init_handle;
|
||||
|
||||
/* Complete initialization */
|
||||
if (svc->entry_points.pVirtualChannelInitEx(svc, svc, init_handle,
|
||||
&svc->channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
|
||||
guac_rdp_svc_handle_init_event) != CHANNEL_RC_OK) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
#ifndef __GUAC_SVC_SERVICE_H
|
||||
#define __GUAC_SVC_SERVICE_H
|
||||
|
||||
#include "config.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
/**
|
||||
* Structure representing the current state of an arbitrary static virtual
|
||||
* channel.
|
||||
*/
|
||||
typedef struct guac_svcPlugin {
|
||||
|
||||
/**
|
||||
* The FreeRDP parts of this plugin. This absolutely MUST be first.
|
||||
* FreeRDP depends on accessing this structure as if it were an instance
|
||||
* of rdpSvcPlugin.
|
||||
*/
|
||||
rdpSvcPlugin plugin;
|
||||
|
||||
/**
|
||||
* The Guacamole-specific SVC structure describing the channel this
|
||||
* instance represents.
|
||||
*/
|
||||
guac_rdp_svc* svc;
|
||||
|
||||
} guac_svcPlugin;
|
||||
|
||||
/**
|
||||
* Handler called when this plugin is loaded by FreeRDP.
|
||||
*/
|
||||
void guac_svc_process_connect(rdpSvcPlugin* plugin);
|
||||
|
||||
/**
|
||||
* Handler called when this plugin receives data along its designated channel.
|
||||
*/
|
||||
void guac_svc_process_receive(rdpSvcPlugin* plugin,
|
||||
wStream* input_stream);
|
||||
|
||||
/**
|
||||
* Handler called when this plugin is being unloaded.
|
||||
*/
|
||||
void guac_svc_process_terminate(rdpSvcPlugin* plugin);
|
||||
|
||||
/**
|
||||
* Handler called when this plugin receives an event.
|
||||
*/
|
||||
void guac_svc_process_event(rdpSvcPlugin* plugin, wMessage* event);
|
||||
|
||||
#endif
|
||||
|
@ -38,9 +38,7 @@
|
||||
#include "rdp_glyph.h"
|
||||
#include "rdp_pointer.h"
|
||||
#include "rdp_stream.h"
|
||||
#if 0
|
||||
#include "rdp_svc.h"
|
||||
#endif
|
||||
#include "svc.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "common-ssh/sftp.h"
|
||||
@ -131,35 +129,15 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
if (settings->remote_app != NULL)
|
||||
guac_rdp_rail_load_plugin(context);
|
||||
|
||||
#if 0
|
||||
/* Load SVC plugin instances for all static channels */
|
||||
if (settings->svc_names != NULL) {
|
||||
|
||||
char** current = settings->svc_names;
|
||||
do {
|
||||
|
||||
guac_rdp_svc* svc = guac_rdp_alloc_svc(client, *current);
|
||||
|
||||
/* Attempt to load guacsvc plugin for new static channel */
|
||||
if (guac_freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacsvc", svc)) {
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Cannot create static channel \"%s\": failed to load guacsvc plugin.",
|
||||
svc->name);
|
||||
guac_rdp_free_svc(svc);
|
||||
}
|
||||
|
||||
/* Store and log on success */
|
||||
else {
|
||||
guac_rdp_add_svc(client, svc);
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Created static channel \"%s\"...",
|
||||
svc->name);
|
||||
}
|
||||
|
||||
guac_rdp_svc_load_plugin(context, *current);
|
||||
} while (*(++current) != NULL);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load plugin providing Dynamic Virtual Channel support, if required */
|
||||
if (instance->settings->SupportDynamicChannels &&
|
||||
|
@ -22,9 +22,6 @@
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_fs.h"
|
||||
#if 0
|
||||
#include "rdp_svc.h"
|
||||
#endif
|
||||
#include "rdp_stream.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
@ -119,39 +116,6 @@ int guac_rdp_upload_file_handler(guac_user* user, guac_stream* stream,
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* name) {
|
||||
|
||||
#if 0
|
||||
guac_rdp_stream* rdp_stream;
|
||||
guac_rdp_svc* svc = guac_rdp_get_svc(user->client, name);
|
||||
|
||||
/* Fail if no such SVC */
|
||||
if (svc == NULL) {
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Requested non-existent pipe: \"%s\".",
|
||||
name);
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Inbound half of channel \"%s\" connected.",
|
||||
name);
|
||||
|
||||
/* Init stream data */
|
||||
stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
|
||||
stream->blob_handler = guac_rdp_svc_blob_handler;
|
||||
rdp_stream->type = GUAC_RDP_INBOUND_SVC_STREAM;
|
||||
rdp_stream->svc = svc;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
@ -201,23 +165,6 @@ int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
#if 0
|
||||
guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
|
||||
|
||||
/* Write blob data to SVC directly */
|
||||
guac_rdp_svc_write(rdp_stream->svc, data, length);
|
||||
#endif
|
||||
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
|
@ -23,9 +23,6 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "common/json.h"
|
||||
#if 0
|
||||
#include "rdp_svc.h"
|
||||
#endif
|
||||
|
||||
#include <guacamole/user.h>
|
||||
#include <guacamole/protocol.h>
|
||||
@ -113,12 +110,7 @@ typedef enum guac_rdp_stream_type {
|
||||
/**
|
||||
* An in-progress stream of a directory listing.
|
||||
*/
|
||||
GUAC_RDP_LS_STREAM,
|
||||
|
||||
/**
|
||||
* The inbound half of a static virtual channel.
|
||||
*/
|
||||
GUAC_RDP_INBOUND_SVC_STREAM
|
||||
GUAC_RDP_LS_STREAM
|
||||
|
||||
} guac_rdp_stream_type;
|
||||
|
||||
@ -147,13 +139,6 @@ typedef struct guac_rdp_stream {
|
||||
*/
|
||||
guac_rdp_ls_status ls_status;
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Associated SVC instance. Only valid for GUAC_RDP_INBOUND_SVC_STREAM.
|
||||
*/
|
||||
guac_rdp_svc* svc;
|
||||
#endif
|
||||
|
||||
} guac_rdp_stream;
|
||||
|
||||
/**
|
||||
@ -161,21 +146,11 @@ typedef struct guac_rdp_stream {
|
||||
*/
|
||||
guac_user_file_handler guac_rdp_upload_file_handler;
|
||||
|
||||
/**
|
||||
* Handler for inbound pipes related to static virtual channels.
|
||||
*/
|
||||
guac_user_pipe_handler guac_rdp_svc_pipe_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream data related to file uploads.
|
||||
*/
|
||||
guac_user_blob_handler guac_rdp_upload_blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream data related to static virtual channels.
|
||||
*/
|
||||
guac_user_blob_handler guac_rdp_svc_blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for end-of-stream related to file uploads.
|
||||
*/
|
||||
|
@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "client.h"
|
||||
#include "common/list.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/string.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name) {
|
||||
|
||||
guac_rdp_svc* svc = malloc(sizeof(guac_rdp_svc));
|
||||
|
||||
/* Init SVC */
|
||||
svc->client = client;
|
||||
svc->plugin = NULL;
|
||||
svc->output_pipe = NULL;
|
||||
|
||||
/* Init name */
|
||||
int name_length = guac_strlcpy(svc->name, name, GUAC_RDP_SVC_MAX_LENGTH);
|
||||
|
||||
/* Warn about name length */
|
||||
if (name_length >= GUAC_RDP_SVC_MAX_LENGTH)
|
||||
guac_client_log(client, GUAC_LOG_INFO,
|
||||
"Static channel name \"%s\" exceeds maximum of %i characters "
|
||||
"and will be truncated", name, GUAC_RDP_SVC_MAX_LENGTH - 1);
|
||||
|
||||
return svc;
|
||||
}
|
||||
|
||||
void guac_rdp_free_svc(guac_rdp_svc* svc) {
|
||||
free(svc);
|
||||
}
|
||||
|
||||
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc) {
|
||||
|
||||
/* Send pipe instruction for the SVC's output stream */
|
||||
guac_protocol_send_pipe(socket, svc->output_pipe,
|
||||
"application/octet-stream", svc->name);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_send_pipes(guac_user* user) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
|
||||
/* Send pipe for each allocated SVC's output stream */
|
||||
guac_common_list_element* current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
guac_rdp_svc_send_pipe(user->socket, (guac_rdp_svc*) current->data);
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Add to list of available SVC */
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
guac_common_list_add(rdp_client->available_svc, svc);
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
}
|
||||
|
||||
guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_list_element* current;
|
||||
guac_rdp_svc* found = NULL;
|
||||
|
||||
/* For each available SVC */
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
|
||||
/* If name matches, found */
|
||||
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
|
||||
if (strcmp(current_svc->name, name) == 0) {
|
||||
found = current_svc;
|
||||
break;
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
|
||||
}
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_list_element* current;
|
||||
guac_rdp_svc* found = NULL;
|
||||
|
||||
/* For each available SVC */
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
|
||||
/* If name matches, remove entry */
|
||||
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
|
||||
if (strcmp(current_svc->name, name) == 0) {
|
||||
guac_common_list_remove(rdp_client->available_svc, current);
|
||||
found = current_svc;
|
||||
break;
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
|
||||
}
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
/* Return removed entry, if any */
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length) {
|
||||
|
||||
wStream* output_stream;
|
||||
|
||||
/* Do not write of plugin not associated */
|
||||
if (svc->plugin == NULL) {
|
||||
guac_client_log(svc->client, GUAC_LOG_ERROR,
|
||||
"Channel \"%s\" output dropped.",
|
||||
svc->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build packet */
|
||||
output_stream = Stream_New(NULL, length);
|
||||
Stream_Write(output_stream, data, length);
|
||||
|
||||
/* Send packet */
|
||||
svc_plugin_send(svc->plugin, output_stream);
|
||||
|
||||
}
|
||||
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
#ifndef __GUAC_RDP_RDP_SVC_H
|
||||
#define __GUAC_RDP_RDP_SVC_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
|
||||
/**
|
||||
* The maximum number of bytes to allow within each channel name, including
|
||||
* null terminator.
|
||||
*/
|
||||
#define GUAC_RDP_SVC_MAX_LENGTH 8
|
||||
|
||||
/**
|
||||
* Structure describing a static virtual channel, and the corresponding
|
||||
* Guacamole pipes.
|
||||
*/
|
||||
typedef struct guac_rdp_svc {
|
||||
|
||||
/**
|
||||
* Reference to the client owning this static channel.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* Reference to associated SVC plugin.
|
||||
*/
|
||||
rdpSvcPlugin* plugin;
|
||||
|
||||
/**
|
||||
* The name of the RDP channel in use, and the name to use for each pipe.
|
||||
*/
|
||||
char name[GUAC_RDP_SVC_MAX_LENGTH];
|
||||
|
||||
/**
|
||||
* The output pipe, opened when the RDP server receives a connection to
|
||||
* the static channel.
|
||||
*/
|
||||
guac_stream* output_pipe;
|
||||
|
||||
} guac_rdp_svc;
|
||||
|
||||
/**
|
||||
* Allocate a new SVC with the given name.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the virtual channel to allocate.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated static virtual channel.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name);
|
||||
|
||||
/**
|
||||
* Free the given SVC.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to free.
|
||||
*/
|
||||
void guac_rdp_free_svc(guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Sends the "pipe" instruction describing the given static virtual channel
|
||||
* along the given socket. This pipe instruction will relate the SVC's
|
||||
* underlying output stream with the SVC's name and the mimetype
|
||||
* "application/octet-stream".
|
||||
*
|
||||
* @param socket
|
||||
* The socket along which the "pipe" instruction should be sent.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel that the "pipe" instruction should describe.
|
||||
*/
|
||||
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Sends the "pipe" instructions describing all static virtual channels
|
||||
* available to the given user along that user's socket. Each pipe instruction
|
||||
* will relate the associated SVC's underlying output stream with the SVC's
|
||||
* name and the mimetype "application/octet-stream".
|
||||
*
|
||||
* @param user
|
||||
* The user to send the "pipe" instructions to.
|
||||
*/
|
||||
void guac_rdp_svc_send_pipes(guac_user* user);
|
||||
|
||||
/**
|
||||
* Add the given SVC to the list of all available SVCs.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to add to the list of all such channels
|
||||
* available.
|
||||
*/
|
||||
void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Retrieve the SVC with the given name from the list stored in the client.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the static virtual channel to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The static virtual channel with the given name, or NULL if no such
|
||||
* virtual channel exists.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name);
|
||||
|
||||
/**
|
||||
* Remove the SVC with the given name from the list stored in the client.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the static virtual channel to remove.
|
||||
*
|
||||
* @return
|
||||
* The static virtual channel that was removed, or NULL if no such virtual
|
||||
* channel exists.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name);
|
||||
|
||||
/**
|
||||
* Write the given blob of data to the virtual channel.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to write data to.
|
||||
*
|
||||
* @param data
|
||||
* The data to write.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes to write.
|
||||
*/
|
||||
void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length);
|
||||
|
||||
#endif
|
||||
|
227
src/protocols/rdp/svc.c
Normal file
227
src/protocols/rdp/svc.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "channels.h"
|
||||
#include "client.h"
|
||||
#include "common/list.h"
|
||||
#include "rdp.h"
|
||||
#include "svc.h"
|
||||
|
||||
#include <freerdp/svc.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/string.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc) {
|
||||
|
||||
/* Send pipe instruction for the SVC's output stream */
|
||||
guac_protocol_send_pipe(socket, svc->output_pipe,
|
||||
"application/octet-stream", svc->channel_def.name);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_send_pipes(guac_user* user) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
|
||||
/* Send pipe for each allocated SVC's output stream */
|
||||
guac_common_list_element* current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
guac_rdp_svc_send_pipe(user->socket, (guac_rdp_svc*) current->data);
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_add(guac_client* client, guac_rdp_svc* svc) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Add to list of available SVC */
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
guac_common_list_add(rdp_client->available_svc, svc);
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
}
|
||||
|
||||
guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_list_element* current;
|
||||
guac_rdp_svc* found = NULL;
|
||||
|
||||
/* For each available SVC */
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
|
||||
/* If name matches, found */
|
||||
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
|
||||
if (strcmp(current_svc->channel_def.name, name) == 0) {
|
||||
found = current_svc;
|
||||
break;
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
|
||||
}
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
guac_rdp_svc* guac_rdp_svc_remove(guac_client* client, const char* name) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_list_element* current;
|
||||
guac_rdp_svc* found = NULL;
|
||||
|
||||
/* For each available SVC */
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
|
||||
/* If name matches, remove entry */
|
||||
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
|
||||
if (strcmp(current_svc->channel_def.name, name) == 0) {
|
||||
guac_common_list_remove(rdp_client->available_svc, current);
|
||||
found = current_svc;
|
||||
break;
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
|
||||
}
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
/* Return removed entry, if any */
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length) {
|
||||
|
||||
/* Do not write of plugin not associated */
|
||||
if (!svc->open_handle) {
|
||||
guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
|
||||
"received from the Guacamole client for SVC \"%s\" are being "
|
||||
"dropped because the remote desktop side of that SVC is "
|
||||
"connected.", length, svc->channel_def.name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FreeRDP_VirtualChannelWriteEx() assumes that sent data is dynamically
|
||||
* allocated and will free() the data after it is sent */
|
||||
void* data_copy = malloc(length);
|
||||
memcpy(data_copy, data, length);
|
||||
|
||||
/* Send received data */
|
||||
svc->entry_points.pVirtualChannelWriteEx(svc->init_handle,
|
||||
svc->open_handle, data_copy, length,
|
||||
NULL /* NOTE: If non-NULL, this MUST be a pointer to a wStream
|
||||
containing the supplied buffer, and that wStream will be
|
||||
automatically freed when FreeRDP handles the write */);
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* name) {
|
||||
|
||||
guac_rdp_svc* svc = guac_rdp_svc_get(user->client, name);
|
||||
|
||||
/* Fail if no such SVC */
|
||||
if (svc == NULL) {
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "User requested non-existent "
|
||||
"pipe (no such SVC configured): \"%s\"", name);
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
guac_user_log(user, GUAC_LOG_DEBUG, "Inbound half of channel \"%s\" "
|
||||
"connected.", name);
|
||||
|
||||
/* Init stream data */
|
||||
stream->data = svc;
|
||||
stream->blob_handler = guac_rdp_svc_blob_handler;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
/* Write blob data to SVC directly */
|
||||
guac_rdp_svc* svc = (guac_rdp_svc*) stream->data;
|
||||
guac_rdp_svc_write(svc, data, length);
|
||||
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_load_plugin(rdpContext* context, char* name) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_svc* svc = calloc(1, sizeof(guac_rdp_svc));
|
||||
svc->client = client;
|
||||
|
||||
/* Init FreeRDP channel definition */
|
||||
int name_length = guac_strlcpy(svc->channel_def.name, name, GUAC_RDP_SVC_MAX_LENGTH);
|
||||
svc->channel_def.options = CHANNEL_OPTION_INITIALIZED
|
||||
| CHANNEL_OPTION_ENCRYPT_RDP
|
||||
| CHANNEL_OPTION_COMPRESS_RDP;
|
||||
|
||||
/* Warn about name length */
|
||||
if (name_length >= GUAC_RDP_SVC_MAX_LENGTH)
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Static channel name \"%s\" exceeds maximum length of %i "
|
||||
"characters and will be truncated to \"%s\".",
|
||||
name, GUAC_RDP_SVC_MAX_LENGTH - 1, svc->channel_def.name);
|
||||
|
||||
/* Attempt to load guacsvc plugin for new static channel */
|
||||
if (guac_freerdp_channels_load_plugin(context->channels, context->settings, "guacsvc", svc)) {
|
||||
guac_client_log(client, GUAC_LOG_WARNING, "Cannot create static "
|
||||
"channel \"%s\": failed to load guacsvc plugin.",
|
||||
svc->channel_def.name);
|
||||
free(svc);
|
||||
}
|
||||
|
||||
/* Store and log on success (SVC structure will be freed on channel termination) */
|
||||
else
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Created static channel "
|
||||
"\"%s\"...", svc->channel_def.name);
|
||||
|
||||
}
|
||||
|
215
src/protocols/rdp/svc.h
Normal file
215
src/protocols/rdp/svc.h
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_SVC_H
|
||||
#define GUAC_RDP_SVC_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freerdp/svc.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
|
||||
/**
|
||||
* The maximum number of bytes to allow within each channel name, including
|
||||
* null terminator.
|
||||
*/
|
||||
#define GUAC_RDP_SVC_MAX_LENGTH 8
|
||||
|
||||
/**
|
||||
* Structure describing a static virtual channel, and the corresponding
|
||||
* Guacamole pipes and FreeRDP resources.
|
||||
*/
|
||||
typedef struct guac_rdp_svc {
|
||||
|
||||
/**
|
||||
* Reference to the client owning this static channel.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* The output pipe, opened when the RDP server receives a connection to
|
||||
* the static channel.
|
||||
*/
|
||||
guac_stream* output_pipe;
|
||||
|
||||
/**
|
||||
* The definition of this static virtual channel, including its name. The
|
||||
* name of the SVC is also used as the name of the associated Guacamole
|
||||
* pipe streams.
|
||||
*/
|
||||
CHANNEL_DEF channel_def;
|
||||
|
||||
/**
|
||||
* Functions and data specific to the FreeRDP side of the virtual channel
|
||||
* and plugin.
|
||||
*/
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX entry_points;
|
||||
|
||||
/**
|
||||
* Handle which identifies the client connection, typically referred to
|
||||
* within the FreeRDP source as pInitHandle. This handle is provided to the
|
||||
* channel entry point and the channel init event handler. The handle must
|
||||
* eventually be used within the channel open event handler to obtain a
|
||||
* handle to the channel itself.
|
||||
*/
|
||||
PVOID init_handle;
|
||||
|
||||
/**
|
||||
* Handle which identifies the channel itself, typically referred to within
|
||||
* the FreeRDP source as OpenHandle. This handle is obtained through a call
|
||||
* to entry_points.pVirtualChannelOpenEx() in response to receiving a
|
||||
* CHANNEL_EVENT_CONNECTED event via the init event handler.
|
||||
*
|
||||
* Data is received in CHANNEL_EVENT_DATA_RECEIVED events via the open
|
||||
* event handler, and data is written through calls to
|
||||
* entry_points.pVirtualChannelWriteEx().
|
||||
*/
|
||||
DWORD open_handle;
|
||||
|
||||
} guac_rdp_svc;
|
||||
|
||||
/**
|
||||
* Initializes arbitrary static virtual channel (SVC) support for RDP, loading
|
||||
* a new instance of Guacamole's arbitrary SVC plugin for FreeRDP ("guacsvc")
|
||||
* supporting the channel having the given name. Data sent from within the RDP
|
||||
* session using this channel will be sent along an identically-named pipe
|
||||
* stream to the Guacamole client, and data sent along a pipe stream having the
|
||||
* same name will be written to the SVC and received within the RDP session. If
|
||||
* failures occur while loading the plugin, messages noting the specifics of
|
||||
* those failures will be logged, and support for the given channel will not be
|
||||
* functional.
|
||||
*
|
||||
* This MUST be called within the PreConnect callback of the freerdp instance
|
||||
* for static virtual channel support to be loaded.
|
||||
*
|
||||
* @param rdpContext
|
||||
* The rdpContext associated with the FreeRDP side of the RDP connection.
|
||||
*
|
||||
* @param name
|
||||
* The name of the SVC which should be handled by the new instance of the
|
||||
* plugin.
|
||||
*/
|
||||
void guac_rdp_svc_load_plugin(rdpContext* context, char* name);
|
||||
|
||||
/**
|
||||
* Sends the "pipe" instruction describing the given static virtual channel
|
||||
* along the given socket. This pipe instruction will relate the SVC's
|
||||
* underlying output stream with the SVC's name and the mimetype
|
||||
* "application/octet-stream".
|
||||
*
|
||||
* @param socket
|
||||
* The socket along which the "pipe" instruction should be sent.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel that the "pipe" instruction should describe.
|
||||
*/
|
||||
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Sends the "pipe" instructions describing all static virtual channels
|
||||
* available to the given user along that user's socket. Each pipe instruction
|
||||
* will relate the associated SVC's underlying output stream with the SVC's
|
||||
* name and the mimetype "application/octet-stream".
|
||||
*
|
||||
* @param user
|
||||
* The user to send the "pipe" instructions to.
|
||||
*/
|
||||
void guac_rdp_svc_send_pipes(guac_user* user);
|
||||
|
||||
/**
|
||||
* Add the given SVC to the list of all available SVCs. This function must be
|
||||
* invoked after the SVC is connected for inbound pipe streams having that
|
||||
* SVC's name to result in received data being sent into the RDP session.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to add to the list of all such channels
|
||||
* available.
|
||||
*/
|
||||
void guac_rdp_svc_add(guac_client* client, guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Retrieve the SVC with the given name from the list stored in the client. The
|
||||
* requested SVC must previously have been added using guac_rdp_svc_add().
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the static virtual channel to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The static virtual channel with the given name, or NULL if no such
|
||||
* virtual channel exists.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name);
|
||||
|
||||
/**
|
||||
* Removes the SVC with the given name from the list stored in the client.
|
||||
* Inbound pipe streams having the given name will no longer be routed to the
|
||||
* associated SVC.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the static virtual channel to remove.
|
||||
*
|
||||
* @return
|
||||
* The static virtual channel that was removed, or NULL if no such virtual
|
||||
* channel exists.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_svc_remove(guac_client* client, const char* name);
|
||||
|
||||
/**
|
||||
* Writes the given blob of data to the virtual channel such that it can be
|
||||
* received within the RDP session.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to write data to.
|
||||
*
|
||||
* @param data
|
||||
* The data to write.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes to write.
|
||||
*/
|
||||
void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length);
|
||||
|
||||
/**
|
||||
* Handler for "blob" instructions which automatically writes received data to
|
||||
* the associated SVC using guac_rdp_svc_write().
|
||||
*/
|
||||
guac_user_blob_handler guac_rdp_svc_blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for "pipe" instructions which automatically prepares received pipe
|
||||
* streams to automatically write received blobs to the SVC having the same
|
||||
* name as the pipe stream. Received pipe streams are associated with the
|
||||
* relevant guac_rdp_svc instance and the SVC-specific "blob" instructino
|
||||
* handler (guac_rdp_svc_blob_handler).
|
||||
*/
|
||||
guac_user_pipe_handler guac_rdp_svc_pipe_handler;
|
||||
|
||||
#endif
|
||||
|
@ -26,9 +26,7 @@
|
||||
#include "rdp.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "rdp_stream.h"
|
||||
#if 0
|
||||
#include "rdp_svc.h"
|
||||
#endif
|
||||
#include "svc.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "sftp.h"
|
||||
@ -87,10 +85,8 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
|
||||
if (rdp_client->audio)
|
||||
guac_audio_stream_add_user(rdp_client->audio, user);
|
||||
|
||||
#if 0
|
||||
/* Bring user up to date with any registered static channels */
|
||||
guac_rdp_svc_send_pipes(user);
|
||||
#endif
|
||||
|
||||
/* Synchronize with current display */
|
||||
guac_common_display_dup(rdp_client->display, user, user->socket);
|
||||
@ -112,10 +108,8 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
|
||||
/* Set generic (non-filesystem) file upload handler */
|
||||
user->file_handler = guac_rdp_user_file_handler;
|
||||
|
||||
#if 0
|
||||
/* Inbound arbitrary named pipes */
|
||||
user->pipe_handler = guac_rdp_svc_pipe_handler;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user