GUACAMOLE-249: Remove "guacsvc" plugin in favor of leveraging common SVC implementation.

This commit is contained in:
Michael Jumper 2019-12-22 13:33:08 -08:00
parent 352b9c517c
commit 3255b182ab
6 changed files with 133 additions and 496 deletions

View File

@ -47,6 +47,7 @@ libguac_client_rdp_la_SOURCES = \
error.c \ error.c \
input.c \ input.c \
keyboard.c \ keyboard.c \
pipe-svc.c \
ptr_string.c \ ptr_string.c \
rail.c \ rail.c \
rdp.c \ rdp.c \
@ -63,7 +64,6 @@ libguac_client_rdp_la_SOURCES = \
rdpdr.c \ rdpdr.c \
rdpsnd.c \ rdpsnd.c \
resolution.c \ resolution.c \
svc.c \
unicode.c \ unicode.c \
user.c user.c
@ -90,6 +90,7 @@ noinst_HEADERS = \
error.h \ error.h \
input.h \ input.h \
keyboard.h \ keyboard.h \
pipe-svc.h \
ptr_string.h \ ptr_string.h \
rail.h \ rail.h \
rdp.h \ rdp.h \
@ -107,7 +108,6 @@ noinst_HEADERS = \
rdpdr.h \ rdpdr.h \
rdpsnd.h \ rdpsnd.h \
resolution.h \ resolution.h \
svc.h \
unicode.h \ unicode.h \
user.h user.h
@ -136,8 +136,7 @@ freerdp_LTLIBRARIES = \
libguac-common-svc-client.la \ libguac-common-svc-client.la \
libguacai-client.la \ libguacai-client.la \
libguacdr-client.la \ libguacdr-client.la \
libguacsnd-client.la \ libguacsnd-client.la
libguacsvc-client.la
freerdpdir = ${libdir}/freerdp2 freerdpdir = ${libdir}/freerdp2
@ -244,30 +243,6 @@ libguacsnd_client_la_LIBADD = \
@COMMON_LTLIB@ \ @COMMON_LTLIB@ \
@LIBGUAC_LTLIB@ @LIBGUAC_LTLIB@
#
# Static Virtual Channels
#
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 # Optional SFTP support
# #

View File

