From 75167744743d49f7c283103ed39b21703d78bb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 1 Jun 2024 15:15:01 +0200 Subject: [PATCH] relay: fix websocket permessage-deflate extension when the client doesn't send the max window bits parameters --- ChangeLog.adoc | 1 + src/plugins/relay/relay-client.c | 6 ++ src/plugins/relay/relay-remote.c | 2 + src/plugins/relay/relay-websocket.c | 72 ++++++++++++++----- src/plugins/relay/relay-websocket.h | 2 + .../plugins/relay/test-relay-websocket.cpp | 48 ++++++++++++- 6 files changed, 112 insertions(+), 19 deletions(-) diff --git a/ChangeLog.adoc b/ChangeLog.adoc index 023ee7688..0f2b7357d 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -14,6 +14,7 @@ [[v4.3.2_fixed]] === Fixed +* relay: fix websocket permessage-deflate extension when the client doesn't send the max window bits parameters * relay: fix allocation and reinit of field "client_context_takeover" in websocket deflate structure [[v4.3.1]] diff --git a/src/plugins/relay/relay-client.c b/src/plugins/relay/relay-client.c index f073f180c..d5cf063a2 100644 --- a/src/plugins/relay/relay-client.c +++ b/src/plugins/relay/relay-client.c @@ -1620,6 +1620,8 @@ relay_client_new_with_infolist (struct t_infolist *infolist) new_client->ws_deflate->client_context_takeover = weechat_infolist_integer (infolist, "ws_deflate_client_context_takeover"); new_client->ws_deflate->window_bits_deflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_deflate"); new_client->ws_deflate->window_bits_inflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_inflate"); + new_client->ws_deflate->server_max_window_bits_recv = weechat_infolist_integer (infolist, "ws_deflate_server_max_window_bits_recv"); + new_client->ws_deflate->client_max_window_bits_recv = weechat_infolist_integer (infolist, "ws_deflate_client_max_window_bits_recv"); new_client->ws_deflate->strm_deflate = NULL; new_client->ws_deflate->strm_inflate = NULL; if (weechat_infolist_search_var (infolist, "ws_deflate_strm_deflate_dict")) @@ -2073,6 +2075,10 @@ relay_client_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_window_bits_inflate", client->ws_deflate->window_bits_inflate)) return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_server_max_window_bits_recv", client->ws_deflate->server_max_window_bits_recv)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_client_max_window_bits_recv", client->ws_deflate->client_max_window_bits_recv)) + return 0; if (!weechat_infolist_new_var_pointer (ptr_item, "ws_deflate_strm_deflate", client->ws_deflate->strm_deflate)) return 0; if (!weechat_infolist_new_var_pointer (ptr_item, "ws_deflate_strm_inflate", client->ws_deflate->strm_inflate)) diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c index 6d4c1b4d7..ccb6f0be4 100644 --- a/src/plugins/relay/relay-remote.c +++ b/src/plugins/relay/relay-remote.c @@ -544,6 +544,8 @@ relay_remote_new_with_infolist (struct t_infolist *infolist) new_remote->ws_deflate->client_context_takeover = weechat_infolist_integer (infolist, "ws_deflate_client_context_takeover"); new_remote->ws_deflate->window_bits_deflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_deflate"); new_remote->ws_deflate->window_bits_inflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_inflate"); + new_remote->ws_deflate->server_max_window_bits_recv = weechat_infolist_integer (infolist, "ws_deflate_server_max_window_bits_recv"); + new_remote->ws_deflate->client_max_window_bits_recv = weechat_infolist_integer (infolist, "ws_deflate_client_max_window_bits_recv"); new_remote->ws_deflate->strm_deflate = NULL; new_remote->ws_deflate->strm_inflate = NULL; if (weechat_infolist_search_var (infolist, "ws_deflate_strm_deflate_dict")) diff --git a/src/plugins/relay/relay-websocket.c b/src/plugins/relay/relay-websocket.c index 69b7aa93e..4e6f167f2 100644 --- a/src/plugins/relay/relay-websocket.c +++ b/src/plugins/relay/relay-websocket.c @@ -52,6 +52,8 @@ relay_websocket_deflate_alloc () new_ws_deflate->client_context_takeover = 0; new_ws_deflate->window_bits_deflate = 0; new_ws_deflate->window_bits_inflate = 0; + new_ws_deflate->server_max_window_bits_recv = 0; + new_ws_deflate->client_max_window_bits_recv = 0; new_ws_deflate->strm_deflate = NULL; new_ws_deflate->strm_inflate = NULL; @@ -148,6 +150,8 @@ relay_websocket_deflate_reinit (struct t_relay_websocket_deflate *ws_deflate) ws_deflate->client_context_takeover = 0; ws_deflate->window_bits_deflate = 0; ws_deflate->window_bits_inflate = 0; + ws_deflate->server_max_window_bits_recv = 0; + ws_deflate->client_max_window_bits_recv = 0; relay_websocket_deflate_free_stream_deflate (ws_deflate); relay_websocket_deflate_free_stream_inflate (ws_deflate); } @@ -322,6 +326,8 @@ relay_websocket_parse_extensions (const char *extensions, ws_deflate->client_context_takeover = 1; ws_deflate->window_bits_deflate = 15; ws_deflate->window_bits_inflate = 15; + ws_deflate->server_max_window_bits_recv = 0; + ws_deflate->client_max_window_bits_recv = 0; for (j = 1; j < num_params; j++) { items = weechat_string_split (params[j], "=", " ", 0, 0, &num_items); @@ -356,9 +362,15 @@ relay_websocket_parse_extensions (const char *extensions, } } if (strcmp (items[0], "server_max_window_bits") == 0) + { + ws_deflate->server_max_window_bits_recv = 1; ws_deflate->window_bits_deflate = (int)number; + } else + { + ws_deflate->client_max_window_bits_recv = 1; ws_deflate->window_bits_inflate = (int)number; + } } } weechat_string_free_split (items); @@ -388,7 +400,7 @@ relay_websocket_build_handshake (struct t_relay_http_request *request) { const char *sec_websocket_key; char *key, sec_websocket_accept[128], handshake[4096], hash[160 / 8]; - char sec_websocket_extensions[512]; + char **extensions, str_window_bits[128], sec_websocket_extensions[1024]; int length, hash_size; if (!request) @@ -426,17 +438,41 @@ relay_websocket_build_handshake (struct t_relay_http_request *request) if (request->ws_deflate->enabled) { + extensions = weechat_string_dyn_alloc (128); + if (!extensions) + return NULL; + weechat_string_dyn_concat (extensions, "permessage-deflate", -1); + if (!request->ws_deflate->server_context_takeover) + { + weechat_string_dyn_concat (extensions, "; ", -1); + weechat_string_dyn_concat (extensions, "server_no_context_takeover", -1); + } + if (!request->ws_deflate->client_context_takeover) + { + weechat_string_dyn_concat (extensions, "; ", -1); + weechat_string_dyn_concat (extensions, "client_no_context_takeover", -1); + } + if (request->ws_deflate->server_max_window_bits_recv) + { + weechat_string_dyn_concat (extensions, "; ", -1); + snprintf (str_window_bits, sizeof (str_window_bits), + "server_max_window_bits=%d", + request->ws_deflate->window_bits_deflate); + weechat_string_dyn_concat (extensions, str_window_bits, -1); + } + if (request->ws_deflate->client_max_window_bits_recv) + { + weechat_string_dyn_concat (extensions, "; ", -1); + snprintf (str_window_bits, sizeof (str_window_bits), + "client_max_window_bits=%d", + request->ws_deflate->window_bits_inflate); + weechat_string_dyn_concat (extensions, str_window_bits, -1); + } snprintf ( sec_websocket_extensions, sizeof (sec_websocket_extensions), - "Sec-WebSocket-Extensions: permessage-deflate; " - "%s" - "%s" - "server_max_window_bits=%d; " - "client_max_window_bits=%d\r\n", - (!request->ws_deflate->server_context_takeover) ? "server_no_context_takeover; " : "", - (!request->ws_deflate->client_context_takeover) ? "client_no_context_takeover; " : "", - request->ws_deflate->window_bits_deflate, - request->ws_deflate->window_bits_inflate); + "Sec-WebSocket-Extensions: %s\r\n", + *extensions); + weechat_string_dyn_free (extensions, 1); } else { @@ -951,11 +987,13 @@ relay_websocket_deflate_print_log (struct t_relay_websocket_deflate *ws_deflate, const char *prefix) { weechat_log_printf ("%s ws_deflate:", prefix); - weechat_log_printf ("%s enabled . . . . . . . . : %d", prefix, ws_deflate->enabled); - weechat_log_printf ("%s server_context_takeover : %d", prefix, ws_deflate->server_context_takeover); - weechat_log_printf ("%s client_context_takeover : %d", prefix, ws_deflate->client_context_takeover); - weechat_log_printf ("%s window_bits_deflate . . : %d", prefix, ws_deflate->window_bits_deflate); - weechat_log_printf ("%s window_bits_inflate . . : %d", prefix, ws_deflate->window_bits_inflate); - weechat_log_printf ("%s strm_deflate. . . . . . : %p", prefix, ws_deflate->strm_deflate); - weechat_log_printf ("%s strm_inflate. . . . . . : %p", prefix, ws_deflate->strm_inflate); + weechat_log_printf ("%s enabled. . . . . . . . . . : %d", prefix, ws_deflate->enabled); + weechat_log_printf ("%s server_context_takeover. . : %d", prefix, ws_deflate->server_context_takeover); + weechat_log_printf ("%s client_context_takeover. . : %d", prefix, ws_deflate->client_context_takeover); + weechat_log_printf ("%s window_bits_deflate. . . . : %d", prefix, ws_deflate->window_bits_deflate); + weechat_log_printf ("%s window_bits_inflate. . . . : %d", prefix, ws_deflate->window_bits_inflate); + weechat_log_printf ("%s server_max_window_bits_recv: %d", prefix, ws_deflate->server_max_window_bits_recv); + weechat_log_printf ("%s client_max_window_bits_recv: %d", prefix, ws_deflate->client_max_window_bits_recv); + weechat_log_printf ("%s strm_deflate . . . . . . . : %p", prefix, ws_deflate->strm_deflate); + weechat_log_printf ("%s strm_inflate . . . . . . . : %p", prefix, ws_deflate->strm_inflate); } diff --git a/src/plugins/relay/relay-websocket.h b/src/plugins/relay/relay-websocket.h index 14e54ef30..64d9d5b7e 100644 --- a/src/plugins/relay/relay-websocket.h +++ b/src/plugins/relay/relay-websocket.h @@ -49,6 +49,8 @@ struct t_relay_websocket_deflate /* ("server_max_window_bits") */ int window_bits_inflate; /* window bits for client (decomp.) */ /* ("client_max_window_bits") */ + int server_max_window_bits_recv; /* "server_max_window_bits" received?*/ + int client_max_window_bits_recv; /* "client_max_window_bits" received?*/ z_stream *strm_deflate; /* stream for deflate (compression) */ z_stream *strm_inflate; /* stream for inflate (decompression)*/ }; diff --git a/tests/unit/plugins/relay/test-relay-websocket.cpp b/tests/unit/plugins/relay/test-relay-websocket.cpp index d54a70ab8..7ff3c00d3 100644 --- a/tests/unit/plugins/relay/test-relay-websocket.cpp +++ b/tests/unit/plugins/relay/test-relay-websocket.cpp @@ -65,6 +65,8 @@ TEST(RelayWebsocket, DeflateAllocFree) LONGS_EQUAL(0, ws_deflate->client_context_takeover); LONGS_EQUAL(0, ws_deflate->window_bits_deflate); LONGS_EQUAL(0, ws_deflate->window_bits_inflate); + LONGS_EQUAL(0, ws_deflate->server_max_window_bits_recv); + LONGS_EQUAL(0, ws_deflate->client_max_window_bits_recv); POINTERS_EQUAL(NULL, ws_deflate->strm_deflate); POINTERS_EQUAL(NULL, ws_deflate->strm_inflate); @@ -164,33 +166,75 @@ TEST(RelayWebsocket, ClientHandshakeValid) hashtable_set (request->headers, "origin", "example.com"); LONGS_EQUAL(0, relay_websocket_client_handshake_valid (request)); + relay_websocket_deflate_reinit (request->ws_deflate); + relay_websocket_parse_extensions ("permessage-deflate", request->ws_deflate); + LONGS_EQUAL(1, request->ws_deflate->enabled); + LONGS_EQUAL(1, request->ws_deflate->server_context_takeover); + LONGS_EQUAL(1, request->ws_deflate->client_context_takeover); + LONGS_EQUAL(15, request->ws_deflate->window_bits_deflate); + LONGS_EQUAL(15, request->ws_deflate->window_bits_inflate); + LONGS_EQUAL(0, request->ws_deflate->server_max_window_bits_recv); + LONGS_EQUAL(0, request->ws_deflate->client_max_window_bits_recv); + WEE_TEST_STR( + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n" + "Sec-WebSocket-Extensions: permessage-deflate\r\n" + "\r\n", + relay_websocket_build_handshake (request)); + + relay_websocket_deflate_reinit (request->ws_deflate); relay_websocket_parse_extensions ( "permessage-deflate; client_max_window_bits", request->ws_deflate); + LONGS_EQUAL(1, request->ws_deflate->enabled); + LONGS_EQUAL(1, request->ws_deflate->server_context_takeover); + LONGS_EQUAL(1, request->ws_deflate->client_context_takeover); + LONGS_EQUAL(15, request->ws_deflate->window_bits_deflate); + LONGS_EQUAL(15, request->ws_deflate->window_bits_inflate); + LONGS_EQUAL(0, request->ws_deflate->server_max_window_bits_recv); + LONGS_EQUAL(1, request->ws_deflate->client_max_window_bits_recv); WEE_TEST_STR( "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n" - "Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=15\r\n" "\r\n", relay_websocket_build_handshake (request)); + relay_websocket_deflate_reinit (request->ws_deflate); relay_websocket_parse_extensions ( "permessage-deflate; client_max_window_bits = 12; server_no_context_takeover", request->ws_deflate); + LONGS_EQUAL(1, request->ws_deflate->enabled); + LONGS_EQUAL(0, request->ws_deflate->server_context_takeover); + LONGS_EQUAL(1, request->ws_deflate->client_context_takeover); + LONGS_EQUAL(15, request->ws_deflate->window_bits_deflate); + LONGS_EQUAL(12, request->ws_deflate->window_bits_inflate); + LONGS_EQUAL(0, request->ws_deflate->server_max_window_bits_recv); + LONGS_EQUAL(1, request->ws_deflate->client_max_window_bits_recv); WEE_TEST_STR( "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n" - "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; server_max_window_bits=15; client_max_window_bits=12\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_max_window_bits=12\r\n" "\r\n", relay_websocket_build_handshake (request)); + relay_websocket_deflate_reinit (request->ws_deflate); relay_websocket_parse_extensions ( "permessage-deflate; client_max_window_bits = 12; server_max_window_bits=8; client_no_context_takeover; server_no_context_takeover", request->ws_deflate); + LONGS_EQUAL(1, request->ws_deflate->enabled); + LONGS_EQUAL(0, request->ws_deflate->server_context_takeover); + LONGS_EQUAL(0, request->ws_deflate->client_context_takeover); + LONGS_EQUAL(8, request->ws_deflate->window_bits_deflate); + LONGS_EQUAL(12, request->ws_deflate->window_bits_inflate); + LONGS_EQUAL(1, request->ws_deflate->server_max_window_bits_recv); + LONGS_EQUAL(1, request->ws_deflate->client_max_window_bits_recv); WEE_TEST_STR( "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n"