in src/protocols/rdp/rdp.c [473:675]
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));
pthread_rwlock_wrlock(&(rdp_client->lock));
/* Create display */
rdp_client->display = guac_common_display_alloc(client,
rdp_client->settings->width,
rdp_client->settings->height);
/* Use lossless compression only if requested (otherwise, use default
* heuristics) */
guac_common_display_set_lossless(rdp_client->display, settings->lossless);
rdp_client->current_surface = rdp_client->display->default_surface;
rdp_client->available_svc = guac_common_list_alloc();
/* Init client */
freerdp* rdp_inst = freerdp_new();
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*) rdp_inst->context)->client = client;
/* Load keymap into client */
rdp_client->keyboard = guac_rdp_keyboard_alloc(client,
settings->server_layout);
/* Set default pointer */
guac_common_cursor_set_pointer(rdp_client->display->cursor);
/* Connect to RDP server */
if (!freerdp_connect(rdp_inst)) {
guac_rdp_client_abort(client, rdp_inst);
goto fail;
}
/* Connection complete */
rdp_client->rdp_inst = rdp_inst;
/* Signal that reconnect has been completed */
guac_rdp_disp_reconnect_complete(rdp_client->disp);
pthread_rwlock_unlock(&(rdp_client->lock));
/* 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_FRAME_START_TIMEOUT);
if (wait_result > 0) {
int processing_lag = guac_client_get_processing_lag(client);
/* Read server messages until frame is built */
do {
guac_timestamp frame_end;
int frame_remaining;
/* Handle any queued FreeRDP events (this may result in RDP
* 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));
/* Abort if FreeRDP event handling fails */
if (!event_result) {
wait_result = -1;
break;
}
/* Continue handling inbound data if we are in the middle of an RDP frame */
if (rdp_client->in_frame) {
wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_FRAME_START_TIMEOUT);
if (wait_result >= 0)
continue;
}
/* Calculate time remaining in frame */
guac_timestamp frame_start = client->last_sent_timestamp;
frame_end = guac_timestamp_current();
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
- frame_end;
/* Calculate time that client needs to catch up */
int time_elapsed = frame_end - frame_start;
int required_wait = processing_lag - time_elapsed;
/* Increase the duration of this frame if client is lagging */
if (required_wait > GUAC_RDP_FRAME_TIMEOUT)
wait_result = rdp_guac_client_wait_for_messages(client,
required_wait);
/* Wait again if frame remaining */
else if (frame_remaining > 0)
wait_result = rdp_guac_client_wait_for_messages(client,
GUAC_RDP_FRAME_TIMEOUT);
else
break;
} while (wait_result > 0);
}
/* Test whether the RDP server is closing the connection */
int connection_closing = freerdp_shall_disconnect(rdp_inst);
/* 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.");
/* Flush frame only if successful and an RDP frame is not known to be
* in progress */
else if (!rdp_client->frames_supported || rdp_client->frames_received) {
guac_common_display_flush(rdp_client->display);
guac_client_end_multiple_frames(client, rdp_client->frames_received);
guac_socket_flush(client->socket);
rdp_client->frames_received = 0;
}
}
pthread_rwlock_wrlock(&(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));
/* Clean up FreeRDP internal GDI implementation */
gdi_free(rdp_inst);
/* 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);
rdp_client->available_svc = NULL;
/* Free RDP keyboard state */
guac_rdp_keyboard_free(rdp_client->keyboard);
rdp_client->keyboard = NULL;
/* Free display */
guac_common_display_free(rdp_client->display);
rdp_client->display = NULL;
pthread_rwlock_unlock(&(rdp_client->lock));
/* Client is now disconnected */
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
return 0;
fail:
pthread_rwlock_unlock(&(rdp_client->lock));
return 1;
}