GUACAMOLE-1283: Merge add synchronization around absolutely all outbound RDP messages.

This commit is contained in:
Virtually Nick 2021-04-17 13:11:32 -04:00 committed by GitHub
commit e90e438cf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 217 additions and 83 deletions

View File

@ -31,9 +31,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc() { guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc(guac_client* client) {
guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer)); guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer));
pthread_mutex_init(&(buffer->lock), NULL); pthread_mutex_init(&(buffer->lock), NULL);
buffer->client = client;
return buffer; return buffer;
} }
@ -270,8 +271,8 @@ void guac_rdp_audio_buffer_write(guac_rdp_audio_buffer* audio_buffer,
/* Only actually invoke if defined */ /* Only actually invoke if defined */
if (audio_buffer->flush_handler) if (audio_buffer->flush_handler)
audio_buffer->flush_handler(audio_buffer->packet, audio_buffer->flush_handler(audio_buffer,
audio_buffer->bytes_written, audio_buffer->data); audio_buffer->bytes_written);
/* Reset buffer in all cases */ /* Reset buffer in all cases */
audio_buffer->bytes_written = 0; audio_buffer->bytes_written = 0;

View File

@ -24,24 +24,27 @@
#include <guacamole/user.h> #include <guacamole/user.h>
#include <pthread.h> #include <pthread.h>
/**
* A buffer of arbitrary audio data. Received audio data can be written to this
* buffer, and will automatically be flushed via a given handler once the
* internal buffer reaches capacity.
*/
typedef struct guac_rdp_audio_buffer guac_rdp_audio_buffer;
/** /**
* Handler which is invoked when a guac_rdp_audio_buffer's internal packet * Handler which is invoked when a guac_rdp_audio_buffer's internal packet
* buffer has reached capacity and must be flushed. * buffer has reached capacity and must be flushed.
* *
* @param buffer * @param audio_buffer
* The buffer which needs to be flushed as an audio packet. * The guac_rdp_audio_buffer that has reached capacity and needs to be
* flushed.
* *
* @param length * @param length
* The number of bytes stored within the buffer. This is guaranteed to be * The number of bytes stored within the buffer. This is guaranteed to be
* identical to the packet_size value specified when the audio buffer was * identical to the packet_size value specified when the audio buffer was
* initialized. * initialized.
*
* @param data
* The arbitrary data pointer provided when the audio buffer was
* initialized.
*/ */
typedef void guac_rdp_audio_buffer_flush_handler(char* buffer, int length, typedef void guac_rdp_audio_buffer_flush_handler(guac_rdp_audio_buffer* audio_buffer, int length);
void* data);
/** /**
* A description of an arbitrary PCM audio format. * A description of an arbitrary PCM audio format.
@ -66,12 +69,7 @@ typedef struct guac_rdp_audio_format {
} guac_rdp_audio_format; } guac_rdp_audio_format;
/** struct guac_rdp_audio_buffer {
* A buffer of arbitrary audio data. Received audio data can be written to this
* buffer, and will automatically be flushed via a given handler once the
* internal buffer reaches capacity.
*/
typedef struct guac_rdp_audio_buffer {
/** /**
* Lock which is acquired/released to ensure accesses to the audio buffer * Lock which is acquired/released to ensure accesses to the audio buffer
@ -79,6 +77,11 @@ typedef struct guac_rdp_audio_buffer {
*/ */
pthread_mutex_t lock; pthread_mutex_t lock;
/**
* The guac_client instance handling the relevant RDP connection.
*/
guac_client* client;
/** /**
* The user from which this audio buffer will receive data. If no user has * The user from which this audio buffer will receive data. If no user has
* yet opened an associated audio stream, this will be NULL. * yet opened an associated audio stream, this will be NULL.
@ -145,17 +148,20 @@ typedef struct guac_rdp_audio_buffer {
*/ */
void* data; void* data;
} guac_rdp_audio_buffer; };
/** /**
* Allocates a new audio buffer. The new audio buffer will ignore any received * Allocates a new audio buffer. The new audio buffer will ignore any received
* data until guac_rdp_audio_buffer_begin() is invoked, and will resume * data until guac_rdp_audio_buffer_begin() is invoked, and will resume
* ignoring received data once guac_rdp_audio_buffer_end() is invoked. * ignoring received data once guac_rdp_audio_buffer_end() is invoked.
* *
* @param client
* The guac_client instance handling the relevant RDP connection.
*
* @return * @return
* A newly-allocated audio buffer. * A newly-allocated audio buffer.
*/ */
guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc(); guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc(guac_client* client);
/** /**
* Associates the given audio buffer with the underlying audio stream which * Associates the given audio buffer with the underlying audio stream which

View File

@ -76,6 +76,9 @@ static UINT guac_rdp_cliprdr_send_format_list(CliprdrClientContext* cliprdr) {
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom; guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL); assert(clipboard != NULL);
guac_client* client = clipboard->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* We support CP-1252 and UTF-16 text */ /* We support CP-1252 and UTF-16 text */
CLIPRDR_FORMAT_LIST format_list = { CLIPRDR_FORMAT_LIST format_list = {
.msgType = CB_FORMAT_LIST, .msgType = CB_FORMAT_LIST,
@ -86,10 +89,12 @@ static UINT guac_rdp_cliprdr_send_format_list(CliprdrClientContext* cliprdr) {
.numFormats = 2 .numFormats = 2
}; };
guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Sending " guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Sending format list");
"format list");
return cliprdr->ClientFormatList(cliprdr, &format_list); pthread_mutex_lock(&(rdp_client->message_lock));
int retval = cliprdr->ClientFormatList(cliprdr, &format_list);
pthread_mutex_unlock(&(rdp_client->message_lock));
return retval;
} }
@ -107,6 +112,17 @@ static UINT guac_rdp_cliprdr_send_format_list(CliprdrClientContext* cliprdr) {
*/ */
static UINT guac_rdp_cliprdr_send_capabilities(CliprdrClientContext* cliprdr) { static UINT guac_rdp_cliprdr_send_capabilities(CliprdrClientContext* cliprdr) {
/* This function is only invoked within FreeRDP-specific handlers for
* CLIPRDR, which are not assigned, and thus not callable, until after the
* relevant guac_rdp_clipboard structure is allocated and associated with
* the CliprdrClientContext */
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL);
guac_client* client = clipboard->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* We support CP-1252 and UTF-16 text */
CLIPRDR_GENERAL_CAPABILITY_SET cap_set = { CLIPRDR_GENERAL_CAPABILITY_SET cap_set = {
.capabilitySetType = CB_CAPSTYPE_GENERAL, /* CLIPRDR specification requires that this is CB_CAPSTYPE_GENERAL, the only defined set type */ .capabilitySetType = CB_CAPSTYPE_GENERAL, /* CLIPRDR specification requires that this is CB_CAPSTYPE_GENERAL, the only defined set type */
.capabilitySetLength = 12, /* The size of the capability set within the PDU - for CB_CAPSTYPE_GENERAL, this is ALWAYS 12 bytes */ .capabilitySetLength = 12, /* The size of the capability set within the PDU - for CB_CAPSTYPE_GENERAL, this is ALWAYS 12 bytes */
@ -119,7 +135,11 @@ static UINT guac_rdp_cliprdr_send_capabilities(CliprdrClientContext* cliprdr) {
.capabilitySets = (CLIPRDR_CAPABILITY_SET*) &cap_set .capabilitySets = (CLIPRDR_CAPABILITY_SET*) &cap_set
}; };
return cliprdr->ClientCapabilities(cliprdr, &caps); pthread_mutex_lock(&(rdp_client->message_lock));
int retval = cliprdr->ClientCapabilities(cliprdr, &caps);
pthread_mutex_unlock(&(rdp_client->message_lock));
return retval;
} }
@ -190,6 +210,9 @@ static UINT guac_rdp_cliprdr_send_format_data_request(
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom; guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL); assert(clipboard != NULL);
guac_client* client = clipboard->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Create new data request */ /* Create new data request */
CLIPRDR_FORMAT_DATA_REQUEST data_request = { CLIPRDR_FORMAT_DATA_REQUEST data_request = {
.requestedFormatId = format .requestedFormatId = format
@ -199,11 +222,14 @@ static UINT guac_rdp_cliprdr_send_format_data_request(
* data is received via a Format Data Response PDU */ * data is received via a Format Data Response PDU */
clipboard->requested_format = format; clipboard->requested_format = format;
guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Sending " guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Sending format data request.");
"format data request.");
/* Send request */ /* Send request */
return cliprdr->ClientFormatDataRequest(cliprdr, &data_request); pthread_mutex_lock(&(rdp_client->message_lock));
int retval = cliprdr->ClientFormatDataRequest(cliprdr, &data_request);
pthread_mutex_unlock(&(rdp_client->message_lock));
return retval;
} }
@ -265,15 +291,19 @@ static UINT guac_rdp_cliprdr_format_list(CliprdrClientContext* cliprdr,
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom; guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL); assert(clipboard != NULL);
guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Received " guac_client* client = clipboard->client;
"format list."); guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Received format list.");
CLIPRDR_FORMAT_LIST_RESPONSE format_list_response = { CLIPRDR_FORMAT_LIST_RESPONSE format_list_response = {
.msgFlags = CB_RESPONSE_OK .msgFlags = CB_RESPONSE_OK
}; };
/* Report successful processing of format list */ /* Report successful processing of format list */
pthread_mutex_lock(&(rdp_client->message_lock));
cliprdr->ClientFormatListResponse(cliprdr, &format_list_response); cliprdr->ClientFormatListResponse(cliprdr, &format_list_response);
pthread_mutex_unlock(&(rdp_client->message_lock));
/* Prefer Unicode (in this case, UTF-16) */ /* Prefer Unicode (in this case, UTF-16) */
if (guac_rdp_cliprdr_format_supported(format_list, CF_UNICODETEXT)) if (guac_rdp_cliprdr_format_supported(format_list, CF_UNICODETEXT))
@ -284,9 +314,9 @@ static UINT guac_rdp_cliprdr_format_list(CliprdrClientContext* cliprdr,
return guac_rdp_cliprdr_send_format_data_request(cliprdr, CF_TEXT); return guac_rdp_cliprdr_send_format_data_request(cliprdr, CF_TEXT);
/* Ignore any unsupported data */ /* Ignore any unsupported data */
guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Ignoring unsupported " guac_client_log(client, GUAC_LOG_DEBUG, "Ignoring unsupported clipboard "
"clipboard data. Only Unicode and text clipboard formats are " "data. Only Unicode and text clipboard formats are currently "
"currently supported."); "supported.");
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
@ -320,8 +350,10 @@ static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom; guac_rdp_clipboard* clipboard = (guac_rdp_clipboard*) cliprdr->custom;
assert(clipboard != NULL); assert(clipboard != NULL);
guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Received " guac_client* client = clipboard->client;
"format data request."); guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Received format data request.");
guac_iconv_write* writer; guac_iconv_write* writer;
const char* input = clipboard->clipboard->buffer; const char* input = clipboard->clipboard->buffer;
@ -341,11 +373,11 @@ static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
/* Warn if clipboard data cannot be sent as intended due to a violation /* Warn if clipboard data cannot be sent as intended due to a violation
* of the CLIPRDR spec */ * of the CLIPRDR spec */
default: default:
guac_client_log(clipboard->client, GUAC_LOG_WARNING, "Received " guac_client_log(client, GUAC_LOG_WARNING, "Received clipboard "
"clipboard data cannot be sent to the RDP server because " "data cannot be sent to the RDP server because the RDP "
"the RDP server has requested a clipboard format which " "server has requested a clipboard format which was not "
"was not declared as available. This violates the " "declared as available. This violates the specification "
"specification for the CLIPRDR channel."); "for the CLIPRDR channel.");
free(output); free(output);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
@ -363,10 +395,12 @@ static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
.msgFlags = CB_RESPONSE_OK .msgFlags = CB_RESPONSE_OK
}; };
guac_client_log(clipboard->client, GUAC_LOG_TRACE, "CLIPRDR: Sending " guac_client_log(client, GUAC_LOG_TRACE, "CLIPRDR: Sending format data response.");
"format data response.");
pthread_mutex_lock(&(rdp_client->message_lock));
UINT result = cliprdr->ClientFormatDataResponse(cliprdr, &data_response); UINT result = cliprdr->ClientFormatDataResponse(cliprdr, &data_response);
pthread_mutex_unlock(&(rdp_client->message_lock));
free(start); free(start);
return result; return result;
@ -407,9 +441,9 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
/* Ignore received data if copy has been disabled */ /* Ignore received data if copy has been disabled */
if (settings->disable_copy) { if (settings->disable_copy) {
guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Ignoring received " guac_client_log(client, GUAC_LOG_DEBUG, "Ignoring received clipboard "
"clipboard data as copying from within the remote desktop has " "data as copying from within the remote desktop has been "
"been explicitly disabled."); "explicitly disabled.");
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -439,9 +473,8 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
* here. The values which may be stored within requested_format are * here. The values which may be stored within requested_format are
* completely within our control. */ * completely within our control. */
default: default:
guac_client_log(clipboard->client, GUAC_LOG_DEBUG, "Requested " guac_client_log(client, GUAC_LOG_DEBUG, "Requested clipboard data "
"clipboard data in unsupported format (0x%X).", "in unsupported format (0x%X).", clipboard->requested_format);
clipboard->requested_format);
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -453,7 +486,7 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
int length = strnlen(received_data, sizeof(received_data)); int length = strnlen(received_data, sizeof(received_data));
guac_common_clipboard_reset(clipboard->clipboard, "text/plain"); guac_common_clipboard_reset(clipboard->clipboard, "text/plain");
guac_common_clipboard_append(clipboard->clipboard, received_data, length); guac_common_clipboard_append(clipboard->clipboard, received_data, length);
guac_common_clipboard_send(clipboard->clipboard, clipboard->client); guac_common_clipboard_send(clipboard->clipboard, client);
} }
return CHANNEL_RC_OK; return CHANNEL_RC_OK;

