diff --git a/libguac/include/error.h b/libguac/include/error.h index aee205fa..af62dcfb 100644 --- a/libguac/include/error.h +++ b/libguac/include/error.h @@ -73,6 +73,12 @@ typedef enum guac_status { */ GUAC_STATUS_INPUT_TIMEOUT, + /** + * An error occurred, and further information about the error is already + * stored in errno. + */ + GUAC_STATUS_SEE_ERRNO, + /** * An error prevented the operation from writing to its associated * output stream. diff --git a/libguac/include/protocol.h b/libguac/include/protocol.h index f8bdf0df..5c165492 100644 --- a/libguac/include/protocol.h +++ b/libguac/include/protocol.h @@ -143,19 +143,7 @@ typedef struct guac_instruction { /** - * Frees all memory allocated to the given instruction opcode - * and arguments. The instruction structure itself will not - * be freed. - * - * @param instruction The instruction to free. - */ -void guac_free_instruction_data(guac_instruction* instruction); - - -/** - * Frees all memory allocated to the given instruction. This - * includes freeing memory allocated for the structure - * itself. + * Frees all memory allocated to the given instruction. * * @param instruction The instruction to free. */ @@ -345,23 +333,20 @@ int guac_instructions_waiting(GUACIO* io, int usec_timeout); /** * Reads a single instruction from the given GUACIO connection. * - * If an error occurs reading the instruction, a non-zero value is - * returned, and guac_error is set appropriately. + * If an error occurs reading the instruction, NULL is returned, + * and guac_error is set appropriately. * * @param io The GUACIO connection to use. * @param usec_timeout The maximum number of microseconds to wait before * giving up. - * @param parsed_instruction A pointer to a guac_instruction structure which - * will be populated with data read from the given - * GUACIO connection. - * @return A positive value if data was successfully read, negative on - * error, or zero if the instruction could not be read completely - * because the timeout elapsed, in which case subsequent calls to + * @return A new instruction if data was successfully read, NULL on + * error or if the instruction could not be read completely + * because the timeout elapsed, in which case guac_error will be + * set to GUAC_STATUS_INPUT_TIMEOUT and subsequent calls to * guac_read_instruction() will return the parsed instruction once * enough data is available. */ -int guac_read_instruction(GUACIO* io, int usec_timeout, - guac_instruction* parsed_instruction); +guac_instruction* guac_read_instruction(GUACIO* io, int usec_timeout); /** * Returns an arbitrary timestamp. The difference between return values of any diff --git a/libguac/src/client.c b/libguac/src/client.c index 96817eba..320c10a3 100644 --- a/libguac/src/client.c +++ b/libguac/src/client.c @@ -151,7 +151,7 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { char** argv; /* Instruction */ - guac_instruction instruction; + guac_instruction* instruction; /* Wait for select instruction */ for (;;) { @@ -172,19 +172,19 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { return NULL; } - result = guac_read_instruction(io, usec_timeout, &instruction); - if (result < 0) { + instruction = guac_read_instruction(io, usec_timeout); + if (instruction == NULL) { guac_close(io); return NULL; } /* Select instruction read */ - if (result > 0) { + else { - if (strcmp(instruction.opcode, "select") == 0) { + if (strcmp(instruction->opcode, "select") == 0) { /* Get protocol from message */ - char* protocol = instruction.argv[0]; + char* protocol = instruction->argv[0]; strcat(protocol_lib, protocol); strcat(protocol_lib, ".so"); @@ -199,7 +199,7 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { guac_send_error(io, "Could not load server-side client plugin."); guac_flush(io); guac_close(io); - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); return NULL; } @@ -213,7 +213,7 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { guac_send_error(io, "Invalid server-side client plugin."); guac_flush(io); guac_close(io); - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); return NULL; } @@ -225,7 +225,7 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { guac_send_error(io, "Invalid server-side client plugin."); guac_flush(io); guac_close(io); - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); return NULL; } @@ -234,16 +234,16 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { || guac_flush(io) ) { guac_close(io); - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); return NULL; } - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); break; } /* end if select */ - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); } } @@ -267,35 +267,35 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { return NULL; } - result = guac_read_instruction(io, usec_timeout, &instruction); - if (result < 0) { + instruction = guac_read_instruction(io, usec_timeout); + if (instruction == NULL) { guac_log_error("Error reading instruction while waiting for connect"); guac_close(io); return NULL; } /* Connect instruction read */ - if (result > 0) { + else { - if (strcmp(instruction.opcode, "connect") == 0) { + if (strcmp(instruction->opcode, "connect") == 0) { /* Initialize client arguments */ - argc = instruction.argc; - argv = instruction.argv; + argc = instruction->argc; + argv = instruction->argv; if (alias.client_init(client, argc, argv) != 0) { /* NOTE: On error, proxy client will send appropriate error message */ - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); guac_close(io); return NULL; } - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); return client; } /* end if connect */ - guac_free_instruction_data(&instruction); + guac_free_instruction(instruction); } } diff --git a/libguac/src/error.c b/libguac/src/error.c index 9f5a0fa2..d7411db1 100644 --- a/libguac/src/error.c +++ b/libguac/src/error.c @@ -49,6 +49,7 @@ const char* __GUAC_STATUS_SUCCESS_STR = "Success"; const char* __GUAC_STATUS_NO_MEMORY_STR = "Insufficient memory"; const char* __GUAC_STATUS_NO_INPUT_STR = "End of input stream"; const char* __GUAC_STATUS_INPUT_TIMEOUT_STR = "Read timeout"; +const char* __GUAC_STATUS_SEE_ERRNO_STR = "(see value of errno)"; const char* __GUAC_STATUS_OUTPUT_ERROR_STR = "Output error"; const char* __GUAC_STATUS_BAD_ARGUMENT_STR = "Invalid argument"; const char* __GUAC_STATUS_BAD_STATE_STR = "Illegal state"; @@ -75,6 +76,10 @@ const char* guac_status_string(guac_status status) { case GUAC_STATUS_INPUT_TIMEOUT: return __GUAC_STATUS_INPUT_TIMEOUT_STR; + /* Further information in errno */ + case GUAC_STATUS_SEE_ERRNO: + return __GUAC_STATUS_SEE_ERRNO_STR; + /* Output error */ case GUAC_STATUS_OUTPUT_ERROR: return __GUAC_STATUS_OUTPUT_ERROR_STR; diff --git a/libguac/src/guacio.c b/libguac/src/guacio.c index 9ed2ba33..cd523c17 100644 --- a/libguac/src/guacio.c +++ b/libguac/src/guacio.c @@ -117,7 +117,7 @@ ssize_t __guac_write(GUACIO* io, const char* buf, int count) { /* Record errors in guac_error */ if (retval < 0) - guac_error = GUAC_STATUS_OUTPUT_ERROR; + guac_error = GUAC_STATUS_SEE_ERRNO; return retval; } @@ -317,7 +317,7 @@ int guac_select(GUACIO* io, int usec_timeout) { } /* Properly set guac_error */ - if (retval < 0) guac_error = GUAC_STATUS_INPUT_ERROR; + if (retval < 0) guac_error = GUAC_STATUS_SEE_ERRNO; if (retval == 0) guac_error = GUAC_STATUS_INPUT_TIMEOUT; return retval; diff --git a/libguac/src/protocol.c b/libguac/src/protocol.c index d7786937..f2c86791 100644 --- a/libguac/src/protocol.c +++ b/libguac/src/protocol.c @@ -62,6 +62,7 @@ #include "guacio.h" #include "protocol.h" +#include "error.h" ssize_t __guac_write_length_string(GUACIO* io, const char* str) { @@ -328,7 +329,7 @@ int guac_send_cursor(GUACIO* io, int x, int y, cairo_surface_t* surface) { } -int __guac_fill___instructionbuf(GUACIO* io) { +int __guac_fill_instructionbuf(GUACIO* io) { int retval; @@ -356,8 +357,7 @@ int __guac_fill___instructionbuf(GUACIO* io) { } /* Returns new instruction if one exists, or NULL if no more instructions. */ -int guac_read_instruction(GUACIO* io, int usec_timeout, - guac_instruction* parsed_instruction) { +guac_instruction* guac_read_instruction(GUACIO* io, int usec_timeout) { int retval; int i = io->__instructionbuf_parse_start; @@ -407,19 +407,59 @@ int guac_read_instruction(GUACIO* io, int usec_timeout, /* Finish parse if terminator is a semicolon */ if (terminator == ';') { + guac_instruction* parsed_instruction; int j; + /* Allocate instruction */ + parsed_instruction = malloc(sizeof(guac_instruction)); + if (parsed_instruction == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + return NULL; + } + /* Init parsed instruction */ parsed_instruction->argc = io->__instructionbuf_elementc - 1; parsed_instruction->argv = malloc(sizeof(char*) * parsed_instruction->argc); + /* Fail if memory could not be alloc'd for argv */ + if (parsed_instruction->argv == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + free(parsed_instruction); + return NULL; + } + /* Set opcode */ parsed_instruction->opcode = strdup(io->__instructionbuf_elementv[0]); + /* Fail if memory could not be alloc'd for opcode */ + if (parsed_instruction->opcode == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + free(parsed_instruction->argv); + free(parsed_instruction); + return NULL; + } + + /* Copy element values to parsed instruction */ - for (j=0; jargc; j++) + for (j=0; jargc; j++) { parsed_instruction->argv[j] = strdup(io->__instructionbuf_elementv[j+1]); + /* Free memory and fail if out of mem */ + if (parsed_instruction->argv[j] == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + + /* Free all alloc'd argv values */ + while (--j >= 0) + free(parsed_instruction->argv[j]); + + free(parsed_instruction->opcode); + free(parsed_instruction->argv); + free(parsed_instruction); + return NULL; + } + + } + /* Reset buffer */ memmove(io->__instructionbuf, io->__instructionbuf + i + 1, io->__instructionbuf_used_length - i - 1); io->__instructionbuf_used_length -= i + 1; @@ -427,7 +467,7 @@ int guac_read_instruction(GUACIO* io, int usec_timeout, io->__instructionbuf_elementc = 0; /* Done */ - return 1; + return parsed_instruction; } /* end if terminator */ @@ -444,12 +484,20 @@ int guac_read_instruction(GUACIO* io, int usec_timeout, /* No instruction yet? Get more data ... */ retval = guac_select(io, usec_timeout); if (retval <= 0) - return retval; + return NULL; /* If more data is available, fill into buffer */ - retval = __guac_fill___instructionbuf(io); - if (retval < 0) return retval; /* Error */ - if (retval == 0) return -1; /* EOF */ + retval = __guac_fill_instructionbuf(io); + + /* Error, guac_error already set */ + if (retval < 0) + return NULL; + + /* EOF */ + if (retval == 0) { + guac_error = GUAC_STATUS_NO_INPUT; + return NULL; + } }