1707 lines
52 KiB
Diff
1707 lines
52 KiB
Diff
From 7a7a6d0757829f2efb2fc416b1ba55aa52216c4b Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Mon, 26 May 2025 16:08:54 +0200
|
|
Subject: [PATCH 01/13] control: Allow controlling all daemon types
|
|
|
|
Useful for gracefully terminating daemon without sending signals.
|
|
---
|
|
src/grd-control.c | 23 ++++++++++++++++++++++-
|
|
1 file changed, 22 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/src/grd-control.c b/src/grd-control.c
|
|
index 858ab10a..284cb113 100644
|
|
--- a/src/grd-control.c
|
|
+++ b/src/grd-control.c
|
|
@@ -33,13 +33,25 @@ main (int argc, char **argv)
|
|
{
|
|
g_autoptr(GApplication) app = NULL;
|
|
gboolean terminate = FALSE;
|
|
+ gboolean headless = FALSE;
|
|
+ gboolean system = FALSE;
|
|
+ gboolean handover = FALSE;
|
|
GOptionEntry entries[] = {
|
|
{ "terminate", 0, 0, G_OPTION_ARG_NONE, &terminate,
|
|
"Terminate the daemon", NULL },
|
|
+ { "headless", 0, 0, G_OPTION_ARG_NONE, &headless,
|
|
+ "Control headless daemon", NULL },
|
|
+#if defined(HAVE_RDP) && defined(HAVE_LIBSYSTEMD)
|
|
+ { "system", 0, 0, G_OPTION_ARG_NONE, &system,
|
|
+ "Control system daemon", NULL },
|
|
+ { "handover", 0, 0, G_OPTION_ARG_NONE, &handover,
|
|
+ "Control handover daemon", NULL },
|
|
+#endif /* HAVE_RDP && HAVE_LIBSYSTEMD */
|
|
{ NULL }
|
|
};
|
|
GError *error = NULL;
|
|
GOptionContext *context;
|
|
+ const char *app_id;
|
|
|
|
context = g_option_context_new ("- control gnome-remote-desktop");
|
|
g_option_context_add_main_entries (context, entries, NULL);
|
|
@@ -56,7 +68,16 @@ main (int argc, char **argv)
|
|
return 1;
|
|
}
|
|
|
|
- app = g_application_new (GRD_DAEMON_USER_APPLICATION_ID, 0);
|
|
+ if (headless)
|
|
+ app_id = GRD_DAEMON_HEADLESS_APPLICATION_ID;
|
|
+ else if (system)
|
|
+ app_id = GRD_DAEMON_SYSTEM_APPLICATION_ID;
|
|
+ else if (handover)
|
|
+ app_id = GRD_DAEMON_HANDOVER_APPLICATION_ID;
|
|
+ else
|
|
+ app_id = GRD_DAEMON_USER_APPLICATION_ID;
|
|
+
|
|
+ app = g_application_new (app_id, G_APPLICATION_DEFAULT_FLAGS);
|
|
if (!g_application_register (app, NULL, NULL))
|
|
{
|
|
g_warning ("Failed to register with application\n");
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 8dcf988fb40ca328d685d3ca3cf2e7f5e2e19467 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 02/13] daemon: Use GError auto pointer
|
|
|
|
---
|
|
src/grd-daemon.c | 4 +---
|
|
1 file changed, 1 insertion(+), 3 deletions(-)
|
|
|
|
diff --git a/src/grd-daemon.c b/src/grd-daemon.c
|
|
index b99f6181..2f6ed024 100644
|
|
--- a/src/grd-daemon.c
|
|
+++ b/src/grd-daemon.c
|
|
@@ -986,7 +986,7 @@ main (int argc, char **argv)
|
|
};
|
|
g_autoptr (GOptionContext) option_context = NULL;
|
|
g_autoptr (GrdDaemon) daemon = NULL;
|
|
- GError *error = NULL;
|
|
+ g_autoptr (GError) error = NULL;
|
|
GrdRuntimeMode runtime_mode;
|
|
|
|
g_set_application_name (_("GNOME Remote Desktop"));
|
|
@@ -996,7 +996,6 @@ main (int argc, char **argv)
|
|
if (!g_option_context_parse (option_context, &argc, &argv, &error))
|
|
{
|
|
g_printerr ("Invalid option: %s\n", error->message);
|
|
- g_error_free (error);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
@@ -1045,7 +1044,6 @@ main (int argc, char **argv)
|
|
if (!daemon)
|
|
{
|
|
g_printerr ("Failed to initialize: %s\n", error->message);
|
|
- g_error_free (error);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From dfa19a9498f7245ac0ce3efd290dde1bdd8b865f Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Mon, 26 May 2025 17:04:45 +0200
|
|
Subject: [PATCH 03/13] rdp-server: Remove empty constructor vfunc
|
|
|
|
No need to implement it if it doesn't do anything.
|
|
---
|
|
src/grd-rdp-server.c | 7 -------
|
|
1 file changed, 7 deletions(-)
|
|
|
|
diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c
|
|
index 4fddfaa2..6ee93c5c 100644
|
|
--- a/src/grd-rdp-server.c
|
|
+++ b/src/grd-rdp-server.c
|
|
@@ -470,12 +470,6 @@ grd_rdp_server_dispose (GObject *object)
|
|
G_OBJECT_CLASS (grd_rdp_server_parent_class)->dispose (object);
|
|
}
|
|
|
|
-static void
|
|
-grd_rdp_server_constructed (GObject *object)
|
|
-{
|
|
- G_OBJECT_CLASS (grd_rdp_server_parent_class)->constructed (object);
|
|
-}
|
|
-
|
|
static void
|
|
grd_rdp_server_init (GrdRdpServer *rdp_server)
|
|
{
|
|
@@ -498,7 +492,6 @@ grd_rdp_server_class_init (GrdRdpServerClass *klass)
|
|
object_class->set_property = grd_rdp_server_set_property;
|
|
object_class->get_property = grd_rdp_server_get_property;
|
|
object_class->dispose = grd_rdp_server_dispose;
|
|
- object_class->constructed = grd_rdp_server_constructed;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_CONTEXT,
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 0a5400d11a032ae0e1b9e75bc72915b892713a77 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Wed, 28 May 2025 14:08:12 +0200
|
|
Subject: [PATCH 04/13] rdp-sam: Dup fd kept in struct
|
|
|
|
The SAM file instance intends to keeps the file descriptor alive so that
|
|
it doesn't go away; however it failed to do this due to fdopen() taking
|
|
ownership of it, thus closing it during fclose(). This meant we'd close
|
|
an arbitrary potentially reused file descriptor when the SAM file
|
|
instance eventually got cleaned up. Address this by reordering things a
|
|
bit, while putting a duplicated file descriptor in the SAM file instance
|
|
struct.
|
|
---
|
|
src/grd-rdp-sam.c | 19 ++++++++++---------
|
|
1 file changed, 10 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/src/grd-rdp-sam.c b/src/grd-rdp-sam.c
|
|
index 5898825a..bcadcb59 100644
|
|
--- a/src/grd-rdp-sam.c
|
|
+++ b/src/grd-rdp-sam.c
|
|
@@ -76,7 +76,7 @@ grd_rdp_sam_create_sam_file (const char *username,
|
|
g_autofree char *file_dir = NULL;
|
|
g_autofree char *filename = NULL;
|
|
g_autofree char *sam_string = NULL;
|
|
- int fd;
|
|
+ g_autofd int fd = -1;
|
|
FILE *sam_file;
|
|
|
|
file_dir = g_strdup_printf ("%s%s", g_get_user_runtime_dir (), grd_path);
|
|
@@ -98,20 +98,21 @@ grd_rdp_sam_create_sam_file (const char *username,
|
|
return NULL;
|
|
}
|
|
|
|
- rdp_sam_file = g_new0 (GrdRdpSAMFile, 1);
|
|
- rdp_sam_file->fd = fd;
|
|
- rdp_sam_file->filename = g_steal_pointer (&filename);
|
|
-
|
|
- sam_string = create_sam_string (username, password);
|
|
-
|
|
- sam_file = fdopen (rdp_sam_file->fd, "w+");
|
|
+ sam_file = fdopen (fd, "w+");
|
|
if (!sam_file)
|
|
{
|
|
g_warning ("[RDP] Failed to open SAM database: %s", g_strerror (errno));
|
|
- grd_rdp_sam_free_sam_file (rdp_sam_file);
|
|
return NULL;
|
|
}
|
|
|
|
+ rdp_sam_file = g_new0 (GrdRdpSAMFile, 1);
|
|
+ rdp_sam_file->fd = dup (fd);
|
|
+ rdp_sam_file->filename = g_steal_pointer (&filename);
|
|
+
|
|
+ g_steal_fd (&fd);
|
|
+
|
|
+ sam_string = create_sam_string (username, password);
|
|
+
|
|
fputs (sam_string, sam_file);
|
|
fclose (sam_file);
|
|
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 6a965f7c383a62e06a1dfb5033e7820ddff5c242 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Wed, 28 May 2025 22:33:54 +0200
|
|
Subject: [PATCH 05/13] rdp-server: Set socket backlog count to 5
|
|
|
|
This limits the number of pending connections.
|
|
---
|
|
src/grd-rdp-server.c | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c
|
|
index 6ee93c5c..537d4e30 100644
|
|
--- a/src/grd-rdp-server.c
|
|
+++ b/src/grd-rdp-server.c
|
|
@@ -35,6 +35,7 @@
|
|
|
|
#define RDP_SERVER_N_BINDING_ATTEMPTS 10
|
|
#define RDP_SERVER_BINDING_ATTEMPT_INTERVAL_MS 500
|
|
+#define RDP_SERVER_SOCKET_BACKLOG_COUNT 5
|
|
|
|
enum
|
|
{
|
|
@@ -302,6 +303,9 @@ bind_socket (GrdRdpServer *rdp_server,
|
|
uint16_t selected_rdp_port = 0;
|
|
gboolean negotiate_port;
|
|
|
|
+ g_socket_listener_set_backlog (G_SOCKET_LISTENER (rdp_server),
|
|
+ RDP_SERVER_SOCKET_BACKLOG_COUNT);
|
|
+
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-port", &rdp_port,
|
|
"rdp-negotiate-port", &negotiate_port,
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From bc1ab53a393b886d3e5fae5b33efb8f2d3205fbc 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 06/13] utils: Add some time conversion helpers
|
|
|
|
Useful when converting between second, microseconds, etc.
|
|
---
|
|
src/grd-utils.h | 18 ++++++++++++++++++
|
|
1 file changed, 18 insertions(+)
|
|
|
|
diff --git a/src/grd-utils.h b/src/grd-utils.h
|
|
index 6e433246..d9ae0801 100644
|
|
--- a/src/grd-utils.h
|
|
+++ b/src/grd-utils.h
|
|
@@ -98,4 +98,22 @@ gboolean grd_systemd_unit_get_active_state (GDBusProxy *unit_pro
|
|
GrdSystemdUnitActiveState *active_state,
|
|
GError **error);
|
|
|
|
+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 */
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 0f90337b241b200fc33565d49b6344e43d93bd0c 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 07/13] 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 | 7 +++++++
|
|
src/grd-utils.h | 2 ++
|
|
2 files changed, 9 insertions(+)
|
|
|
|
diff --git a/src/grd-utils.c b/src/grd-utils.c
|
|
index 9ded2092..20e6bffa 100644
|
|
--- a/src/grd-utils.c
|
|
+++ b/src/grd-utils.c
|
|
@@ -461,3 +461,10 @@ grd_systemd_unit_get_active_state (GDBusProxy *unit_proxy,
|
|
|
|
return TRUE;
|
|
}
|
|
+
|
|
+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 d9ae0801..eeb0a01c 100644
|
|
--- a/src/grd-utils.h
|
|
+++ b/src/grd-utils.h
|
|
@@ -98,6 +98,8 @@ gboolean grd_systemd_unit_get_active_state (GDBusProxy *unit_pro
|
|
GrdSystemdUnitActiveState *active_state,
|
|
GError **error);
|
|
|
|
+void grd_close_connection_and_notify (GSocketConnection *connection);
|
|
+
|
|
static inline int64_t
|
|
us (int64_t us)
|
|
{
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 4e09eee9a71e8da5bc1fb0fcaac999fc8efff953 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 08/13] 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 f92f9f1e..1e2cc69c 100644
|
|
--- a/src/meson.build
|
|
+++ b/src/meson.build
|
|
@@ -72,6 +72,8 @@ daemon_sources = files([
|
|
'grd-settings-user.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 1dbffe778f0392a64c69152083290f125bfb450f Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
Date: Wed, 28 May 2025 22:46:27 +0200
|
|
Subject: [PATCH 09/13] rdp-server: Throttle connections using GrdThrottler
|
|
|
|
This avoids system resource exhaustion by a single attacker.
|
|
|
|
Related: CVE-2025-5024
|
|
---
|
|
src/grd-rdp-server.c | 27 +++++++++++++++++++++++----
|
|
src/grd-session-rdp.c | 6 ++++++
|
|
2 files changed, 29 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c
|
|
index 537d4e30..228939b5 100644
|
|
--- a/src/grd-rdp-server.c
|
|
+++ b/src/grd-rdp-server.c
|
|
@@ -31,6 +31,7 @@
|
|
#include "grd-context.h"
|
|
#include "grd-hwaccel-nvidia.h"
|
|
#include "grd-session-rdp.h"
|
|
+#include "grd-throttler.h"
|
|
#include "grd-utils.h"
|
|
|
|
#define RDP_SERVER_N_BINDING_ATTEMPTS 10
|
|
@@ -59,6 +60,8 @@ struct _GrdRdpServer
|
|
{
|
|
GSocketService parent;
|
|
|
|
+ GrdThrottler *throttler;
|
|
+
|
|
GList *sessions;
|
|
|
|
GList *stopped_sessions;
|
|
@@ -216,13 +219,25 @@ on_incoming (GSocketService *service,
|
|
GSocketConnection *connection)
|
|
{
|
|
GrdRdpServer *rdp_server = GRD_RDP_SERVER (service);
|
|
+
|
|
+ grd_throttler_handle_connection (rdp_server->throttler,
|
|
+ connection);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+allow_connection_cb (GrdThrottler *throttler,
|
|
+ GSocketConnection *connection,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GrdRdpServer *rdp_server = GRD_RDP_SERVER (user_data);
|
|
GrdSessionRdp *session_rdp;
|
|
|
|
- g_debug ("New incoming RDP connection");
|
|
+ g_debug ("Creating new RDP session");
|
|
|
|
if (!(session_rdp = grd_session_rdp_new (rdp_server, connection,
|
|
rdp_server->hwaccel_nvidia)))
|
|
- return TRUE;
|
|
+ return;
|
|
|
|
rdp_server->sessions = g_list_append (rdp_server->sessions, session_rdp);
|
|
|
|
@@ -233,8 +248,6 @@ on_incoming (GSocketService *service,
|
|
g_signal_connect (session_rdp, "post-connected",
|
|
G_CALLBACK (on_session_post_connect),
|
|
rdp_server);
|
|
-
|
|
- return TRUE;
|
|
}
|
|
|
|
void
|
|
@@ -410,6 +423,8 @@ grd_rdp_server_stop (GrdRdpServer *rdp_server)
|
|
g_clear_handle_id (&rdp_server->cleanup_sessions_idle_id, g_source_remove);
|
|
grd_rdp_server_cleanup_stopped_sessions (rdp_server);
|
|
|
|
+ g_clear_object (&rdp_server->throttler);
|
|
+
|
|
if (rdp_server->cancellable)
|
|
{
|
|
g_cancellable_cancel (rdp_server->cancellable);
|
|
@@ -468,6 +483,7 @@ grd_rdp_server_dispose (GObject *object)
|
|
g_assert (!rdp_server->binding_timeout_source_id);
|
|
g_assert (!rdp_server->cleanup_sessions_idle_id);
|
|
g_assert (!rdp_server->stopped_sessions);
|
|
+ g_assert (!rdp_server->throttler);
|
|
|
|
g_assert (!rdp_server->hwaccel_nvidia);
|
|
|
|
@@ -477,6 +493,9 @@ grd_rdp_server_dispose (GObject *object)
|
|
static void
|
|
grd_rdp_server_init (GrdRdpServer *rdp_server)
|
|
{
|
|
+ rdp_server->throttler = grd_throttler_new (allow_connection_cb,
|
|
+ rdp_server);
|
|
+
|
|
rdp_server->pending_binding_attempts = RDP_SERVER_N_BINDING_ATTEMPTS;
|
|
|
|
winpr_InitializeSSL (WINPR_SSL_INIT_DEFAULT);
|
|
diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c
|
|
index 3bf1fb7a..bdccc981 100644
|
|
--- a/src/grd-session-rdp.c
|
|
+++ b/src/grd-session-rdp.c
|
|
@@ -47,6 +47,7 @@
|
|
#include "grd-rdp-session-metrics.h"
|
|
#include "grd-rdp-telemetry.h"
|
|
#include "grd-settings.h"
|
|
+#include "grd-utils.h"
|
|
|
|
#define MAX_MONITOR_COUNT_HEADLESS 16
|
|
#define MAX_MONITOR_COUNT_SCREEN_SHARE 1
|
|
@@ -1577,6 +1578,7 @@ grd_session_rdp_stop (GrdSession *session)
|
|
g_clear_object (&session_rdp->renderer);
|
|
|
|
peer->Close (peer);
|
|
+ grd_close_connection_and_notify (session_rdp->connection);
|
|
g_clear_object (&session_rdp->connection);
|
|
|
|
g_clear_object (&rdp_peer_context->network_autodetection);
|
|
@@ -1761,6 +1763,10 @@ grd_session_rdp_dispose (GObject *object)
|
|
g_clear_object (&session_rdp->layout_manager);
|
|
clear_rdp_peer (session_rdp);
|
|
|
|
+ if (session_rdp->connection)
|
|
+ grd_close_connection_and_notify (session_rdp->connection);
|
|
+ g_clear_object (&session_rdp->connection);
|
|
+
|
|
g_clear_object (&session_rdp->renderer);
|
|
|
|
g_clear_object (&session_rdp->rdp_event_queue);
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From 61aa2a856d920ecc64abf38dfb1ca089f2626192 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 10/13] 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-rdp-server.c | 3 ++-
|
|
src/grd-throttler.c | 64 ++++++++++++++++++++++++++++++++++++--------
|
|
src/grd-throttler.h | 14 ++++++++--
|
|
3 files changed, 67 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c
|
|
index 228939b5..207d9e2d 100644
|
|
--- a/src/grd-rdp-server.c
|
|
+++ b/src/grd-rdp-server.c
|
|
@@ -493,7 +493,8 @@ grd_rdp_server_dispose (GObject *object)
|
|
static void
|
|
grd_rdp_server_init (GrdRdpServer *rdp_server)
|
|
{
|
|
- rdp_server->throttler = grd_throttler_new (allow_connection_cb,
|
|
+ rdp_server->throttler = grd_throttler_new (grd_throttler_limits_new (),
|
|
+ allow_connection_cb,
|
|
rdp_server);
|
|
|
|
rdp_server->pending_binding_attempts = RDP_SERVER_N_BINDING_ATTEMPTS;
|
|
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 a0fa2c8afb072d2aa0886012925eccc36c2cdfb9 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 11/13] 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 | 48 +++++++++++++++++++++++++++++++------------
|
|
2 files changed, 37 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
|
|
index 6e118d88..8f4caec0 100644
|
|
--- a/src/grd-session-vnc.c
|
|
+++ b/src/grd-session-vnc.c
|
|
@@ -34,6 +34,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"
|
|
|
|
@@ -942,6 +943,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_object (&session_vnc->clipboard_vnc);
|
|
g_clear_pointer (&session_vnc->rfb_screen->frameBuffer, g_free);
|
|
diff --git a/src/grd-vnc-server.c b/src/grd-vnc-server.c
|
|
index 2f8229b2..fc5020f0 100644
|
|
--- a/src/grd-vnc-server.c
|
|
+++ b/src/grd-vnc-server.c
|
|
@@ -31,6 +31,7 @@
|
|
#include "grd-context.h"
|
|
#include "grd-debug.h"
|
|
#include "grd-session-vnc.h"
|
|
+#include "grd-throttler.h"
|
|
#include "grd-utils.h"
|
|
#include "grd-vnc-tls.h"
|
|
|
|
@@ -45,6 +46,8 @@ struct _GrdVncServer
|
|
{
|
|
GSocketService parent;
|
|
|
|
+ GrdThrottler *throttler;
|
|
+
|
|
GList *sessions;
|
|
|
|
GList *stopped_sessions;
|
|
@@ -55,6 +58,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)
|
|
{
|
|
@@ -105,22 +113,15 @@ 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_message ("Refusing new VNC connection: already an active session");
|
|
- return TRUE;
|
|
- }
|
|
+ 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);
|
|
@@ -128,7 +129,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;
|
|
}
|
|
|
|
@@ -228,6 +238,8 @@ grd_vnc_server_stop (GrdVncServer *vnc_server)
|
|
grd_vnc_server_cleanup_stopped_sessions (vnc_server);
|
|
g_clear_handle_id (&vnc_server->cleanup_sessions_idle_id,
|
|
g_source_remove);
|
|
+
|
|
+ g_clear_object (&vnc_server->throttler);
|
|
}
|
|
|
|
static void
|
|
@@ -276,6 +288,7 @@ grd_vnc_server_dispose (GObject *object)
|
|
g_assert (!vnc_server->sessions);
|
|
g_assert (!vnc_server->stopped_sessions);
|
|
g_assert (!vnc_server->cleanup_sessions_idle_id);
|
|
+ g_assert (!vnc_server->throttler);
|
|
|
|
G_OBJECT_CLASS (grd_vnc_server_parent_class)->dispose (object);
|
|
}
|
|
@@ -302,6 +315,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 5ba32deb4fca13b0a56d724f1bf79c5c76c39154 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:12 +0200
|
|
Subject: [PATCH 12/13] daemon: Add missing newline to error message
|
|
|
|
---
|
|
src/grd-daemon.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/src/grd-daemon.c b/src/grd-daemon.c
|
|
index 2f6ed024..2802bb1e 100644
|
|
--- a/src/grd-daemon.c
|
|
+++ b/src/grd-daemon.c
|
|
@@ -1007,7 +1007,7 @@ main (int argc, char **argv)
|
|
|
|
if (count_trues (3, headless, system, handover) > 1)
|
|
{
|
|
- g_printerr ("Invalid option: More than one runtime mode specified");
|
|
+ g_printerr ("Invalid option: More than one runtime mode specified\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
--
|
|
2.49.0
|
|
|
|
|
|
From e94d1dd3015df17e88a7ee78f45d79aadfd45be6 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 13/13] 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 | 24 ++++++++++++++++++++++++
|
|
src/grd-rdp-server.c | 19 +++++++++++++++----
|
|
src/grd-settings.c | 19 +++++++++++++++++++
|
|
src/grd-settings.h | 5 +++++
|
|
src/grd-throttler.c | 9 ++++++---
|
|
src/grd-throttler.h | 4 +++-
|
|
src/grd-vnc-server.c | 18 +++++++++---------
|
|
7 files changed, 81 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/src/grd-daemon.c b/src/grd-daemon.c
|
|
index 2802bb1e..c04475a7 100644
|
|
--- a/src/grd-daemon.c
|
|
+++ b/src/grd-daemon.c
|
|
@@ -48,6 +48,8 @@
|
|
|
|
#define RDP_SERVER_RESTART_DELAY_MS 3000
|
|
|
|
+#define DEFAULT_MAX_PARALLEL_CONNECTIONS 10
|
|
+
|
|
enum
|
|
{
|
|
PROP_0,
|
|
@@ -91,6 +93,9 @@ typedef struct _GrdDaemonPrivate
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GrdDaemon, grd_daemon, G_TYPE_APPLICATION)
|
|
|
|
+#define QUOTE1(a) #a
|
|
+#define QUOTE(a) QUOTE1(a)
|
|
+
|
|
#ifdef HAVE_RDP
|
|
static void maybe_start_rdp_server (GrdDaemon *daemon);
|
|
#endif
|
|
@@ -966,6 +971,7 @@ main (int argc, char **argv)
|
|
gboolean handover = FALSE;
|
|
int rdp_port = -1;
|
|
int vnc_port = -1;
|
|
+ int max_parallel_connections = DEFAULT_MAX_PARALLEL_CONNECTIONS;
|
|
|
|
GOptionEntry entries[] = {
|
|
{ "version", 0, 0, G_OPTION_ARG_NONE, &print_version,
|
|
@@ -982,6 +988,10 @@ main (int argc, char **argv)
|
|
"RDP port", 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) option_context = NULL;
|
|
@@ -1011,6 +1021,17 @@ main (int argc, char **argv)
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
+ if (max_parallel_connections == 0)
|
|
+ {
|
|
+ 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_FAILURE;
|
|
+ }
|
|
+
|
|
if (headless)
|
|
runtime_mode = GRD_RUNTIME_MODE_HEADLESS;
|
|
else if (system)
|
|
@@ -1057,5 +1078,8 @@ main (int argc, char **argv)
|
|
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 (G_APPLICATION (daemon), argc, argv);
|
|
}
|
|
diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c
|
|
index 207d9e2d..b4952935 100644
|
|
--- a/src/grd-rdp-server.c
|
|
+++ b/src/grd-rdp-server.c
|
|
@@ -491,11 +491,14 @@ grd_rdp_server_dispose (GObject *object)
|
|
}
|
|
|
|
static void
|
|
-grd_rdp_server_init (GrdRdpServer *rdp_server)
|
|
+grd_rdp_server_constructed (GObject *object)
|
|
{
|
|
- rdp_server->throttler = grd_throttler_new (grd_throttler_limits_new (),
|
|
- allow_connection_cb,
|
|
- rdp_server);
|
|
+ GrdRdpServer *rdp_server = GRD_RDP_SERVER (object);
|
|
+
|
|
+ rdp_server->throttler =
|
|
+ grd_throttler_new (grd_throttler_limits_new (rdp_server->context),
|
|
+ allow_connection_cb,
|
|
+ rdp_server);
|
|
|
|
rdp_server->pending_binding_attempts = RDP_SERVER_N_BINDING_ATTEMPTS;
|
|
|
|
@@ -506,6 +509,13 @@ grd_rdp_server_init (GrdRdpServer *rdp_server)
|
|
* Run the primitives benchmark here to save time, when initializing a session
|
|
*/
|
|
primitives_get ();
|
|
+
|
|
+ G_OBJECT_CLASS (grd_rdp_server_parent_class)->constructed (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+grd_rdp_server_init (GrdRdpServer *rdp_server)
|
|
+{
|
|
}
|
|
|
|
static void
|
|
@@ -516,6 +526,7 @@ grd_rdp_server_class_init (GrdRdpServerClass *klass)
|
|
object_class->set_property = grd_rdp_server_set_property;
|
|
object_class->get_property = grd_rdp_server_get_property;
|
|
object_class->dispose = grd_rdp_server_dispose;
|
|
+ object_class->constructed = grd_rdp_server_constructed;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_CONTEXT,
|
|
diff --git a/src/grd-settings.c b/src/grd-settings.c
|
|
index f3475010..0720fcc4 100644
|
|
--- a/src/grd-settings.c
|
|
+++ b/src/grd-settings.c
|
|
@@ -87,6 +87,8 @@ typedef struct _GrdSettingsPrivate
|
|
GrdVncAuthMethod auth_method;
|
|
GrdVncEncryption encryption;
|
|
} vnc;
|
|
+
|
|
+ int max_parallel_connections;
|
|
} GrdSettingsPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GrdSettings, grd_settings, G_TYPE_OBJECT)
|
|
@@ -103,6 +105,23 @@ grd_settings_get_runtime_mode (GrdSettings *settings)
|
|
return priv->runtime_mode;
|
|
}
|
|
|
|
+void
|
|
+grd_settings_override_max_parallel_connections (GrdSettings *settings,
|
|
+ int max_parallel_connections)
|
|
+{
|
|
+ GrdSettingsPrivate *priv = grd_settings_get_instance_private (settings);
|
|
+
|
|
+ priv->max_parallel_connections = max_parallel_connections;
|
|
+}
|
|
+
|
|
+int
|
|
+grd_settings_get_max_parallel_connections (GrdSettings *settings)
|
|
+{
|
|
+ GrdSettingsPrivate *priv = grd_settings_get_instance_private (settings);
|
|
+
|
|
+ return priv->max_parallel_connections;
|
|
+}
|
|
+
|
|
void
|
|
grd_settings_override_rdp_port (GrdSettings *settings,
|
|
int port)
|
|
diff --git a/src/grd-settings.h b/src/grd-settings.h
|
|
index 547b1417..5abf8e4e 100644
|
|
--- a/src/grd-settings.h
|
|
+++ b/src/grd-settings.h
|
|
@@ -38,6 +38,11 @@ struct _GrdSettingsClass
|
|
|
|
GrdRuntimeMode grd_settings_get_runtime_mode (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_rdp_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 fc5020f0..705ed416 100644
|
|
--- a/src/grd-vnc-server.c
|
|
+++ b/src/grd-vnc-server.c
|
|
@@ -298,6 +298,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_get_debug_flags () & GRD_DEBUG_VNC)
|
|
rfbLogEnable (1);
|
|
@@ -309,21 +310,20 @@ 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 ();
|
|
+ limits = grd_throttler_limits_new (vnc_server->context);
|
|
/* 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
|
|
|