import libsoup-2.62.3-2.el8

This commit is contained in:
CentOS Sources 2020-11-03 06:55:26 -05:00 committed by Andrew Lukoshko
parent c8ab009314
commit cd534d36f3
4 changed files with 554 additions and 1 deletions

View File

@ -0,0 +1,95 @@
From d9c729aa5a7991182fa7bdb8d94442f8f0cf055b Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <cgarcia@igalia.com>
Date: Fri, 19 Jul 2019 14:56:05 +0200
Subject: [PATCH] WebSockets: ignore any messages after close has been sent and
received
We currently ignore data frames when close has been received, but we
should also ignore any frame after close has been sent and received.
Currently, if we receive two close frames we end up with the code and
reason of the second frame, while the RFC says: "The WebSocket
Connection Close Code is defined as the status code contained in the
first Close control frame received by the application implementing
this protocol."
---
libsoup/soup-websocket-connection.c | 3 ++
tests/websocket-test.c | 48 +++++++++++++++++++++++++++++
2 files changed, 51 insertions(+)
diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
--- a/libsoup/soup-websocket-connection.c
+++ b/libsoup/soup-websocket-connection.c
@@ -690,6 +690,9 @@
SoupWebsocketConnectionPrivate *pv = self->pv;
GBytes *message;
+ if (pv->close_sent && pv->close_received)
+ return;
+
if (control) {
/* Control frames must never be fragmented */
if (!fin) {
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -707,6 +707,49 @@
}
static gpointer
+close_after_close_server_thread (gpointer user_data)
+{
+ Test *test = user_data;
+ gsize written;
+ const char frames[] =
+ "\x88\x09\x03\xe8""reason1"
+ "\x88\x09\x03\xe8""reason2";
+ GError *error = NULL;
+
+ g_mutex_lock (&test->mutex);
+ g_mutex_unlock (&test->mutex);
+
+ g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
+ frames, sizeof (frames) -1, &written, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (written, ==, sizeof (frames) - 1);
+ g_io_stream_close (test->raw_server, NULL, &error);
+ g_assert_no_error (error);
+
+ return NULL;
+}
+
+static void
+test_close_after_close (Test *test,
+ gconstpointer data)
+{
+ GThread *thread;
+
+ g_mutex_lock (&test->mutex);
+
+ thread = g_thread_new ("close-after-close-thread", close_after_close_server_thread, test);
+
+ soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_NORMAL, "reason1");
+ g_mutex_unlock (&test->mutex);
+
+ g_thread_join (thread);
+
+ WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
+ g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
+ g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
+}
+
+static gpointer
timeout_server_thread (gpointer user_data)
{
Test *test = user_data;
@@ -918,6 +961,11 @@
test_message_after_closing,
teardown_soup_connection);
+ g_test_add ("/websocket/direct/close-after-close", Test, NULL,
+ setup_half_direct_connection,
+ test_close_after_close,
+ teardown_direct_connection);
+
g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL,
test_protocol_negotiate_direct,

View File

