diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2024-03-31 09:49:10 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2024-04-07 13:18:13 +0200 |
commit | 90b855e1aa23886dbb4bd794e2527a26973dd5fb (patch) | |
tree | 2ffc437362634955d801bb0ca8442e611bbcb321 /src/plugins/relay/relay-websocket.c | |
parent | fd3219246432b35f165c5d1edfa78c76a743723e (diff) | |
download | weechat-90b855e1aa23886dbb4bd794e2527a26973dd5fb.zip |
relay: add connection to remote (issue #2066)
Connection to remote:
- handshake: offer support for all supported hash algorithms
- network connect with a socket
- upgrade to websocket and authenticate with remote (password/TOTP)
- check websocket response
- get list of buffers (not used yet)
Note: connection to remote with TLS or a proxy is not yet supported.
Diffstat (limited to 'src/plugins/relay/relay-websocket.c')
-rw-r--r-- | src/plugins/relay/relay-websocket.c | 155 |
1 files changed, 95 insertions, 60 deletions
diff --git a/src/plugins/relay/relay-websocket.c b/src/plugins/relay/relay-websocket.c index 3ba4d408e..5259f471d 100644 --- a/src/plugins/relay/relay-websocket.c +++ b/src/plugins/relay/relay-websocket.c @@ -23,6 +23,7 @@ #include <unistd.h> #include <stdio.h> #include <string.h> +#include <gcrypt.h> #include <zlib.h> #include "../weechat-plugin.h" @@ -34,13 +35,6 @@ /* - * globally unique identifier that is concatenated to HTTP header - * "Sec-WebSocket-Key" - */ -#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - -/* * Allocates a t_relay_websocket_deflate structure. */ @@ -547,17 +541,23 @@ error: * frame is first decompressed if "permessage-deflate" websocket extension * is used). * + * If argument "expect_masked_frame" is 1 and a frame is not masked, + * the function returns an error. + * * Returns: * 1: frame(s) decoded successfully * 0: error decoding frame (connection must be closed if it happens) */ int -relay_websocket_decode_frame (struct t_relay_client *client, - const unsigned char *buffer, +relay_websocket_decode_frame (const unsigned char *buffer, unsigned long long buffer_length, + int expect_masked_frame, + struct t_relay_websocket_deflate *ws_deflate, struct t_relay_websocket_frame **frames, - int *num_frames) + int *num_frames, + char **partial_ws_frame, + int *partial_ws_frame_size) { unsigned long long i, index_buffer, index_buffer_start_frame; unsigned long long length_frame_size, length_frame; @@ -565,7 +565,7 @@ relay_websocket_decode_frame (struct t_relay_client *client, size_t size_decompressed; char *payload_decompressed; struct t_relay_websocket_frame *frames2, *ptr_frame; - int size; + int size, masked_frame, mask[4]; if (!buffer || !frames || !num_frames) return 0; @@ -586,11 +586,15 @@ relay_websocket_decode_frame (struct t_relay_client *client, opcode = buffer[index_buffer] & 15; + /* check if frame is masked */ + masked_frame = (buffer[index_buffer + 1] & 128) ? 1 : 0; + /* - * 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) + * error if the frame is not masked and we expect it to be masked, + * in this case we must reject it and close the connection + * (see RFC 6455) */ - if (!(buffer[index_buffer + 1] & 128)) + if (!masked_frame && expect_masked_frame) return 0; /* decode frame length */ @@ -611,15 +615,17 @@ relay_websocket_decode_frame (struct t_relay_client *client, 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++) + if (masked_frame) { - masks[i] = (int)((unsigned char)buffer[index_buffer + i]); + /* read mask (4 bytes) */ + if (index_buffer + 4 > buffer_length) + goto missing_data; + for (i = 0; i < 4; i++) + { + mask[i] = (int)((unsigned char)buffer[index_buffer + i]); + } + index_buffer += 4; } - index_buffer += 4; /* check if we have enough data */ if ((length_frame > buffer_length) @@ -646,13 +652,13 @@ relay_websocket_decode_frame (struct t_relay_client *client, switch (opcode) { case WEBSOCKET_FRAME_OPCODE_PING: - ptr_frame->opcode = RELAY_CLIENT_MSG_PING; + ptr_frame->opcode = RELAY_MSG_PING; break; case WEBSOCKET_FRAME_OPCODE_CLOSE: - ptr_frame->opcode = RELAY_CLIENT_MSG_CLOSE; + ptr_frame->opcode = RELAY_MSG_CLOSE; break; default: - ptr_frame->opcode = RELAY_CLIENT_MSG_STANDARD; + ptr_frame->opcode = RELAY_MSG_STANDARD; break; } @@ -663,9 +669,16 @@ relay_websocket_decode_frame (struct t_relay_client *client, ptr_frame->payload_size = length_frame; /* fill payload */ - for (i = 0; i < length_frame; i++) + if (masked_frame) + { + for (i = 0; i < length_frame; i++) + { + ptr_frame->payload[i] = (int)((unsigned char)buffer[index_buffer + i]) ^ mask[i % 4]; + } + } + else { - ptr_frame->payload[i] = (int)((unsigned char)buffer[index_buffer + i]) ^ masks[i % 4]; + memcpy (ptr_frame->payload, buffer + index_buffer, length_frame); } ptr_frame->payload[length_frame] = '\0'; @@ -673,58 +686,58 @@ relay_websocket_decode_frame (struct t_relay_client *client, * decompress data if frame is not empty and if "permessage-deflate" * is enabled */ - if ((length_frame > 0) && client->ws_deflate->enabled) + if ((length_frame > 0) && ws_deflate && ws_deflate->enabled) { - if (!client->ws_deflate->strm_inflate) + if (!ws_deflate->strm_inflate) { - client->ws_deflate->strm_inflate = calloc ( - 1, sizeof (*client->ws_deflate->strm_inflate)); - if (!client->ws_deflate->strm_inflate) + ws_deflate->strm_inflate = calloc ( + 1, sizeof (*ws_deflate->strm_inflate)); + if (!ws_deflate->strm_inflate) return 0; - if (!relay_websocket_deflate_init_stream_inflate (client->ws_deflate)) + if (!relay_websocket_deflate_init_stream_inflate (ws_deflate)) return 0; } payload_decompressed = relay_websocket_inflate ( ptr_frame->payload, ptr_frame->payload_size, - client->ws_deflate->strm_inflate, + ws_deflate->strm_inflate, &size_decompressed); if (!payload_decompressed) return 0; free (ptr_frame->payload); ptr_frame->payload = payload_decompressed; ptr_frame->payload_size = size_decompressed; - if (!client->ws_deflate->client_context_takeover) - relay_websocket_deflate_free_stream_inflate (client->ws_deflate); + if (!ws_deflate->client_context_takeover) + relay_websocket_deflate_free_stream_inflate (ws_deflate); } index_buffer += length_frame; } - if (client->partial_ws_frame) + if (*partial_ws_frame) { - free (client->partial_ws_frame); - client->partial_ws_frame = NULL; - client->partial_ws_frame_size = 0; + free (*partial_ws_frame); + *partial_ws_frame = NULL; + *partial_ws_frame_size = 0; } return 1; missing_data: - if (client->partial_ws_frame) + if (*partial_ws_frame) { - free (client->partial_ws_frame); - client->partial_ws_frame = NULL; - client->partial_ws_frame_size = 0; + free (*partial_ws_frame); + *partial_ws_frame = NULL; + *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) + *partial_ws_frame = malloc (size); + if (!*partial_ws_frame) return 0; - memcpy (client->partial_ws_frame, buffer + index_buffer_start_frame, size); - client->partial_ws_frame_size = size; + memcpy (*partial_ws_frame, buffer + index_buffer_start_frame, size); + *partial_ws_frame_size = size; } return 1; } @@ -779,12 +792,16 @@ relay_websocket_deflate (const void *data, size_t size, z_stream *strm, * Returns websocket frame, NULL if error. * Argument "length_frame" is set with the length of frame built. * + * Argument "mask_frame" must be 1 when sending to server (remote) and 0 when + * sending to a client. + * * Note: result must be freed after use. */ char * -relay_websocket_encode_frame (struct t_relay_client *client, +relay_websocket_encode_frame (struct t_relay_websocket_deflate *ws_deflate, int opcode, + int mask_frame, const char *payload, unsigned long long payload_size, unsigned long long *length_frame) @@ -792,8 +809,8 @@ relay_websocket_encode_frame (struct t_relay_client *client, const char *ptr_data; char *payload_compressed; size_t size_compressed; - unsigned char *frame; - unsigned long long index, data_size; + unsigned char *frame, *ptr_mask; + unsigned long long i, index, data_size; *length_frame = 0; @@ -810,21 +827,22 @@ relay_websocket_encode_frame (struct t_relay_client *client, if (((opcode == WEBSOCKET_FRAME_OPCODE_TEXT) || (opcode == WEBSOCKET_FRAME_OPCODE_BINARY)) && (payload_size > 0) - && client->ws_deflate->enabled) + && ws_deflate + && ws_deflate->enabled) { - if (!client->ws_deflate->strm_deflate) + if (!ws_deflate->strm_deflate) { - client->ws_deflate->strm_deflate = calloc ( - 1, sizeof (*client->ws_deflate->strm_deflate)); - if (!client->ws_deflate->strm_deflate) + ws_deflate->strm_deflate = calloc ( + 1, sizeof (*ws_deflate->strm_deflate)); + if (!ws_deflate->strm_deflate) return NULL; - if (!relay_websocket_deflate_init_stream_deflate (client->ws_deflate)) + if (!relay_websocket_deflate_init_stream_deflate (ws_deflate)) return NULL; } payload_compressed = relay_websocket_deflate ( payload, payload_size, - client->ws_deflate->strm_deflate, + ws_deflate->strm_deflate, &size_compressed); if (!payload_compressed) return NULL; @@ -838,13 +856,13 @@ relay_websocket_encode_frame (struct t_relay_client *client, { data_size -= 4; } - if (!client->ws_deflate->server_context_takeover) - relay_websocket_deflate_free_stream_deflate (client->ws_deflate); + if (!ws_deflate->server_context_takeover) + relay_websocket_deflate_free_stream_deflate (ws_deflate); /* set bit RSV1: indicate permessage-deflate compressed data */ opcode |= 0x40; } - frame = malloc (data_size + 10); + frame = malloc (data_size + 14); if (!frame) { if (payload_compressed) @@ -884,9 +902,26 @@ relay_websocket_encode_frame (struct t_relay_client *client, index = 10; } + if (mask_frame) + { + frame[1] |= 128; + ptr_mask = frame + index; + gcry_create_nonce (ptr_mask, 4); + index += 4; + } + /* copy buffer after data_size */ memcpy (frame + index, ptr_data, data_size); + /* mask frame */ + if (mask_frame) + { + for (i = 0; i < data_size; i++) + { + frame[index + i] = frame[index + i] ^ ptr_mask[i % 4]; + } + } + *length_frame = index + data_size; if (payload_compressed) |