GUACAMOLE-249: Remove "guacsvc" plugin in favor of leveraging common SVC implementation.
This commit is contained in:
parent
352b9c517c
commit
3255b182ab
@ -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
|
||||
#
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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 <freerdp/svc.h>
|
||||
#include <guacamole/client.h>
|
||||
@ -33,15 +34,15 @@
|
||||
|
||||
#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 */
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -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 <freerdp/svc.h>
|
||||
#include <guacamole/client.h>
|
||||
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user