View File

@ -89,12 +89,16 @@ void guac_rdp_common_svc_write(guac_rdp_common_svc* svc,
return; return;
} }
guac_rdp_client* rdp_client = (guac_rdp_client*) svc->client->data;
/* NOTE: The wStream sent via pVirtualChannelWriteEx will automatically be /* NOTE: The wStream sent via pVirtualChannelWriteEx will automatically be
* freed later with a call to Stream_Free() when handling the * freed later with a call to Stream_Free() when handling the
* corresponding write cancel/completion event. */ * corresponding write cancel/completion event. */
pthread_mutex_lock(&(rdp_client->message_lock));
svc->_entry_points.pVirtualChannelWriteEx(svc->_init_handle, svc->_entry_points.pVirtualChannelWriteEx(svc->_init_handle,
svc->_open_handle, Stream_Buffer(output_stream), svc->_open_handle, Stream_Buffer(output_stream),
Stream_GetPosition(output_stream), output_stream); Stream_GetPosition(output_stream), output_stream);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }

View File

@ -31,9 +31,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
guac_rdp_disp* guac_rdp_disp_alloc() { guac_rdp_disp* guac_rdp_disp_alloc(guac_client* client) {
guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp)); guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp));
disp->client = client;
/* Not yet connected */ /* Not yet connected */
disp->disp = NULL; disp->disp = NULL;
@ -220,8 +221,17 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
}}; }};
/* Send display update notification if display channel is connected */ /* Send display update notification if display channel is connected */
if (disp->disp != NULL) if (disp->disp != NULL) {
guac_client* client = disp->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
pthread_mutex_lock(&(rdp_client->message_lock));
disp->disp->SendMonitorLayout(disp->disp, 1, monitors); disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
} }
} }

