diff options
-rw-r--r-- | src/plugins/relay/relay-client.c | 67 | ||||
-rw-r--r-- | src/plugins/relay/relay-client.h | 2 | ||||
-rw-r--r-- | src/plugins/relay/relay-websocket.c | 85 |
3 files changed, 121 insertions, 33 deletions
diff --git a/src/plugins/relay/relay-client.c b/src/plugins/relay/relay-client.c index 29e2cb6ff..58946ab3d 100644 --- a/src/plugins/relay/relay-client.c +++ b/src/plugins/relay/relay-client.c @@ -638,8 +638,8 @@ int relay_client_recv_cb (const void *pointer, void *data, int fd) { struct t_relay_client *client; - static char buffer[4096]; - int i, num_read, rc, num_frames; + static char buffer[4096], *buffer2; + int i, num_read, rc, num_frames, buffer2_size; struct t_relay_websocket_frame *frames; /* make C compiler happy */ @@ -693,15 +693,44 @@ relay_client_recv_cb (const void *pointer, void *data, int fd) if (client->websocket == RELAY_CLIENT_WEBSOCKET_READY) { /* websocket used, decode message */ + buffer2 = NULL; + buffer2_size = 0; + if (client->partial_ws_frame) + { + buffer2_size = num_read + client->partial_ws_frame_size; + buffer2 = malloc (buffer2_size); + if (!buffer2) + { + weechat_printf_date_tags ( + NULL, 0, "relay_client", + _("%s%s: not enough memory for received data"), + weechat_prefix ("error"), RELAY_PLUGIN_NAME); + return WEECHAT_RC_OK; + } + memcpy (buffer2, client->partial_ws_frame, + client->partial_ws_frame_size); + memcpy (buffer2 + client->partial_ws_frame_size, + buffer, num_read); + } frames = NULL; num_frames = 0; - rc = relay_websocket_decode_frame (client, - (unsigned char *)buffer, - (unsigned long long)num_read, - &frames, &num_frames); + rc = relay_websocket_decode_frame ( + client, + (buffer2) ? (unsigned char *)buffer2 : (unsigned char *)buffer, + (buffer2) ? (unsigned long long)buffer2_size : (unsigned long long)num_read, + &frames, + &num_frames); + if (buffer2) + free (buffer2); if (!rc) { - /* error when decoding frame: close connection */ + /* fatal error when decoding frame: close connection */ + for (i = 0; i < num_frames; i++) + { + if (frames[i].payload) + free (frames[i].payload); + } + free (frames); weechat_printf_date_tags ( NULL, 0, "relay_client", _("%s%s: error decoding websocket frame for client " @@ -1420,6 +1449,8 @@ relay_client_new (int sock, const char *address, struct t_relay_server *server) new_client->send_data_type = RELAY_CLIENT_DATA_TEXT_MULTILINE; break; } + new_client->partial_ws_frame = NULL; + new_client->partial_ws_frame_size = 0; new_client->partial_message = NULL; relay_client_set_desc (new_client); @@ -1586,7 +1617,8 @@ relay_client_new_with_infolist (struct t_infolist *infolist) struct t_relay_client *new_client; const char *str; Bytef *ptr_dict; - int dict_size; + int dict_size, ws_frame_size; + void *ptr_ws_frame; new_client = malloc (sizeof (*new_client)); if (new_client) @@ -1686,6 +1718,16 @@ relay_client_new_with_infolist (struct t_infolist *infolist) "%llu", &(new_client->bytes_sent)); new_client->recv_data_type = weechat_infolist_integer (infolist, "recv_data_type"); new_client->send_data_type = weechat_infolist_integer (infolist, "send_data_type"); + ptr_ws_frame = weechat_infolist_buffer (infolist, "partial_ws_frame", &ws_frame_size); + if (ptr_ws_frame && (ws_frame_size > 0)) + { + new_client->partial_ws_frame = malloc (ws_frame_size); + if (new_client->partial_ws_frame) + { + memcpy (new_client->partial_ws_frame, ptr_ws_frame, ws_frame_size); + new_client->partial_ws_frame_size = ws_frame_size; + } + } str = weechat_infolist_string (infolist, "partial_message"); new_client->partial_message = (str) ? strdup (str) : NULL; @@ -1891,6 +1933,8 @@ relay_client_free (struct t_relay_client *client) weechat_unhook (client->hook_fd); if (client->hook_timer_send) weechat_unhook (client->hook_timer_send); + if (client->partial_ws_frame) + free (client->partial_ws_frame); if (client->partial_message) free (client->partial_message); if (client->protocol_data) @@ -2013,6 +2057,8 @@ relay_client_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_time (ptr_item, "end_time", time (NULL))) return 0; + if (!weechat_infolist_new_var_buffer (ptr_item, "partial_ws_frame", NULL, 0)) + return 0; if (!weechat_infolist_new_var_string (ptr_item, "partial_message", NULL)) return 0; } @@ -2028,6 +2074,8 @@ relay_client_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_time (ptr_item, "end_time", client->end_time)) return 0; + if (!weechat_infolist_new_var_buffer (ptr_item, "partial_ws_frame", client->partial_ws_frame, client->partial_ws_frame_size)) + return 0; if (!weechat_infolist_new_var_string (ptr_item, "partial_message", client->partial_message)) return 0; } @@ -2194,6 +2242,9 @@ relay_client_print_log () weechat_log_printf (" send_data_type. . . . . . : %d (%s)", ptr_client->send_data_type, relay_client_data_type_string[ptr_client->send_data_type]); + weechat_log_printf (" partial_ws_frame. . . . . : 0x%lx (%d bytes)", + ptr_client->partial_ws_frame, + ptr_client->partial_ws_frame_size); weechat_log_printf (" partial_message . . . . . : '%s'", ptr_client->partial_message); weechat_log_printf (" protocol_data . . . . . . : 0x%lx", ptr_client->protocol_data); switch (ptr_client->protocol) diff --git a/src/plugins/relay/relay-client.h b/src/plugins/relay/relay-client.h index 9d22f629f..e4482037a 100644 --- a/src/plugins/relay/relay-client.h +++ b/src/plugins/relay/relay-client.h @@ -129,6 +129,8 @@ struct t_relay_client unsigned long long bytes_sent; /* bytes sent to client */ enum t_relay_client_data_type recv_data_type; /* type recv from client */ enum t_relay_client_data_type send_data_type; /* type sent to client */ + char *partial_ws_frame; /* part. binary websocket frame recv */ + int partial_ws_frame_size; /* size of partial websocket frame */ char *partial_message; /* partial text message received */ void *protocol_data; /* data depending on protocol used */ struct t_relay_client_outqueue *outqueue; /* queue for outgoing msgs */ diff --git a/src/plugins/relay/relay-websocket.c b/src/plugins/relay/relay-websocket.c index b74fb937e..2d136620b 100644 --- a/src/plugins/relay/relay-websocket.c +++ b/src/plugins/relay/relay-websocket.c @@ -547,7 +547,7 @@ error: * is used). * * Returns: - * 1: frame decoded successfully + * 1: frame(s) decoded successfully * 0: error decoding frame (connection must be closed if it happens) */ @@ -558,11 +558,13 @@ relay_websocket_decode_frame (struct t_relay_client *client, struct t_relay_websocket_frame **frames, int *num_frames) { - unsigned long long i, index_buffer, length_frame_size, length_frame; + unsigned long long i, index_buffer, index_buffer_start_frame; + unsigned long long length_frame_size, length_frame; unsigned char opcode; size_t size_decompressed; char *payload_decompressed; struct t_relay_websocket_frame *frames2, *ptr_frame; + int size; if (!buffer || !frames || !num_frames) return 0; @@ -571,22 +573,15 @@ relay_websocket_decode_frame (struct t_relay_client *client, *num_frames = 0; index_buffer = 0; + index_buffer_start_frame = 0; /* loop to decode all frames in message */ - while (index_buffer + 1 < buffer_length) + while (index_buffer < buffer_length) { - (*num_frames)++; - - frames2 = realloc (*frames, sizeof (**frames) * (*num_frames)); - if (!frames2) - return 0; - *frames = frames2; + index_buffer_start_frame = index_buffer; - ptr_frame = &((*frames)[*num_frames - 1]); - - ptr_frame->opcode = 0; - ptr_frame->payload_size = 0; - ptr_frame->payload = NULL; + if (index_buffer + 1 >= buffer_length) + goto missing_data; opcode = buffer[index_buffer] & 15; @@ -597,16 +592,16 @@ relay_websocket_decode_frame (struct t_relay_client *client, if (!(buffer[index_buffer + 1] & 128)) return 0; - /* decode frame */ + /* decode frame length */ length_frame = buffer[index_buffer + 1] & 127; index_buffer += 2; if (index_buffer >= buffer_length) - return 0; + goto missing_data; if ((length_frame == 126) || (length_frame == 127)) { length_frame_size = (length_frame == 126) ? 2 : 8; if (index_buffer + length_frame_size > buffer_length) - return 0; + goto missing_data; length_frame = 0; for (i = 0; i < length_frame_size; i++) { @@ -617,7 +612,7 @@ relay_websocket_decode_frame (struct t_relay_client *client, /* read masks (4 bytes) */ if (index_buffer + 4 > buffer_length) - return 0; + goto missing_data; int masks[4]; for (i = 0; i < 4; i++) { @@ -625,6 +620,27 @@ relay_websocket_decode_frame (struct t_relay_client *client, } index_buffer += 4; + /* check if we have enough data */ + if ((length_frame > buffer_length) + || (index_buffer + length_frame > buffer_length)) + { + goto missing_data; + } + + /* add a new frame in array */ + (*num_frames)++; + + frames2 = realloc (*frames, sizeof (**frames) * (*num_frames)); + if (!frames2) + return 0; + *frames = frames2; + + ptr_frame = &((*frames)[*num_frames - 1]); + + ptr_frame->opcode = 0; + ptr_frame->payload_size = 0; + ptr_frame->payload = NULL; + /* save opcode */ switch (opcode) { @@ -639,13 +655,7 @@ relay_websocket_decode_frame (struct t_relay_client *client, break; } - /* decode data using masks */ - if ((length_frame > buffer_length) - || (index_buffer + length_frame > buffer_length)) - { - return 0; - } - + /* allocate payload */ ptr_frame->payload = malloc (length_frame + 1); if (!ptr_frame->payload) return 0; @@ -690,6 +700,31 @@ relay_websocket_decode_frame (struct t_relay_client *client, index_buffer += length_frame; } + if (client->partial_ws_frame) + { + free (client->partial_ws_frame); + client->partial_ws_frame = NULL; + client->partial_ws_frame_size = 0; + } + + return 1; + +missing_data: + if (client->partial_ws_frame) + { + free (client->partial_ws_frame); + client->partial_ws_frame = NULL; + client->partial_ws_frame_size = 0; + } + size = buffer_length - index_buffer_start_frame; + if (size >= 0) + { + client->partial_ws_frame = malloc (size); + if (!client->partial_ws_frame) + return 0; + memcpy (client->partial_ws_frame, buffer + index_buffer_start_frame, size); + client->partial_ws_frame_size = size; + } return 1; } |