mirror of
https://github.com/weechat/weechat.git
synced 2026-06-12 14:14:48 +02:00
relay: fix decoding of websocket frame when a partial frame is received
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,10 +573,61 @@ 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)
|
||||
{
|
||||
index_buffer_start_frame = index_buffer;
|
||||
|
||||
if (index_buffer + 1 >= buffer_length)
|
||||
goto missing_data;
|
||||
|
||||
opcode = buffer[index_buffer] & 15;
|
||||
|
||||
/*
|
||||
* check if frame is masked: client MUST send a masked frame; if frame is
|
||||
* not masked, we MUST reject it and close the connection (see RFC 6455)
|
||||
*/
|
||||
if (!(buffer[index_buffer + 1] & 128))
|
||||
return 0;
|
||||
|
||||
/* decode frame length */
|
||||
length_frame = buffer[index_buffer + 1] & 127;
|
||||
index_buffer += 2;
|
||||
if (index_buffer >= buffer_length)
|
||||
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)
|
||||
goto missing_data;
|
||||
length_frame = 0;
|
||||
for (i = 0; i < length_frame_size; i++)
|
||||
{
|
||||
length_frame += (unsigned long long)buffer[index_buffer + i] << ((length_frame_size - i - 1) * 8);
|
||||
}
|
||||
index_buffer += length_frame_size;
|
||||
}
|
||||
|
||||
/* read masks (4 bytes) */
|
||||
if (index_buffer + 4 > buffer_length)
|
||||
goto missing_data;
|
||||
int masks[4];
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
masks[i] = (int)((unsigned char)buffer[index_buffer + i]);
|
||||
}
|
||||
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));
|
||||
@@ -588,43 +641,6 @@ relay_websocket_decode_frame (struct t_relay_client *client,
|
||||
ptr_frame->payload_size = 0;
|
||||
ptr_frame->payload = NULL;
|
||||
|
||||
opcode = buffer[index_buffer] & 15;
|
||||
|
||||
/*
|
||||
* check if frame is masked: client MUST send a masked frame; if frame is
|
||||
* not masked, we MUST reject it and close the connection (see RFC 6455)
|
||||
*/
|
||||
if (!(buffer[index_buffer + 1] & 128))
|
||||
return 0;
|
||||
|
||||
/* decode frame */
|
||||
length_frame = buffer[index_buffer + 1] & 127;
|
||||
index_buffer += 2;
|
||||
if (index_buffer >= buffer_length)
|
||||
return 0;
|
||||
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;
|
||||
length_frame = 0;
|
||||
for (i = 0; i < length_frame_size; i++)
|
||||
{
|
||||
length_frame += (unsigned long long)buffer[index_buffer + i] << ((length_frame_size - i - 1) * 8);
|
||||
}
|
||||
index_buffer += length_frame_size;
|
||||
}
|
||||
|
||||
/* read masks (4 bytes) */
|
||||
if (index_buffer + 4 > buffer_length)
|
||||
return 0;
|
||||
int masks[4];
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
masks[i] = (int)((unsigned char)buffer[index_buffer + i]);
|
||||
}
|
||||
index_buffer += 4;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user