in src/protocols/rdp/rdp.c [511:698]
static int guac_rdp_handle_connection(guac_client* client) {
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_settings* settings = rdp_client->settings;
/* Init random number generator */
srandom(time(NULL));
guac_rwlock_acquire_write_lock(&(rdp_client->lock));
/* Create display */
rdp_client->display = guac_display_alloc(client);
guac_display_layer* default_layer = guac_display_default_layer(rdp_client->display);
guac_display_layer_resize(default_layer, rdp_client->settings->width, rdp_client->settings->height);
/* Use lossless compression only if requested (otherwise, use default
* heuristics) */
guac_display_layer_set_lossless(default_layer, settings->lossless);
rdp_client->current_surface = default_layer;
rdp_client->available_svc = guac_common_list_alloc();
/* Init client */
freerdp* rdp_inst = freerdp_new();
/*
* If the freerdp instance has a LoadChannels callback for loading plugins
* we use that instead of the PreConnect callback to load plugins.
*/
#ifdef RDP_INST_HAS_LOAD_CHANNELS
rdp_inst->LoadChannels = rdp_freerdp_load_channels;
#endif
rdp_inst->PreConnect = rdp_freerdp_pre_connect;
rdp_inst->Authenticate = rdp_freerdp_authenticate;
#ifdef HAVE_FREERDP_VERIFYCERTIFICATEEX
rdp_inst->VerifyCertificateEx = rdp_freerdp_verify_certificate;
#else
rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
#endif
/* Allocate FreeRDP context */
rdp_inst->ContextSize = sizeof(rdp_freerdp_context);
if (!freerdp_context_new(rdp_inst)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"FreeRDP initialization failed before connecting. Please "
"check for errors earlier in the logs and/or enable "
"debug-level logging for guacd.");
goto fail;
}
((rdp_freerdp_context*) GUAC_RDP_CONTEXT(rdp_inst))->client = client;
/* Load keymap into client */
rdp_client->keyboard = guac_rdp_keyboard_alloc(client,
settings->server_layout);
/* Set default pointer */
guac_display_set_cursor(rdp_client->display, GUAC_DISPLAY_CURSOR_POINTER);
/*
* Downgrade the lock to allow for concurrent read access.
* Access to read locks needs to be made available for other processes such
* as the join_pending_handler to use while we await credentials from the user.
*/
guac_rwlock_release_lock(&(rdp_client->lock));
guac_rwlock_acquire_read_lock(&(rdp_client->lock));
/* Connect to RDP server */
if (!freerdp_connect(rdp_inst)) {
guac_rdp_client_abort(client, rdp_inst);
goto fail;
}
/* Upgrade to write lock again for further exclusive operations */
guac_rwlock_release_lock(&(rdp_client->lock));
guac_rwlock_acquire_write_lock(&(rdp_client->lock));
/* Connection complete */
rdp_client->rdp_inst = rdp_inst;
/* Signal that reconnect has been completed */
guac_rdp_disp_reconnect_complete(rdp_client->disp);
guac_rwlock_release_lock(&(rdp_client->lock));
rdp_client->render_thread = guac_display_render_thread_create(rdp_client->display);
/* Handle messages from RDP server while client is running */
while (client->state == GUAC_CLIENT_RUNNING
&& !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {
/* Update remote display size */
guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);
/* Wait for data and construct a reasonable frame */
int wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_MESSAGE_CHECK_INTERVAL);
if (wait_result < 0)
break;
/* Handle any queued FreeRDP events (this may result in RDP messages
* being sent), aborting later if FreeRDP event handling fails */
if (!guac_rdp_handle_events(rdp_client))
wait_result = -1;
/* Test whether the RDP server is closing the connection */
int connection_closing;
#ifdef HAVE_DISCONNECT_CONTEXT
connection_closing = freerdp_shall_disconnect_context(rdp_inst->context);
#else
connection_closing = freerdp_shall_disconnect(rdp_inst);
#endif
/* Close connection cleanly if server is disconnecting */
if (connection_closing)
guac_rdp_client_abort(client, rdp_inst);
/* If a low-level connection error occurred, fail */
else if (wait_result < 0)
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Connection closed.");
}
guac_rwlock_acquire_write_lock(&(rdp_client->lock));
/* Clean up print job, if active */
if (rdp_client->active_job != NULL) {
guac_rdp_print_job_kill(rdp_client->active_job);
guac_rdp_print_job_free(rdp_client->active_job);
}
/* Disconnect client and channels */
pthread_mutex_lock(&(rdp_client->message_lock));
freerdp_disconnect(rdp_inst);
pthread_mutex_unlock(&(rdp_client->message_lock));
/* Stop render loop */
guac_display_render_thread_destroy(rdp_client->render_thread);
rdp_client->render_thread = NULL;
/* Remove reference to FreeRDP's GDI buffer so that it can be safely freed
* prior to freeing the guac_display */
guac_display_layer_raw_context* context = guac_display_layer_open_raw(default_layer);
context->buffer = NULL;
guac_display_layer_close_raw(default_layer, context);
/* Clean up FreeRDP internal GDI implementation (this must be done BEFORE
* freeing the guac_display, as freeing the GDI will free objects like
* rdpPointer that will attempt to free associated guac_display_layer
* instances during cleanup) */
gdi_free(rdp_inst);
/* Free display */
guac_display_free(rdp_client->display);
rdp_client->display = NULL;
/* Clean up RDP client context */
freerdp_context_free(rdp_inst);
/* Clean up RDP client */
freerdp_free(rdp_inst);
rdp_client->rdp_inst = NULL;
/* Free SVC list */
guac_common_list_free(rdp_client->available_svc, NULL);
rdp_client->available_svc = NULL;
/* Free RDP keyboard state */
guac_rdp_keyboard_free(rdp_client->keyboard);
rdp_client->keyboard = NULL;
guac_rwlock_release_lock(&(rdp_client->lock));
/* Client is now disconnected */
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
return 0;
fail:
guac_rwlock_release_lock(&(rdp_client->lock));
return 1;
}