diff --git a/Makefile.am b/Makefile.am index 9100f8f7..9b5ab08f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,27 +42,27 @@ SUBDIRS = \ tests if ENABLE_COMMON_SSH - SUBDIRS += src/common-ssh +SUBDIRS += src/common-ssh endif if ENABLE_TERMINAL - SUBDIRS += src/terminal +SUBDIRS += src/terminal endif if ENABLE_RDP - SUBDIRS += src/protocols/rdp +SUBDIRS += src/protocols/rdp endif if ENABLE_SSH - SUBDIRS += src/protocols/ssh +SUBDIRS += src/protocols/ssh endif if ENABLE_TELNET - SUBDIRS += src/protocols/telnet +SUBDIRS += src/protocols/telnet endif if ENABLE_VNC - SUBDIRS += src/protocols/vnc +SUBDIRS += src/protocols/vnc endif EXTRA_DIST = \ diff --git a/configure.ac b/configure.ac index 43bb38d4..d19e2380 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # -# Copyright (C) 2013 Glyptodon LLC +# Copyright (C) 2015 Glyptodon LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -21,7 +21,7 @@ # AC_PREREQ([2.61]) -AC_INIT([guacamole-server], [0.9.7]) +AC_INIT([guacamole-server], [0.9.8]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_SILENT_RULES([yes]) diff --git a/doc/Doxyfile b/doc/Doxyfile index 3f7894dc..37c683a7 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -31,7 +31,7 @@ PROJECT_NAME = libguac # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.9.7 +PROJECT_NUMBER = 0.9.8 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c index b5a305ec..1ccff569 100644 --- a/src/common-ssh/guac_sftp.c +++ b/src/common-ssh/guac_sftp.c @@ -34,6 +34,78 @@ #include #include +/** + * Translates the last error message received by the SFTP layer of an SSH + * session into a Guacamole protocol status code. + * + * @param filesystem + * The Guacamole protocol object defining the filesystem associated with + * the SFTP and SSH sessions. + * + * @return + * The Guacamole protocol status code corresponding to the last reported + * error of the SFTP layer, if nay, or GUAC_PROTOCOL_STATUS_SUCCESS if no + * error has occurred. + */ +static guac_protocol_status guac_sftp_get_status(guac_object* filesystem) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) filesystem->data; + + /* Get libssh2 objects */ + LIBSSH2_SFTP* sftp = sftp_data->sftp_session; + LIBSSH2_SESSION* session = sftp_data->ssh_session->session; + + /* Return success code if no error occurred */ + if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_SFTP_PROTOCOL) + return GUAC_PROTOCOL_STATUS_SUCCESS; + + /* Translate SFTP error codes defined by + * https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 (the most + * commonly-implemented standard) */ + switch (libssh2_sftp_last_error(sftp)) { + + /* SSH_FX_OK (not an error) */ + case 0: + return GUAC_PROTOCOL_STATUS_SUCCESS; + + /* SSH_FX_EOF (technically not an error) */ + case 1: + return GUAC_PROTOCOL_STATUS_SUCCESS; + + /* SSH_FX_NO_SUCH_FILE */ + case 2: + return GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND; + + /* SSH_FX_PERMISSION_DENIED */ + case 3: + return GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; + + /* SSH_FX_FAILURE */ + case 4: + return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; + + /* SSH_FX_BAD_MESSAGE */ + case 5: + return GUAC_PROTOCOL_STATUS_SERVER_ERROR; + + /* SSH_FX_NO_CONNECTION / SSH_FX_CONNECTION_LOST */ + case 6: + case 7: + return GUAC_PROTOCOL_STATUS_UPSTREAM_TIMEOUT; + + /* SSH_FX_OP_UNSUPPORTED */ + case 8: + return GUAC_PROTOCOL_STATUS_UNSUPPORTED; + + /* Return generic error if cause unknown */ + default: + return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; + + } + +} + /** * Concatenates the given filename with the given path, separating the two * with a single forward slash. The full result must be no more than @@ -249,7 +321,7 @@ int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem, guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\"", fullpath); guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", - GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); + guac_sftp_get_status(filesystem)); guac_socket_flush(client->socket); } @@ -642,7 +714,7 @@ static int guac_common_ssh_sftp_put_handler(guac_client* client, guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\"", name); guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", - GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); + guac_sftp_get_status(object)); } /* Set handlers for file stream */ diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index b68e4956..db1e7a54 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -77,7 +77,7 @@ initdir = @init_dir@ init_SCRIPTS = init.d/guacd init.d/guacd: init.d/guacd.in - sed -e 's,[@]sbindir[@],$(sbindir),g' < init.d/guacd.in > init.d/guacd - chmod +x init.d/guacd + sed -e 's,[@]sbindir[@],$(sbindir),g' < init.d/guacd.in > init.d/guacd + chmod +x init.d/guacd endif diff --git a/src/guacd/man/guacd.8 b/src/guacd/man/guacd.8 index a230786d..ade41476 100644 --- a/src/guacd/man/guacd.8 +++ b/src/guacd/man/guacd.8 @@ -1,4 +1,4 @@ -.TH guacd 8 "8 Jun 2015" "version 0.9.7" "Guacamole" +.TH guacd 8 "4 Sep 2015" "version 0.9.8" "Guacamole" . .SH NAME guacd \- Guacamole proxy daemon diff --git a/src/guacd/man/guacd.conf.5 b/src/guacd/man/guacd.conf.5 index 8406efac..4bf7a579 100644 --- a/src/guacd/man/guacd.conf.5 +++ b/src/guacd/man/guacd.conf.5 @@ -1,4 +1,4 @@ -.TH guacd.conf 5 "8 Jun 2015" "version 0.9.7" "Guacamole" +.TH guacd.conf 5 "4 Sep 2015" "version 0.9.8" "Guacamole" . .SH NAME /etc/guacamole/guacd.conf \- Configuration file for guacd diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index 7e0d1696..4cd58423 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -98,13 +98,13 @@ endif libguac_la_CFLAGS = \ -Werror -Wall -pedantic -Iguacamole -libguac_la_LDFLAGS = \ - -version-info 9:0:0 \ - @CAIRO_LIBS@ \ - @JPEG_LIBS@ \ - @PNG_LIBS@ \ - @PTHREAD_LIBS@ \ - @UUID_LIBS@ \ +libguac_la_LDFLAGS = \ + -version-info 10:0:0 \ + @CAIRO_LIBS@ \ + @JPEG_LIBS@ \ + @PNG_LIBS@ \ + @PTHREAD_LIBS@ \ + @UUID_LIBS@ \ @VORBIS_LIBS@ libguac_la_LIBADD = \ diff --git a/src/protocols/telnet/guac_handlers.c b/src/protocols/telnet/guac_handlers.c index e50c58d7..9832eb52 100644 --- a/src/protocols/telnet/guac_handlers.c +++ b/src/protocols/telnet/guac_handlers.c @@ -84,6 +84,19 @@ int guac_telnet_client_key_handler(guac_client* client, int keysym, int pressed) } + /* Intercept and handle Pause / Break / Ctrl+0 as "IAC BRK" */ + if (pressed && ( + keysym == 0xFF13 /* Pause */ + || keysym == 0xFF6B /* Break */ + || (term->mod_ctrl && keysym == '0') /* Ctrl + 0 */ + )) { + + /* Send IAC BRK */ + telnet_iac(client_data->telnet, TELNET_BREAK); + + return 0; + } + /* Send key */ guac_terminal_send_key(term, keysym, pressed); diff --git a/src/protocols/vnc/pulse.c b/src/protocols/vnc/pulse.c index f0e332ab..57ed10af 100644 --- a/src/protocols/vnc/pulse.c +++ b/src/protocols/vnc/pulse.c @@ -27,8 +27,41 @@ #include #include +#include #include +/** + * Returns whether the given buffer contains only silence (only null bytes). + * + * @param buffer + * The audio buffer to check. + * + * @param length + * The length of the buffer to check. + * + * @return + * Non-zero if the audio buffer contains silence, zero otherwise. + */ +static int guac_pa_is_silence(const void* buffer, size_t length) { + + int i; + + const unsigned char* current = (const unsigned char*) buffer; + + /* For each byte in buffer */ + for (i = 0; i < length; i++) { + + /* If current value non-zero, then not silence */ + if (*(current++)) + return 0; + + } + + /* Otherwise, the buffer contains 100% silence */ + return 1; + +} + static void __stream_read_callback(pa_stream* stream, size_t length, void* data) { @@ -41,16 +74,22 @@ static void __stream_read_callback(pa_stream* stream, size_t length, /* Read data */ pa_stream_peek(stream, &buffer, &length); - /* Write data */ - guac_audio_stream_write_pcm(audio, 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); + } - /* 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); } /* Advance buffer */