GUACAMOLE-422: Change handshake to ignore order of opcodes.
This commit is contained in:
parent
5480b288e8
commit
0ee47e0186
@ -505,6 +505,79 @@ struct guac_user {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for Guacamole protocol opcode specific to the handshake that
|
||||||
|
* happens between client and server at the beginning of the connection. The
|
||||||
|
* handler will be invoked when the matching opcode is received during the
|
||||||
|
* handshake process.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user that initiated the handshake.
|
||||||
|
*
|
||||||
|
* @param parser
|
||||||
|
* The parser allocated for parsing the data provided by the client.
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* The timeout, in microseconds, for parsing the value.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero if the handshake instruction is successfully parsed; otherwise
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
typedef int __guac_handshake_handler(guac_user* user, int argc, char** argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure that maps opcodes received during the handshake phase of the
|
||||||
|
* connection to callback functions used when those opcodes are received.
|
||||||
|
*/
|
||||||
|
typedef struct __guac_handshake_mapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instruction opcode which maps to the handler.
|
||||||
|
*/
|
||||||
|
char* opcode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The handler function used when specified opcode is received.
|
||||||
|
*/
|
||||||
|
__guac_handshake_handler* handler;
|
||||||
|
|
||||||
|
} __guac_handshake_mapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal handler function that is called when the size instruction is
|
||||||
|
* received during the handshake process.
|
||||||
|
*/
|
||||||
|
__guac_handshake_handler __guac_handshake_size_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal handler function that is called when the audio instruction is
|
||||||
|
* received during the handshake process, specifying the audio mimetypes
|
||||||
|
* available to the client.
|
||||||
|
*/
|
||||||
|
__guac_handshake_handler __guac_handshake_audio_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal handler function that is called when the video instruction is
|
||||||
|
* received during the handshake process, specifying the video mimetypes
|
||||||
|
* available to the client.
|
||||||
|
*/
|
||||||
|
__guac_handshake_handler __guac_handshake_video_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal handler function that is called when the image instruction is
|
||||||
|
* received during the handshake process, specifying the image mimetypes
|
||||||
|
* available to the client.
|
||||||
|
*/
|
||||||
|
__guac_handshake_handler __guac_handshake_image_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal handler function that is called when the timezone instruction is
|
||||||
|
* received during the handshake process, specifying the timezone of the
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
__guac_handshake_handler __guac_handshake_timezone_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates a new, blank user, not associated with any specific client or
|
* Allocates a new, blank user, not associated with any specific client or
|
||||||
* socket.
|
* socket.
|
||||||
|
@ -171,6 +171,80 @@ static void guac_free_mimetypes(char** mimetypes) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Guacamole handshake handler functions. */
|
||||||
|
|
||||||
|
int __guac_handshake_size_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
/* Validate size of instruction. */
|
||||||
|
if (argc < 2) {
|
||||||
|
guac_user_log(user, GUAC_LOG_ERROR, "Received \"size\" "
|
||||||
|
"instruction lacked required arguments.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse optimal screen dimensions from size instruction */
|
||||||
|
user->info.optimal_width = atoi(argv[0]);
|
||||||
|
user->info.optimal_height = atoi(argv[1]);
|
||||||
|
|
||||||
|
/* If DPI given, set the user resolution */
|
||||||
|
if (argc >= 3)
|
||||||
|
user->info.optimal_resolution = atoi(argv[2]);
|
||||||
|
|
||||||
|
/* Otherwise, use a safe default for rough backwards compatibility */
|
||||||
|
else
|
||||||
|
user->info.optimal_resolution = 96;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int __guac_handshake_audio_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
/* Store audio mimetypes */
|
||||||
|
user->info.audio_mimetypes = (const char**) guac_copy_mimetypes(argv, argc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int __guac_handshake_video_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
/* Store video mimetypes */
|
||||||
|
user->info.video_mimetypes = (const char**) guac_copy_mimetypes(argv, argc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int __guac_handshake_image_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
/* Store image mimetypes */
|
||||||
|
user->info.image_mimetypes = (const char**) guac_copy_mimetypes(argv, argc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int __guac_handshake_timezone_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
/* Store timezone, if present */
|
||||||
|
if (argc > 0 && strcmp(argv[0], ""))
|
||||||
|
user->info.timezone = (const char*) strdup(argv[0]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Guacamole handshake handler mappings. */
|
||||||
|
__guac_handshake_mapping __guac_handshake_map[] = {
|
||||||
|
{"size", __guac_handshake_size_handler},
|
||||||
|
{"audio", __guac_handshake_audio_handler},
|
||||||
|
{"video", __guac_handshake_video_handler},
|
||||||
|
{"image", __guac_handshake_image_handler},
|
||||||
|
{"timezone", __guac_handshake_timezone_handler},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The thread which handles all user input, calling event handlers for received
|
* The thread which handles all user input, calling event handlers for received
|
||||||
* instructions.
|
* instructions.
|
||||||
@ -305,122 +379,53 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
|
|||||||
|
|
||||||
guac_parser* parser = guac_parser_alloc();
|
guac_parser* parser = guac_parser_alloc();
|
||||||
|
|
||||||
/* Get optimal screen size */
|
/* Handle each of the opcodes. */
|
||||||
if (guac_parser_expect(parser, socket, usec_timeout, "size")) {
|
while (1) {
|
||||||
|
if (guac_parser_read(parser, socket, usec_timeout)) {
|
||||||
/* Log error */
|
|
||||||
guac_user_log_handshake_failure(user);
|
guac_user_log_handshake_failure(user);
|
||||||
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
||||||
"Error reading \"size\"");
|
"Error while reading opcode instruction.");
|
||||||
|
|
||||||
guac_parser_free(parser);
|
guac_parser_free(parser);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate content of size instruction */
|
/* If we receive the connect opcode, we're done. */
|
||||||
if (parser->argc < 2) {
|
if (strcmp(parser->opcode, "connect") == 0)
|
||||||
guac_user_log(user, GUAC_LOG_ERROR, "Received \"size\" "
|
break;
|
||||||
"instruction lacked required arguments.");
|
|
||||||
guac_parser_free(parser);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse optimal screen dimensions from size instruction */
|
/* Loop available opcodes and run handler if/when match found. */
|
||||||
user->info.optimal_width = atoi(parser->argv[0]);
|
__guac_handshake_mapping* current = __guac_handshake_map;
|
||||||
user->info.optimal_height = atoi(parser->argv[1]);
|
while (current->opcode != NULL) {
|
||||||
|
|
||||||
/* If DPI given, set the user resolution */
|
/* Check if loop opcode matches parsed opcode. */
|
||||||
if (parser->argc >= 3)
|
if (strcmp(parser->opcode, current->opcode) == 0) {
|
||||||
user->info.optimal_resolution = atoi(parser->argv[2]);
|
|
||||||
|
|
||||||
/* Otherwise, use a safe default for rough backwards compatibility */
|
/* If calling the handler fails, log it and return. */
|
||||||
else
|
if (current->handler(user, parser->argc, parser->argv)) {
|
||||||
user->info.optimal_resolution = 96;
|
|
||||||
|
|
||||||
/* Get supported audio formats */
|
|
||||||
if (guac_parser_expect(parser, socket, usec_timeout, "audio")) {
|
|
||||||
|
|
||||||
/* Log error */
|
|
||||||
guac_user_log_handshake_failure(user);
|
guac_user_log_handshake_failure(user);
|
||||||
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
||||||
"Error reading \"audio\"");
|
"Error handling handling opcode during handshake.");
|
||||||
|
guac_user_log(user, GUAC_LOG_DEBUG, "Failed opcode: %s",
|
||||||
|
current->opcode);
|
||||||
|
|
||||||
guac_parser_free(parser);
|
guac_parser_free(parser);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store audio mimetypes */
|
/* If calling the handler has succeeded, log it and break. */
|
||||||
char** audio_mimetypes = guac_copy_mimetypes(parser->argv, parser->argc);
|
else {
|
||||||
user->info.audio_mimetypes = (const char**) audio_mimetypes;
|
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||||
|
"Successfully processed instruction: \"%s\"",
|
||||||
/* Get supported video formats */
|
current->opcode);
|
||||||
if (guac_parser_expect(parser, socket, usec_timeout, "video")) {
|
break;
|
||||||
|
|
||||||
/* Log error */
|
|
||||||
guac_user_log_handshake_failure(user);
|
|
||||||
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
|
||||||
"Error reading \"video\"");
|
|
||||||
|
|
||||||
guac_parser_free(parser);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store video mimetypes */
|
|
||||||
char** video_mimetypes = guac_copy_mimetypes(parser->argv, parser->argc);
|
|
||||||
user->info.video_mimetypes = (const char**) video_mimetypes;
|
|
||||||
|
|
||||||
/* Get supported image formats */
|
|
||||||
if (guac_parser_expect(parser, socket, usec_timeout, "image")) {
|
|
||||||
|
|
||||||
/* Log error */
|
|
||||||
guac_user_log_handshake_failure(user);
|
|
||||||
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
|
||||||
"Error reading \"image\"");
|
|
||||||
|
|
||||||
guac_parser_free(parser);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store image mimetypes */
|
|
||||||
char** image_mimetypes = guac_copy_mimetypes(parser->argv, parser->argc);
|
|
||||||
user->info.image_mimetypes = (const char**) image_mimetypes;
|
|
||||||
|
|
||||||
/* Get client timezone */
|
|
||||||
if (guac_parser_expect(parser, socket, usec_timeout, "timezone")) {
|
|
||||||
|
|
||||||
/* Log error */
|
|
||||||
guac_user_log_handshake_failure(user);
|
|
||||||
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
|
||||||
"Error reading \"timezone\"");
|
|
||||||
|
|
||||||
guac_parser_free(parser);
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check number of timezone arguments */
|
/* Move to next opcode. */
|
||||||
if (parser->argc < 1) {
|
current++;
|
||||||
guac_user_log(user, GUAC_LOG_ERROR, "Received \"timezone\" instruction "
|
|
||||||
"lacked required arguments.");
|
|
||||||
guac_parser_free(parser);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store timezone, if present */
|
|
||||||
char* timezone = parser->argv[0];
|
|
||||||
if (!strcmp(timezone, ""))
|
|
||||||
user->info.timezone = (const char*) timezone;
|
|
||||||
|
|
||||||
/* Get args from connect instruction */
|
|
||||||
if (guac_parser_expect(parser, socket, usec_timeout, "connect")) {
|
|
||||||
|
|
||||||
/* Log error */
|
|
||||||
guac_user_log_handshake_failure(user);
|
|
||||||
guac_user_log_guac_error(user, GUAC_LOG_DEBUG,
|
|
||||||
"Error reading \"connect\"");
|
|
||||||
|
|
||||||
guac_parser_free(parser);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Acknowledge connection availability */
|
/* Acknowledge connection availability */
|
||||||
@ -449,14 +454,13 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free mimetype lists */
|
/* Free mimetype character arrays. */
|
||||||
guac_free_mimetypes(audio_mimetypes);
|
guac_free_mimetypes((char **) user->info.audio_mimetypes);
|
||||||
guac_free_mimetypes(video_mimetypes);
|
guac_free_mimetypes((char **) user->info.image_mimetypes);
|
||||||
guac_free_mimetypes(image_mimetypes);
|
guac_free_mimetypes((char **) user->info.video_mimetypes);
|
||||||
|
|
||||||
/* Free timezone */
|
/* Free timezone info. */
|
||||||
if (timezone != NULL)
|
free((char *) user->info.timezone);
|
||||||
free(timezone);
|
|
||||||
|
|
||||||
guac_parser_free(parser);
|
guac_parser_free(parser);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user