1356 lines
40 KiB
Diff
1356 lines
40 KiB
Diff
From 1ee5b053496fcd9de7990d05e8180d548e8ce0da Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Mon, 26 May 2025 16:09:45 +0200
|
|
Subject: [PATCH 1/7] daemon: Use GError auto pointer
|
|
|
|
---
|
|
src/grd-daemon.c | 3 +--
|
|
1 file changed, 1 insertion(+), 2 deletions(-)
|
|
|
|
diff --git a/src/grd-daemon.c b/src/grd-daemon.c
|
|
index 6f7bb460..f5595005 100644
|
|
--- a/src/grd-daemon.c
|
|
+++ b/src/grd-daemon.c
|
|
@@ -292,7 +292,7 @@ main (int argc, char **argv)
|
|
};
|
|
g_autoptr(GOptionContext) context = NULL;
|
|
g_autoptr(GApplication) app = NULL;
|
|
- GError *error = NULL;
|
|
+ g_autoptr (GError) error = NULL;
|
|
|
|
g_set_application_name (_("GNOME Remote Desktop"));
|
|
|
|
@@ -301,7 +301,6 @@ main (int argc, char **argv)
|
|
if (!g_option_context_parse (context, &argc, &argv, &error))
|
|
{
|
|
g_printerr ("Invalid option: %s\n", error->message);
|
|
- g_error_free (error);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 29e0f5834079069fd445e4a0b643c671eb404dc2 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Wed, 28 May 2025 22:55:20 +0200
|
|
Subject: [PATCH 2/7] utils: Add some time conversion helpers
|
|
|
|
Useful when converting between second, microseconds, etc.
|
|
---
|
|
src/grd-utils.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
|
|
src/meson.build | 1 +
|
|
2 files changed, 45 insertions(+)
|
|
create mode 100644 src/grd-utils.h
|
|
|
|
diff --git a/src/grd-utils.h b/src/grd-utils.h
|
|
new file mode 100644
|
|
index 00000000..9fdb020a
|
|
--- /dev/null
|
|
+++ b/src/grd-utils.h
|
|
@@ -0,0 +1,44 @@
|
|
+/*
|
|
+ * Copyright (C) 2022 Pascal Nowack
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but
|
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
+ * 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+#ifndef GRD_UTILS_H
|
|
+#define GRD_UTILS_H
|
|
+
|
|
+#include <gio/gio.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+static inline int64_t
|
|
+us (int64_t us)
|
|
+{
|
|
+ return us;
|
|
+}
|
|
+
|
|
+static inline int64_t
|
|
+ms2us (int64_t ms)
|
|
+{
|
|
+ return us (ms * 1000);
|
|
+}
|
|
+
|
|
+static inline int64_t
|
|
+s2us (uint64_t s)
|
|
+{
|
|
+ return ms2us (s * 1000);
|
|
+}
|
|
+
|
|
+#endif /* GRD_UTILS_H */
|
|
diff --git a/src/meson.build b/src/meson.build
|
|
index 9d2f1cea..a2d738ac 100644
|
|
--- a/src/meson.build
|
|
+++ b/src/meson.build
|
|
@@ -15,6 +15,7 @@ daemon_sources = files([
|
|
'grd-stream.c',
|
|
'grd-stream.h',
|
|
'grd-types.h',
|
|
+ 'grd-utils.h',
|
|
'grd-vnc-cursor.c',
|
|
'grd-vnc-cursor.h',
|
|
'grd-vnc-pipewire-stream.c',
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 72ea71aa2a4696ff51b22d86f11adc9f0dbff1d8 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Mon, 30 Jun 2025 16:16:31 +0200
|
|
Subject: [PATCH 3/7] utils: Add helper to close connection and notify
|
|
|
|
Closing doesn't notify that it was closed, so lets add a helper that
|
|
does so, that we then can uses to rely on the closed property being up
|
|
to date.
|
|
---
|
|
src/grd-utils.c | 32 ++++++++++++++++++++++++++++++++
|
|
src/grd-utils.h | 2 ++
|
|
src/meson.build | 1 +
|
|
3 files changed, 35 insertions(+)
|
|
create mode 100644 src/grd-utils.c
|
|
|
|
diff --git a/src/grd-utils.c b/src/grd-utils.c
|
|
new file mode 100644
|
|
index 00000000..772d6fc4
|
|
--- /dev/null
|
|
+++ b/src/grd-utils.c
|
|
@@ -0,0 +1,32 @@
|
|
+/*
|
|
+ * Copyright (C) 2010 Intel Corp.
|
|
+ * Copyright (C) 2014 Jonas Ådahl
|
|
+ * Copyright (C) 2016-2022 Red Hat Inc.
|
|
+ * Copyright (C) 2022 Pascal Nowack
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but
|
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
+ * 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+
|
|
+#include "grd-utils.h"
|
|
+
|
|
+void
|
|
+grd_close_connection_and_notify (GSocketConnection *connection)
|
|
+{
|
|
+ g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
|
|
+ g_object_notify (G_OBJECT (connection), "closed");
|
|
+}
|
|
diff --git a/src/grd-utils.h b/src/grd-utils.h
|
|
index 9fdb020a..6fb81717 100644
|
|
--- a/src/grd-utils.h
|
|
+++ b/src/grd-utils.h
|
|
@@ -23,6 +23,8 @@
|
|
#include <gio/gio.h>
|
|
#include <stdint.h>
|
|
|
|
+void grd_close_connection_and_notify (GSocketConnection *connection);
|
|
+
|
|
static inline int64_t
|
|
us (int64_t us)
|
|
{
|
|
diff --git a/src/meson.build b/src/meson.build
|
|
index a2d738ac..de6b8cff 100644
|
|
--- a/src/meson.build
|
|
+++ b/src/meson.build
|
|
@@ -15,6 +15,7 @@ daemon_sources = files([
|
|
'grd-stream.c',
|
|
'grd-stream.h',
|
|
'grd-types.h',
|
|
+ 'grd-utils.c',
|
|
'grd-utils.h',
|
|
'grd-vnc-cursor.c',
|
|
'grd-vnc-cursor.h',
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 5104e01271fadbefcc277b46cf49c15f977727fa Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Wed, 28 May 2025 22:40:38 +0200
|
|
Subject: [PATCH 4/7] Introduce throttler class
|
|
|
|
This handles the throttling of incoming connections, and is intended to
|
|
act as a first line of defence against denial of service attacks. It
|
|
implements the following:
|
|
|
|
* It limits the global number of concurrent active connections
|
|
* It limits the number of concurrent active connections per peer
|
|
(roughly IP address)
|
|
* It limits the number of new connections per second coming from a peer
|
|
* It maintains a limited number of pending connections that is waiting
|
|
to be handled
|
|
|
|
This avoids running into situations where an uncontrolled number of
|
|
connection attempts causes resources to be exhausted, e.g. number of
|
|
file descriptor exceeding the process limit.
|
|
|
|
The limits are currently hard coded.
|
|
|
|
Related: CVE-2025-5024
|
|
---
|
|
src/grd-throttler.c | 467 ++++++++++++++++++++++++++++++++++++++++++++
|
|
src/grd-throttler.h | 41 ++++
|
|
src/meson.build | 2 +
|
|
3 files changed, 510 insertions(+)
|
|
create mode 100644 src/grd-throttler.c
|
|
create mode 100644 src/grd-throttler.h
|
|
|
|
diff --git a/src/grd-throttler.c b/src/grd-throttler.c
|
|
new file mode 100644
|
|
index 00000000..e584433a
|
|
--- /dev/null
|
|
+++ b/src/grd-throttler.c
|
|
@@ -0,0 +1,467 @@
|
|
+/*
|
|
+ * Copyright (C) 2025 Red Hat
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but
|
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
+ * 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+
|
|
+#include "grd-throttler.h"
|
|
+
|
|
+#include "grd-utils.h"
|
|
+
|
|
+#define MAX_GLOBAL_CONNECTIONS 10
|
|
+#define MAX_CONNECTIONS_PER_PEER 5
|
|
+#define MAX_PENDING_CONNECTIONS 5
|
|
+#define MAX_ATTEMPTS_PER_SECOND 5
|
|
+
|
|
+#define PRUNE_TIME_CUTOFF_US (s2us (1))
|
|
+
|
|
+typedef struct _GrdPeer
|
|
+{
|
|
+ GrdThrottler *throttler;
|
|
+ char *name;
|
|
+ int active_connections;
|
|
+ GArray *connect_timestamps;
|
|
+ GQueue *delayed_connections;
|
|
+ int64_t last_accept_us;
|
|
+} GrdPeer;
|
|
+
|
|
+struct _GrdThrottler
|
|
+{
|
|
+ GObject parent;
|
|
+
|
|
+ int active_connections;
|
|
+
|
|
+ GrdThrottlerAllowCallback allow_callback;
|
|
+ gpointer user_data;
|
|
+
|
|
+ GHashTable *peers;
|
|
+
|
|
+ GSource *delayed_connections_source;
|
|
+};
|
|
+
|
|
+G_DEFINE_TYPE (GrdThrottler, grd_throttler, G_TYPE_OBJECT)
|
|
+
|
|
+static GQuark quark_remote_address;
|
|
+
|
|
+static void
|
|
+maybe_queue_timeout (GrdThrottler *throttler);
|
|
+
|
|
+static void
|
|
+prune_old_timestamps (GArray *timestamps,
|
|
+ int64_t now_us)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (!timestamps)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < timestamps->len; i++)
|
|
+ {
|
|
+ int64_t ts_us = g_array_index (timestamps, int64_t, i);
|
|
+
|
|
+ if (now_us - ts_us < PRUNE_TIME_CUTOFF_US)
|
|
+ break;
|
|
+ }
|
|
+ g_array_remove_range (timestamps, 0, i);
|
|
+}
|
|
+
|
|
+static void
|
|
+maybe_dispose_peer (GrdThrottler *throttler,
|
|
+ GrdPeer *peer,
|
|
+ GHashTableIter *iter)
|
|
+{
|
|
+ if (peer->active_connections > 0)
|
|
+ return;
|
|
+
|
|
+ if (peer->connect_timestamps && peer->connect_timestamps->len > 0)
|
|
+ return;
|
|
+
|
|
+ if (!g_queue_is_empty (peer->delayed_connections))
|
|
+ return;
|
|
+
|
|
+ if (iter)
|
|
+ g_hash_table_iter_remove (iter);
|
|
+ else
|
|
+ g_hash_table_remove (throttler->peers, peer->name);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_register_connection (GrdThrottler *throttler,
|
|
+ GrdPeer *peer)
|
|
+{
|
|
+ int64_t now_us;
|
|
+
|
|
+ peer->active_connections++;
|
|
+ throttler->active_connections++;
|
|
+
|
|
+ if (!peer->connect_timestamps)
|
|
+ peer->connect_timestamps = g_array_new (FALSE, FALSE, sizeof (int64_t));
|
|
+
|
|
+ now_us = g_get_monotonic_time ();
|
|
+
|
|
+ prune_old_timestamps (peer->connect_timestamps, now_us);
|
|
+ g_array_append_val (peer->connect_timestamps, now_us);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_unregister_connection (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ GrdPeer *peer)
|
|
+{
|
|
+ g_return_if_fail (peer->active_connections > 0);
|
|
+ g_return_if_fail (throttler->active_connections > 0);
|
|
+
|
|
+ peer->active_connections--;
|
|
+ throttler->active_connections--;
|
|
+
|
|
+ maybe_dispose_peer (throttler, peer, NULL);
|
|
+ maybe_queue_timeout (throttler);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_deny_connection (GrdThrottler *throttler,
|
|
+ const char *peer_name,
|
|
+ GSocketConnection *connection)
|
|
+{
|
|
+ g_debug ("Denying connection from %s", peer_name);
|
|
+ g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+on_connection_closed_changed (GSocketConnection *connection,
|
|
+ GParamSpec *pspec,
|
|
+ GrdThrottler *throttler)
|
|
+{
|
|
+ const char *peer_name;
|
|
+ GrdPeer *peer;
|
|
+
|
|
+ g_assert (g_io_stream_is_closed (G_IO_STREAM (connection)));
|
|
+
|
|
+ peer_name = g_object_get_qdata (G_OBJECT (connection), quark_remote_address);
|
|
+ peer = g_hash_table_lookup (throttler->peers, peer_name);
|
|
+ grd_throttler_unregister_connection (throttler, connection, peer);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_allow_connection (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ GrdPeer *peer)
|
|
+{
|
|
+ g_debug ("Accepting connection from %s", peer->name);
|
|
+
|
|
+ throttler->allow_callback (throttler, connection, throttler->user_data);
|
|
+
|
|
+ peer->last_accept_us = g_get_monotonic_time ();
|
|
+
|
|
+ g_object_set_qdata_full (G_OBJECT (connection), quark_remote_address,
|
|
+ g_strdup (peer->name), g_free);
|
|
+ grd_throttler_register_connection (throttler, peer);
|
|
+ g_signal_connect (connection, "notify::closed",
|
|
+ G_CALLBACK (on_connection_closed_changed), throttler);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+source_dispatch (GSource *source,
|
|
+ GSourceFunc callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ g_source_set_ready_time (source, -1);
|
|
+
|
|
+ return callback (user_data);
|
|
+}
|
|
+
|
|
+static GSourceFuncs source_funcs =
|
|
+{
|
|
+ .dispatch = source_dispatch,
|
|
+};
|
|
+
|
|
+static void
|
|
+prune_closed_connections (GQueue *queue)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ l = queue->head;
|
|
+ while (l)
|
|
+ {
|
|
+ GSocketConnection *connection = G_SOCKET_CONNECTION (l->data);
|
|
+ GList *l_next = l->next;
|
|
+
|
|
+ if (g_io_stream_is_closed (G_IO_STREAM (connection)))
|
|
+ {
|
|
+ g_queue_delete_link (queue, l);
|
|
+ g_object_unref (connection);
|
|
+ }
|
|
+
|
|
+ l = l_next;
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+is_connection_limit_reached (GrdThrottler *throttler,
|
|
+ GrdPeer *peer)
|
|
+{
|
|
+ if (peer->active_connections >= MAX_CONNECTIONS_PER_PEER)
|
|
+ return TRUE;
|
|
+
|
|
+ if (throttler->active_connections >= MAX_GLOBAL_CONNECTIONS)
|
|
+ return TRUE;
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+is_new_connection_allowed (GrdThrottler *throttler,
|
|
+ GrdPeer *peer)
|
|
+{
|
|
+ if (is_connection_limit_reached (throttler, peer))
|
|
+ return FALSE;
|
|
+
|
|
+ if (peer->connect_timestamps &&
|
|
+ peer->connect_timestamps->len >= MAX_ATTEMPTS_PER_SECOND)
|
|
+ return FALSE;
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+dispatch_delayed_connections (gpointer user_data)
|
|
+{
|
|
+ GrdThrottler *throttler = GRD_THROTTLER (user_data);
|
|
+ GHashTableIter iter;
|
|
+ gpointer key, value;
|
|
+
|
|
+ g_hash_table_iter_init (&iter, throttler->peers);
|
|
+ while (g_hash_table_iter_next (&iter, &key, &value))
|
|
+ {
|
|
+ GrdPeer *peer = value;
|
|
+ GQueue *queue = peer->delayed_connections;
|
|
+ GSocketConnection *connection;
|
|
+
|
|
+ prune_closed_connections (queue);
|
|
+ connection = g_queue_peek_tail (queue);
|
|
+
|
|
+ if (!connection)
|
|
+ {
|
|
+ maybe_dispose_peer (throttler, peer, &iter);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (is_new_connection_allowed (throttler, peer))
|
|
+ {
|
|
+ g_queue_pop_tail (queue);
|
|
+ grd_throttler_allow_connection (throttler, connection, peer);
|
|
+ g_object_unref (connection);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ maybe_queue_timeout (throttler);
|
|
+
|
|
+ return G_SOURCE_CONTINUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+ensure_delayed_connections_source (GrdThrottler *throttler)
|
|
+{
|
|
+ if (throttler->delayed_connections_source)
|
|
+ return;
|
|
+
|
|
+ throttler->delayed_connections_source = g_source_new (&source_funcs,
|
|
+ sizeof (GSource));
|
|
+ g_source_set_callback (throttler->delayed_connections_source,
|
|
+ dispatch_delayed_connections, throttler, NULL);
|
|
+ g_source_attach (throttler->delayed_connections_source, NULL);
|
|
+ g_source_unref (throttler->delayed_connections_source);
|
|
+}
|
|
+
|
|
+static void
|
|
+maybe_queue_timeout (GrdThrottler *throttler)
|
|
+{
|
|
+ GHashTableIter iter;
|
|
+ gpointer key, value;
|
|
+ int64_t next_timeout_us = INT64_MAX;
|
|
+
|
|
+
|
|
+ g_hash_table_iter_init (&iter, throttler->peers);
|
|
+ while (g_hash_table_iter_next (&iter, &key, &value))
|
|
+ {
|
|
+ GrdPeer *peer = value;
|
|
+
|
|
+ if (is_connection_limit_reached (throttler, peer))
|
|
+ continue;
|
|
+
|
|
+ if (g_queue_is_empty (peer->delayed_connections))
|
|
+ continue;
|
|
+
|
|
+ next_timeout_us = MIN (next_timeout_us,
|
|
+ peer->last_accept_us +
|
|
+ G_USEC_PER_SEC / MAX_ATTEMPTS_PER_SECOND);
|
|
+ }
|
|
+
|
|
+
|
|
+ if (next_timeout_us == INT64_MAX)
|
|
+ next_timeout_us = -1;
|
|
+
|
|
+ if (next_timeout_us >= 0)
|
|
+ {
|
|
+ ensure_delayed_connections_source (throttler);
|
|
+ g_source_set_ready_time (throttler->delayed_connections_source,
|
|
+ next_timeout_us);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+maybe_delay_connection (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ GrdPeer *peer,
|
|
+ int64_t now_us)
|
|
+{
|
|
+ GQueue *delayed_connections;
|
|
+
|
|
+ delayed_connections = peer->delayed_connections;
|
|
+ if (!delayed_connections)
|
|
+ {
|
|
+ delayed_connections = g_queue_new ();
|
|
+ peer->delayed_connections = delayed_connections;
|
|
+ }
|
|
+
|
|
+ if (g_queue_get_length (delayed_connections) > MAX_PENDING_CONNECTIONS)
|
|
+ {
|
|
+ grd_throttler_deny_connection (throttler, peer->name, connection);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_debug ("Delaying connection from %s", peer->name);
|
|
+
|
|
+ g_queue_push_head (delayed_connections, g_object_ref (connection));
|
|
+ maybe_queue_timeout (throttler);
|
|
+}
|
|
+
|
|
+static GrdPeer *
|
|
+ensure_peer (GrdThrottler *throttler,
|
|
+ const char *peer_name)
|
|
+{
|
|
+ GrdPeer *peer;
|
|
+
|
|
+ peer = g_hash_table_lookup (throttler->peers, peer_name);
|
|
+ if (peer)
|
|
+ return peer;
|
|
+
|
|
+ peer = g_new0 (GrdPeer, 1);
|
|
+ peer->throttler = throttler;
|
|
+ peer->name = g_strdup (peer_name);
|
|
+ peer->delayed_connections = g_queue_new ();
|
|
+
|
|
+ g_hash_table_insert (throttler->peers,
|
|
+ g_strdup (peer_name), peer);
|
|
+
|
|
+ return peer;
|
|
+}
|
|
+
|
|
+void
|
|
+grd_throttler_handle_connection (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection)
|
|
+{
|
|
+ g_autoptr (GError) error = NULL;
|
|
+ g_autoptr (GSocketAddress) remote_address = NULL;
|
|
+ GInetAddress *inet_address;
|
|
+ g_autofree char *peer_name = NULL;
|
|
+ GrdPeer *peer;
|
|
+ int64_t now_us;
|
|
+
|
|
+ remote_address = g_socket_connection_get_remote_address (connection, &error);
|
|
+ if (!remote_address)
|
|
+ {
|
|
+ g_warning ("Failed to get remote address: %s", error->message);
|
|
+ grd_throttler_deny_connection (throttler, "unknown peer", connection);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ inet_address =
|
|
+ g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (remote_address));
|
|
+ peer_name = g_inet_address_to_string (inet_address);
|
|
+
|
|
+ g_debug ("New incoming connection from %s", peer_name);
|
|
+
|
|
+ peer = ensure_peer (throttler, peer_name);
|
|
+
|
|
+ prune_closed_connections (peer->delayed_connections);
|
|
+
|
|
+ now_us = g_get_monotonic_time ();
|
|
+ prune_old_timestamps (peer->connect_timestamps, now_us);
|
|
+ if (is_new_connection_allowed (throttler, peer) &&
|
|
+ g_queue_get_length (peer->delayed_connections) == 0)
|
|
+ {
|
|
+ grd_throttler_allow_connection (throttler, connection, peer);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ maybe_delay_connection (throttler, connection, peer, now_us);
|
|
+}
|
|
+
|
|
+GrdThrottler *
|
|
+grd_throttler_new (GrdThrottlerAllowCallback allow_callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GrdThrottler *throttler;
|
|
+
|
|
+ throttler = g_object_new (GRD_TYPE_THROTTLER, NULL);
|
|
+ throttler->allow_callback = allow_callback;
|
|
+ throttler->user_data = user_data;
|
|
+
|
|
+ return throttler;
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_peer_free (GrdPeer *peer)
|
|
+{
|
|
+ if (peer->delayed_connections)
|
|
+ g_queue_free_full (peer->delayed_connections, g_object_unref);
|
|
+ g_clear_pointer (&peer->connect_timestamps, g_array_unref);
|
|
+ g_clear_pointer (&peer->name, g_free);
|
|
+ g_free (peer);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_finalize (GObject *object)
|
|
+{
|
|
+ GrdThrottler *throttler = GRD_THROTTLER(object);
|
|
+
|
|
+ g_clear_pointer (&throttler->delayed_connections_source, g_source_destroy);
|
|
+ g_clear_pointer (&throttler->peers, g_hash_table_unref);
|
|
+
|
|
+ G_OBJECT_CLASS (grd_throttler_parent_class)->finalize (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_init (GrdThrottler *throttler)
|
|
+{
|
|
+ throttler->peers =
|
|
+ g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
+ g_free, (GDestroyNotify) grd_peer_free);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_throttler_class_init (GrdThrottlerClass *klass)
|
|
+{
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
+
|
|
+ object_class->finalize = grd_throttler_finalize;
|
|
+
|
|
+ quark_remote_address =
|
|
+ g_quark_from_static_string ("grd-remote-address-string");
|
|
+}
|
|
diff --git a/src/grd-throttler.h b/src/grd-throttler.h
|
|
new file mode 100644
|
|
index 00000000..d57d653a
|
|
--- /dev/null
|
|
+++ b/src/grd-throttler.h
|
|
@@ -0,0 +1,41 @@
|
|
+/*
|
|
+ * Copyright (C) 2025 Red Hat
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful, but
|
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
+ * 02111-1307, USA.
|
|
+ */
|
|
+
|
|
+#ifndef GRD_THROTTLER_H
|
|
+#define GRD_THROTTLER_H
|
|
+
|
|
+#include <gio/gio.h>
|
|
+#include <glib-object.h>
|
|
+
|
|
+#define GRD_TYPE_THROTTLER (grd_throttler_get_type())
|
|
+G_DECLARE_FINAL_TYPE (GrdThrottler, grd_throttler, GRD, THROTTLER, GObject)
|
|
+
|
|
+typedef void (* GrdThrottlerAllowCallback) (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ gpointer user_data);
|
|
+
|
|
+void
|
|
+grd_throttler_handle_connection (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection);
|
|
+
|
|
+GrdThrottler *
|
|
+grd_throttler_new (GrdThrottlerAllowCallback allow_callback,
|
|
+ gpointer user_data);
|
|
+
|
|
+#endif /* GRD_THROTTLER_H */
|
|
diff --git a/src/meson.build b/src/meson.build
|
|
index de6b8cff..26a1039c 100644
|
|
--- a/src/meson.build
|
|
+++ b/src/meson.build
|
|
@@ -14,6 +14,8 @@ daemon_sources = files([
|
|
'grd-settings.h',
|
|
'grd-stream.c',
|
|
'grd-stream.h',
|
|
+ 'grd-throttler.c',
|
|
+ 'grd-throttler.h',
|
|
'grd-types.h',
|
|
'grd-utils.c',
|
|
'grd-utils.h',
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From c1a2a397f80ea4df6f8d7e7bd116c90ff6f9bb70 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Fri, 30 May 2025 16:54:00 +0200
|
|
Subject: [PATCH 5/7] throttler: Introduce limits struct
|
|
|
|
This will be used by the server implementation to customize throttling
|
|
limits. This will be used by the VNC server to limit to one global
|
|
session at a time, replacing it's own existing condition.
|
|
---
|
|
src/grd-throttler.c | 64 +++++++++++++++++++++++++++++++++++++--------
|
|
src/grd-throttler.h | 14 ++++++++--
|
|
2 files changed, 65 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/src/grd-throttler.c b/src/grd-throttler.c
|
|
index e584433a..0429a089 100644
|
|
--- a/src/grd-throttler.c
|
|
+++ b/src/grd-throttler.c
|
|
@@ -23,10 +23,18 @@
|
|
|
|
#include "grd-utils.h"
|
|
|
|
-#define MAX_GLOBAL_CONNECTIONS 10
|
|
-#define MAX_CONNECTIONS_PER_PEER 5
|
|
-#define MAX_PENDING_CONNECTIONS 5
|
|
-#define MAX_ATTEMPTS_PER_SECOND 5
|
|
+#define DEFAULT_MAX_GLOBAL_CONNECTIONS 10
|
|
+#define DEFAULT_MAX_CONNECTIONS_PER_PEER 5
|
|
+#define DEFAULT_MAX_PENDING_CONNECTIONS 5
|
|
+#define DEFAULT_MAX_ATTEMPTS_PER_SECOND 10
|
|
+
|
|
+struct _GrdThrottlerLimits
|
|
+{
|
|
+ int max_global_connections;
|
|
+ int max_connections_per_peer;
|
|
+ int max_pending_connections;
|
|
+ int max_attempts_per_second;
|
|
+};
|
|
|
|
#define PRUNE_TIME_CUTOFF_US (s2us (1))
|
|
|
|
@@ -44,6 +52,8 @@ struct _GrdThrottler
|
|
{
|
|
GObject parent;
|
|
|
|
+ GrdThrottlerLimits *limits;
|
|
+
|
|
int active_connections;
|
|
|
|
GrdThrottlerAllowCallback allow_callback;
|
|
@@ -215,10 +225,12 @@ static gboolean
|
|
is_connection_limit_reached (GrdThrottler *throttler,
|
|
GrdPeer *peer)
|
|
{
|
|
- if (peer->active_connections >= MAX_CONNECTIONS_PER_PEER)
|
|
+ GrdThrottlerLimits *limits = throttler->limits;
|
|
+
|
|
+ if (peer->active_connections >= limits->max_connections_per_peer)
|
|
return TRUE;
|
|
|
|
- if (throttler->active_connections >= MAX_GLOBAL_CONNECTIONS)
|
|
+ if (throttler->active_connections >= limits->max_global_connections)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
@@ -228,11 +240,13 @@ static gboolean
|
|
is_new_connection_allowed (GrdThrottler *throttler,
|
|
GrdPeer *peer)
|
|
{
|
|
+ GrdThrottlerLimits *limits = throttler->limits;
|
|
+
|
|
if (is_connection_limit_reached (throttler, peer))
|
|
return FALSE;
|
|
|
|
if (peer->connect_timestamps &&
|
|
- peer->connect_timestamps->len >= MAX_ATTEMPTS_PER_SECOND)
|
|
+ peer->connect_timestamps->len >= limits->max_attempts_per_second)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
@@ -291,6 +305,7 @@ ensure_delayed_connections_source (GrdThrottler *throttler)
|
|
static void
|
|
maybe_queue_timeout (GrdThrottler *throttler)
|
|
{
|
|
+ GrdThrottlerLimits *limits = throttler->limits;
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
int64_t next_timeout_us = INT64_MAX;
|
|
@@ -309,7 +324,7 @@ maybe_queue_timeout (GrdThrottler *throttler)
|
|
|
|
next_timeout_us = MIN (next_timeout_us,
|
|
peer->last_accept_us +
|
|
- G_USEC_PER_SEC / MAX_ATTEMPTS_PER_SECOND);
|
|
+ G_USEC_PER_SEC / limits->max_attempts_per_second);
|
|
}
|
|
|
|
|
|
@@ -330,6 +345,7 @@ maybe_delay_connection (GrdThrottler *throttler,
|
|
GrdPeer *peer,
|
|
int64_t now_us)
|
|
{
|
|
+ GrdThrottlerLimits *limits = throttler->limits;
|
|
GQueue *delayed_connections;
|
|
|
|
delayed_connections = peer->delayed_connections;
|
|
@@ -339,7 +355,7 @@ maybe_delay_connection (GrdThrottler *throttler,
|
|
peer->delayed_connections = delayed_connections;
|
|
}
|
|
|
|
- if (g_queue_get_length (delayed_connections) > MAX_PENDING_CONNECTIONS)
|
|
+ if (g_queue_get_length (delayed_connections) > limits->max_pending_connections)
|
|
{
|
|
grd_throttler_deny_connection (throttler, peer->name, connection);
|
|
return;
|
|
@@ -413,15 +429,40 @@ grd_throttler_handle_connection (GrdThrottler *throttler,
|
|
maybe_delay_connection (throttler, connection, peer, now_us);
|
|
}
|
|
|
|
+void
|
|
+grd_throttler_limits_set_max_global_connections (GrdThrottlerLimits *limits,
|
|
+ int limit)
|
|
+{
|
|
+ limits->max_global_connections = limit;
|
|
+}
|
|
+
|
|
+GrdThrottlerLimits *
|
|
+grd_throttler_limits_new (void)
|
|
+{
|
|
+ GrdThrottlerLimits *limits;
|
|
+
|
|
+ limits = g_new0 (GrdThrottlerLimits, 1);
|
|
+ limits->max_global_connections = DEFAULT_MAX_GLOBAL_CONNECTIONS;
|
|
+ limits->max_connections_per_peer = DEFAULT_MAX_CONNECTIONS_PER_PEER;
|
|
+ limits->max_pending_connections = DEFAULT_MAX_PENDING_CONNECTIONS;
|
|
+ limits->max_attempts_per_second = DEFAULT_MAX_ATTEMPTS_PER_SECOND;
|
|
+
|
|
+ return limits;
|
|
+}
|
|
+
|
|
GrdThrottler *
|
|
-grd_throttler_new (GrdThrottlerAllowCallback allow_callback,
|
|
- gpointer user_data)
|
|
+grd_throttler_new (GrdThrottlerLimits *limits,
|
|
+ GrdThrottlerAllowCallback allow_callback,
|
|
+ gpointer user_data)
|
|
{
|
|
GrdThrottler *throttler;
|
|
|
|
+ g_assert (limits);
|
|
+
|
|
throttler = g_object_new (GRD_TYPE_THROTTLER, NULL);
|
|
throttler->allow_callback = allow_callback;
|
|
throttler->user_data = user_data;
|
|
+ throttler->limits = limits;
|
|
|
|
return throttler;
|
|
}
|
|
@@ -443,6 +484,7 @@ grd_throttler_finalize (GObject *object)
|
|
|
|
g_clear_pointer (&throttler->delayed_connections_source, g_source_destroy);
|
|
g_clear_pointer (&throttler->peers, g_hash_table_unref);
|
|
+ g_clear_pointer (&throttler->limits, g_free);
|
|
|
|
G_OBJECT_CLASS (grd_throttler_parent_class)->finalize (object);
|
|
}
|
|
diff --git a/src/grd-throttler.h b/src/grd-throttler.h
|
|
index d57d653a..9e72b8f1 100644
|
|
--- a/src/grd-throttler.h
|
|
+++ b/src/grd-throttler.h
|
|
@@ -23,6 +23,8 @@
|
|
#include <gio/gio.h>
|
|
#include <glib-object.h>
|
|
|
|
+typedef struct _GrdThrottlerLimits GrdThrottlerLimits;
|
|
+
|
|
#define GRD_TYPE_THROTTLER (grd_throttler_get_type())
|
|
G_DECLARE_FINAL_TYPE (GrdThrottler, grd_throttler, GRD, THROTTLER, GObject)
|
|
|
|
@@ -34,8 +36,16 @@ void
|
|
grd_throttler_handle_connection (GrdThrottler *throttler,
|
|
GSocketConnection *connection);
|
|
|
|
+void
|
|
+grd_throttler_limits_set_max_global_connections (GrdThrottlerLimits *limits,
|
|
+ int limit);
|
|
+
|
|
+GrdThrottlerLimits *
|
|
+grd_throttler_limits_new (void);
|
|
+
|
|
GrdThrottler *
|
|
-grd_throttler_new (GrdThrottlerAllowCallback allow_callback,
|
|
- gpointer user_data);
|
|
+grd_throttler_new (GrdThrottlerLimits *limits,
|
|
+ GrdThrottlerAllowCallback allow_callback,
|
|
+ gpointer user_data);
|
|
|
|
#endif /* GRD_THROTTLER_H */
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 70a97969a131bd64f0e7e529c592fa6a7ecbb6b6 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Fri, 30 May 2025 16:55:46 +0200
|
|
Subject: [PATCH 6/7] vnc-server: Hook up VNC server to the throttler
|
|
|
|
This allows us to replace existing single session limitation with
|
|
limiting the throttler to one accepted connection at any given time.
|
|
---
|
|
src/grd-session-vnc.c | 2 ++
|
|
src/grd-vnc-server.c | 45 +++++++++++++++++++++++++++++++++++--------
|
|
2 files changed, 39 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
|
|
index 7950d1e0..fccbf741 100644
|
|
--- a/src/grd-session-vnc.c
|
|
+++ b/src/grd-session-vnc.c
|
|
@@ -32,6 +32,7 @@
|
|
#include "grd-prompt.h"
|
|
#include "grd-settings.h"
|
|
#include "grd-stream.h"
|
|
+#include "grd-utils.h"
|
|
#include "grd-vnc-server.h"
|
|
#include "grd-vnc-pipewire-stream.h"
|
|
|
|
@@ -775,6 +776,7 @@ grd_session_vnc_stop (GrdSession *session)
|
|
|
|
grd_session_vnc_pause (session_vnc);
|
|
|
|
+ grd_close_connection_and_notify (session_vnc->connection);
|
|
g_clear_object (&session_vnc->connection);
|
|
g_clear_pointer (&session_vnc->rfb_screen->frameBuffer, g_free);
|
|
g_clear_pointer (&session_vnc->rfb_screen, rfbScreenCleanup);
|
|
diff --git a/src/grd-vnc-server.c b/src/grd-vnc-server.c
|
|
index f9c68dba..f7240d37 100644
|
|
--- a/src/grd-vnc-server.c
|
|
+++ b/src/grd-vnc-server.c
|
|
@@ -30,9 +30,10 @@
|
|
|
|
#include "grd-context.h"
|
|
#include "grd-session-vnc.h"
|
|
+#include "grd-throttler.h"
|
|
+#include "grd-utils.h"
|
|
#include "grd-vnc-tls.h"
|
|
|
|
-
|
|
enum
|
|
{
|
|
PROP_0,
|
|
@@ -44,6 +45,8 @@ struct _GrdVncServer
|
|
{
|
|
GSocketService parent;
|
|
|
|
+ GrdThrottler *throttler;
|
|
+
|
|
GList *sessions;
|
|
|
|
GList *stopped_sessions;
|
|
@@ -54,6 +57,11 @@ struct _GrdVncServer
|
|
|
|
G_DEFINE_TYPE (GrdVncServer, grd_vnc_server, G_TYPE_SOCKET_SERVICE);
|
|
|
|
+static void
|
|
+allow_connection_cb (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ gpointer user_data);
|
|
+
|
|
GrdContext *
|
|
grd_vnc_server_get_context (GrdVncServer *vnc_server)
|
|
{
|
|
@@ -104,23 +112,24 @@ on_session_stopped (GrdSession *session, GrdVncServer *vnc_server)
|
|
}
|
|
}
|
|
|
|
-static gboolean
|
|
-on_incoming (GSocketService *service,
|
|
- GSocketConnection *connection)
|
|
+static void
|
|
+allow_connection_cb (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ gpointer user_data)
|
|
{
|
|
- GrdVncServer *vnc_server = GRD_VNC_SERVER (service);
|
|
+ GrdVncServer *vnc_server = GRD_VNC_SERVER (user_data);
|
|
GrdSessionVnc *session_vnc;
|
|
|
|
- g_debug ("New incoming VNC connection");
|
|
-
|
|
if (vnc_server->sessions)
|
|
{
|
|
/* TODO: Add the rfbScreen instance to GrdVncServer to support multiple
|
|
* sessions. */
|
|
g_debug ("Refusing new VNC connection: already an active session");
|
|
- return TRUE;
|
|
+ return;
|
|
}
|
|
|
|
+ g_debug ("Creating new VNC session");
|
|
+
|
|
session_vnc = grd_session_vnc_new (vnc_server, connection);
|
|
vnc_server->sessions = g_list_append (vnc_server->sessions, session_vnc);
|
|
grd_context_add_session (vnc_server->context, GRD_SESSION (session_vnc));
|
|
@@ -128,7 +137,16 @@ on_incoming (GSocketService *service,
|
|
g_signal_connect (session_vnc, "stopped",
|
|
G_CALLBACK (on_session_stopped),
|
|
vnc_server);
|
|
+}
|
|
|
|
+static gboolean
|
|
+on_incoming (GSocketService *service,
|
|
+ GSocketConnection *connection)
|
|
+{
|
|
+ GrdVncServer *vnc_server = GRD_VNC_SERVER (service);
|
|
+
|
|
+ grd_throttler_handle_connection (vnc_server->throttler,
|
|
+ connection);
|
|
return TRUE;
|
|
}
|
|
|
|
@@ -252,6 +270,8 @@ grd_vnc_server_dispose (GObject *object)
|
|
vnc_server->sessions = NULL;
|
|
}
|
|
|
|
+ g_clear_object (&vnc_server->throttler);
|
|
+
|
|
G_OBJECT_CLASS (grd_vnc_server_parent_class)->dispose (object);
|
|
}
|
|
|
|
@@ -277,6 +297,15 @@ grd_vnc_server_constructed (GObject *object)
|
|
static void
|
|
grd_vnc_server_init (GrdVncServer *vnc_server)
|
|
{
|
|
+ GrdThrottlerLimits *limits;
|
|
+
|
|
+ limits = grd_throttler_limits_new ();
|
|
+ /* TODO: Add the rfbScreen instance to GrdVncServer to support multiple
|
|
+ * sessions. */
|
|
+ grd_throttler_limits_set_max_global_connections (limits, 1);
|
|
+ vnc_server->throttler = grd_throttler_new (limits,
|
|
+ allow_connection_cb,
|
|
+ vnc_server);
|
|
}
|
|
|
|
static void
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 0f23aed843dacb1dceeb88cbeb9f545117d1c42d Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Mon, 30 Jun 2025 16:01:37 +0200
|
|
Subject: [PATCH 7/7] throttler: Allow overriding hard coded parallel
|
|
connections limit
|
|
|
|
Overriding is done via an argument passed to the daemon. This allows
|
|
using the same daemon with a larger amount of users, which one would
|
|
achieve by overriding the relevant systemd service file by both adding
|
|
the --max-parallel-connections argument, as well as bumping the limit of
|
|
number of open file descriptors to something adequate.
|
|
---
|
|
src/grd-daemon.c | 32 +++++++++++++++++++++++++++-----
|
|
src/grd-settings.c | 15 +++++++++++++++
|
|
src/grd-settings.h | 5 +++++
|
|
src/grd-throttler.c | 9 ++++++---
|
|
src/grd-throttler.h | 4 +++-
|
|
src/grd-vnc-server.c | 17 +++++++++--------
|
|
6 files changed, 65 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/src/grd-daemon.c b/src/grd-daemon.c
|
|
index f5595005..e00304d8 100644
|
|
--- a/src/grd-daemon.c
|
|
+++ b/src/grd-daemon.c
|
|
@@ -35,6 +35,8 @@
|
|
#include "grd-session.h"
|
|
#include "grd-vnc-server.h"
|
|
|
|
+#define DEFAULT_MAX_PARALLEL_CONNECTIONS 10
|
|
+
|
|
struct _GrdDaemon
|
|
{
|
|
GApplication parent;
|
|
@@ -50,6 +52,9 @@ struct _GrdDaemon
|
|
|
|
G_DEFINE_TYPE (GrdDaemon, grd_daemon, G_TYPE_APPLICATION)
|
|
|
|
+#define QUOTE1(a) #a
|
|
+#define QUOTE(a) QUOTE1(a)
|
|
+
|
|
static gboolean
|
|
is_daemon_ready (GrdDaemon *daemon)
|
|
{
|
|
@@ -282,17 +287,23 @@ main (int argc, char **argv)
|
|
{
|
|
gboolean print_version = FALSE;
|
|
int vnc_port = -1;
|
|
+ int max_parallel_connections = DEFAULT_MAX_PARALLEL_CONNECTIONS;
|
|
|
|
GOptionEntry entries[] = {
|
|
{ "version", 0, 0, G_OPTION_ARG_NONE, &print_version,
|
|
"Print version", NULL },
|
|
{ "vnc-port", 0, 0, G_OPTION_ARG_INT, &vnc_port,
|
|
"VNC port", NULL },
|
|
+ { "max-parallel-connections", 0, 0,
|
|
+ G_OPTION_ARG_INT, &max_parallel_connections,
|
|
+ "Max number of parallel connections (0 for unlimited, "
|
|
+ "default: " QUOTE(DEFAULT_MAX_PARALLEL_CONNECTIONS) ")", NULL },
|
|
{ NULL }
|
|
};
|
|
g_autoptr(GOptionContext) context = NULL;
|
|
g_autoptr(GApplication) app = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
+ GrdSettings *settings;
|
|
|
|
g_set_application_name (_("GNOME Remote Desktop"));
|
|
|
|
@@ -317,13 +328,24 @@ main (int argc, char **argv)
|
|
|
|
add_actions (app);
|
|
|
|
- if (vnc_port != -1)
|
|
+ if (max_parallel_connections == 0)
|
|
{
|
|
- GrdSettings *settings;
|
|
-
|
|
- settings = grd_context_get_settings (GRD_DAEMON (app)->context);
|
|
- grd_settings_override_vnc_port (settings, vnc_port);
|
|
+ max_parallel_connections = INT_MAX;
|
|
}
|
|
+ else if (max_parallel_connections < 0)
|
|
+ {
|
|
+ g_printerr ("Invalid number of max parallel connections: %d\n",
|
|
+ max_parallel_connections);
|
|
+ return EXIT_SUCCESS;
|
|
+ }
|
|
+
|
|
+ settings = grd_context_get_settings (GRD_DAEMON (app)->context);
|
|
+
|
|
+ if (vnc_port != -1)
|
|
+ grd_settings_override_vnc_port (settings, vnc_port);
|
|
+
|
|
+ grd_settings_override_max_parallel_connections (settings,
|
|
+ max_parallel_connections);
|
|
|
|
return g_application_run (app, argc, argv);
|
|
}
|
|
diff --git a/src/grd-settings.c b/src/grd-settings.c
|
|
index 7324310b..52cb1702 100644
|
|
--- a/src/grd-settings.c
|
|
+++ b/src/grd-settings.c
|
|
@@ -50,6 +50,8 @@ struct _GrdSettings
|
|
int port;
|
|
GrdVncEncryption encryption;
|
|
} vnc;
|
|
+
|
|
+ int max_parallel_connections;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdSettings, grd_settings, G_TYPE_OBJECT)
|
|
@@ -75,6 +77,19 @@ grd_settings_get_vnc_port (GrdSettings *settings)
|
|
return settings->vnc.port;
|
|
}
|
|
|
|
+void
|
|
+grd_settings_override_max_parallel_connections (GrdSettings *settings,
|
|
+ int max_parallel_connections)
|
|
+{
|
|
+ settings->max_parallel_connections = max_parallel_connections;
|
|
+}
|
|
+
|
|
+int
|
|
+grd_settings_get_max_parallel_connections (GrdSettings *settings)
|
|
+{
|
|
+ return settings->max_parallel_connections;
|
|
+}
|
|
+
|
|
void
|
|
grd_settings_override_vnc_port (GrdSettings *settings,
|
|
int port)
|
|
diff --git a/src/grd-settings.h b/src/grd-settings.h
|
|
index 0575ec1e..999581f4 100644
|
|
--- a/src/grd-settings.h
|
|
+++ b/src/grd-settings.h
|
|
@@ -35,6 +35,11 @@ const SecretSchema * cc_grd_vnc_password_get_schema (void);
|
|
|
|
int grd_settings_get_vnc_port (GrdSettings *settings);
|
|
|
|
+void grd_settings_override_max_parallel_connections (GrdSettings *settings,
|
|
+ int max_parallel_connections);
|
|
+
|
|
+int grd_settings_get_max_parallel_connections (GrdSettings *settings);
|
|
+
|
|
void grd_settings_override_vnc_port (GrdSettings *settings,
|
|
int port);
|
|
|
|
diff --git a/src/grd-throttler.c b/src/grd-throttler.c
|
|
index 0429a089..9ec31b35 100644
|
|
--- a/src/grd-throttler.c
|
|
+++ b/src/grd-throttler.c
|
|
@@ -21,9 +21,10 @@
|
|
|
|
#include "grd-throttler.h"
|
|
|
|
+#include "grd-context.h"
|
|
+#include "grd-settings.h"
|
|
#include "grd-utils.h"
|
|
|
|
-#define DEFAULT_MAX_GLOBAL_CONNECTIONS 10
|
|
#define DEFAULT_MAX_CONNECTIONS_PER_PEER 5
|
|
#define DEFAULT_MAX_PENDING_CONNECTIONS 5
|
|
#define DEFAULT_MAX_ATTEMPTS_PER_SECOND 10
|
|
@@ -437,12 +438,14 @@ grd_throttler_limits_set_max_global_connections (GrdThrottlerLimits *limits,
|
|
}
|
|
|
|
GrdThrottlerLimits *
|
|
-grd_throttler_limits_new (void)
|
|
+grd_throttler_limits_new (GrdContext *context)
|
|
{
|
|
+ GrdSettings *settings = grd_context_get_settings (context);
|
|
GrdThrottlerLimits *limits;
|
|
|
|
limits = g_new0 (GrdThrottlerLimits, 1);
|
|
- limits->max_global_connections = DEFAULT_MAX_GLOBAL_CONNECTIONS;
|
|
+ limits->max_global_connections =
|
|
+ grd_settings_get_max_parallel_connections (settings);
|
|
limits->max_connections_per_peer = DEFAULT_MAX_CONNECTIONS_PER_PEER;
|
|
limits->max_pending_connections = DEFAULT_MAX_PENDING_CONNECTIONS;
|
|
limits->max_attempts_per_second = DEFAULT_MAX_ATTEMPTS_PER_SECOND;
|
|
diff --git a/src/grd-throttler.h b/src/grd-throttler.h
|
|
index 9e72b8f1..a9554202 100644
|
|
--- a/src/grd-throttler.h
|
|
+++ b/src/grd-throttler.h
|
|
@@ -23,6 +23,8 @@
|
|
#include <gio/gio.h>
|
|
#include <glib-object.h>
|
|
|
|
+#include "grd-types.h"
|
|
+
|
|
typedef struct _GrdThrottlerLimits GrdThrottlerLimits;
|
|
|
|
#define GRD_TYPE_THROTTLER (grd_throttler_get_type())
|
|
@@ -41,7 +43,7 @@ grd_throttler_limits_set_max_global_connections (GrdThrottlerLimits *limits,
|
|
int limit);
|
|
|
|
GrdThrottlerLimits *
|
|
-grd_throttler_limits_new (void);
|
|
+grd_throttler_limits_new (GrdContext *context);
|
|
|
|
GrdThrottler *
|
|
grd_throttler_new (GrdThrottlerLimits *limits,
|
|
diff --git a/src/grd-vnc-server.c b/src/grd-vnc-server.c
|
|
index f7240d37..bddc6909 100644
|
|
--- a/src/grd-vnc-server.c
|
|
+++ b/src/grd-vnc-server.c
|
|
@@ -280,6 +280,7 @@ grd_vnc_server_constructed (GObject *object)
|
|
{
|
|
GrdVncServer *vnc_server = GRD_VNC_SERVER (object);
|
|
GrdSettings *settings = grd_context_get_settings (vnc_server->context);
|
|
+ GrdThrottlerLimits *limits;
|
|
|
|
if (grd_context_get_debug_flags (vnc_server->context) & GRD_DEBUG_VNC)
|
|
rfbLogEnable (1);
|
|
@@ -291,21 +292,21 @@ grd_vnc_server_constructed (GObject *object)
|
|
vnc_server);
|
|
sync_encryption_settings (vnc_server);
|
|
|
|
- G_OBJECT_CLASS (grd_vnc_server_parent_class)->constructed (object);
|
|
-}
|
|
-
|
|
-static void
|
|
-grd_vnc_server_init (GrdVncServer *vnc_server)
|
|
-{
|
|
- GrdThrottlerLimits *limits;
|
|
+ limits = grd_throttler_limits_new (vnc_server->context);
|
|
|
|
- limits = grd_throttler_limits_new ();
|
|
/* TODO: Add the rfbScreen instance to GrdVncServer to support multiple
|
|
* sessions. */
|
|
grd_throttler_limits_set_max_global_connections (limits, 1);
|
|
vnc_server->throttler = grd_throttler_new (limits,
|
|
allow_connection_cb,
|
|
vnc_server);
|
|
+
|
|
+ G_OBJECT_CLASS (grd_vnc_server_parent_class)->constructed (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_vnc_server_init (GrdVncServer *vnc_server)
|
|
+{
|
|
}
|
|
|
|
static void
|
|
--
|
|
2.49.0
|
|
|