1
0
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:
Sébastien Helleu
2024-02-04 18:52:00 +01:00
parent b7ecf93a22
commit 0414c139b0
3 changed files with 143 additions and 55 deletions
+59 -8
View File
@@ -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)
+2
View File
@@ -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 */
+82 -47
View File
@@ -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;
}