View File

@ -48,6 +48,11 @@
*/ */
typedef struct guac_rdp_disp { typedef struct guac_rdp_disp {
/**
* The guac_client instance handling the relevant RDP connection.
*/
guac_client* client;
/** /**
* Display control interface. * Display control interface.
*/ */
@ -81,10 +86,13 @@ typedef struct guac_rdp_disp {
* Allocates a new display update module, which will ultimately control the * Allocates a new display update module, which will ultimately control the
* display update channel once connected. * display update channel once connected.
* *
* @param client
* The guac_client instance handling the relevant RDP connection.
*
* @return * @return
* A newly-allocated display update module. * A newly-allocated display update module.
*/ */
guac_rdp_disp* guac_rdp_disp_alloc(); guac_rdp_disp* guac_rdp_disp_alloc(guac_client* client);
/** /**
* Frees the resources associated with support for the RDP Display Update * Frees the resources associated with support for the RDP Display Update

View File

@ -86,7 +86,10 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
}; };
/* Send client handshake response */ /* Send client handshake response */
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientHandshake(rail, &handshake); status = rail->ClientHandshake(rail, &handshake);
pthread_mutex_unlock(&(rdp_client->message_lock));
if (status != CHANNEL_RC_OK) if (status != CHANNEL_RC_OK)
return status; return status;
@ -95,7 +98,10 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
}; };
/* Send client status */ /* Send client status */
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientInformation(rail, &client_status); status = rail->ClientInformation(rail, &client_status);
pthread_mutex_unlock(&(rdp_client->message_lock));
if (status != CHANNEL_RC_OK) if (status != CHANNEL_RC_OK)
return status; return status;
@ -139,7 +145,10 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
}; };
/* Send client system parameters */ /* Send client system parameters */
pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientSystemParam(rail, &sysparam); status = rail->ClientSystemParam(rail, &sysparam);
pthread_mutex_unlock(&(rdp_client->message_lock));
if (status != CHANNEL_RC_OK) if (status != CHANNEL_RC_OK)
return status; return status;
@ -151,7 +160,11 @@ static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
}; };
/* Execute desired RemoteApp command */ /* Execute desired RemoteApp command */
return rail->ClientExecute(rail, &exec); pthread_mutex_lock(&(rdp_client->message_lock));
status = rail->ClientExecute(rail, &exec);
pthread_mutex_unlock(&(rdp_client->message_lock));
return status;
} }

