From 3255b182ab6ca1f73a54f4eee2cccdda497d83cc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 22 Dec 2019 13:33:08 -0800 Subject: [PATCH] GUACAMOLE-249: Remove "guacsvc" plugin in favor of leveraging common SVC implementation. --- src/protocols/rdp/Makefile.am | 31 +-- src/protocols/rdp/guac_svc/svc_service.c | 311 ----------------------- src/protocols/rdp/{svc.c => pipe-svc.c} | 156 ++++++------ src/protocols/rdp/{svc.h => pipe-svc.h} | 121 ++++----- src/protocols/rdp/rdp.c | 4 +- src/protocols/rdp/user.c | 6 +- 6 files changed, 133 insertions(+), 496 deletions(-) delete mode 100644 src/protocols/rdp/guac_svc/svc_service.c rename src/protocols/rdp/{svc.c => pipe-svc.c} (51%) rename src/protocols/rdp/{svc.h => pipe-svc.h} (59%) diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 9a3a5fe2..0df45a30 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -47,6 +47,7 @@ libguac_client_rdp_la_SOURCES = \ error.c \ input.c \ keyboard.c \ + pipe-svc.c \ ptr_string.c \ rail.c \ rdp.c \ @@ -63,7 +64,6 @@ libguac_client_rdp_la_SOURCES = \ rdpdr.c \ rdpsnd.c \ resolution.c \ - svc.c \ unicode.c \ user.c @@ -90,6 +90,7 @@ noinst_HEADERS = \ error.h \ input.h \ keyboard.h \ + pipe-svc.h \ ptr_string.h \ rail.h \ rdp.h \ @@ -107,7 +108,6 @@ noinst_HEADERS = \ rdpdr.h \ rdpsnd.h \ resolution.h \ - svc.h \ unicode.h \ user.h @@ -136,8 +136,7 @@ freerdp_LTLIBRARIES = \ libguac-common-svc-client.la \ libguacai-client.la \ libguacdr-client.la \ - libguacsnd-client.la \ - libguacsvc-client.la + libguacsnd-client.la freerdpdir = ${libdir}/freerdp2 @@ -244,30 +243,6 @@ libguacsnd_client_la_LIBADD = \ @COMMON_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 # diff --git a/src/protocols/rdp/guac_svc/svc_service.c b/src/protocols/rdp/guac_svc/svc_service.c deleted file mode 100644 index 07f17fb9..00000000 --- a/src/protocols/rdp/guac_svc/svc_service.c +++ /dev/null @@ -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 -#include - -#include -#include -#include -#include -#include -#include - -/** - * 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; - -} - diff --git a/src/protocols/rdp/svc.c b/src/protocols/rdp/pipe-svc.c similarity index 51% rename from src/protocols/rdp/svc.c rename to src/protocols/rdp/pipe-svc.c index be420727..05cd758c 100644 --- a/src/protocols/rdp/svc.c +++ b/src/protocols/rdp/pipe-svc.c @@ -22,8 +22,9 @@ #include "channels.h" #include "client.h" #include "common/list.h" +#include "common-svc.h" +#include "pipe-svc.h" #include "rdp.h" -#include "svc.h" #include #include @@ -33,15 +34,15 @@ #include -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 */ - guac_protocol_send_pipe(socket, svc->output_pipe, - "application/octet-stream", svc->channel_def.name); + guac_protocol_send_pipe(socket, pipe_svc->output_pipe, + "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_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 */ 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); + guac_rdp_pipe_svc_send_pipe(user->socket, (guac_rdp_pipe_svc*) current->data); 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; /* 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_add(rdp_client->available_svc, pipe_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_common_list_element* current; - guac_rdp_svc* found = NULL; + guac_rdp_pipe_svc* found = NULL; /* For each 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) { /* If name matches, found */ - guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data; - if (strcmp(current_svc->channel_def.name, name) == 0) { + guac_rdp_pipe_svc* current_svc = (guac_rdp_pipe_svc*) current->data; + if (strcmp(current_svc->svc->name, name) == 0) { found = current_svc; 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_common_list_element* current; - guac_rdp_svc* found = NULL; + guac_rdp_pipe_svc* found = NULL; /* For each 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) { /* 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_rdp_pipe_svc* current_svc = (guac_rdp_pipe_svc*) current->data; + if (strcmp(current_svc->svc->name, name) == 0) { guac_common_list_remove(rdp_client->available_svc, current); found = current_svc; 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) { - - /* 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, +int guac_rdp_pipe_svc_pipe_handler(guac_user* user, guac_stream* stream, 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 */ - if (svc == NULL) { + if (pipe_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)", @@ -170,19 +146,22 @@ int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream, "connected.", name); /* Init stream data */ - stream->data = svc; - stream->blob_handler = guac_rdp_svc_blob_handler; + stream->data = pipe_svc; + stream->blob_handler = guac_rdp_pipe_svc_blob_handler; 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) { + guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) stream->data; + /* Write blob data to SVC directly */ - guac_rdp_svc* svc = (guac_rdp_svc*) stream->data; - guac_rdp_svc_write(svc, data, length); + wStream* output_stream = Stream_New(NULL, 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_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; - guac_rdp_svc* svc = calloc(1, sizeof(guac_rdp_svc)); - svc->client = client; + /* Associate SVC with new Guacamole pipe */ + guac_rdp_pipe_svc* pipe_svc = malloc(sizeof(guac_rdp_pipe_svc)); + pipe_svc->svc = svc; + pipe_svc->output_pipe = guac_client_alloc_stream(svc->client); + svc->data = pipe_svc; - /* 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; + /* SVC may now receive data from client */ + guac_rdp_pipe_svc_add(svc->client, pipe_svc); - /* 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); + /* Notify of pipe's existence */ + guac_rdp_pipe_svc_send_pipe(svc->client->socket, pipe_svc); + +} + +void guac_rdp_pipe_svc_process_receive(guac_rdp_common_svc* svc, + wStream* input_stream) { + + guac_rdp_pipe_svc* pipe_svc = (guac_rdp_pipe_svc*) svc->data; + + /* Fail if output not created */ + if (pipe_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.", + Stream_Length(input_stream), svc->name); + return; + } + + /* 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); } diff --git a/src/protocols/rdp/svc.h b/src/protocols/rdp/pipe-svc.h similarity index 59% rename from src/protocols/rdp/svc.h rename to src/protocols/rdp/pipe-svc.h index 232b8c2c..1abcf09c 100644 --- a/src/protocols/rdp/svc.h +++ b/src/protocols/rdp/pipe-svc.h @@ -17,10 +17,11 @@ * under the License. */ -#ifndef GUAC_RDP_SVC_H -#define GUAC_RDP_SVC_H +#ifndef GUAC_RDP_PIPE_SVC_H +#define GUAC_RDP_PIPE_SVC_H #include "config.h" +#include "common-svc.h" #include #include @@ -34,15 +35,10 @@ #define GUAC_RDP_SVC_MAX_LENGTH 8 /** - * Structure describing a static virtual channel, and the corresponding - * Guacamole pipes and FreeRDP resources. + * Structure describing a static virtual channel and a corresponding Guacamole + * pipe stream; */ -typedef struct guac_rdp_svc { - - /** - * Reference to the client owning this static channel. - */ - guac_client* client; +typedef struct guac_rdp_pipe_svc { /** * 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; /** - * 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. + * The underlying static channel. Data written to this SVC by the RDP + * server will be forwarded along the pipe stream to the Guacamole client, + * 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; - /** - * 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; +} guac_rdp_pipe_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 + * Initializes arbitrary static virtual channel (SVC) support for RDP, handling + * 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 * 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 @@ -104,10 +73,9 @@ typedef struct guac_rdp_svc { * 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. + * The name of the SVC which should be handled. */ -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 @@ -121,7 +89,7 @@ void guac_rdp_svc_load_plugin(rdpContext* context, char* name); * @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); +void guac_rdp_pipe_svc_send_pipe(guac_socket* socket, guac_rdp_pipe_svc* svc); /** * 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 * 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 @@ -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 * 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 - * 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 * 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 * 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. @@ -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 * 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); +guac_rdp_pipe_svc* guac_rdp_pipe_svc_remove(guac_client* client, const char* name); /** * 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 * 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). + * relevant guac_rdp_pipe_svc instance and the SVC-specific "blob" instructino + * 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 diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 6d5d2254..b2423da8 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -29,6 +29,7 @@ #include "disp.h" #include "error.h" #include "keyboard.h" +#include "pipe-svc.h" #include "rail.h" #include "rdp.h" #include "rdp_bitmap.h" @@ -40,7 +41,6 @@ #include "rdp_stream.h" #include "rdpdr.h" #include "rdpsnd.h" -#include "svc.h" #ifdef ENABLE_COMMON_SSH #include "common-ssh/sftp.h" @@ -122,7 +122,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { char** current = settings->svc_names; do { - guac_rdp_svc_load_plugin(context, *current); + guac_rdp_pipe_svc_load_plugin(context, *current); } while (*(++current) != NULL); } diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index 47ba93ad..1fc1189e 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -23,10 +23,10 @@ #include "common/display.h" #include "input.h" #include "user.h" +#include "pipe-svc.h" #include "rdp.h" #include "rdp_settings.h" #include "rdp_stream.h" -#include "svc.h" #ifdef ENABLE_COMMON_SSH #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); /* 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 */ 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; /* Inbound arbitrary named pipes */ - user->pipe_handler = guac_rdp_svc_pipe_handler; + user->pipe_handler = guac_rdp_pipe_svc_pipe_handler; }