From 352b9c517c24f9d8894b83b8d26a1909ba848d3c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 22 Dec 2019 12:18:01 -0800 Subject: [PATCH] GUACAMOLE-249: Add common SVC plugin implementation as future simplified basis for RDPSND, RDPDR, etc. --- src/protocols/rdp/Makefile.am | 30 +- src/protocols/rdp/common-svc.c | 103 ++++++ src/protocols/rdp/common-svc.h | 230 +++++++++++++ .../rdp/guac-common-svc/common-svc-main.c | 310 ++++++++++++++++++ 4 files changed, 669 insertions(+), 4 deletions(-) create mode 100644 src/protocols/rdp/common-svc.c create mode 100644 src/protocols/rdp/common-svc.h create mode 100644 src/protocols/rdp/guac-common-svc/common-svc-main.c diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index aacfda6a..9a3a5fe2 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -41,6 +41,7 @@ libguac_client_rdp_la_SOURCES = \ channels.c \ client.c \ clipboard.c \ + common-svc.c \ decompose.c \ disp.c \ error.c \ @@ -83,6 +84,7 @@ noinst_HEADERS = \ client.h \ clipboard.h \ channels.h \ + common-svc.h \ decompose.h \ disp.h \ error.h \ @@ -130,14 +132,34 @@ libguac_client_rdp_la_LIBADD = \ # Plugins for FreeRDP # -freerdp_LTLIBRARIES = \ - libguacai-client.la \ - libguacdr-client.la \ - libguacsnd-client.la \ +freerdp_LTLIBRARIES = \ + libguac-common-svc-client.la \ + libguacai-client.la \ + libguacdr-client.la \ + libguacsnd-client.la \ libguacsvc-client.la freerdpdir = ${libdir}/freerdp2 +# +# Common SVC plugin (shared by RDPDR, RDPSND, etc.) +# + +libguac_common_svc_client_la_SOURCES = \ + guac-common-svc/common-svc-main.c + +libguac_common_svc_client_la_CFLAGS = \ + -Werror -Wall -Iinclude \ + @LIBGUAC_INCLUDE@ \ + @RDP_CFLAGS@ + +libguac_common_svc_client_la_LDFLAGS = \ + -module -avoid-version -shared \ + @RDP_LIBS@ + +libguac_common_svc_client_la_LIBADD = \ + @LIBGUAC_LTLIB@ + # # RDPDR # diff --git a/src/protocols/rdp/common-svc.c b/src/protocols/rdp/common-svc.c new file mode 100644 index 00000000..3e092a95 --- /dev/null +++ b/src/protocols/rdp/common-svc.c @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" +#include "channels.h" +#include "common-svc.h" +#include "rdp.h" + +#include +#include +#include +#include +#include +#include +#include + +int guac_rdp_common_svc_load_plugin(rdpContext* context, + char* name, ULONG channel_options, + guac_rdp_common_svc_connect_handler* connect_handler, + guac_rdp_common_svc_receive_handler* receive_handler, + guac_rdp_common_svc_terminate_handler* terminate_handler) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + + guac_rdp_common_svc* svc = calloc(1, sizeof(guac_rdp_common_svc)); + svc->client = client; + svc->name = svc->_channel_def.name; + svc->_connect_handler = connect_handler; + svc->_receive_handler = receive_handler; + svc->_terminate_handler = terminate_handler; + + /* 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_options; + + /* 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->name); + + int result = guac_freerdp_channels_load_plugin(context->channels, + context->settings, "guac-common-svc", svc); + + /* Attempt to load the common SVC plugin for new static channel */ + if (result) { + guac_client_log(client, GUAC_LOG_WARNING, "Cannot create static " + "channel \"%s\": failed to load \"guac-common-svc\" plugin " + "for FreeRDP.", svc->name); + free(svc); + } + + /* Store and log on success (SVC structure will be freed on channel termination) */ + else + guac_client_log(client, GUAC_LOG_DEBUG, "Support for static channel " + "\"%s\" loaded.", svc->name); + + return result; + +} + +void guac_rdp_common_svc_write(guac_rdp_common_svc* svc, + wStream* output_stream) { + + /* Do not write if plugin not associated */ + if (!svc->_open_handle) { + guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data " + "written to SVC \"%s\" are being dropped because the remote " + "desktop side of that SVC is not yet connected.", + Stream_Length(output_stream), svc->name); + return; + } + + /* NOTE: Data sent via pVirtualChannelWriteEx MUST always be dynamically + * allocated, as it will be automatically freed using free(). If provided, + * the last parameter (user data) MUST be a pointer to a wStream, as it + * will automatically be freed by FreeRDP using Stream_Free() */ + svc->_entry_points.pVirtualChannelWriteEx(svc->_init_handle, + svc->_open_handle, Stream_Buffer(output_stream), + Stream_GetPosition(output_stream), output_stream); + +} + diff --git a/src/protocols/rdp/common-svc.h b/src/protocols/rdp/common-svc.h new file mode 100644 index 00000000..5370d5f6 --- /dev/null +++ b/src/protocols/rdp/common-svc.h @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_RDP_SVC_H +#define GUAC_RDP_SVC_H + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +/** + * The maximum number of bytes to allow within each channel name, including + * null terminator. + */ +#define GUAC_RDP_SVC_MAX_LENGTH 8 + +/** + * The maximum number of bytes that the RDP server will be allowed to send + * within any single write operation, regardless of the number of chunks that + * write is split into. Bytes beyond this limit may be dropped. + */ +#define GUAC_SVC_MAX_ASSEMBLED_LENGTH 1048576 + +/** + * Structure describing a static virtual channel, and the corresponding + * Guacamole pipes and FreeRDP resources. + */ +typedef struct guac_rdp_common_svc guac_rdp_common_svc; + +/** + * Handler which is invoked when a CHANNEL_EVENT_CONNECTED event has been + * processed and the connection/initialization process of the SVC is now + * complete. + * + * @param svc + * The guac_rdp_common_svc structure representing the SVC that is now + * connected. + */ +typedef void guac_rdp_common_svc_connect_handler(guac_rdp_common_svc* svc); + +/** + * Handler which is invoked when a logical block of data has been received + * along an SVC, having been reassembled from a series of + * CHANNEL_EVENT_DATA_RECEIVED events. + * + * @param svc + * The guac_rdp_common_svc structure representing the SVC that received the + * data. + * + * @param input_stream + * The reassembled block of data that was received. + */ +typedef void guac_rdp_common_svc_receive_handler(guac_rdp_common_svc* svc, wStream* input_stream); + +/** + * Handler which is invoked when a CHANNEL_EVENT_TERMINATED event has been + * processed and all resources associated with the SVC must now be freed. + * + * @param svc + * The guac_rdp_common_svc structure representing the SVC that has been + * terminated. + */ +typedef void guac_rdp_common_svc_terminate_handler(guac_rdp_common_svc* svc); + +struct guac_rdp_common_svc { + + /** + * Reference to the client owning this static channel. + */ + guac_client* client; + + /** + * The name of the static virtual channel, as specified to + * guac_rdp_common_svc_load_plugin(). This value is stored and defined + * internally by the CHANNEL_DEF. + */ + const char* name; + + /** + * Arbitrary channel-specific data which may be assigned and referenced by + * channel implementations leveraging the "guac-common-svc" plugin. + */ + void* data; + + /** + * Handler which is invoked when handling a CHANNEL_EVENT_CONNECTED event. + */ + guac_rdp_common_svc_connect_handler* _connect_handler; + + /** + * Handler which is invoked when all chunks of data for a single logical + * block have been received via CHANNEL_EVENT_DATA_RECEIVED events and + * reassembled. + */ + guac_rdp_common_svc_receive_handler* _receive_handler; + + /** + * Handler which is invokved when the SVC has been disconnected and is + * about to be freed. + */ + guac_rdp_common_svc_terminate_handler* _terminate_handler; + + /** + * The definition of this static virtual channel, including its name. + */ + CHANNEL_DEF _channel_def; + + /** + * Functions and data specific to the FreeRDP side of the virtual channel + * and plugin. + */ + CHANNEL_ENTRY_POINTS_FREERDP_EX _entry_points; + + /** + * Handle which identifies the client connection, typically referred to + * within the FreeRDP source as pInitHandle. This handle is provided to the + * channel entry point and the channel init event handler. The handle must + * eventually be used within the channel open event handler to obtain a + * handle to the channel itself. + */ + PVOID _init_handle; + + /** + * Handle which identifies the channel itself, typically referred to within + * the FreeRDP source as OpenHandle. This handle is obtained through a call + * to entry_points.pVirtualChannelOpenEx() in response to receiving a + * CHANNEL_EVENT_CONNECTED event via the init event handler. + * + * Data is received in CHANNEL_EVENT_DATA_RECEIVED events via the open + * event handler, and data is written through calls to + * entry_points.pVirtualChannelWriteEx(). + */ + DWORD _open_handle; + + /** + * All data that has been received thus far from the current RDP server + * write operation. Data received along virtual channels is sent in chunks + * (typically 1600 bytes), and thus must be gradually reassembled as it is + * received. + */ + wStream* _input_stream; + +}; + +/** + * Initializes arbitrary static virtual channel (SVC) support for RDP, loading + * a new instance of Guacamole's arbitrary SVC plugin for FreeRDP ("guacsvc") + * supporting the channel having the given name. Data sent from within the RDP + * session using this channel will be sent along an identically-named pipe + * stream to the Guacamole client, and data sent along a pipe stream having the + * same name will be written to the SVC and received within the RDP session. If + * failures occur while loading the plugin, messages noting the specifics of + * those failures will be logged, and support for the given channel will not be + * functional. + * + * This MUST be called within the PreConnect callback of the freerdp instance + * for static virtual channel support to be loaded. + * + * @param rdpContext + * The rdpContext associated with the FreeRDP side of the RDP connection. + * + * @param name + * The name of the SVC which should be handled by the new instance of the + * plugin. + * + * @param channel_options + * Bitwise OR of any of the several CHANNEL_OPTION_* flags. Regardless of + * whether specified here, the CHANNEL_OPTION_INTIALIZED and + * CHANNEL_OPTION_ENCRYPT_RDP flags will automatically be set. + * + * @param connect_handler + * The function to invoke when the SVC has been connected. + * + * @param receive_handler + * The function to invoke when the SVC has received a logical block of + * data, reassembled from perhaps several smaller chunks of data. + * + * @param terminate_handler + * The function to invoke when the SVC has been disconnected and is about + * to be freed. + * + * @return + * Zero if the plugin was loaded successfully, non-zero if the plugin could + * not be loaded. + */ +int guac_rdp_common_svc_load_plugin(rdpContext* context, + char* name, ULONG channel_options, + guac_rdp_common_svc_connect_handler* connect_handler, + guac_rdp_common_svc_receive_handler* receive_handler, + guac_rdp_common_svc_terminate_handler* terminate_handler); + +/** + * Writes the given data to the virtual channel such that it can be received + * within the RDP session. The given data MUST be dynamically allocated, as the + * write operation may be queued and the actual write may not occur until + * later. The provided wStream and the buffer it points to will be + * automatically freed after the write occurs. + * + * @param svc + * The static virtual channel to write data to. + * + * @param output_stream + * The data to write, which MUST be dynamically allocated. + */ +void guac_rdp_common_svc_write(guac_rdp_common_svc* svc, + wStream* output_stream); + +#endif + diff --git a/src/protocols/rdp/guac-common-svc/common-svc-main.c b/src/protocols/rdp/guac-common-svc/common-svc-main.c new file mode 100644 index 00000000..441823b7 --- /dev/null +++ b/src/protocols/rdp/guac-common-svc/common-svc-main.c @@ -0,0 +1,310 @@ +/* + * 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 "common-svc.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * 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_common_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 expected to be received from the RDP server + * due to this single write (from the server's perspective). Each write may + * actually be split into multiple chunks, thus resulting in multiple + * receive events for the same logical block of data. The relationship + * between chunks is indicated with the CHANNEL_FLAG_FIRST and + * CHANNEL_FLAG_LAST flags. + * + * @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_common_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_common_svc* svc = (guac_rdp_common_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->name, open_handle, svc->_open_handle); + return; + } + + /* If receiving first chunk, allocate sufficient space for all remaining + * chunks */ + if (data_flags & CHANNEL_FLAG_FIRST) { + + /* Limit maximum received size */ + if (total_length > GUAC_SVC_MAX_ASSEMBLED_LENGTH) { + guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server has " + "requested to send a sequence of %i bytes, but this " + "exceeds the maximum buffer space of %i bytes. Received " + "data may be truncated.", total_length, + GUAC_SVC_MAX_ASSEMBLED_LENGTH); + total_length = GUAC_SVC_MAX_ASSEMBLED_LENGTH; + } + + svc->_input_stream = Stream_New(NULL, total_length); + } + + /* Add chunk to buffer only if sufficient space remains */ + if (Stream_EnsureRemainingCapacity(svc->_input_stream, data_length)) + Stream_Write(svc->_input_stream, data, data_length); + else + 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 maximum available " + "space for received data has been exceeded.", data_length, + svc->name); + + /* Fire event once last chunk has been received */ + if (data_flags & CHANNEL_FLAG_LAST) { + + Stream_SealLength(svc->_input_stream); + Stream_SetPosition(svc->_input_stream, 0); + + /* Handle channel-specific data receipt tasks, if any */ + if (svc->_receive_handler) + svc->_receive_handler(svc, svc->_input_stream); + + Stream_Free(svc->_input_stream, TRUE); + + } + +} + +/** + * Processes a CHANNEL_EVENT_CONNECTED event, completing the + * connection/initialization process of the channel. + * + * @param rdpsnd + * The guac_rdp_common_svc structure representing the channel. + */ +static void guac_rdp_common_svc_process_connect(guac_rdp_common_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_common_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->name, + WTSErrorToString(open_status), open_status); + return; + } + + /* Handle channel-specific connect tasks, if any */ + if (svc->_connect_handler) + svc->_connect_handler(svc); + + /* Channel is now ready */ + guac_client_log(svc->client, GUAC_LOG_DEBUG, "SVC \"%s\" connected.", + svc->name); + +} + +/** + * Processes a CHANNEL_EVENT_TERMINATED event, freeing all resources associated + * with the channel. + * + * @param svc + * The guac_rdp_common_svc structure representing the channel. + */ +static void guac_rdp_common_svc_process_terminate(guac_rdp_common_svc* svc) { + + /* Handle channel-specific termination tasks, if any */ + if (svc->_terminate_handler) + svc->_terminate_handler(svc); + + guac_client_log(svc->client, GUAC_LOG_DEBUG, "SVC \"%s\" disconnected.", + svc->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_common_svc_process_connect() and + * guac_rdp_common_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_common_svc_handle_init_event(LPVOID user_param, + LPVOID init_handle, UINT event, LPVOID data, UINT data_length) { + + guac_rdp_common_svc* svc = (guac_rdp_common_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->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_common_svc_process_connect(svc); + break; + + /* The channel has disconnected and now must be cleaned up */ + case CHANNEL_EVENT_TERMINATED: + guac_rdp_common_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_common_svc* svc = (guac_rdp_common_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_common_svc_handle_init_event) != CHANNEL_RC_OK) { + return FALSE; + } + + return TRUE; + +} +