@ -1,311 +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 "svc.h"
#include <stdlib.h>
#include <string.h>
#include <freerdp/constants.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/string.h>
#include <winpr/stream.h>
/**
* 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.
*/
static void guac_rdp_svc_process_receive(guac_rdp_svc* svc,
void* data, int length) {
/* 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;
}
/* Send blob */
guac_protocol_send_blob(svc->client->socket, svc->output_pipe, data, length);
guac_socket_flush(svc->client->socket);
}
/**
* 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) {
/* Ignore all events except for received data */
if (event != CHANNEL_EVENT_DATA_RECEIVED)
return;
guac_rdp_svc* svc = (guac_rdp_svc*) user_param;
/* 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);
/* Notify of pipe's existence */
guac_rdp_svc_send_pipe(svc->client->socket, svc);
/* Log connection to static channel */
guac_client_log(svc->client, GUAC_LOG_INFO,
"Static channel \"%s\" connected.", svc->channel_def.name);
}
/**
* 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->channel_def.name);
guac_rdp_svc_remove(svc->client, svc->channel_def.name);
free(svc);
}
/**
* 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) {
guac_rdp_svc* svc = (guac_rdp_svc*) user_param;
/* 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;
}
switch (event) {
/* 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;
}

View File

@ -22,8 +22,9 @@
#include "channels.h" #include "channels.h"
#include "client.h" #include "client.h"
#include "common/list.h" #include "common/list.h"
#include "common-svc.h"
#include "pipe-svc.h"
#include "rdp.h" #include "rdp.h"
#include "svc.h"
#include <freerdp/svc.h> #include <freerdp/svc.h>
#include <guacamole/client.h> #include <guacamole/client.h>
@ -33,15 +34,15 @@
#include <stdlib.h> #include <stdlib.h>
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc) { void guac_rdp_pipe_svc_send_pipe(guac_socket* socket, guac_rdp_pipe_svc* pipe_svc) {
/* Send pipe instruction for the SVC's output stream */ /* Send pipe instruction for the SVC's output stream */
guac_protocol_send_pipe(socket, svc->output_pipe, guac_protocol_send_pipe(socket, pipe_svc->output_pipe,
"application/octet-stream", svc->channel_def.name); "application/octet-stream", pipe_svc->svc->name);
} }
void guac_rdp_svc_send_pipes(guac_user* user) { void guac_rdp_pipe_svc_send_pipes(guac_user* user) {
guac_client* client = user->client; guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@ -51,7 +52,7 @@ void guac_rdp_svc_send_pipes(guac_user* user) {
/* Send pipe for each allocated SVC's output stream */ /* Send pipe for each allocated SVC's output stream */
guac_common_list_element* current = rdp_client->available_svc->head; guac_common_list_element* current = rdp_client->available_svc->head;
while (current != NULL) { while (current != NULL) {
guac_rdp_svc_send_pipe(user->socket, (guac_rdp_svc*) current->data); guac_rdp_pipe_svc_send_pipe(user->socket, (guac_rdp_pipe_svc*) current->data);
current = current->next; current = current->next;
} }
@ -59,22 +60,22 @@ void guac_rdp_svc_send_pipes(guac_user* user) {
} }
void guac_rdp_svc_add(guac_client* client, guac_rdp_svc* svc) { void guac_rdp_pipe_svc_add(guac_client* client, guac_rdp_pipe_svc* pipe_svc) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Add to list of available SVC */ /* Add to list of available SVC */
guac_common_list_lock(rdp_client->available_svc); guac_common_list_lock(rdp_client->available_svc);
guac_common_list_add(rdp_client->available_svc, svc); guac_common_list_add(rdp_client->available_svc, pipe_svc);
guac_common_list_unlock(rdp_client->available_svc); guac_common_list_unlock(rdp_client->available_svc);
} }
guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name) { guac_rdp_pipe_svc* guac_rdp_pipe_svc_get(guac_client* client, const char* name) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_common_list_element* current; guac_common_list_element* current;
guac_rdp_svc* found = NULL; guac_rdp_pipe_svc* found = NULL;
/* For each available SVC */ /* For each available SVC */
guac_common_list_lock(rdp_client->available_svc); guac_common_list_lock(rdp_client->available_svc);
@ -82,8 +83,8 @@ guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name) {
while (current != NULL) { while (current != NULL) {
/* If name matches, found */ /* If name matches, found */
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data; guac_rdp_pipe_svc* current_svc = (guac_rdp_pipe_svc*) current->data;
if (strcmp(current_svc->channel_def.name, name) == 0) { if (strcmp(current_svc->svc->name, name) == 0) {
found = current_svc; found = current_svc;
break; break;
} }
@ -97,11 +98,11 @@ guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name) {
} }
guac_rdp_svc* guac_rdp_svc_remove(guac_client* client, const char* name) { guac_rdp_pipe_svc* guac_rdp_pipe_svc_remove(guac_client* client, const char* name) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_common_list_element* current; guac_common_list_element* current;
guac_rdp_svc* found = NULL; guac_rdp_pipe_svc* found = NULL;
/* For each available SVC */ /* For each available SVC */
guac_common_list_lock(rdp_client->available_svc); guac_common_list_lock(rdp_client->available_svc);
@ -109,8 +110,8 @@ guac_rdp_svc* guac_rdp_svc_remove(guac_client* client, const char* name) {
while (current != NULL) { while (current != NULL) {
/* If name matches, remove entry */ /* If name matches, remove entry */
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data; guac_rdp_pipe_svc* current_svc = (guac_rdp_pipe_svc*) current->data;
if (strcmp(current_svc->channel_def.name, name) == 0) { if (strcmp(current_svc->svc->name, name) == 0) {
guac_common_list_remove(rdp_client->available_svc, current); guac_common_list_remove(rdp_client->available_svc, current);
found = current_svc; found = current_svc;
break; break;
@ -126,38 +127,13 @@ guac_rdp_svc* guac_rdp_svc_remove(guac_client* client, const char* name) {
} }
void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length) { int guac_rdp_pipe_svc_pipe_handler(guac_user* user, guac_stream* stream,
/* 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) { char* mimetype, char* name) {
guac_rdp_svc* svc = guac_rdp_svc_get(user->client, name); guac_rdp_pipe_svc* pipe_svc = guac_rdp_pipe_svc_get(user->client, name);
/* Fail if no such SVC */ /* Fail if no such SVC */
if (svc == NULL) { if (pipe_svc == NULL) {
guac_user_log(user, GUAC_LOG_WARNING, "User requested non-existent " guac_user_log(user, GUAC_LOG_WARNING, "User requested non-existent "
"pipe (no such SVC configured): \"%s\"", name); "pipe (no such SVC configured): \"%s\"", name);
guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)", guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)",
@ -170,19 +146,22 @@ int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream,
"connected.", name); "connected.", name);
/* Init stream data */ /* Init stream data */
stream->data = svc; stream->data = pipe_svc;
stream->blob_handler = guac_rdp_svc_blob_handler; stream->blob_handler = guac_rdp_pipe_svc_blob_handler;
return 0; return 0;
} }
int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream, int guac_rdp_pipe_svc_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) { void* data, int length) {
guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) stream->data;
/* Write blob data to SVC directly */ /* Write blob data to SVC directly */
guac_rdp_svc* svc = (guac_rdp_svc*) stream->data; wStream* output_stream = Stream_New(NULL, length);
guac_rdp_svc_write(svc, data, length); Stream_Write(output_stream, data, length);
guac_rdp_common_svc_write(pipe_svc->svc, output_stream);
guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)", guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
GUAC_PROTOCOL_STATUS_SUCCESS); GUAC_PROTOCOL_STATUS_SUCCESS);
@ -191,37 +170,60 @@ int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream,
} }
void guac_rdp_svc_load_plugin(rdpContext* context, char* name) { void guac_rdp_pipe_svc_process_connect(guac_rdp_common_svc* svc) {
guac_client* client = ((rdp_freerdp_context*) context)->client; /* Associate SVC with new Guacamole pipe */
guac_rdp_svc* svc = calloc(1, sizeof(guac_rdp_svc)); guac_rdp_pipe_svc* pipe_svc = malloc(sizeof(guac_rdp_pipe_svc));
svc->client = client; pipe_svc->svc = svc;
pipe_svc->output_pipe = guac_client_alloc_stream(svc->client);
svc->data = pipe_svc;
/* Init FreeRDP channel definition */ /* SVC may now receive data from client */
int name_length = guac_strlcpy(svc->channel_def.name, name, GUAC_RDP_SVC_MAX_LENGTH); guac_rdp_pipe_svc_add(svc->client, pipe_svc);
svc->channel_def.options = CHANNEL_OPTION_INITIALIZED
| CHANNEL_OPTION_ENCRYPT_RDP
| CHANNEL_OPTION_COMPRESS_RDP;
/* Warn about name length */ /* Notify of pipe's existence */
if (name_length >= GUAC_RDP_SVC_MAX_LENGTH) guac_rdp_pipe_svc_send_pipe(svc->client->socket, pipe_svc);
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); void guac_rdp_pipe_svc_process_receive(guac_rdp_common_svc* svc,
wStream* input_stream) {
/* Attempt to load guacsvc plugin for new static channel */
if (guac_freerdp_channels_load_plugin(context->channels, context->settings, "guacsvc", svc)) { guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) svc->data;
guac_client_log(client, GUAC_LOG_WARNING, "Cannot create static "
"channel \"%s\": failed to load guacsvc plugin.", /* Fail if output not created */
svc->channel_def.name); if (pipe_svc->output_pipe == NULL) {
free(svc); 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 "
/* Store and log on success (SVC structure will be freed on channel termination) */ "for that SVC is not yet open. This should NOT happen.",
else Stream_Length(input_stream), svc->name);
guac_client_log(client, GUAC_LOG_INFO, "Created static channel " return;
"\"%s\"...", svc->channel_def.name); }
/* Send received data as blob */
guac_protocol_send_blob(svc->client->socket, pipe_svc->output_pipe, Stream_Buffer(input_stream), Stream_Length(input_stream));
guac_socket_flush(svc->client->socket);
}
void guac_rdp_pipe_svc_process_terminate(guac_rdp_common_svc* svc) {
guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) svc->data;
/* Remove and free SVC */
guac_rdp_pipe_svc_remove(svc->client, svc->name);
free(pipe_svc);
}
void guac_rdp_pipe_svc_load_plugin(rdpContext* context, char* name) {
/* Attempt to load support for static channel */
guac_rdp_common_svc_load_plugin(context, name, CHANNEL_OPTION_COMPRESS_RDP,
guac_rdp_pipe_svc_process_connect,
guac_rdp_pipe_svc_process_receive,
guac_rdp_pipe_svc_process_terminate);
} }

