int guac_sftp_blob_handler(guac_client* client, guac_stream* stream, void* data, int length) { /* Pull file from stream */ ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; /* Attempt write */ if (libssh2_sftp_write(file, data, length) == length) { guac_protocol_send_ack(client->socket, stream, "SFTP: OK", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); } /* Inform of any errors */ else { guac_client_log_error(client, "Unable to write to file: %s", libssh2_sftp_last_error(client_data->sftp_session)); guac_protocol_send_ack(client->socket, stream, "SFTP: Write failed", GUAC_PROTOCOL_STATUS_INTERNAL_ERROR); guac_socket_flush(client->socket); } return 0; }
int guac_rdp_upload_end_handler(guac_client* client, guac_stream* stream) { guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; /* Get filesystem, return error if no filesystem */ guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem; if (fs == NULL) { guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)", GUAC_PROTOCOL_STATUS_SERVER_ERROR); guac_socket_flush(client->socket); return 0; } /* Close file */ guac_rdp_fs_close(fs, rdp_stream->upload_status.file_id); /* Acknowledge stream end */ guac_protocol_send_ack(client->socket, stream, "OK (STREAM END)", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); free(rdp_stream); return 0; }
int guac_rdp_download_ack_handler(guac_client* client, guac_stream* stream, char* message, guac_protocol_status status) { guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; /* Get filesystem, return error if no filesystem */ guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem; if (fs == NULL) { guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)", GUAC_PROTOCOL_STATUS_SERVER_ERROR); guac_socket_flush(client->socket); return 0; } /* If successful, read data */ if (status == GUAC_PROTOCOL_STATUS_SUCCESS) { /* Attempt read into buffer */ char buffer[4096]; int bytes_read = guac_rdp_fs_read(fs, rdp_stream->download_status.file_id, rdp_stream->download_status.offset, buffer, sizeof(buffer)); /* If bytes read, send as blob */ if (bytes_read > 0) { rdp_stream->download_status.offset += bytes_read; guac_protocol_send_blob(client->socket, stream, buffer, bytes_read); } /* If EOF, send end */ else if (bytes_read == 0) { guac_protocol_send_end(client->socket, stream); guac_client_free_stream(client, stream); free(rdp_stream); } /* Otherwise, fail stream */ else { guac_client_log(client, GUAC_LOG_ERROR, "Error reading file for download"); guac_protocol_send_end(client->socket, stream); guac_client_free_stream(client, stream); free(rdp_stream); } guac_socket_flush(client->socket); } /* Otherwise, return stream to client */ else guac_client_free_stream(client, stream); return 0; }
static void __stream_read_callback(pa_stream* stream, size_t length, void* data) { guac_client* client = (guac_client*) data; vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data; guac_audio_stream* audio = client_data->audio; const void* buffer; /* Read data */ pa_stream_peek(stream, &buffer, &length); /* Avoid sending silence unless data is waiting to be flushed */ if (audio->pcm_bytes_written != 0 || !guac_pa_is_silence(buffer, length)) { /* Write data */ guac_audio_stream_write_pcm(audio, buffer, length); /* Flush occasionally */ if (audio->pcm_bytes_written > GUAC_VNC_PCM_WRITE_RATE) { guac_audio_stream_end(audio); guac_audio_stream_begin(client_data->audio, GUAC_VNC_AUDIO_RATE, GUAC_VNC_AUDIO_CHANNELS, GUAC_VNC_AUDIO_BPS); guac_socket_flush(client->socket); } } /* Advance buffer */ pa_stream_drop(stream); }
int guac_rdp_user_file_handler(guac_user* user, guac_stream* stream, char* mimetype, char* filename) { guac_rdp_client* rdp_client = (guac_rdp_client*) user->client->data; #ifdef ENABLE_COMMON_SSH guac_rdp_settings* settings = rdp_client->settings; /* If SFTP is enabled, it should be used for default uploads only if RDPDR * is not enabled or its upload directory has been set */ if (rdp_client->sftp_filesystem != NULL) { if (!settings->drive_enabled || settings->sftp_directory != NULL) return guac_rdp_sftp_file_handler(user, stream, mimetype, filename); } #endif /* Default to using RDPDR uploads (if enabled) */ if (rdp_client->filesystem != NULL) return guac_rdp_upload_file_handler(user, stream, mimetype, filename); /* File transfer not enabled */ guac_protocol_send_ack(user->socket, stream, "File transfer disabled", GUAC_PROTOCOL_STATUS_UNSUPPORTED); guac_socket_flush(user->socket); return 0; }
int guac_rdp_svc_pipe_handler(guac_client* client, guac_stream* stream, char* mimetype, char* name) { guac_rdp_stream* rdp_stream; guac_rdp_svc* svc = guac_rdp_get_svc(client, name); /* Fail if no such SVC */ if (svc == NULL) { guac_client_log(client, GUAC_LOG_ERROR, "Requested non-existent pipe: \"%s\".", name); guac_protocol_send_ack(client->socket, stream, "FAIL (NO SUCH PIPE)", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST); guac_socket_flush(client->socket); return 0; } else guac_client_log(client, GUAC_LOG_ERROR, "Inbound half of channel \"%s\" connected.", name); /* Init stream data */ stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream)); stream->blob_handler = guac_rdp_svc_blob_handler; rdp_stream->type = GUAC_RDP_INBOUND_SVC_STREAM; rdp_stream->svc = svc; svc->input_pipe = stream; return 0; }
guac_stream* guac_sftp_download_file(guac_client* client, char* filename) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_stream* stream; LIBSSH2_SFTP_HANDLE* file; /* Attempt to open file for reading */ file = libssh2_sftp_open(client_data->sftp_session, filename, LIBSSH2_FXF_READ, 0); if (file == NULL) { guac_client_log_error(client, "Unable to read file \"%s\": %s", filename, libssh2_sftp_last_error(client_data->sftp_session)); return NULL; } /* Allocate stream */ stream = guac_client_alloc_stream(client); stream->ack_handler = guac_sftp_ack_handler; stream->data = file; /* Send stream start, strip name */ filename = basename(filename); guac_protocol_send_file(client->socket, stream, "application/octet-stream", filename); guac_socket_flush(client->socket); return stream; }
int guac_vnc_user_join_handler(guac_user* user, int argc, char** argv) { guac_vnc_client* vnc_client = (guac_vnc_client*) user->client->data; /* Connect via VNC if owner */ if (user->owner) { /* Parse arguments into client */ guac_vnc_settings* settings = vnc_client->settings = guac_vnc_parse_args(user, argc, (const char**) argv); /* Fail if settings cannot be parsed */ if (settings == NULL) { guac_user_log(user, GUAC_LOG_INFO, "Badly formatted client arguments."); return 1; } /* Start client thread */ if (pthread_create(&vnc_client->client_thread, NULL, guac_vnc_client_thread, user->client)) { guac_user_log(user, GUAC_LOG_ERROR, "Unable to start VNC client thread."); return 1; } } /* If not owner, synchronize with current state */ else { #ifdef ENABLE_PULSE /* Synchronize an audio stream */ if (vnc_client->audio) guac_audio_stream_add_user(vnc_client->audio, user); #endif /* Synchronize with current display */ guac_common_display_dup(vnc_client->display, user, user->socket); guac_socket_flush(user->socket); } /* Only handle events if not read-only */ if (vnc_client->settings->read_only == 0) { /* General mouse/keyboard/clipboard events */ user->mouse_handler = guac_vnc_user_mouse_handler; user->key_handler = guac_vnc_user_key_handler; user->clipboard_handler = guac_vnc_clipboard_handler; #ifdef ENABLE_COMMON_SSH /* Set generic (non-filesystem) file upload handler */ user->file_handler = guac_vnc_sftp_file_handler; #endif } return 0; }
int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream, void* data, int length) { int bytes_written; guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; /* Get filesystem, return error if no filesystem 0*/ guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem; if (fs == NULL) { guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)", GUAC_PROTOCOL_STATUS_SERVER_ERROR); guac_socket_flush(client->socket); return 0; } /* Write entire block */ while (length > 0) { /* Attempt write */ bytes_written = guac_rdp_fs_write(fs, rdp_stream->upload_status.file_id, rdp_stream->upload_status.offset, data, length); /* On error, abort */ if (bytes_written < 0) { guac_protocol_send_ack(client->socket, stream, "FAIL (BAD WRITE)", GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); guac_socket_flush(client->socket); return 0; } /* Update counters */ rdp_stream->upload_status.offset += bytes_written; data += bytes_written; length -= bytes_written; } guac_protocol_send_ack(client->socket, stream, "OK (DATA RECEIVED)", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); return 0; }
/** * Called periodically by guacd whenever the plugin should handle accumulated * data and render a frame. * * @param client * The guac_client associated with the plugin that should render a frame. * * @return * Non-zero if an error occurs, zero otherwise. */ static int streamtest_client_message_handler(guac_client* client) { /* Get stream state from client */ streamtest_state* state = (streamtest_state*) client->data; int frame_start; int frame_duration; /* Record start of frame */ frame_start = streamtest_utime(); /* Read from stream and write as blob(s) */ if (!state->paused) { /* Attempt to fill the available buffer space */ int length = streamtest_fill_buffer(state->fd, state->frame_buffer, state->frame_bytes); /* Abort connection if we cannot read */ if (length == -1) { guac_client_log(client, GUAC_LOG_ERROR, "Unable to read from specified file: %s", strerror(errno)); return 1; } /* Write all data read as blobs */ if (length > 0) streamtest_write_blobs(client->socket, state->stream, state->frame_buffer, length); /* Disconnect on EOF */ else { guac_client_log(client, GUAC_LOG_INFO, "Media streaming complete"); guac_client_stop(client); } } /* Update progress bar */ streamtest_render_progress(client); guac_socket_flush(client->socket); /* Sleep for remainder of frame */ frame_duration = streamtest_utime() - frame_start; if (frame_duration < state->frame_duration) streamtest_usleep(state->frame_duration - frame_duration); /* Warn (at debug level) if frame takes too long */ else guac_client_log(client, GUAC_LOG_DEBUG, "Frame took longer than requested duration: %i microseconds", frame_duration); /* Success */ return 0; }
void* __guacd_client_output_thread(void* data) { guac_client* client = (guac_client*) data; guac_socket* socket = client->socket; /* Guacamole client output loop */ while (client->state == GUAC_CLIENT_RUNNING) { /* Handle server messages */ if (client->handle_messages) { /* Only handle messages if synced within threshold */ if (client->last_sent_timestamp - client->last_received_timestamp < GUACD_SYNC_THRESHOLD) { int retval = client->handle_messages(client); if (retval) { guacd_client_log_guac_error(client, "Error handling server messages"); guac_client_stop(client); return NULL; } /* Send sync instruction */ client->last_sent_timestamp = guac_timestamp_current(); if (guac_protocol_send_sync(socket, client->last_sent_timestamp)) { guacd_client_log_guac_error(client, "Error sending \"sync\" instruction"); guac_client_stop(client); return NULL; } /* Flush */ if (guac_socket_flush(socket)) { guacd_client_log_guac_error(client, "Error flushing output"); guac_client_stop(client); return NULL; } } /* Do not spin while waiting for old sync */ else __guacdd_sleep(GUACD_MESSAGE_HANDLE_FREQUENCY); } /* If no message handler, just sleep until next sync ping */ else __guacdd_sleep(GUACD_SYNC_FREQUENCY); } /* End of output loop */ guac_client_stop(client); return NULL; }
/** * Callback which is invoked by guac_client_foreach_user() to flush all * pending data on the given user's socket. If an error occurs while flushing * a user's socket, that user is signalled to stop with guac_user_stop(). * * @param user * The user whose socket should be flushed. * * @param data * Arbitrary data passed to guac_client_foreach_user(). This is not needed * by this callback, and should be left as NULL. * * @return * Always NULL. */ static void* __flush_callback(guac_user* user, void* data) { /* Attempt flush, disconnect on failure */ if (guac_socket_flush(user->socket)) guac_user_stop(user); return NULL; }
int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream, char* mimetype, char* filename) { int file_id; guac_rdp_stream* rdp_stream; char file_path[GUAC_RDP_FS_MAX_PATH]; /* Get filesystem, return error if no filesystem */ guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem; if (fs == NULL) { guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)", GUAC_PROTOCOL_STATUS_SERVER_ERROR); guac_socket_flush(client->socket); return 0; } /* Translate name */ __generate_upload_path(filename, file_path); /* Open file */ file_id = guac_rdp_fs_open(fs, file_path, ACCESS_GENERIC_WRITE, 0, DISP_FILE_OVERWRITE_IF, 0); if (file_id < 0) { guac_protocol_send_ack(client->socket, stream, "FAIL (CANNOT OPEN)", GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); guac_socket_flush(client->socket); return 0; } /* Init upload status */ rdp_stream = malloc(sizeof(guac_rdp_stream)); rdp_stream->type = GUAC_RDP_UPLOAD_STREAM; rdp_stream->upload_status.offset = 0; rdp_stream->upload_status.file_id = file_id; stream->data = rdp_stream; stream->blob_handler = guac_rdp_upload_blob_handler; stream->end_handler = guac_rdp_upload_end_handler; guac_protocol_send_ack(client->socket, stream, "OK (STREAM BEGIN)", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); return 0; }
int guac_sftp_end_handler(guac_client* client, guac_stream* stream) { /* Pull file from stream */ LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; /* Attempt to close file */ if (libssh2_sftp_close(file) == 0) { guac_protocol_send_ack(client->socket, stream, "SFTP: OK", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); } else { guac_client_log_error(client, "Unable to close file"); guac_protocol_send_ack(client->socket, stream, "SFTP: Close failed", GUAC_PROTOCOL_STATUS_SERVER_ERROR); guac_socket_flush(client->socket); } return 0; }
int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) { guac_rdp_client* rdp_client = (guac_rdp_client*) user->client->data; /* Connect via RDP if owner */ if (user->owner) { /* Parse arguments into client */ guac_rdp_settings* settings = rdp_client->settings = guac_rdp_parse_args(user, argc, (const char**) argv); /* Fail if settings cannot be parsed */ if (settings == NULL) { guac_user_log(user, GUAC_LOG_INFO, "Badly formatted client arguments."); return 1; } /* Start client thread */ if (pthread_create(&rdp_client->client_thread, NULL, guac_rdp_client_thread, user->client)) { guac_user_log(user, GUAC_LOG_ERROR, "Unable to start VNC client thread."); return 1; } } /* If not owner, synchronize with current state */ else { /* Synchronize any audio stream */ if (rdp_client->audio) guac_audio_stream_add_user(rdp_client->audio, user); /* Bring user up to date with any registered static channels */ guac_rdp_svc_send_pipes(user); /* Synchronize with current display */ guac_common_display_dup(rdp_client->display, user, user->socket); guac_socket_flush(user->socket); } user->file_handler = guac_rdp_user_file_handler; user->mouse_handler = guac_rdp_user_mouse_handler; user->key_handler = guac_rdp_user_key_handler; user->size_handler = guac_rdp_user_size_handler; user->pipe_handler = guac_rdp_svc_pipe_handler; user->clipboard_handler = guac_rdp_clipboard_handler; return 0; }
int guac_rdp_upload_put_handler(guac_client* client, guac_object* object, guac_stream* stream, char* mimetype, char* name) { /* Get filesystem, return error if no filesystem */ guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem; if (fs == NULL) { guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)", GUAC_PROTOCOL_STATUS_SERVER_ERROR); guac_socket_flush(client->socket); return 0; } /* Open file */ int file_id = guac_rdp_fs_open(fs, name, ACCESS_GENERIC_WRITE, 0, DISP_FILE_OVERWRITE_IF, 0); /* Abort on failure */ if (file_id < 0) { guac_protocol_send_ack(client->socket, stream, "FAIL (CANNOT OPEN)", GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); guac_socket_flush(client->socket); return 0; } /* Init upload stream data */ guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream)); rdp_stream->type = GUAC_RDP_UPLOAD_STREAM; rdp_stream->upload_status.offset = 0; rdp_stream->upload_status.file_id = file_id; /* Allocate stream, init for file upload */ stream->data = rdp_stream; stream->blob_handler = guac_rdp_upload_blob_handler; stream->end_handler = guac_rdp_upload_end_handler; /* Acknowledge stream creation */ guac_protocol_send_ack(client->socket, stream, "OK (STREAM BEGIN)", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); return 0; }
void guac_terminal_select_end(guac_terminal* terminal) { guac_client* client = terminal->client; guac_socket* socket = client->socket; /* If no text is selected, nothing to do */ if (!terminal->text_selected) return; /* Selection is now committed */ terminal->selection_committed = true; /* Reset current clipboard contents */ guac_common_clipboard_reset(terminal->clipboard, "text/plain"); int start_row, start_col; int end_row, end_col; /* Ensure proper ordering of start and end coords */ guac_terminal_select_normalized_range(terminal, &start_row, &start_col, &end_row, &end_col); /* If only one row, simply copy */ if (end_row == start_row) guac_terminal_clipboard_append_row(terminal, start_row, start_col, end_col); /* Otherwise, copy multiple rows */ else { /* Store first row */ guac_terminal_clipboard_append_row(terminal, start_row, start_col, -1); /* Store all middle rows */ for (int row = start_row + 1; row < end_row; row++) { guac_common_clipboard_append(terminal->clipboard, "\n", 1); guac_terminal_clipboard_append_row(terminal, row, 0, -1); } /* Store last row */ guac_common_clipboard_append(terminal->clipboard, "\n", 1); guac_terminal_clipboard_append_row(terminal, end_row, 0, end_col); } /* Send data */ if (!terminal->disable_copy) { guac_common_clipboard_send(terminal->clipboard, client); guac_socket_flush(socket); } guac_terminal_notify(terminal); }
int guac_rdp_svc_blob_handler(guac_client* client, guac_stream* stream, void* data, int length) { guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; /* Write blob data to SVC directly */ guac_rdp_svc_write(rdp_stream->svc, data, length); guac_protocol_send_ack(client->socket, stream, "OK (DATA RECEIVED)", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); return 0; }
void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path) { /* Get client and stream */ guac_client* client = device->rdpdr->client; int file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data, path, ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0); /* If file opened successfully, start stream */ if (file_id >= 0) { guac_rdp_stream* rdp_stream; const char* basename; int i; char c; /* Associate stream with transfer status */ guac_stream* stream = guac_client_alloc_stream(client); stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream)); stream->ack_handler = guac_rdp_download_ack_handler; rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM; rdp_stream->download_status.file_id = file_id; rdp_stream->download_status.offset = 0; /* Get basename from absolute path */ i=0; basename = path; do { c = path[i]; if (c == '/' || c == '\\') basename = &(path[i+1]); i++; } while (c != '\0'); GUAC_RDP_DEBUG(2, "Initiating download of \"%s\"", path); /* Begin stream */ guac_protocol_send_file(client->socket, stream, "application/octet-stream", basename); guac_socket_flush(client->socket); } else guac_client_log(client, GUAC_LOG_ERROR, "Unable to download \"%s\"", path); }
int guac_ssh_user_join_handler(guac_user* user, int argc, char** argv) { guac_client* client = user->client; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; /* Connect via SSH if owner */ if (user->owner) { /* Parse arguments into client */ guac_ssh_settings* settings = ssh_client->settings = guac_ssh_parse_args(user, argc, (const char**) argv); /* Fail if settings cannot be parsed */ if (settings == NULL) { guac_user_log(user, GUAC_LOG_INFO, "Badly formatted client arguments."); return 1; } /* Start client thread */ if (pthread_create(&(ssh_client->client_thread), NULL, ssh_client_thread, (void*) client)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start SSH client thread"); return 1; } } /* If not owner, synchronize with current display */ else { guac_terminal_dup(ssh_client->term, user, user->socket); guac_socket_flush(user->socket); } /* Set per-user event handlers */ user->key_handler = guac_ssh_user_key_handler; user->mouse_handler = guac_ssh_user_mouse_handler; user->size_handler = guac_ssh_user_size_handler; user->clipboard_handler = guac_ssh_clipboard_handler; /* Set generic (non-filesystem) file upload handler */ user->file_handler = guac_sftp_file_handler; return 0; }
/** * Clears the currently-selected region, removing the highlight. */ static void __guac_terminal_display_clear_select(guac_terminal_display* display) { guac_socket* socket = display->client->socket; guac_layer* select_layer = display->select_layer; guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1); guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer, 0x00, 0x00, 0x00, 0x00); guac_client_end_frame(display->client); guac_socket_flush(socket); /* Text is no longer selected */ display->text_selected = display->selection_committed = false; }
void vguac_client_abort(guac_client* client, guac_protocol_status status, const char* format, va_list ap) { /* Only relevant if client is running */ if (client->state == GUAC_CLIENT_RUNNING) { /* Log detail of error */ vguac_client_log_error(client, format, ap); /* Send error immediately, limit information given */ guac_protocol_send_error(client->socket, "Aborted. See logs.", status); guac_socket_flush(client->socket); /* Stop client */ guac_client_stop(client); } }
int guac_sftp_ack_handler(guac_client* client, guac_stream* stream, char* message, guac_protocol_status status) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; /* If successful, read data */ if (status == GUAC_PROTOCOL_STATUS_SUCCESS) { /* Attempt read into buffer */ char buffer[4096]; int bytes_read = libssh2_sftp_read(file, buffer, sizeof(buffer)); /* If bytes read, send as blob */ if (bytes_read > 0) guac_protocol_send_blob(client->socket, stream, buffer, bytes_read); /* If EOF, send end */ else if (bytes_read == 0) { guac_protocol_send_end(client->socket, stream); guac_client_free_stream(client, stream); } /* Otherwise, fail stream */ else { guac_client_log_error(client, "Error reading file: %s", libssh2_sftp_last_error(client_data->sftp_session)); guac_protocol_send_end(client->socket, stream); guac_client_free_stream(client, stream); } guac_socket_flush(client->socket); } /* Otherwise, return stream to client */ else guac_client_free_stream(client, stream); return 0; }
int ssh_guac_client_size_handler(guac_client* client, int width, int height) { /* Get terminal */ ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; guac_terminal* terminal = guac_client_data->term; /* Calculate dimensions */ int rows = height / terminal->display->char_height; int columns = width / terminal->display->char_width; pthread_mutex_lock(&(terminal->lock)); /* If size has changed */ if (columns != terminal->term_width || rows != terminal->term_height) { /* Resize terminal */ guac_terminal_resize(terminal, columns, rows); /* Update cursor */ guac_terminal_commit_cursor(terminal); /* Update SSH pty size if connected */ if (guac_client_data->term_channel != NULL) libssh2_channel_request_pty_size(guac_client_data->term_channel, terminal->term_width, terminal->term_height); /* Reset scroll region */ terminal->scroll_end = rows - 1; guac_terminal_display_flush(terminal->display); guac_protocol_send_sync(terminal->client->socket, client->last_sent_timestamp); guac_socket_flush(terminal->client->socket); } pthread_mutex_unlock(&(terminal->lock)); return 0; }
boolean rdp_freerdp_post_connect(freerdp* instance) { rdpContext* context = instance->context; guac_client* client = ((rdp_freerdp_context*) context)->client; rdpChannels* channels = instance->context->channels; /* Init channels (post-connect) */ if (freerdp_channels_post_connect(channels, instance)) { guac_protocol_send_error(client->socket, "Error initializing RDP client channel manager"); guac_socket_flush(client->socket); return false; } /* Client handlers */ client->free_handler = rdp_guac_client_free_handler; client->handle_messages = rdp_guac_client_handle_messages; client->mouse_handler = rdp_guac_client_mouse_handler; client->key_handler = rdp_guac_client_key_handler; client->clipboard_handler = rdp_guac_client_clipboard_handler; return true; }
void guac_svc_process_receive(rdpSvcPlugin* plugin, wStream* input_stream) { /* Get corresponding guac_rdp_svc */ guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin; guac_rdp_svc* svc = svc_plugin->svc; /* Fail if output not created */ if (svc->output_pipe == NULL) { guac_client_log(svc->client, GUAC_LOG_ERROR, "Output for channel \"%s\" dropped.", svc->name); return; } /* Send blob */ guac_protocol_send_blob(svc->client->socket, svc->output_pipe, Stream_Buffer(input_stream), Stream_Length(input_stream)); guac_socket_flush(svc->client->socket); }
int guac_sftp_file_handler(guac_client* client, guac_stream* stream, char* mimetype, char* filename) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; char fullpath[GUAC_SFTP_MAX_PATH]; LIBSSH2_SFTP_HANDLE* file; int i; /* Ensure filename is a valid filename and not a path */ if (!__ssh_guac_valid_filename(filename)) { guac_protocol_send_ack(client->socket, stream, "SFTP: Illegal filename", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST); guac_socket_flush(client->socket); return 0; } /* Copy upload path, append trailing slash */ for (i=0; i<GUAC_SFTP_MAX_PATH; i++) { char c = client_data->sftp_upload_path[i]; if (c == '\0') { fullpath[i++] = '/'; break; } fullpath[i] = c; } /* Append filename */ for (; i<GUAC_SFTP_MAX_PATH; i++) { char c = *(filename++); if (c == '\0') break; fullpath[i] = c; } /* If path + filename exceeds max length, abort */ if (i == GUAC_SFTP_MAX_PATH) { guac_protocol_send_ack(client->socket, stream, "SFTP: Name too long", GUAC_PROTOCOL_STATUS_CLIENT_OVERRUN); guac_socket_flush(client->socket); return 0; } /* Terminate path string */ fullpath[i] = '\0'; /* Open file via SFTP */ file = libssh2_sftp_open(client_data->sftp_session, fullpath, LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, S_IRUSR | S_IWUSR); /* Inform of status */ if (file != NULL) { guac_protocol_send_ack(client->socket, stream, "SFTP: File opened", GUAC_PROTOCOL_STATUS_SUCCESS); guac_socket_flush(client->socket); } else { guac_client_log_error(client, "Unable to open file \"%s\": %s", fullpath, libssh2_sftp_last_error(client_data->sftp_session)); guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); guac_socket_flush(client->socket); } /* Set handlers for file stream */ stream->blob_handler = guac_sftp_blob_handler; stream->end_handler = guac_sftp_end_handler; /* Store file within stream */ stream->data = file; return 0; }
/** * Creates a new guac_client for the connection on the given socket, adding * it to the client map based on its ID. */ static void guacd_handle_connection(guacd_client_map* map, guac_socket* socket) { guac_client* client; guac_client_plugin* plugin; guac_instruction* select; guac_instruction* size; guac_instruction* audio; guac_instruction* video; guac_instruction* connect; int init_result; /* Reset guac_error */ guac_error = GUAC_STATUS_SUCCESS; guac_error_message = NULL; /* Get protocol from select instruction */ select = guac_instruction_expect( socket, GUACD_USEC_TIMEOUT, "select"); if (select == NULL) { /* Log error */ guacd_log_guac_error("Error reading \"select\""); /* Free resources */ guac_socket_free(socket); return; } /* Validate args to select */ if (select->argc != 1) { /* Log error */ guacd_log_error("Bad number of arguments to \"select\" (%i)", select->argc); /* Free resources */ guac_socket_free(socket); return; } guacd_log_info("Protocol \"%s\" selected", select->argv[0]); /* Get plugin from protocol in select */ plugin = guac_client_plugin_open(select->argv[0]); guac_instruction_free(select); if (plugin == NULL) { /* Log error */ guacd_log_guac_error("Error loading client plugin"); /* Free resources */ guac_socket_free(socket); return; } /* Send args response */ if (guac_protocol_send_args(socket, plugin->args) || guac_socket_flush(socket)) { /* Log error */ guacd_log_guac_error("Error sending \"args\""); if (guac_client_plugin_close(plugin)) guacd_log_guac_error("Error closing client plugin"); guac_socket_free(socket); return; } /* Get optimal screen size */ size = guac_instruction_expect( socket, GUACD_USEC_TIMEOUT, "size"); if (size == NULL) { /* Log error */ guacd_log_guac_error("Error reading \"size\""); /* Free resources */ guac_socket_free(socket); return; } /* Get supported audio formats */ audio = guac_instruction_expect( socket, GUACD_USEC_TIMEOUT, "audio"); if (audio == NULL) { /* Log error */ guacd_log_guac_error("Error reading \"audio\""); /* Free resources */ guac_socket_free(socket); return; } /* Get supported video formats */ video = guac_instruction_expect( socket, GUACD_USEC_TIMEOUT, "video"); if (video == NULL) { /* Log error */ guacd_log_guac_error("Error reading \"video\""); /* Free resources */ guac_socket_free(socket); return; } /* Get args from connect instruction */ connect = guac_instruction_expect( socket, GUACD_USEC_TIMEOUT, "connect"); if (connect == NULL) { /* Log error */ guacd_log_guac_error("Error reading \"connect\""); if (guac_client_plugin_close(plugin)) guacd_log_guac_error("Error closing client plugin"); guac_socket_free(socket); return; } /* Get client */ client = guac_client_alloc(); if (client == NULL) { guacd_log_guac_error("Client could not be allocated"); guac_socket_free(socket); return; } client->socket = socket; client->log_info_handler = guacd_client_log_info; client->log_error_handler = guacd_client_log_error; /* Parse optimal screen dimensions from size instruction */ client->info.optimal_width = atoi(size->argv[0]); client->info.optimal_height = atoi(size->argv[1]); /* If DPI given, set the client resolution */ if (size->argc >= 3) client->info.optimal_resolution = atoi(size->argv[2]); /* Otherwise, use a safe default for rough backwards compatibility */ else client->info.optimal_resolution = 96; /* Store audio mimetypes */ client->info.audio_mimetypes = malloc(sizeof(char*) * (audio->argc+1)); memcpy(client->info.audio_mimetypes, audio->argv, sizeof(char*) * audio->argc); client->info.audio_mimetypes[audio->argc] = NULL; /* Store video mimetypes */ client->info.video_mimetypes = malloc(sizeof(char*) * (video->argc+1)); memcpy(client->info.video_mimetypes, video->argv, sizeof(char*) * video->argc); client->info.video_mimetypes[video->argc] = NULL; /* Store client */ if (guacd_client_map_add(map, client)) guacd_log_error("Unable to add client. Internal client storage has failed"); /* Send connection ID */ guacd_log_info("Connection ID is \"%s\"", client->connection_id); guac_protocol_send_ready(socket, client->connection_id); /* Init client */ init_result = guac_client_plugin_init_client(plugin, client, connect->argc, connect->argv); guac_instruction_free(connect); /* If client could not be started, free everything and fail */ if (init_result) { guac_client_free(client); guacd_log_guac_error("Error instantiating client"); if (guac_client_plugin_close(plugin)) guacd_log_guac_error("Error closing client plugin"); guac_socket_free(socket); return; } /* Start client threads */ guacd_log_info("Starting client"); if (guacd_client_start(client)) guacd_log_error("Client finished abnormally"); else guacd_log_info("Client finished normally"); /* Remove client */ if (guacd_client_map_remove(map, client->connection_id) == NULL) guacd_log_error("Unable to remove client. Internal client storage has failed"); /* Free mimetype lists */ free(client->info.audio_mimetypes); free(client->info.video_mimetypes); /* Free remaining instructions */ guac_instruction_free(audio); guac_instruction_free(video); guac_instruction_free(size); /* Clean up */ guac_client_free(client); if (guac_client_plugin_close(plugin)) guacd_log_error("Error closing client plugin"); /* Close socket */ guac_socket_free(socket); }
int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; guac_telnet_client_data* client_data = malloc(sizeof(guac_telnet_client_data)); /* Init client data */ client->data = client_data; client_data->telnet = NULL; client_data->socket_fd = -1; client_data->naws_enabled = 0; client_data->echo_enabled = 1; if (argc != TELNET_ARGS_COUNT) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong number of arguments"); return -1; } /* Read parameters */ strcpy(client_data->hostname, argv[IDX_HOSTNAME]); /* Read port */ if (argv[IDX_PORT][0] != 0) strcpy(client_data->port, argv[IDX_PORT]); else strcpy(client_data->port, GUAC_TELNET_DEFAULT_PORT); /* Read font name */ if (argv[IDX_FONT_NAME][0] != 0) strcpy(client_data->font_name, argv[IDX_FONT_NAME]); else strcpy(client_data->font_name, GUAC_TELNET_DEFAULT_FONT_NAME ); /* Read font size */ if (argv[IDX_FONT_SIZE][0] != 0) client_data->font_size = atoi(argv[IDX_FONT_SIZE]); else client_data->font_size = GUAC_TELNET_DEFAULT_FONT_SIZE; /* Create terminal */ client_data->term = guac_terminal_create(client, client_data->font_name, client_data->font_size, client->info.optimal_resolution, client->info.optimal_width, client->info.optimal_height); /* Fail if terminal init failed */ if (client_data->term == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Terminal initialization failed"); return -1; } /* Send initial name */ guac_protocol_send_name(socket, client_data->hostname); guac_socket_flush(socket); /* Set basic handlers */ client->handle_messages = guac_telnet_client_handle_messages; client->key_handler = guac_telnet_client_key_handler; client->mouse_handler = guac_telnet_client_mouse_handler; client->size_handler = guac_telnet_client_size_handler; client->free_handler = guac_telnet_client_free_handler; client->clipboard_handler = guac_telnet_clipboard_handler; /* Start client thread */ if (pthread_create(&(client_data->client_thread), NULL, guac_telnet_client_thread, (void*) client)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start telnet client thread"); return -1; } /* Success */ return 0; }
void guac_rdp_client_abort(guac_client* client) { /* * NOTE: The RDP status codes translated here are documented within * [MS-RDPBCGR], section 2.2.5.1.1: "Set Error Info PDU Data", in the * description of the "errorInfo" field. * * https://msdn.microsoft.com/en-us/library/cc240544.aspx */ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; freerdp* rdp_inst = rdp_client->rdp_inst; guac_protocol_status status; const char* message; /* Read disconnect reason code from connection */ int error_info = freerdp_error_info(rdp_inst); /* Translate reason code into Guacamole protocol status */ switch (error_info) { /* Normal disconnect */ case 0x0: /* ERRINFO_SUCCESS */ status = GUAC_PROTOCOL_STATUS_SUCCESS; message = "Disconnected."; break; /* Forced disconnect (possibly by admin) */ case 0x1: /* ERRINFO_RPC_INITIATED_DISCONNECT */ status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; message = "Forcibly disconnected."; break; /* The user was logged off (possibly by admin) */ case 0x2: /* ERRINFO_RPC_INITIATED_LOGOFF */ status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; message = "Logged off."; break; /* The user was idle long enough that the RDP server disconnected */ case 0x3: /* ERRINFO_IDLE_TIMEOUT */ status = GUAC_PROTOCOL_STATUS_SESSION_TIMEOUT; message = "Idle session time limit exceeded."; break; /* The user's session has been active for too long */ case 0x4: /* ERRINFO_LOGON_TIMEOUT */ status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; message = "Active session time limit exceeded."; break; /* Another user logged on, disconnecting this user */ case 0x5: /* ERRINFO_DISCONNECTED_BY_OTHER_CONNECTION */ status = GUAC_PROTOCOL_STATUS_SESSION_CONFLICT; message = "Disconnected by other connection."; break; /* The RDP server is refusing to service the connection */ case 0x6: /* ERRINFO_OUT_OF_MEMORY */ case 0x7: /* ERRINFO_SERVER_DENIED_CONNECTION */ status = GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE; message = "Server refused connection."; break; /* The user does not have permission to connect */ case 0x9: /* ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES */ status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; message = "Insufficient privileges."; break; /* The user's credentials have expired */ case 0xA: /* ERRINFO_SERVER_FRESH_CREDENTIALS_REQUIRED */ status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; message = "Credentials expired."; break; /* The user manually disconnected using an administrative tool within * the session */ case 0xB: /* ERRINFO_RPC_INITIATED_DISCONNECT_BYUSER */ status = GUAC_PROTOCOL_STATUS_SUCCESS; message = "Manually disconnected."; break; /* The user manually logged off */ case 0xC: /* ERRINFO_LOGOFF_BY_USER */ status = GUAC_PROTOCOL_STATUS_SUCCESS; message = "Manually logged off."; break; /* Unimplemented/unknown disconnect reason code */ default: status = GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; message = "Upstream error."; } /* Send error code if an error occurred */ if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { guac_protocol_send_error(client->socket, message, status); guac_socket_flush(client->socket); } /* Log human-readable description of disconnect at info level */ guac_client_log(client, GUAC_LOG_INFO, "RDP server closed connection: %s", message); /* Log internal disconnect reason code at debug level */ if (error_info) guac_client_log(client, GUAC_LOG_DEBUG, "Disconnect reason " "code: 0x%X.", error_info); /* Abort connection */ guac_client_stop(client); }