@ -0,0 +1,152 @@
From 109bb2f692c746bc63a0ade8737b584aecb0b1ad Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <cgarcia@igalia.com>
Date: Thu, 27 Jun 2019 16:03:21 +0200
Subject: [PATCH] WebSockets: allow null characters in text messages data
RFC 6455 says that text messages should contains valid UTF-8, and null
characters valid according to RFC 3629. However, we are using
g_utf8_validate(), which considers null characters as errors, to
validate WebSockets text messages. This patch adds an internal
utf8_validate() function based on g_utf8_validate() but allowing null
characters and just returning a gboolean since we are always ignoring
the end parameter in case of errors.
soup_websocket_connection_send_text() assumes the given text is null
terminated, so we need a new public function to allow sending text
messages containing null characters. This patch adds
soup_websocket_connection_send_message() that receives a
SoupWebsocketDataType and GBytes, which is consistent with
SoupWebsocketConnection::message signal.
For RHEL backport, drop the addition of soup_websocket_connection_send_message()
as we don't need it and don't want to expose new API.
diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
index 66bd6871..67a98731 100644
--- a/libsoup/soup-websocket-connection.c
+++ b/libsoup/soup-websocket-connection.c
@@ -155,6 +155,82 @@
static void protocol_error_and_close (SoupWebsocketConnection *self);
+/* Code below is based on g_utf8_validate() implementation,
+ * but handling NULL characters as valid, as expected by
+ * WebSockets and compliant with RFC 3629.
+ */
+#define VALIDATE_BYTE(mask, expect) \
+ G_STMT_START { \
+ if (G_UNLIKELY((*(guchar *)p & (mask)) != (expect))) \
+ return FALSE; \
+ } G_STMT_END
+
+/* see IETF RFC 3629 Section 4 */
+static gboolean
+utf8_validate (const char *str,
+ gsize max_len)
+
+{
+ const gchar *p;
+
+ for (p = str; ((p - str) < max_len); p++) {
+ if (*(guchar *)p < 128)
+ /* done */;
+ else {
+ if (*(guchar *)p < 0xe0) { /* 110xxxxx */
+ if (G_UNLIKELY (max_len - (p - str) < 2))
+ return FALSE;
+
+ if (G_UNLIKELY (*(guchar *)p < 0xc2))
+ return FALSE;
+ } else {
+ if (*(guchar *)p < 0xf0) { /* 1110xxxx */
+ if (G_UNLIKELY (max_len - (p - str) < 3))
+ return FALSE;
+
+ switch (*(guchar *)p++ & 0x0f) {
+ case 0:
+ VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
+ break;
+ case 0x0d:
+ VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
+ break;
+ default:
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
+ }
+ } else if (*(guchar *)p < 0xf5) { /* 11110xxx excluding out-of-range */
+ if (G_UNLIKELY (max_len - (p - str) < 4))
+ return FALSE;
+
+ switch (*(guchar *)p++ & 0x07) {
+ case 0:
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
+ if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
+ return FALSE;
+ break;
+ case 4:
+ VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
+ break;
+ default:
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
+ }
+ p++;
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
+ } else {
+ return FALSE;
+ }
+ }
+
+ p++;
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
+ }
+ }
+
+ return TRUE;
+}
+
+#undef VALIDATE_BYTE
+
static void
frame_free (gpointer data)
{
@@ -629,7 +705,7 @@
data += 2;
len -= 2;
- if (!g_utf8_validate ((char *)data, len, NULL)) {
+ if (!utf8_validate ((const char *)data, len)) {
g_debug ("received non-UTF8 close data: %d '%.*s' %d", (int)len, (int)len, (char *)data, (int)data[0]);
protocol_error_and_close (self);
return;
@@ -777,9 +853,8 @@
/* Actually deliver the message? */
if (fin) {
if (pv->message_opcode == 0x01 &&
- !g_utf8_validate((char *)pv->message_data->data,
- pv->message_data->len,
- NULL)) {
+ !utf8_validate((const char *)pv->message_data->data,
+ pv->message_data->len)) {
g_debug ("received invalid non-UTF8 text data");
@@ -1699,7 +1774,9 @@
* @self: the WebSocket
* @text: the message contents
*
- * Send a text (UTF-8) message to the peer.
+ * Send a %NULL-terminated text (UTF-8) message to the peer. If you need
+ * to send text messages containing %NULL characters use
+ * soup_websocket_connection_send_message() instead.
*
* The message is queued to be sent and will be sent when the main loop
* is run.
@@ -1717,7 +1794,7 @@
g_return_if_fail (text != NULL);
length = strlen (text);
- g_return_if_fail (g_utf8_validate (text, length, NULL));
+ g_return_if_fail (utf8_validate (text, length));
send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x01, (const guint8 *) text, length);
}
--
2.26.2

View File

@ -0,0 +1,299 @@
From 35f1bac5ff9ec694e64b65e51f0e7a3226aa3aaf Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <cgarcia@igalia.com>
Date: Wed, 28 Aug 2019 10:51:18 +0200
Subject: [PATCH] WebSockets: only poll IO stream when needed
Instead of having two pollable sources constantly running, always try to
read/write without blocking and start polling if the operation returns
G_IO_ERROR_WOULD_BLOCK. This patch also fixes test
/websocket/direct/close-after-close that was passing but not actually
testing what we wanted, because the client close was never sent. When
the mutex is released, the frame has been queued, but not sent.
diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
index 345040fe..6afbbe67 100644
--- a/libsoup/soup-websocket-connection.c
+++ b/libsoup/soup-websocket-connection.c
@@ -147,6 +147,7 @@
};
#define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT 128 * 1024
+#define READ_BUFFER_SIZE 1024
G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, soup_websocket_connection, G_TYPE_OBJECT)
@@ -155,6 +156,11 @@
static void protocol_error_and_close (SoupWebsocketConnection *self);
+static gboolean on_web_socket_input (GObject *pollable_stream,
+ gpointer user_data);
+static gboolean on_web_socket_output (GObject *pollable_stream,
+ gpointer user_data);
+
/* Code below is based on g_utf8_validate() implementation,
* but handling NULL characters as valid, as expected by
* WebSockets and compliant with RFC 3629.
@@ -283,7 +289,20 @@
}
static void
-stop_input (SoupWebsocketConnection *self)
+soup_websocket_connection_start_input_source (SoupWebsocketConnection *self)
+{
+ SoupWebsocketConnectionPrivate *pv = self->pv;
+
+ if (pv->input_source)
+ return;
+
+ pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
+ g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
+ g_source_attach (pv->input_source, pv->main_context);
+}
+
+static void
+soup_websocket_connection_stop_input_source (SoupWebsocketConnection *self)
{
SoupWebsocketConnectionPrivate *pv = self->pv;
@@ -296,7 +315,20 @@
}
static void
-stop_output (SoupWebsocketConnection *self)
+soup_websocket_connection_start_output_source (SoupWebsocketConnection *self)
+{
+ SoupWebsocketConnectionPrivate *pv = self->pv;
+
+ if (pv->output_source)
+ return;
+
+ pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
+ g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
+ g_source_attach (pv->output_source, pv->main_context);
+}
+
+static void
+soup_websocket_connection_stop_output_source (SoupWebsocketConnection *self)
{
SoupWebsocketConnectionPrivate *pv = self->pv;
@@ -341,8 +373,8 @@
close_io_stop_timeout (self);
if (!pv->io_closing) {
- stop_input (self);
- stop_output (self);
+ soup_websocket_connection_stop_input_source (self);
+ soup_websocket_connection_stop_output_source (self);
pv->io_closing = TRUE;
g_debug ("closing io stream");
g_io_stream_close_async (pv->io_stream, G_PRIORITY_DEFAULT,
@@ -359,7 +391,7 @@
GSocket *socket;
GError *error = NULL;
- stop_output (self);
+ soup_websocket_connection_stop_output_source (self);
if (G_IS_SOCKET_CONNECTION (pv->io_stream)) {
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (pv->io_stream));
@@ -612,9 +644,6 @@
self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ? "server" : "client",
payload_len, self->pv->max_incoming_payload_size);
emit_error_and_close (self, error, TRUE);
-
- /* The input is in an invalid state now */
- stop_input (self);
}
static void
@@ -981,32 +1010,31 @@
;
}
-static gboolean
-on_web_socket_input (GObject *pollable_stream,
- gpointer user_data)
+static void
+soup_websocket_connection_read (SoupWebsocketConnection *self)
{
- SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
SoupWebsocketConnectionPrivate *pv = self->pv;
GError *error = NULL;
gboolean end = FALSE;
gssize count;
gsize len;
+ soup_websocket_connection_stop_input_source (self);
+
do {
len = pv->incoming->len;
- g_byte_array_set_size (pv->incoming, len + 1024);
+ g_byte_array_set_size (pv->incoming, len + READ_BUFFER_SIZE);
count = g_pollable_input_stream_read_nonblocking (pv->input,
pv->incoming->data + len,
- 1024, NULL, &error);
-
+ READ_BUFFER_SIZE, NULL, &error);
if (count < 0) {
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
g_error_free (error);
count = 0;
} else {
emit_error_and_close (self, error, TRUE);
- return TRUE;
+ return;
}
} else if (count == 0) {
end = TRUE;
@@ -1026,16 +1054,24 @@
}
close_io_stream (self);
+ return;
}
- return TRUE;
+ soup_websocket_connection_start_input_source (self);
}
static gboolean
-on_web_socket_output (GObject *pollable_stream,
- gpointer user_data)
+on_web_socket_input (GObject *pollable_stream,
+ gpointer user_data)
+{
+ soup_websocket_connection_read (SOUP_WEBSOCKET_CONNECTION (user_data));
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+soup_websocket_connection_write (SoupWebsocketConnection *self)
{
- SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
SoupWebsocketConnectionPrivate *pv = self->pv;
const guint8 *data;
GError *error = NULL;
@@ -1043,19 +1079,18 @@
gssize count;
gsize len;
+ soup_websocket_connection_stop_output_source (self);
+
if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
g_debug ("Ignoring message since the connection is closed");
- stop_output (self);
- return TRUE;
+ return;
}
frame = g_queue_peek_head (&pv->outgoing);
/* No more frames to send */
- if (frame == NULL) {
- stop_output (self);
- return TRUE;
- }
+ if (frame == NULL)
+ return;
data = g_bytes_get_data (frame->data, &len);
g_assert (len > 0);
@@ -1075,7 +1110,7 @@
frame->pending = TRUE;
} else {
emit_error_and_close (self, error, TRUE);
- return FALSE;
+ return;
}
}
@@ -1093,23 +1128,21 @@
}
}
frame_free (frame);
+
+ if (g_queue_is_empty (&pv->outgoing))
+ return;
}
- return TRUE;
+ soup_websocket_connection_start_output_source (self);
}
-static void
-start_output (SoupWebsocketConnection *self)
+static gboolean
+on_web_socket_output (GObject *pollable_stream,
+ gpointer user_data)
{
- SoupWebsocketConnectionPrivate *pv = self->pv;
-
- if (pv->output_source)
- return;
+ soup_websocket_connection_write (SOUP_WEBSOCKET_CONNECTION (user_data));
- g_debug ("starting output source");
- pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
- g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
- g_source_attach (pv->output_source, pv->main_context);
+ return G_SOURCE_REMOVE;
}
static void
@@ -1150,7 +1183,7 @@
g_queue_push_tail (&pv->outgoing, frame);
}
- start_output (self);
+ soup_websocket_connection_write (self);
}
static void
@@ -1175,9 +1208,7 @@
pv->output = G_POLLABLE_OUTPUT_STREAM (os);
g_return_if_fail (g_pollable_output_stream_can_poll (pv->output));
- pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
- g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
- g_source_attach (pv->input_source, pv->main_context);
+ soup_websocket_connection_start_input_source (self);
}
static void
diff --git tests/websocket-test.c tests/websocket-test.c
index 146fdf82..26d064df 100644
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -733,6 +733,7 @@
const char frames[] =
"\x88\x09\x03\xe8""reason1"
"\x88\x09\x03\xe8""reason2";
+ GSocket *socket;
GError *error = NULL;
g_mutex_lock (&test->mutex);
@@ -742,7 +743,8 @@
frames, sizeof (frames) -1, &written, NULL, &error);
g_assert_no_error (error);
g_assert_cmpuint (written, ==, sizeof (frames) - 1);
- g_io_stream_close (test->raw_server, NULL, &error);
+ socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (test->raw_server));
+ g_socket_shutdown (socket, FALSE, TRUE, &error);
g_assert_no_error (error);
return NULL;
@@ -766,6 +768,7 @@
WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
+ g_io_stream_close (test->raw_server, NULL, NULL);
}
static gpointer
--
2.26.2