View File

@ -17,10 +17,11 @@
* under the License. * under the License.
*/ */
#ifndef GUAC_RDP_SVC_H #ifndef GUAC_RDP_PIPE_SVC_H
#define GUAC_RDP_SVC_H #define GUAC_RDP_PIPE_SVC_H
#include "config.h" #include "config.h"
#include "common-svc.h"
#include <freerdp/svc.h> #include <freerdp/svc.h>
#include <guacamole/client.h> #include <guacamole/client.h>
@ -34,15 +35,10 @@
#define GUAC_RDP_SVC_MAX_LENGTH 8 #define GUAC_RDP_SVC_MAX_LENGTH 8
/** /**
* Structure describing a static virtual channel, and the corresponding * Structure describing a static virtual channel and a corresponding Guacamole
* Guacamole pipes and FreeRDP resources. * pipe stream;
*/ */
typedef struct guac_rdp_svc { typedef struct guac_rdp_pipe_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 output pipe, opened when the RDP server receives a connection to
@ -51,46 +47,19 @@ typedef struct guac_rdp_svc {
guac_stream* output_pipe; guac_stream* output_pipe;
/** /**
* The definition of this static virtual channel, including its name. The * The underlying static channel. Data written to this SVC by the RDP
* name of the SVC is also used as the name of the associated Guacamole * server will be forwarded along the pipe stream to the Guacamole client,
* pipe streams. * and data written to the pipe stream by the Guacamole client will be
* forwarded along the SVC to the RDP server.
*/ */
CHANNEL_DEF channel_def; guac_rdp_common_svc* svc;
} guac_rdp_pipe_svc;
/** /**
* Functions and data specific to the FreeRDP side of the virtual channel * Initializes arbitrary static virtual channel (SVC) support for RDP, handling
* and plugin. * communication for the SVC having the given name. Data sent from within the
*/ * RDP session using this channel will be sent along an identically-named pipe
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 * 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 * 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 * failures occur while loading the plugin, messages noting the specifics of
@ -104,10 +73,9 @@ typedef struct guac_rdp_svc {
* The rdpContext associated with the FreeRDP side of the RDP connection. * The rdpContext associated with the FreeRDP side of the RDP connection.
* *
* @param name * @param name
* The name of the SVC which should be handled by the new instance of the * The name of the SVC which should be handled.
* plugin.
*/ */
void guac_rdp_svc_load_plugin(rdpContext* context, char* name); void guac_rdp_pipe_svc_load_plugin(rdpContext* context, char* name);
/** /**
* Sends the "pipe" instruction describing the given static virtual channel * Sends the "pipe" instruction describing the given static virtual channel
@ -121,7 +89,7 @@ void guac_rdp_svc_load_plugin(rdpContext* context, char* name);
* @param svc * @param svc
* The static virtual channel that the "pipe" instruction should describe. * The static virtual channel that the "pipe" instruction should describe.
*/ */
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc); void guac_rdp_pipe_svc_send_pipe(guac_socket* socket, guac_rdp_pipe_svc* svc);
/** /**
* Sends the "pipe" instructions describing all static virtual channels * Sends the "pipe" instructions describing all static virtual channels
@ -132,7 +100,7 @@ void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc);
* @param user * @param user
* The user to send the "pipe" instructions to. * The user to send the "pipe" instructions to.
*/ */
void guac_rdp_svc_send_pipes(guac_user* user); void guac_rdp_pipe_svc_send_pipes(guac_user* user);
/** /**
* Add the given SVC to the list of all available SVCs. This function must be * Add the given SVC to the list of all available SVCs. This function must be
@ -146,11 +114,11 @@ void guac_rdp_svc_send_pipes(guac_user* user);
* The static virtual channel to add to the list of all such channels * The static virtual channel to add to the list of all such channels
* available. * available.
*/ */
void guac_rdp_svc_add(guac_client* client, guac_rdp_svc* svc); void guac_rdp_pipe_svc_add(guac_client* client, guac_rdp_pipe_svc* svc);
/** /**
* Retrieve the SVC with the given name from the list stored in the client. The * 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(). * requested SVC must previously have been added using guac_rdp_pipe_svc_add().
* *
* @param client * @param client
* The guac_client associated with the current RDP session. * The guac_client associated with the current RDP session.
@ -162,7 +130,7 @@ void guac_rdp_svc_add(guac_client* client, guac_rdp_svc* svc);
* The static virtual channel with the given name, or NULL if no such * The static virtual channel with the given name, or NULL if no such
* virtual channel exists. * virtual channel exists.
*/ */
guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name); guac_rdp_pipe_svc* guac_rdp_pipe_svc_get(guac_client* client, const char* name);
/** /**
* Removes the SVC with the given name from the list stored in the client. * Removes the SVC with the given name from the list stored in the client.
@ -179,37 +147,40 @@ guac_rdp_svc* guac_rdp_svc_get(guac_client* client, const char* name);
* The static virtual channel that was removed, or NULL if no such virtual * The static virtual channel that was removed, or NULL if no such virtual
* channel exists. * channel exists.
*/ */
guac_rdp_svc* guac_rdp_svc_remove(guac_client* client, const char* name); guac_rdp_pipe_svc* guac_rdp_pipe_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 * Handler for "blob" instructions which automatically writes received data to
* the associated SVC using guac_rdp_svc_write(). * the associated SVC using guac_rdp_pipe_svc_write().
*/ */
guac_user_blob_handler guac_rdp_svc_blob_handler; guac_user_blob_handler guac_rdp_pipe_svc_blob_handler;
/** /**
* Handler for "pipe" instructions which automatically prepares received pipe * Handler for "pipe" instructions which automatically prepares received pipe
* streams to automatically write received blobs to the SVC having the same * streams to automatically write received blobs to the SVC having the same
* name as the pipe stream. Received pipe streams are associated with the * name as the pipe stream. Received pipe streams are associated with the
* relevant guac_rdp_svc instance and the SVC-specific "blob" instructino * relevant guac_rdp_pipe_svc instance and the SVC-specific "blob" instructino
* handler (guac_rdp_svc_blob_handler). * handler (guac_rdp_pipe_svc_blob_handler).
*/ */
guac_user_pipe_handler guac_rdp_svc_pipe_handler; guac_user_pipe_handler guac_rdp_pipe_svc_pipe_handler;
/**
* Handler which is invoked when an SVC associated with a Guacamole pipe stream
* is connected to the RDP server.
*/
guac_rdp_common_svc_connect_handler guac_rdp_pipe_svc_process_connect;
/**
* Handler which is invoked when an SVC associated with a Guacamole pipe stream
* received data from the RDP server.
*/
guac_rdp_common_svc_receive_handler guac_rdp_pipe_svc_process_receive;
/**
* Handler which is invoked when an SVC associated with a Guacamole pipe stream
* has disconnected and is about to be freed.
*/
guac_rdp_common_svc_terminate_handler guac_rdp_pipe_svc_process_terminate;
#endif #endif

