From 5046a9b596018c50ba295014aa957926937557bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Tue, 9 Apr 2024 13:32:34 +0200 Subject: [PATCH] relay/api: fix decoding of large websocket frames in remote client (closes #2107) --- .../relay/api/remote/relay-remote-network.c | 49 ++++++++++++++----- src/plugins/relay/relay-remote.c | 24 ++++++++- src/plugins/relay/relay-remote.h | 2 + 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/plugins/relay/api/remote/relay-remote-network.c b/src/plugins/relay/api/remote/relay-remote-network.c index c4eb7ec0c..fe6c38c07 100644 --- a/src/plugins/relay/api/remote/relay-remote-network.c +++ b/src/plugins/relay/api/remote/relay-remote-network.c @@ -466,8 +466,8 @@ relay_remote_network_recv_buffer (struct t_relay_remote *remote, const char *buffer, int buffer_size) { struct t_relay_websocket_frame *frames; - char *partial_ws_frame; - int rc, i, num_frames, partial_ws_frame_size; + int rc, i, buffer2_size, num_frames; + char *buffer2; /* if authenticating is in progress, check if it was successful */ if (remote->status == RELAY_STATUS_AUTHENTICATING) @@ -476,26 +476,51 @@ relay_remote_network_recv_buffer (struct t_relay_remote *remote, } else if (remote->status == RELAY_STATUS_CONNECTED) { - partial_ws_frame = NULL; - partial_ws_frame_size = 0; + buffer2 = NULL; + buffer2_size = 0; + if (remote->partial_ws_frame) + { + buffer2_size = buffer_size + remote->partial_ws_frame_size; + buffer2 = malloc (buffer2_size); + if (!buffer2) + { + weechat_printf ( + NULL, + _("%sremote[%s]: not enough memory"), + weechat_prefix ("error"), + remote->name); + return; + } + memcpy (buffer2, remote->partial_ws_frame, + remote->partial_ws_frame_size); + memcpy (buffer2 + remote->partial_ws_frame_size, + buffer, buffer_size); + } + frames = NULL; + num_frames = 0; rc = relay_websocket_decode_frame ( - (const unsigned char *)buffer, - buffer_size, + (buffer2) ? (unsigned char *)buffer2 : (const unsigned char *)buffer, + (buffer2) ? (unsigned long long)buffer2_size : (unsigned long long)buffer_size, 0, /* expect_masked_frame */ remote->ws_deflate, &frames, &num_frames, - &partial_ws_frame, - &partial_ws_frame_size); + &remote->partial_ws_frame, + &remote->partial_ws_frame_size); + if (buffer2) + free (buffer2); if (!rc) { /* fatal error when decoding frame: close connection */ - for (i = 0; i < num_frames; i++) + if (frames) { - if (frames[i].payload) - free (frames[i].payload); + for (i = 0; i < num_frames; i++) + { + if (frames[i].payload) + free (frames[i].payload); + } + free (frames); } - free (frames); weechat_printf ( NULL, _("%sremote[%s]: error decoding websocket frame"), diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c index ca63befb0..161d809a7 100644 --- a/src/plugins/relay/relay-remote.c +++ b/src/plugins/relay/relay-remote.c @@ -327,6 +327,8 @@ relay_remote_alloc (const char *name) new_remote->gnutls_sess = NULL; new_remote->ws_deflate = relay_websocket_deflate_alloc (); new_remote->synced = 0; + new_remote->partial_ws_frame = NULL; + new_remote->partial_ws_frame_size = 0; new_remote->prev_remote = NULL; new_remote->next_remote = NULL; @@ -487,7 +489,8 @@ relay_remote_new_with_infolist (struct t_infolist *infolist) { struct t_relay_remote *new_remote; Bytef *ptr_dict; - int dict_size; + int dict_size, ws_frame_size; + void *ptr_ws_frame; new_remote = malloc (sizeof (*new_remote)); if (!new_remote) @@ -550,6 +553,16 @@ relay_remote_new_with_infolist (struct t_infolist *infolist) } } new_remote->synced = weechat_infolist_integer (infolist, "synced"); + ptr_ws_frame = weechat_infolist_buffer (infolist, "partial_ws_frame", &ws_frame_size); + if (ptr_ws_frame && (ws_frame_size > 0)) + { + new_remote->partial_ws_frame = malloc (ws_frame_size); + if (new_remote->partial_ws_frame) + { + memcpy (new_remote->partial_ws_frame, ptr_ws_frame, ws_frame_size); + new_remote->partial_ws_frame_size = ws_frame_size; + } + } new_remote->prev_remote = NULL; new_remote->next_remote = relay_remotes; if (relay_remotes) @@ -766,6 +779,8 @@ relay_remote_free (struct t_relay_remote *remote) if (remote->hook_fd) weechat_unhook (remote->hook_fd); relay_websocket_deflate_free (remote->ws_deflate); + if (remote->partial_ws_frame) + free (remote->partial_ws_frame); free (remote); @@ -835,6 +850,8 @@ relay_remote_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_integer (ptr_item, "sock", -1)) return 0; + if (!weechat_infolist_new_var_buffer (ptr_item, "partial_ws_frame", NULL, 0)) + return 0; } else { @@ -842,6 +859,8 @@ relay_remote_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_integer (ptr_item, "sock", remote->sock)) return 0; + if (!weechat_infolist_new_var_buffer (ptr_item, "partial_ws_frame", remote->partial_ws_frame, remote->partial_ws_frame_size)) + return 0; } if (remote->ws_deflate->strm_deflate || remote->ws_deflate->strm_inflate) { @@ -916,6 +935,9 @@ relay_remote_print_log () weechat_log_printf (" gnutls_sess . . . . . . : 0x%lx", ptr_remote->gnutls_sess); relay_websocket_deflate_print_log (ptr_remote->ws_deflate, ""); weechat_log_printf (" synced. . . . . . . . . : %d", ptr_remote->synced); + weechat_log_printf (" partial_ws_frame. . . . . : %p (%d bytes)", + ptr_remote->partial_ws_frame, + ptr_remote->partial_ws_frame_size); weechat_log_printf (" prev_remote . . . . . . : 0x%lx", ptr_remote->prev_remote); weechat_log_printf (" next_remote . . . . . . : 0x%lx", ptr_remote->next_remote); } diff --git a/src/plugins/relay/relay-remote.h b/src/plugins/relay/relay-remote.h index f7524d3fd..2e3719867 100644 --- a/src/plugins/relay/relay-remote.h +++ b/src/plugins/relay/relay-remote.h @@ -56,6 +56,8 @@ struct t_relay_remote gnutls_session_t gnutls_sess; /* gnutls session (only if TLS used) */ struct t_relay_websocket_deflate *ws_deflate; /* websocket deflate data */ int synced; /* 1 if synced with remote */ + char *partial_ws_frame; /* part. binary websocket frame recv */ + int partial_ws_frame_size; /* size of partial websocket frame */ struct t_relay_remote *prev_remote;/* link to previous remote */ struct t_relay_remote *next_remote;/* link to next remote */ };