View File

@ -2,13 +2,17 @@
Name: libsoup Name: libsoup
Version: 2.62.3 Version: 2.62.3
Release: 1%{?dist} Release: 2%{?dist}
Summary: Soup, an HTTP library implementation Summary: Soup, an HTTP library implementation
License: LGPLv2 License: LGPLv2
URL: https://wiki.gnome.org/Projects/libsoup URL: https://wiki.gnome.org/Projects/libsoup
Source0: https://download.gnome.org/sources/%{name}/2.62/%{name}-%{version}.tar.xz Source0: https://download.gnome.org/sources/%{name}/2.62/%{name}-%{version}.tar.xz
Patch0001: 0001-WebSockets-ignore-any-messages-after-close-has-been-.patch
Patch0002: 0002-WebSockets-allow-null-characters-in-text-messages-da.patch
Patch0003: 0003-WebSockets-only-poll-IO-stream-when-needed.patch
BuildRequires: chrpath BuildRequires: chrpath
BuildRequires: glib2-devel >= %{glib2_version} BuildRequires: glib2-devel >= %{glib2_version}
BuildRequires: glib-networking BuildRequires: glib-networking
@ -82,6 +86,9 @@ chrpath --delete $RPM_BUILD_ROOT%{_libdir}/*.so
%{_datadir}/vala/vapi/libsoup-2.4.vapi %{_datadir}/vala/vapi/libsoup-2.4.vapi
%changelog %changelog
* Thu Aug 27 2020 Martin Pitt <mpitt@redhat.com> - 2.62.3-2
- Some WebSocket fixes to unbreak cockpit-desktop (rhbz#1872270)
* Fri Aug 10 2018 Kalev Lember <klember@redhat.com> - 2.62.3-1 * Fri Aug 10 2018 Kalev Lember <klember@redhat.com> - 2.62.3-1
- Update to 2.62.3 - Update to 2.62.3