View File

@ -32,9 +32,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
guac_rdp_rdpei* guac_rdp_rdpei_alloc() { guac_rdp_rdpei* guac_rdp_rdpei_alloc(guac_client* client) {
guac_rdp_rdpei* rdpei = malloc(sizeof(guac_rdp_rdpei)); guac_rdp_rdpei* rdpei = malloc(sizeof(guac_rdp_rdpei));
rdpei->client = client;
/* Not yet connected */ /* Not yet connected */
rdpei->rdpei = NULL; rdpei->rdpei = NULL;
@ -107,6 +108,9 @@ void guac_rdp_rdpei_load_plugin(rdpContext* context) {
int guac_rdp_rdpei_touch_update(guac_rdp_rdpei* rdpei, int id, int x, int y, int guac_rdp_rdpei_touch_update(guac_rdp_rdpei* rdpei, int id, int x, int y,
double force) { double force) {
guac_client* client = rdpei->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
int contact_id; /* Ignored */ int contact_id; /* Ignored */
/* Track touches only if channel is connected */ /* Track touches only if channel is connected */
@ -149,20 +153,31 @@ int guac_rdp_rdpei_touch_update(guac_rdp_rdpei* rdpei, int id, int x, int y,
if (!touch->active) if (!touch->active)
return 1; return 1;
pthread_mutex_lock(&(rdp_client->message_lock));
context->TouchEnd(context, id, x, y, &contact_id); context->TouchEnd(context, id, x, y, &contact_id);
pthread_mutex_unlock(&(rdp_client->message_lock));
touch->active = 0; touch->active = 0;
} }
/* Signal the start of a touch if this is the first we've seen it */ /* Signal the start of a touch if this is the first we've seen it */
else if (!touch->active) { else if (!touch->active) {
pthread_mutex_lock(&(rdp_client->message_lock));
context->TouchBegin(context, id, x, y, &contact_id); context->TouchBegin(context, id, x, y, &contact_id);
pthread_mutex_unlock(&(rdp_client->message_lock));
touch->active = 1; touch->active = 1;
} }
/* Established touches need only be updated */ /* Established touches need only be updated */
else else {
pthread_mutex_lock(&(rdp_client->message_lock));
context->TouchUpdate(context, id, x, y, &contact_id); context->TouchUpdate(context, id, x, y, &contact_id);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
return 0; return 0;

View File

@ -66,6 +66,11 @@ typedef struct guac_rdp_rdpei_touch {
*/ */
typedef struct guac_rdp_rdpei { typedef struct guac_rdp_rdpei {
/**
* The guac_client instance handling the relevant RDP connection.
*/
guac_client* client;
/** /**
* RDPEI control interface. * RDPEI control interface.
*/ */
@ -83,10 +88,13 @@ typedef struct guac_rdp_rdpei {
* channel once connected. The RDPEI channel allows multi-touch input * channel once connected. The RDPEI channel allows multi-touch input
* events to be sent to the RDP server. * events to be sent to the RDP server.
* *
* @param client
* The guac_client instance handling the relevant RDP connection.
*
* @return * @return
* A newly-allocated RDPEI module. * A newly-allocated RDPEI module.
*/ */
guac_rdp_rdpei* guac_rdp_rdpei_alloc(); guac_rdp_rdpei* guac_rdp_rdpei_alloc(guac_client* client);
/** /**
* Frees the resources associated with support for the RDPEI channel. Only * Frees the resources associated with support for the RDPEI channel. Only

View File

@ -145,10 +145,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
rdp_client->clipboard = guac_rdp_clipboard_alloc(client); rdp_client->clipboard = guac_rdp_clipboard_alloc(client);
/* Init display update module */ /* Init display update module */
rdp_client->disp = guac_rdp_disp_alloc(); rdp_client->disp = guac_rdp_disp_alloc(client);
/* Init multi-touch support module (RDPEI) */ /* Init multi-touch support module (RDPEI) */
rdp_client->rdpei = guac_rdp_rdpei_alloc(); rdp_client->rdpei = guac_rdp_rdpei_alloc(client);
/* Redirect FreeRDP log messages to guac_client_log() */ /* Redirect FreeRDP log messages to guac_client_log() */
guac_rdp_redirect_wlog(client); guac_rdp_redirect_wlog(client);
@ -158,8 +158,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
pthread_mutexattr_settype(&(rdp_client->attributes), pthread_mutexattr_settype(&(rdp_client->attributes),
PTHREAD_MUTEX_RECURSIVE); PTHREAD_MUTEX_RECURSIVE);
/* Initalize the lock */ /* Init required locks */
pthread_rwlock_init(&(rdp_client->lock), NULL); pthread_rwlock_init(&(rdp_client->lock), NULL);
pthread_mutex_init(&(rdp_client->message_lock), &(rdp_client->attributes));
/* Set handlers */ /* Set handlers */
client->join_handler = guac_rdp_user_join_handler; client->join_handler = guac_rdp_user_join_handler;
@ -226,6 +227,7 @@ int guac_rdp_client_free_handler(guac_client* client) {
guac_rdp_audio_buffer_free(rdp_client->audio_input); guac_rdp_audio_buffer_free(rdp_client->audio_input);
pthread_rwlock_destroy(&(rdp_client->lock)); pthread_rwlock_destroy(&(rdp_client->lock));
pthread_mutex_destroy(&(rdp_client->message_lock));
/* Free client data */ /* Free client data */
free(rdp_client); free(rdp_client);

View File

@ -54,8 +54,11 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
guac_common_recording_report_mouse(rdp_client->recording, x, y, mask); guac_common_recording_report_mouse(rdp_client->recording, x, y, mask);
/* If button mask unchanged, just send move event */ /* If button mask unchanged, just send move event */
if (mask == rdp_client->mouse_button_mask) if (mask == rdp_client->mouse_button_mask) {
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y); rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
}
/* Otherwise, send events describing button change */ /* Otherwise, send events describing button change */
else { else {
@ -75,7 +78,9 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3; if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2; if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y); rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }
@ -91,7 +96,9 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88; if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88;
/* Send event */ /* Send event */
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y); rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }
@ -99,18 +106,18 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
if (pressed_mask & 0x18) { if (pressed_mask & 0x18) {
/* Down */ /* Down */
if (pressed_mask & 0x08) if (pressed_mask & 0x08) {
rdp_inst->input->MouseEvent( pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input, rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_WHEEL | 0x78, x, y);
PTR_FLAGS_WHEEL | 0x78, pthread_mutex_unlock(&(rdp_client->message_lock));
x, y); }
/* Up */ /* Up */
if (pressed_mask & 0x10) if (pressed_mask & 0x10) {
rdp_inst->input->MouseEvent( pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input, rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88, x, y);
PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88, pthread_mutex_unlock(&(rdp_client->message_lock));
x, y); }
} }

View File

@ -104,8 +104,9 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client,
return; return;
/* Send actual key */ /* Send actual key */
rdp_inst->input->KeyboardEvent(rdp_inst->input, pthread_mutex_lock(&(rdp_client->message_lock));
flags | pressed_flags, scancode); rdp_inst->input->KeyboardEvent(rdp_inst->input, flags | pressed_flags, scancode);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }
@ -132,9 +133,9 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
return; return;
/* Send Unicode event */ /* Send Unicode event */
rdp_inst->input->UnicodeKeyboardEvent( pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input, rdp_inst->input->UnicodeKeyboardEvent(rdp_inst->input, 0, codepoint);
0, codepoint); pthread_mutex_unlock(&(rdp_client->message_lock));
} }
@ -161,7 +162,9 @@ static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client,
return; return;
/* Synchronize lock key states */ /* Synchronize lock key states */
pthread_mutex_lock(&(rdp_client->message_lock));
rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags); rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }

