diff --git a/libguac/include/client.h b/libguac/include/client.h index 83c04560..21c3a97d 100644 --- a/libguac/include/client.h +++ b/libguac/include/client.h @@ -57,6 +57,29 @@ */ #define GUAC_SYNC_THRESHOLD 500 +/** + * The time to allow between server sync messages in milliseconds. A sync + * message from the server will be sent every GUAC_SYNC_FREQUENCY milliseconds. + * As this will induce a response from a client that is not malfunctioning, + * this is used to detect when a client has died. This must be set to a + * reasonable value to avoid clients being disconnected unnecessarily due + * to timeout. + */ +#define GUAC_SYNC_FREQUENCY 5000 + +/** + * The number of milliseconds to wait for messages in any phase before + * timing out and closing the connection with an error. + */ +#define GUAC_TIMEOUT 15000 + +/** + * The number of microseconds to wait for messages in any phase before + * timing out and closing the conncetion with an error. This is always + * equal to GUAC_TIMEOUT * 1000. + */ +#define GUAC_USEC_TIMEOUT (GUAC_TIMEOUT*1000) + /** * The amount of time to wait after handling server messages. If a client * plugin has a message handler, and sends instructions when server messages diff --git a/libguac/src/client.c b/libguac/src/client.c index 8b76f84f..dfd49e22 100644 --- a/libguac/src/client.c +++ b/libguac/src/client.c @@ -119,9 +119,24 @@ guac_client* guac_get_client(int client_fd) { /* Wait for select instruction */ for (;;) { - int result = guac_read_instruction(io, &instruction); + int result; + + /* Wait for data until timeout */ + result = guac_select(io, GUAC_USEC_TIMEOUT); + if (result == 0) { + guac_send_error(io, "Select timeout."); + guac_close(io); + return NULL; + } + + /* If error occurs while waiting, exit with failure */ + if (result < 0) { + guac_close(io); + return NULL; + } + + result = guac_read_instruction(io, &instruction); if (result < 0) { - GUAC_LOG_ERROR("Error reading instruction while waiting for select"); guac_close(io); return NULL; } @@ -194,7 +209,23 @@ guac_client* guac_get_client(int client_fd) { /* Wait for connect instruction */ for (;;) { - int result = guac_read_instruction(io, &instruction); + int result; + + /* Wait for data until timeout */ + result = guac_select(io, GUAC_USEC_TIMEOUT); + if (result == 0) { + guac_send_error(io, "Connect timeout."); + guac_close(io); + return NULL; + } + + /* If error occurs while waiting, exit with failure */ + if (result < 0) { + guac_close(io); + return NULL; + } + + result = guac_read_instruction(io, &instruction); if (result < 0) { GUAC_LOG_ERROR("Error reading instruction while waiting for connect"); guac_close(io); @@ -308,6 +339,21 @@ void guac_start_client(guac_client* client) { /* VNC Client Loop */ for (;;) { + /* Get current time and check timeout */ + long timestamp = __guac_current_timestamp(); + if (timestamp - last_received_timestamp > GUAC_TIMEOUT) { + guac_send_error(io, "Sync timeout."); + guac_flush(io); + return; + } + + /* If not timed out, ping client with sync */ + if (timestamp - last_sent_timestamp > GUAC_SYNC_FREQUENCY) { + last_sent_timestamp = timestamp; + guac_send_sync(io, last_sent_timestamp); + guac_flush(io); + } + /* Handle server messages */ if (client->handle_messages) {