View File

@ -29,6 +29,7 @@
#include "disp.h" #include "disp.h"
#include "error.h" #include "error.h"
#include "keyboard.h" #include "keyboard.h"
#include "pipe-svc.h"
#include "rail.h" #include "rail.h"
#include "rdp.h" #include "rdp.h"
#include "rdp_bitmap.h" #include "rdp_bitmap.h"
@ -40,7 +41,6 @@
#include "rdp_stream.h" #include "rdp_stream.h"
#include "rdpdr.h" #include "rdpdr.h"
#include "rdpsnd.h" #include "rdpsnd.h"
#include "svc.h"
#ifdef ENABLE_COMMON_SSH #ifdef ENABLE_COMMON_SSH
#include "common-ssh/sftp.h" #include "common-ssh/sftp.h"
@ -122,7 +122,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
char** current = settings->svc_names; char** current = settings->svc_names;
do { do {
guac_rdp_svc_load_plugin(context, *current); guac_rdp_pipe_svc_load_plugin(context, *current);
} while (*(++current) != NULL); } while (*(++current) != NULL);
} }

View File

@ -23,10 +23,10 @@
#include "common/display.h" #include "common/display.h"
#include "input.h" #include "input.h"
#include "user.h" #include "user.h"
#include "pipe-svc.h"
#include "rdp.h" #include "rdp.h"
#include "rdp_settings.h" #include "rdp_settings.h"
#include "rdp_stream.h" #include "rdp_stream.h"
#include "svc.h"
#ifdef ENABLE_COMMON_SSH #ifdef ENABLE_COMMON_SSH
#include "sftp.h" #include "sftp.h"
@ -86,7 +86,7 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
guac_audio_stream_add_user(rdp_client->audio, user); guac_audio_stream_add_user(rdp_client->audio, user);
/* Bring user up to date with any registered static channels */ /* Bring user up to date with any registered static channels */
guac_rdp_svc_send_pipes(user); guac_rdp_pipe_svc_send_pipes(user);
/* Synchronize with current display */ /* Synchronize with current display */
guac_common_display_dup(rdp_client->display, user, user->socket); guac_common_display_dup(rdp_client->display, user, user->socket);
@ -109,7 +109,7 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
user->file_handler = guac_rdp_user_file_handler; user->file_handler = guac_rdp_user_file_handler;
/* Inbound arbitrary named pipes */ /* Inbound arbitrary named pipes */
user->pipe_handler = guac_rdp_svc_pipe_handler; user->pipe_handler = guac_rdp_pipe_svc_pipe_handler;
} }