summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2019-07-04 10:13:57 +0200
committerClaudio Saavedra <csaavedra@igalia.com>2020-12-11 16:40:19 +0200
commit4e7b45a512d3374212ac8238de01928a568d1427 (patch)
tree0d46b56eb67bb81a06fd57a600aba97169ca573a
parent912b4e4271f362c45509c2e880ed50552d89895e (diff)
downloadlibsoup-4e7b45a512d3374212ac8238de01928a568d1427.tar.gz
WebSockets: server must close the connection when receiving an unmasked frame
RFC 6455: The server MUST close the connection upon receiving a frame that is not masked.
-rw-r--r--libsoup/soup-websocket-connection.c9
-rw-r--r--tests/websocket-test.c49
2 files changed, 55 insertions, 3 deletions
diff --git a/libsoup/soup-websocket-connection.c b/libsoup/soup-websocket-connection.c
index a41f05cd..03647413 100644
--- a/libsoup/soup-websocket-connection.c
+++ b/libsoup/soup-websocket-connection.c
@@ -921,6 +921,15 @@ process_frame (SoupWebsocketConnection *self)
return FALSE;
}
+ if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER && !masked) {
+ /* The server MUST close the connection upon receiving a frame
+ * that is not masked.
+ */
+ g_debug ("The client should always mask frames");
+ protocol_error_and_close (self);
+ return FALSE;
+ }
+
/* We do not support extensions, reserved bits must be 0 */
if (header[0] & 0x70) {
protocol_error_and_close (self);
diff --git a/tests/websocket-test.c b/tests/websocket-test.c
index 59cbba39..890f358c 100644
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -507,11 +507,11 @@ test_send_bad_data (Test *test,
io = soup_websocket_connection_get_io_stream (test->client);
/* Bad UTF-8 frame */
- frame = "\x81\x04\xEE\xEE\xEE\xEE";
+ frame = "\x81\x84\x00\x00\x00\x00\xEE\xEE\xEE\xEE";
if (!g_output_stream_write_all (g_io_stream_get_output_stream (io),
- frame, 6, &written, NULL, NULL))
+ frame, 10, &written, NULL, NULL))
g_assert_not_reached ();
- g_assert_cmpuint (written, ==, 6);
+ g_assert_cmpuint (written, ==, 10);
WAIT_UNTIL (error != NULL);
g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
@@ -1028,6 +1028,40 @@ test_client_receive_masked_frame (Test *test,
}
static void
+test_server_receive_unmasked_frame (Test *test,
+ gconstpointer data)
+{
+ GError *error = NULL;
+ GIOStream *io;
+ gsize written;
+ const char *frame;
+ gboolean close_event = FALSE;
+
+ g_signal_handlers_disconnect_by_func (test->server, on_error_not_reached, NULL);
+ g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error);
+ g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
+
+ io = soup_websocket_connection_get_io_stream (test->client);
+
+ /* Unmasked frame */
+ frame = "\x81\x0bHello World";
+ if (!g_output_stream_write_all (g_io_stream_get_output_stream (io),
+ frame, 13, &written, NULL, NULL))
+ g_assert_not_reached ();
+ g_assert_cmpuint (written, ==, 13);
+
+ WAIT_UNTIL (error != NULL);
+ g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
+ g_clear_error (&error);
+
+ WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
+ g_assert (close_event);
+
+ g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
+
+}
+
+static void
test_client_context_got_server_connection (SoupServer *server,
SoupWebsocketConnection *connection,
const char *path,
@@ -1214,6 +1248,15 @@ main (int argc,
test_client_receive_masked_frame,
teardown_direct_connection);
+ g_test_add ("/websocket/direct/server-receive-unmasked-frame", Test, NULL,
+ setup_direct_connection,
+ test_server_receive_unmasked_frame,
+ teardown_direct_connection);
+ g_test_add ("/websocket/soup/server-receive-unmasked-frame", Test, NULL,
+ setup_soup_connection,
+ test_server_receive_unmasked_frame,
+ teardown_soup_connection);
+
if (g_test_slow ()) {
g_test_add ("/websocket/direct/close-after-timeout", Test, NULL,
setup_half_direct_connection,