View File

@ -247,6 +247,8 @@ static void guac_rdp_ai_send_formatchange(IWTSVirtualChannel* channel,
void guac_rdp_ai_process_version(guac_client* client, void guac_rdp_ai_process_version(guac_client* client,
IWTSVirtualChannel* channel, wStream* stream) { IWTSVirtualChannel* channel, wStream* stream) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Verify we have at least 4 bytes available (UINT32) */ /* Verify we have at least 4 bytes available (UINT32) */
if (Stream_GetRemainingLength(stream) < 4) { if (Stream_GetRemainingLength(stream) < 4) {
guac_client_log(client, GUAC_LOG_WARNING, "Audio input Versoin PDU " guac_client_log(client, GUAC_LOG_WARNING, "Audio input Versoin PDU "
@ -269,8 +271,9 @@ void guac_rdp_ai_process_version(guac_client* client,
Stream_Write_UINT32(response, 1); /* Version */ Stream_Write_UINT32(response, 1); /* Version */
/* Send response */ /* Send response */
channel->Write(channel, (UINT32) Stream_GetPosition(response), pthread_mutex_lock(&(rdp_client->message_lock));
Stream_Buffer(response), NULL); channel->Write(channel, (UINT32) Stream_GetPosition(response), Stream_Buffer(response), NULL);
pthread_mutex_unlock(&(rdp_client->message_lock));
Stream_Free(response, TRUE); Stream_Free(response, TRUE);
} }
@ -313,26 +316,34 @@ void guac_rdp_ai_process_formats(guac_client* client,
format.channels, format.bps / 8); format.channels, format.bps / 8);
/* Accept single format */ /* Accept single format */
pthread_mutex_lock(&(rdp_client->message_lock));
guac_rdp_ai_send_incoming_data(channel); guac_rdp_ai_send_incoming_data(channel);
guac_rdp_ai_send_formats(channel, &format, 1); guac_rdp_ai_send_formats(channel, &format, 1);
pthread_mutex_unlock(&(rdp_client->message_lock));
return; return;
} }
/* No formats available */ /* No formats available */
guac_client_log(client, GUAC_LOG_WARNING, "AUDIO_INPUT: No WAVE format."); guac_client_log(client, GUAC_LOG_WARNING, "AUDIO_INPUT: No WAVE format.");
pthread_mutex_lock(&(rdp_client->message_lock));
guac_rdp_ai_send_incoming_data(channel); guac_rdp_ai_send_incoming_data(channel);
guac_rdp_ai_send_formats(channel, NULL, 0); guac_rdp_ai_send_formats(channel, NULL, 0);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }
void guac_rdp_ai_flush_packet(char* buffer, int length, void* data) { void guac_rdp_ai_flush_packet(guac_rdp_audio_buffer* audio_buffer, int length) {
IWTSVirtualChannel* channel = (IWTSVirtualChannel*) data; guac_client* client = audio_buffer->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
IWTSVirtualChannel* channel = (IWTSVirtualChannel*) audio_buffer->data;
/* Send data over channel */ /* Send data over channel */
pthread_mutex_lock(&(rdp_client->message_lock));
guac_rdp_ai_send_incoming_data(channel); guac_rdp_ai_send_incoming_data(channel);
guac_rdp_ai_send_data(channel, buffer, length); guac_rdp_ai_send_data(channel, audio_buffer->packet, length);
pthread_mutex_unlock(&(rdp_client->message_lock));
} }
@ -363,8 +374,10 @@ void guac_rdp_ai_process_open(guac_client* client,
audio_buffer->out_format.bps); audio_buffer->out_format.bps);
/* Success */ /* Success */
pthread_mutex_lock(&(rdp_client->message_lock));
guac_rdp_ai_send_formatchange(channel, initial_format); guac_rdp_ai_send_formatchange(channel, initial_format);
guac_rdp_ai_send_open_reply(channel, 0); guac_rdp_ai_send_open_reply(channel, 0);
pthread_mutex_unlock(&(rdp_client->message_lock));
/* Begin receiving audio data */ /* Begin receiving audio data */
guac_rdp_audio_buffer_begin(audio_buffer, packet_frames, guac_rdp_audio_buffer_begin(audio_buffer, packet_frames,

View File

@ -107,7 +107,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
/* Load "AUDIO_INPUT" plugin for audio input*/ /* Load "AUDIO_INPUT" plugin for audio input*/
if (settings->enable_audio_input) { if (settings->enable_audio_input) {
rdp_client->audio_input = guac_rdp_audio_buffer_alloc(); rdp_client->audio_input = guac_rdp_audio_buffer_alloc(client);
guac_rdp_audio_load_plugin(instance->context); guac_rdp_audio_load_plugin(instance->context);
} }
@ -564,13 +564,16 @@ static int guac_rdp_handle_connection(guac_client* client) {
guac_timestamp frame_end; guac_timestamp frame_end;
int frame_remaining; int frame_remaining;
/* Check the libfreerdp fds */ /* Handle any queued FreeRDP events (this may result in RDP
if (!freerdp_check_event_handles(rdp_inst->context)) { * messages being sent) */
pthread_mutex_lock(&(rdp_client->message_lock));
int event_result = freerdp_check_event_handles(rdp_inst->context);
pthread_mutex_unlock(&(rdp_client->message_lock));
/* Flag connection failure */ /* Abort if FreeRDP event handling fails */
if (!event_result) {
wait_result = -1; wait_result = -1;
break; break;
} }
/* Calculate time remaining in frame */ /* Calculate time remaining in frame */
@ -634,7 +637,9 @@ static int guac_rdp_handle_connection(guac_client* client) {
} }
/* Disconnect client and channels */ /* Disconnect client and channels */
pthread_mutex_lock(&(rdp_client->message_lock));
freerdp_disconnect(rdp_inst); freerdp_disconnect(rdp_inst);
pthread_mutex_unlock(&(rdp_client->message_lock));
/* Clean up FreeRDP internal GDI implementation */ /* Clean up FreeRDP internal GDI implementation */
gdi_free(rdp_inst); gdi_free(rdp_inst);

View File

@ -172,6 +172,12 @@ typedef struct guac_rdp_client {
*/ */
pthread_rwlock_t lock; pthread_rwlock_t lock;
/**
* Lock which synchronizes the sending of each RDP message, ensuring
* attempts to send RDP messages never overlap.
*/
pthread_mutex_t message_lock;
} guac_rdp_client; } guac_rdp_client;
/** /**