diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 0df45a30..0a832514 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -63,6 +63,7 @@ libguac_client_rdp_la_SOURCES = \ rdp_stream.c \ rdpdr.c \ rdpsnd.c \ + rdpsnd_messages.c \ resolution.c \ unicode.c \ user.c @@ -78,8 +79,6 @@ noinst_HEADERS = \ guac_rdpdr/rdpdr_messages.h \ guac_rdpdr/rdpdr_printer.h \ guac_rdpdr/rdpdr_service.h \ - guac_rdpsnd/rdpsnd_messages.h \ - guac_rdpsnd/rdpsnd_service.h \ audio_input.h \ client.h \ clipboard.h \ @@ -107,6 +106,7 @@ noinst_HEADERS = \ rdp_stream.h \ rdpdr.h \ rdpsnd.h \ + rdpsnd_messages.h \ resolution.h \ unicode.h \ user.h @@ -135,8 +135,7 @@ libguac_client_rdp_la_LIBADD = \ freerdp_LTLIBRARIES = \ libguac-common-svc-client.la \ libguacai-client.la \ - libguacdr-client.la \ - libguacsnd-client.la + libguacdr-client.la freerdpdir = ${libdir}/freerdp2 @@ -219,30 +218,6 @@ libguacai_client_la_LIBADD = \ @COMMON_LTLIB@ \ @LIBGUAC_LTLIB@ -# -# RDPSND -# - -libguacsnd_client_la_SOURCES = \ - guac_rdpsnd/rdpsnd_messages.c \ - guac_rdpsnd/rdpsnd_service.c - -libguacsnd_client_la_CFLAGS = \ - -Werror -Wall -Iinclude \ - @COMMON_INCLUDE@ \ - @COMMON_SSH_INCLUDE@ \ - @LIBGUAC_INCLUDE@ \ - @RDP_CFLAGS@ - -libguacsnd_client_la_LDFLAGS = \ - -module -avoid-version -shared \ - @PTHREAD_LIBS@ \ - @RDP_LIBS@ - -libguacsnd_client_la_LIBADD = \ - @COMMON_LTLIB@ \ - @LIBGUAC_LTLIB@ - # # Optional SFTP support # diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c deleted file mode 100644 index 88048a3a..00000000 --- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c +++ /dev/null @@ -1,364 +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 "rdpsnd_service.h" -#include "rdpsnd_messages.h" - -#include -#include - -#include -#include -#include - -/** - * Processes data received along the RDPSND channel via a - * CHANNEL_EVENT_DATA_RECEIVED event, forwarding the data along an established, - * outbound pipe stream to the Guacamole client. - * - * @param rdpsnd - * The guac_rdpsnd structure representing the RDPSND channel. - * - * @param input_stream - * The data that was received. - */ -static void guac_rdpsnd_process_receive(guac_rdpsnd* rdpsnd, - wStream* input_stream) { - - guac_rdpsnd_pdu_header header; - - /* Read RDPSND PDU header */ - Stream_Read_UINT8(input_stream, header.message_type); - Stream_Seek_UINT8(input_stream); - Stream_Read_UINT16(input_stream, header.body_size); - - /* - * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously), - * ignore the header and parse as a Wave PDU. - */ - if (rdpsnd->next_pdu_is_wave) { - guac_rdpsnd_wave_handler(rdpsnd, input_stream, &header); - return; - } - - /* Dispatch message to standard handlers */ - switch (header.message_type) { - - /* Server Audio Formats and Version PDU */ - case SNDC_FORMATS: - guac_rdpsnd_formats_handler(rdpsnd, input_stream, &header); - break; - - /* Training PDU */ - case SNDC_TRAINING: - guac_rdpsnd_training_handler(rdpsnd, input_stream, &header); - break; - - /* WaveInfo PDU */ - case SNDC_WAVE: - guac_rdpsnd_wave_info_handler(rdpsnd, input_stream, &header); - break; - - /* Close PDU */ - case SNDC_CLOSE: - guac_rdpsnd_close_handler(rdpsnd, input_stream, &header); - break; - - } - -} - -/** - * Event handler for events which deal with data transmitted over the RDPSND - * channel. This specific implementation of the event handler currently - * handles only the CHANNEL_EVENT_DATA_RECEIVED event, delegating actual - * handling of that event to guac_rdpsnd_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_rdpsnd_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_rdpsnd* rdpsnd = (guac_rdpsnd*) user_param; - - /* Validate relevant handle matches that of the RDPSND channel */ - if (open_handle != rdpsnd->open_handle) { - guac_client_log(rdpsnd->client, GUAC_LOG_WARNING, "%i bytes of data " - "received from within the remote desktop session for the " - "RDPSND channel are being dropped because the relevant open " - "handle (0x%X) does not match the open handle of RDPSND " - "(0x%X).", data_length, rdpsnd->channel_def.name, open_handle, - rdpsnd->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(rdpsnd->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; - } - - rdpsnd->input_stream = Stream_New(NULL, total_length); - } - - /* Add chunk to buffer only if sufficient space remains */ - if (Stream_EnsureRemainingCapacity(rdpsnd->input_stream, data_length)) - Stream_Write(rdpsnd->input_stream, data, data_length); - else - guac_client_log(rdpsnd->client, GUAC_LOG_WARNING, "%i bytes of data " - "received from within the remote desktop session for the " - "RDPSND channel are being dropped because the maximum " - "available space for received data has been exceeded.", - data_length, rdpsnd->channel_def.name, open_handle, - rdpsnd->open_handle); - - /* Fire event once last chunk has been received */ - if (data_flags & CHANNEL_FLAG_LAST) { - - Stream_SealLength(rdpsnd->input_stream); - Stream_SetPosition(rdpsnd->input_stream, 0); - - guac_rdpsnd_process_receive(rdpsnd, rdpsnd->input_stream); - - Stream_Free(rdpsnd->input_stream, TRUE); - - } - -} - -/** - * Processes a CHANNEL_EVENT_CONNECTED event, completing the - * connection/initialization process of the RDPSND channel. - * - * @param rdpsnd - * The guac_rdpsnd structure representing the RDPSND channel. - */ -static void guac_rdpsnd_process_connect(guac_rdpsnd* rdpsnd) { - - /* Open FreeRDP side of connected channel */ - UINT32 open_status = - rdpsnd->entry_points.pVirtualChannelOpenEx(rdpsnd->init_handle, - &rdpsnd->open_handle, rdpsnd->channel_def.name, - guac_rdpsnd_handle_open_event); - - /* Warn if the channel cannot be opened after all */ - if (open_status != CHANNEL_RC_OK) { - guac_client_log(rdpsnd->client, GUAC_LOG_WARNING, "RDPSND channel " - "could not be opened: %s (error %i)", - WTSErrorToString(open_status), open_status); - return; - } - - /* Log that sound has been loaded */ - guac_client_log(rdpsnd->client, GUAC_LOG_INFO, "RDPSND channel " - "connected."); - -} - -/** - * Processes a CHANNEL_EVENT_TERMINATED event, freeing all resources associated - * with the RDPSND channel. - * - * @param rdpsnd - * The guac_rdpsnd structure representing the RDPSND channel. - */ -static void guac_rdpsnd_process_terminate(guac_rdpsnd* rdpsnd) { - guac_client_log(rdpsnd->client, GUAC_LOG_INFO, "RDPSND channel disconnected."); - free(rdpsnd); -} - -/** - * Event handler for events which deal with the overall lifecycle of the RDPSND - * channel. 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_rdpsnd_process_connect() - * and guac_rdpsnd_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_rdpsnd_handle_init_event(LPVOID user_param, - LPVOID init_handle, UINT event, LPVOID data, UINT data_length) { - - guac_rdpsnd* rdpsnd = (guac_rdpsnd*) user_param; - - /* Validate relevant handle matches that of the RDPSND channel */ - if (init_handle != rdpsnd->init_handle) { - guac_client_log(rdpsnd->client, GUAC_LOG_WARNING, "An init event " - "(#%i) for the RDPSND channel has been dropped because the " - "relevant init handle (0x%X) does not match the init handle " - "of the RDPSND channel (0x%X).", event, init_handle, - rdpsnd->init_handle); - return; - } - - switch (event) { - - /* The RDPSND channel has been connected */ - case CHANNEL_EVENT_CONNECTED: - guac_rdpsnd_process_connect(rdpsnd); - break; - - /* The RDPSND channel has disconnected and now must be cleaned up */ - case CHANNEL_EVENT_TERMINATED: - guac_rdpsnd_process_terminate(rdpsnd); - 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; - - /* Allocate plugin */ - guac_rdpsnd* rdpsnd = (guac_rdpsnd*) calloc(1, sizeof(guac_rdpsnd)); - - /* Init channel def */ - strcpy(rdpsnd->channel_def.name, "rdpsnd"); - rdpsnd->channel_def.options = CHANNEL_OPTION_INITIALIZED - | CHANNEL_OPTION_ENCRYPT_RDP; - - /* Maintain reference to associated guac_client */ - rdpsnd->client = (guac_client*) entry_points_ex->pExtendedData; - - /* Copy FreeRDP data into RDPSND structure for future reference */ - rdpsnd->entry_points = *entry_points_ex; - rdpsnd->init_handle = init_handle; - - /* Complete initialization */ - if (rdpsnd->entry_points.pVirtualChannelInitEx(rdpsnd, rdpsnd, init_handle, - &rdpsnd->channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, - guac_rdpsnd_handle_init_event) != CHANNEL_RC_OK) { - return FALSE; - } - - return TRUE; - -} - diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h deleted file mode 100644 index f78df847..00000000 --- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - -#ifndef GUAC_RDPSND_SERVICE_H -#define GUAC_RDPSND_SERVICE_H - -#include "config.h" - -#include -#include -#include -#include - -/** - * The maximum number of PCM formats to accept during the initial RDPSND - * handshake with the RDP server. - */ -#define GUAC_RDP_MAX_FORMATS 16 - -/** - * 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 - -/** - * Abstract representation of a PCM format, including the sample rate, number - * of channels, and bits per sample. - */ -typedef struct guac_pcm_format { - - /** - * The sample rate of this PCM format. - */ - int rate; - - /** - * The number off channels used by this PCM format. This will typically - * be 1 or 2. - */ - int channels; - - /** - * The number of bits per sample within this PCM format. This should be - * either 8 or 16. - */ - int bps; - -} guac_pcm_format; - -/** - * Structure representing the current state of the Guacamole RDPSND plugin for - * FreeRDP. - */ -typedef struct guac_rdpsnd { - - /** - * The Guacamole client associated with the guac_audio_stream that this - * plugin should use to stream received audio packets. - */ - guac_client* client; - - /** - * The definition of this virtual channel (RDPSND). - */ - 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; - - /** - * The block number of the last SNDC_WAVE (WaveInfo) PDU received. - */ - int waveinfo_block_number; - - /** - * Whether the next PDU coming is a SNDWAVE (Wave) PDU. Wave PDUs do not - * have headers, and are indicated by the receipt of a WaveInfo PDU. - */ - int next_pdu_is_wave; - - /** - * The wave data received within the last SNDC_WAVE (WaveInfo) PDU. - */ - unsigned char initial_wave_data[4]; - - /** - * The size, in bytes, of the wave data in the coming Wave PDU, if any. - * This does not include the initial wave data received within the last - * SNDC_WAVE (WaveInfo) PDU, which is always the first four bytes of the - * actual wave data block. - */ - int incoming_wave_size; - - /** - * The last received server timestamp. - */ - int server_timestamp; - - /** - * All formats agreed upon by server and client during the initial format - * exchange. All of these formats will be PCM, which is the only format - * guaranteed to be supported (based on the official RDP documentation). - */ - guac_pcm_format formats[GUAC_RDP_MAX_FORMATS]; - - /** - * The total number of formats. - */ - int format_count; - -} guac_rdpsnd; - -#endif - diff --git a/src/protocols/rdp/rdpsnd.c b/src/protocols/rdp/rdpsnd.c index 23b31336..0e1d2369 100644 --- a/src/protocols/rdp/rdpsnd.c +++ b/src/protocols/rdp/rdpsnd.c @@ -18,26 +18,89 @@ */ #include "config.h" -#include "channels.h" +#include "common-svc.h" #include "rdp.h" +#include "rdpsnd.h" +#include "rdpsnd_messages.h" #include #include +#include +#include + +#include + +void guac_rdpsnd_process_receive(guac_rdp_common_svc* svc, + wStream* input_stream) { + + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; + guac_rdpsnd_pdu_header header; + + /* Read RDPSND PDU header */ + Stream_Read_UINT8(input_stream, header.message_type); + Stream_Seek_UINT8(input_stream); + Stream_Read_UINT16(input_stream, header.body_size); + + /* + * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously), + * ignore the header and parse as a Wave PDU. + */ + if (rdpsnd->next_pdu_is_wave) { + guac_rdpsnd_wave_handler(svc, input_stream, &header); + return; + } + + /* Dispatch message to standard handlers */ + switch (header.message_type) { + + /* Server Audio Formats and Version PDU */ + case SNDC_FORMATS: + guac_rdpsnd_formats_handler(svc, input_stream, &header); + break; + + /* Training PDU */ + case SNDC_TRAINING: + guac_rdpsnd_training_handler(svc, input_stream, &header); + break; + + /* WaveInfo PDU */ + case SNDC_WAVE: + guac_rdpsnd_wave_info_handler(svc, input_stream, &header); + break; + + /* Close PDU */ + case SNDC_CLOSE: + guac_rdpsnd_close_handler(svc, input_stream, &header); + break; + + } + +} + +void guac_rdpsnd_process_connect(guac_rdp_common_svc* svc) { + + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) calloc(1, sizeof(guac_rdpsnd)); + svc->data = rdpsnd; + +} + +void guac_rdpsnd_process_terminate(guac_rdp_common_svc* svc) { + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; + free(rdpsnd); +} void guac_rdpsnd_load_plugin(rdpContext* context) { guac_client* client = ((rdp_freerdp_context*) context)->client; - /* Load RDPSND plugin */ - if (guac_freerdp_channels_load_plugin(context->channels, context->settings, "guacsnd", client)) { + /* Load support for RDPSND */ + if (guac_rdp_common_svc_load_plugin(context, "rdpsnd", 0, + guac_rdpsnd_process_connect, guac_rdpsnd_process_receive, + guac_rdpsnd_process_terminate)) { guac_client_log(client, GUAC_LOG_WARNING, "Support for the RDPSND " "channel (audio output) could not be loaded. Sound will not " "work. Drive redirection and printing MAY not work."); - return; } - guac_client_log(client, GUAC_LOG_DEBUG, "Support for RDPSND (audio " - "output) registered. Awaiting channel connection."); - } diff --git a/src/protocols/rdp/rdpsnd.h b/src/protocols/rdp/rdpsnd.h index 51e86150..deec0de3 100644 --- a/src/protocols/rdp/rdpsnd.h +++ b/src/protocols/rdp/rdpsnd.h @@ -21,6 +21,89 @@ #define GUAC_RDP_RDPSND_H #include "config.h" +#include "common-svc.h" + +#include + +/** + * The maximum number of PCM formats to accept during the initial RDPSND + * handshake with the RDP server. + */ +#define GUAC_RDP_MAX_FORMATS 16 + +/** + * Abstract representation of a PCM format, including the sample rate, number + * of channels, and bits per sample. + */ +typedef struct guac_rdpsnd_pcm_format { + + /** + * The sample rate of this PCM format. + */ + int rate; + + /** + * The number off channels used by this PCM format. This will typically + * be 1 or 2. + */ + int channels; + + /** + * The number of bits per sample within this PCM format. This should be + * either 8 or 16. + */ + int bps; + +} guac_rdpsnd_pcm_format; + +/** + * Structure representing the current state of the Guacamole RDPSND plugin for + * FreeRDP. + */ +typedef struct guac_rdpsnd { + + /** + * The block number of the last SNDC_WAVE (WaveInfo) PDU received. + */ + int waveinfo_block_number; + + /** + * Whether the next PDU coming is a SNDWAVE (Wave) PDU. Wave PDUs do not + * have headers, and are indicated by the receipt of a WaveInfo PDU. + */ + int next_pdu_is_wave; + + /** + * The wave data received within the last SNDC_WAVE (WaveInfo) PDU. + */ + unsigned char initial_wave_data[4]; + + /** + * The size, in bytes, of the wave data in the coming Wave PDU, if any. + * This does not include the initial wave data received within the last + * SNDC_WAVE (WaveInfo) PDU, which is always the first four bytes of the + * actual wave data block. + */ + int incoming_wave_size; + + /** + * The last received server timestamp. + */ + int server_timestamp; + + /** + * All formats agreed upon by server and client during the initial format + * exchange. All of these formats will be PCM, which is the only format + * guaranteed to be supported (based on the official RDP documentation). + */ + guac_rdpsnd_pcm_format formats[GUAC_RDP_MAX_FORMATS]; + + /** + * The total number of formats. + */ + int format_count; + +} guac_rdpsnd; /** * Initializes audio output support for RDP and handling of the RDPSND channel. @@ -35,5 +118,23 @@ */ void guac_rdpsnd_load_plugin(rdpContext* context); +/** + * Handler which is invoked when the RDPSND channel is connected to the RDP + * server. + */ +guac_rdp_common_svc_connect_handler guac_rdpsnd_process_connect; + +/** + * Handler which is invoked when the RDPSND channel has received data from the + * RDP server. + */ +guac_rdp_common_svc_receive_handler guac_rdpsnd_process_receive; + +/** + * Handler which is invoked when the RDPSND channel has disconnected and is + * about to be freed. + */ +guac_rdp_common_svc_terminate_handler guac_rdpsnd_process_terminate; + #endif diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c b/src/protocols/rdp/rdpsnd_messages.c similarity index 86% rename from src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c rename to src/protocols/rdp/rdpsnd_messages.c index 90c59a18..0102e283 100644 --- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c +++ b/src/protocols/rdp/rdpsnd_messages.c @@ -20,8 +20,8 @@ #include "config.h" #include "rdp.h" +#include "rdpsnd.h" #include "rdpsnd_messages.h" -#include "rdpsnd_service.h" #include #include @@ -31,9 +31,7 @@ #include #include -/* MESSAGE HANDLERS */ - -void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header) { int server_format_count; @@ -44,11 +42,10 @@ void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, int output_body_size; unsigned char* output_stream_end; - /* Get associated client data */ - guac_client* client = rdpsnd->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_client* client = svc->client; + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; - /* Get audio stream from client data */ + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_audio_stream* audio = rdp_client->audio; /* Reset own format count */ @@ -181,9 +178,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, Stream_SetPointer(output_stream, output_stream_end); /* Send accepted formats */ - rdpsnd->entry_points.pVirtualChannelWriteEx(rdpsnd->init_handle, - rdpsnd->open_handle, Stream_Buffer(output_stream), - Stream_GetPosition(output_stream), output_stream); + guac_rdp_common_svc_write(svc, output_stream); /* If version greater than 6, must send Quality Mode PDU */ if (server_version >= 6) { @@ -196,21 +191,21 @@ void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, Stream_Write_UINT16(output_stream, HIGH_QUALITY); Stream_Write_UINT16(output_stream, 0); - rdpsnd->entry_points.pVirtualChannelWriteEx(rdpsnd->init_handle, - rdpsnd->open_handle, Stream_Buffer(output_stream), - Stream_GetPosition(output_stream), output_stream); + guac_rdp_common_svc_write(svc, output_stream); } } /* server is getting a feel of the round trip time */ -void guac_rdpsnd_training_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header) { int data_size; wStream* output_stream; + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; + /* Read timestamp and data size */ Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp); Stream_Read_UINT16(input_stream, data_size); @@ -223,22 +218,19 @@ void guac_rdpsnd_training_handler(guac_rdpsnd* rdpsnd, Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp); Stream_Write_UINT16(output_stream, data_size); - rdpsnd->entry_points.pVirtualChannelWriteEx(rdpsnd->init_handle, - rdpsnd->open_handle, Stream_Buffer(output_stream), - Stream_GetPosition(output_stream), output_stream); + guac_rdp_common_svc_write(svc, output_stream); } -void guac_rdpsnd_wave_info_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header) { int format; - /* Get associated client data */ - guac_client* client = rdpsnd->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_client* client = svc->client; + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; - /* Get audio stream from client data */ + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_audio_stream* audio = rdp_client->audio; /* Read wave information */ @@ -267,14 +259,13 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsnd* rdpsnd, } -void guac_rdpsnd_wave_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header) { - /* Get associated client data */ - guac_client* client = rdpsnd->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_client* client = svc->client; + guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; - /* Get audio stream from client data */ + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_audio_stream* audio = rdp_client->audio; /* Wave Confirmation PDU */ @@ -302,16 +293,14 @@ void guac_rdpsnd_wave_handler(guac_rdpsnd* rdpsnd, Stream_Write_UINT8(output_stream, 0); /* Send Wave Confirmation PDU */ - rdpsnd->entry_points.pVirtualChannelWriteEx(rdpsnd->init_handle, - rdpsnd->open_handle, Stream_Buffer(output_stream), - Stream_GetPosition(output_stream), output_stream); + guac_rdp_common_svc_write(svc, output_stream); /* We no longer expect to receive wave data */ rdpsnd->next_pdu_is_wave = FALSE; } -void guac_rdpsnd_close_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header) { /* Do nothing */ diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h b/src/protocols/rdp/rdpsnd_messages.h similarity index 88% rename from src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h rename to src/protocols/rdp/rdpsnd_messages.h index 700c41b6..91d0223b 100644 --- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h +++ b/src/protocols/rdp/rdpsnd_messages.h @@ -22,8 +22,7 @@ #define GUAC_RDPSND_MESSAGES_H #include "config.h" - -#include "rdpsnd_service.h" +#include "common-svc.h" #include @@ -117,8 +116,8 @@ typedef struct guac_rdpsnd_pdu_header { * SNDC_FORMATS PDU describes all audio formats supported by the RDP server, as * well as the version of RDPSND implemented. * - * @param rdpsnd - * The Guacamole RDPSND plugin receiving the SNDC_FORMATS PDU. + * @param svc + * The RDPSND channel receiving the SNDC_FORMATS PDU. * * @param input_stream * The FreeRDP input stream containing the remaining raw bytes (after the @@ -128,7 +127,7 @@ typedef struct guac_rdpsnd_pdu_header { * The header content of the SNDC_FORMATS PDU. All RDPSND messages contain * the same header information. */ -void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header); /** @@ -138,8 +137,8 @@ void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, * * https://msdn.microsoft.com/en-us/library/cc240961.aspx * - * @param rdpsnd - * The Guacamole RDPSND plugin receiving the SNDC_TRAINING PDU. + * @param svc + * The RDPSND channel receiving the SNDC_TRAINING PDU. * * @param input_stream * The FreeRDP input stream containing the remaining raw bytes (after the @@ -149,7 +148,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsnd* rdpsnd, * The header content of the SNDC_TRAINING PDU. All RDPSND messages contain * the same header information. */ -void guac_rdpsnd_training_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header); /** @@ -161,8 +160,8 @@ void guac_rdpsnd_training_handler(guac_rdpsnd* rdpsnd, * * https://msdn.microsoft.com/en-us/library/cc240963.aspx * - * @param rdpsnd - * The Guacamole RDPSND plugin receiving the SNDC_WAVE PDU. + * @param svc + * The RDPSND channel receiving the SNDC_WAVE PDU. * * @param input_stream * The FreeRDP input stream containing the remaining raw bytes (after the @@ -172,7 +171,7 @@ void guac_rdpsnd_training_handler(guac_rdpsnd* rdpsnd, * The header content of the SNDC_WAVE PDU. All RDPSND messages contain * the same header information. */ -void guac_rdpsnd_wave_info_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header); /** @@ -180,8 +179,8 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsnd* rdpsnd, * PDU contains the actual audio data, less the four bytes of audio data * included in the SNDC_WAVE PDU. * - * @param rdpsnd - * The Guacamole RDPSND plugin receiving the SNDWAV PDU. + * @param svc + * The RDPSND channel receiving the SNDWAV PDU. * * @param input_stream * The FreeRDP input stream containing the remaining raw bytes (after the @@ -191,7 +190,7 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsnd* rdpsnd, * The header content of the SNDWAV PDU. All RDPSND messages contain * the same header information. */ -void guac_rdpsnd_wave_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header); /** @@ -200,8 +199,8 @@ void guac_rdpsnd_wave_handler(guac_rdpsnd* rdpsnd, * * https://msdn.microsoft.com/en-us/library/cc240970.aspx * - * @param rdpsnd - * The Guacamole RDPSND plugin receiving the SNDC_CLOSE PDU. + * @param svc + * The RDPSND channel receiving the SNDC_CLOSE PDU. * * @param input_stream * The FreeRDP input stream containing the remaining raw bytes (after the @@ -211,7 +210,7 @@ void guac_rdpsnd_wave_handler(guac_rdpsnd* rdpsnd, * The header content of the SNDC_CLOSE PDU. All RDPSND messages contain * the same header information. */ -void guac_rdpsnd_close_handler(guac_rdpsnd* rdpsnd, +void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc, wStream* input_stream, guac_rdpsnd_pdu_header* header); #endif