diff options
-rw-r--r-- | .bzrignore | 2 | ||||
-rw-r--r-- | src/seat-xvnc.c | 2 | ||||
-rw-r--r-- | src/xserver-xvnc.c | 30 | ||||
-rw-r--r-- | src/xserver-xvnc.h | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/scripts/vnc-login.conf | 61 | ||||
-rw-r--r-- | tests/src/Makefile.am | 26 | ||||
-rw-r--r-- | tests/src/X.c | 960 | ||||
-rw-r--r-- | tests/src/Xvnc.c | 286 | ||||
-rw-r--r-- | tests/src/status.c | 9 | ||||
-rw-r--r-- | tests/src/test-runner.c | 23 | ||||
-rw-r--r-- | tests/src/vnc-client.c | 98 | ||||
-rw-r--r-- | tests/src/x-authority.c | 229 | ||||
-rw-r--r-- | tests/src/x-authority.h | 71 | ||||
-rw-r--r-- | tests/src/x-common.c | 160 | ||||
-rw-r--r-- | tests/src/x-common.h | 48 | ||||
-rw-r--r-- | tests/src/x-server.c | 783 | ||||
-rw-r--r-- | tests/src/x-server.h | 190 | ||||
-rw-r--r-- | tests/src/xdmcp-client.c | 456 | ||||
-rw-r--r-- | tests/src/xdmcp-client.h | 81 | ||||
-rwxr-xr-x | tests/test-vnc-login | 2 |
21 files changed, 2683 insertions, 840 deletions
@@ -65,6 +65,8 @@ tests/src/test-qt-greeter tests/src/test-runner tests/src/test-script-hook tests/src/test-session +tests/src/vnc-client tests/src/X +tests/src/Xvnc utils/lightdm-set-defaults utils/dm-tool diff --git a/src/seat-xvnc.c b/src/seat-xvnc.c index d1e16965..5b95aa82 100644 --- a/src/seat-xvnc.c +++ b/src/seat-xvnc.c @@ -37,7 +37,7 @@ seat_xvnc_create_display_server (Seat *seat) XServerXVNC *xserver; xserver = xserver_xvnc_new (); - xserver_xvnc_set_stdin (xserver, g_socket_get_fd (SEAT_XVNC (seat)->priv->connection)); + xserver_xvnc_set_socket (xserver, g_socket_get_fd (SEAT_XVNC (seat)->priv->connection)); return DISPLAY_SERVER (xserver); } diff --git a/src/xserver-xvnc.c b/src/xserver-xvnc.c index 9712f344..6e867b3f 100644 --- a/src/xserver-xvnc.c +++ b/src/xserver-xvnc.c @@ -33,7 +33,7 @@ struct XServerXVNCPrivate GFile *authority_file; /* File descriptor to use for standard input */ - gint stdin_fd; + gint socket_fd; /* Geometry and colour depth */ gint width, height, depth; @@ -60,17 +60,17 @@ xserver_xvnc_new (void) } void -xserver_xvnc_set_stdin (XServerXVNC *server, int fd) +xserver_xvnc_set_socket (XServerXVNC *server, int fd) { g_return_if_fail (server != NULL); - server->priv->stdin_fd = fd; + server->priv->socket_fd = fd; } int -xserver_xvnc_get_stdin (XServerXVNC *server) +xserver_xvnc_get_socket (XServerXVNC *server) { g_return_val_if_fail (server != NULL, 0); - return server->priv->stdin_fd; + return server->priv->socket_fd; } void @@ -123,7 +123,9 @@ static void run_cb (Process *process, XServerXVNC *server) { /* Connect input */ - dup2 (server->priv->stdin_fd, STDIN_FILENO); + dup2 (server->priv->socket_fd, STDIN_FILENO); + dup2 (server->priv->socket_fd, STDOUT_FILENO); + close (server->priv->socket_fd); /* Redirect output to logfile */ if (server->priv->log_file) @@ -135,7 +137,6 @@ run_cb (Process *process, XServerXVNC *server) g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno)); else { - dup2 (fd, STDOUT_FILENO); dup2 (fd, STDERR_FILENO); close (fd); } @@ -250,19 +251,28 @@ xserver_xvnc_start (DisplayServer *display_server) g_free (absolute_command); g_string_append_printf (command, " :%d", xserver_get_display_number (XSERVER (server))); - g_string_append_printf (command, " -auth %s ", path); + g_string_append_printf (command, " -auth %s", path); g_free (path); g_string_append (command, " -inetd -nolisten tcp"); if (server->priv->width > 0 && server->priv->height > 0) - g_string_append_printf (command, " -geometry %dx%d ", server->priv->width, server->priv->height); + g_string_append_printf (command, " -geometry %dx%d", server->priv->width, server->priv->height); if (server->priv->depth > 0) - g_string_append_printf (command, " -depth %d ", server->priv->depth); + g_string_append_printf (command, " -depth %d", server->priv->depth); process_set_command (server->priv->xserver_process, command->str); g_string_free (command, TRUE); g_debug ("Launching Xvnc server"); + /* Variable required for regression tests */ + if (g_getenv ("LIGHTDM_TEST_STATUS_SOCKET")) + { + process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_STATUS_SOCKET", g_getenv ("LIGHTDM_TEST_STATUS_SOCKET")); + process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_CONFIG", g_getenv ("LIGHTDM_TEST_CONFIG")); + process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_HOME_DIR", g_getenv ("LIGHTDM_TEST_HOME_DIR")); + process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH")); + } + result = process_start (server->priv->xserver_process); if (result) diff --git a/src/xserver-xvnc.h b/src/xserver-xvnc.h index 12cc5a59..b7893630 100644 --- a/src/xserver-xvnc.h +++ b/src/xserver-xvnc.h @@ -41,9 +41,9 @@ gboolean xserver_xvnc_check_available (void); XServerXVNC *xserver_xvnc_new (void); -void xserver_xvnc_set_stdin (XServerXVNC *server, int fd); +void xserver_xvnc_set_socket (XServerXVNC *server, int fd); -int xserver_xvnc_get_stdin (XServerXVNC *server); +int xserver_xvnc_get_socket (XServerXVNC *server); void xserver_xvnc_set_geometry (XServerXVNC *server, gint width, gint height); diff --git a/tests/Makefile.am b/tests/Makefile.am index 88514363..a20a3bfb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -46,6 +46,7 @@ TESTS = \ test-switch-to-user \ test-switch-to-user-existing-session \ test-switch-to-user-no-password \ + test-vnc-login \ test-xdmcp-login # test-session-exit-error @@ -123,5 +124,6 @@ EXTRA_DIST = \ scripts/switch-to-user.conf \ scripts/switch-to-user-existing-session.conf \ scripts/switch-to-user-no-password.conf \ + scripts/vnc-login.conf \ scripts/xdmcp-login.conf \ scripts/xserver-fail-start.conf diff --git a/tests/scripts/vnc-login.conf b/tests/scripts/vnc-login.conf new file mode 100644 index 00000000..b401ce48 --- /dev/null +++ b/tests/scripts/vnc-login.conf @@ -0,0 +1,61 @@ +# +# Check that LightDM correctly negotiates VNC and starts the session to the remote server. +# + +[LightDM] +minimum-display-number=50 +start-default-seat=false + +[VNCServer] +enabled=true +port=9999 + +[test-greeter-config] +username=alice +password=password + +#?RUNNER DAEMON-START +#?*WAIT 1 + +# Start a VNC client +#?*START-VNC-CLIENT ARGS="::9999" +#?VNC-CLIENT START +#?VNC-CLIENT CONNECT SERVER=::9999 + +# Xvnc server starts +#?XSERVER :50 START GEOMETRY=1024x768 DEPTH=8 + +# Negotiate with Xvnc +#?VNC-CLIENT CONNECTED VERSION="RFB 003.007" + +#?XSERVER :50 INDICATE-READY + +#?XSERVER :50 VNC-CLIENT-CONNECT VERSION="RFB 003.003" + +# LightDM connects to X server +#?XSERVER :50 ACCEPT-CONNECT + +# Greeter starts and connects to remote X server +#?GREETER START +#?XSERVER :50 ACCEPT-CONNECT +#?GREETER CONNECT-XSERVER :50 +#?GREETER CONNECT-TO-DAEMON +#?GREETER CONNECTED-TO-DAEMON + +# Log in +#?GREETER AUTHENTICATE USERNAME=alice +#?GREETER SHOW-PROMPT TEXT="Password:" +#?GREETER RESPOND TEXT="password" +#?GREETER AUTHENTICATION-COMPLETE USERNAME=alice AUTHENTICATED=TRUE +#?GREETER TERMINATE SIGNAL=15 + +# Session starts +#?SESSION START USER=alice +#?XSERVER :50 ACCEPT-CONNECT +#?SESSION CONNECT-XSERVER + +# Clean up +#?*STOP-DAEMON +#?(SESSION TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15) +#?(SESSION TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15) +#?RUNNER DAEMON-EXIT STATUS=0 diff --git a/tests/src/Makefile.am b/tests/src/Makefile.am index d6a93d75..be8b8ecb 100644 --- a/tests/src/Makefile.am +++ b/tests/src/Makefile.am @@ -1,4 +1,4 @@ -noinst_PROGRAMS = test-runner X test-gobject-greeter test-session test-script-hook guest-account initctl plymouth +noinst_PROGRAMS = test-runner X Xvnc test-gobject-greeter test-session test-script-hook guest-account initctl plymouth vnc-client noinst_SCRIPTS = lightdm-session lib_LTLIBRARIES = libsystem.la @@ -20,7 +20,7 @@ test_runner_LDADD = \ $(GLIB_LIBS) \ $(GIO_LIBS) -X_SOURCES = X.c status.c status.h +X_SOURCES = X.c x-authority.c x-authority.h x-common.c x-common.h x-server.c x-server.h xdmcp-client.c xdmcp-client.h status.c status.h X_CFLAGS = \ $(WARN_CFLAGS) \ $(GOBJECT_CFLAGS) \ @@ -33,6 +33,19 @@ X_LDADD = \ $(GIO_LIBS) \ $(GIO_UNIX_LIBS) +Xvnc_SOURCES = Xvnc.c x-authority.c x-authority.h x-common.c x-common.h x-server.c x-server.h status.c status.h +Xvnc_CFLAGS = \ + $(WARN_CFLAGS) \ + $(GOBJECT_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GIO_CFLAGS) \ + $(GIO_UNIX_CFLAGS) +Xvnc_LDADD = \ + $(GOBJECT_LIBS) \ + $(GLIB_LIBS) \ + $(GIO_LIBS) \ + $(GIO_UNIX_LIBS) + test_gobject_greeter_SOURCES = test-gobject-greeter.c status.c status.h test_gobject_greeter_CFLAGS = \ -I$(top_srcdir)/liblightdm-gobject \ @@ -97,6 +110,15 @@ plymouth_CFLAGS = \ plymouth_LDADD = \ $(GLIB_LIBS) +vnc_client_SOURCES = vnc-client.c status.c status.h +vnc_client_CFLAGS = \ + $(WARN_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GIO_CFLAGS) +vnc_client_LDADD = \ + $(GLIB_LIBS) \ + $(GIO_LIBS) + CLEANFILES = \ test-qt-greeter_moc.cpp diff --git a/tests/src/X.c b/tests/src/X.c index bfb041fa..014b683a 100644 --- a/tests/src/X.c +++ b/tests/src/X.c @@ -1,113 +1,48 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <errno.h> -#include <signal.h> +#include <sys/types.h> #include <unistd.h> +#include <signal.h> #include <sys/stat.h> #include <fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <glib.h> -#include <gio/gio.h> -#include <gio/gunixsocketaddress.h> +#include <errno.h> #include "status.h" +#include "x-server.h" +#include "x-authority.h" +#include "xdmcp-client.h" static GKeyFile *config; -static gchar *socket_path = NULL; +/* Path to lock file */ static gchar *lock_path = NULL; -static gchar *auth_path = NULL; -typedef struct -{ - GIOChannel *parent_channel; - GIOChannel *channel; - guint8 byte_order; - gboolean connected; -} Connection; -static GHashTable *connections; +/* Path to authority database to use */ +static gchar *auth_path = NULL; +/* Display number being served */ static int display_number = 0; -static gboolean listen_unix = TRUE; -static gboolean listen_tcp = TRUE; -static GSocket *unix_socket = NULL; -static GIOChannel *unix_channel = NULL; -static GSocket *tcp_socket = NULL; -static GIOChannel *tcp_channel = NULL; - -static int xdmcp_port = 177; -static gboolean do_xdmcp = FALSE; -static gchar *xdmcp_host = NULL; -static GSocket *xdmcp_socket = NULL; -static guint xdmcp_query_timer = 0; -static gchar *xdmcp_authorization_name = NULL; -static gint xdmcp_authorization_data_length = 0; -static guint8 *xdmcp_authorization_data = NULL; +/* X server */ +static XServer *xserver = NULL; -#define BYTE_ORDER_MSB 'B' -#define BYTE_ORDER_LSB 'l' +/* XDMCP client */ +static XDMCPClient *xdmcp_client = NULL; -#define PROTOCOL_MAJOR_VERSION 11 -#define PROTOCOL_MINOR_VERSION 0 - -#define RELEASE_NUMBER 0 -#define RESOURCE_ID_BASE 0x04e00000 -#define RESOURCE_ID_MASK 0x001fffff -#define MOTION_BUFFER_SIZE 256 -#define MAXIMUM_REQUEST_LENGTH 65535 -#define BITMAP_FORMAT_SCANLINE_UNIT 32 -#define BITMAP_FORMAT_SCANLINE_PAD 32 -#define MIN_KEYCODE 8 -#define MAX_KEYCODE 255 -#define VENDOR "LightDM" - -#define XAUTH_FAMILY_INTERNET 0 -#define XAUTH_FAMILY_DECNET 1 -#define XAUTH_FAMILY_CHAOS 2 -#define XAUTH_FAMILY_SERVER_INTERPRETED 5 -#define XAUTH_FAMILY_INTERNET6 6 -#define XAUTH_FAMILY_LOCALHOST 252 -#define XAUTH_FAMILY_KRB5_PRINCIPAL 253 -#define XAUTH_FAMILY_NETNAME 254 -#define XAUTH_FAMILY_LOCAL 256 -#define XAUTH_FAMILY_WILD 65535 - -enum -{ - Failed = 0, - Success = 1, - Authenticate = 2 -}; - -typedef enum -{ - XDMCP_BroadcastQuery = 1, - XDMCP_Query = 2, - XDMCP_IndirectQuery = 3, - XDMCP_ForwardQuery = 4, - XDMCP_Willing = 5, - XDMCP_Unwilling = 6, - XDMCP_Request = 7, - XDMCP_Accept = 8, - XDMCP_Decline = 9, - XDMCP_Manage = 10, - XDMCP_Refuse = 11, - XDMCP_Failed = 12, - XDMCP_KeepAlive = 13, - XDMCP_Alive = 14 -} XDMCPOpcode; +/* Authorization provided by XDMCP server */ +static guint16 xdmcp_cookie_length = 0; +static guint8 *xdmcp_cookie = NULL; static void cleanup () { if (lock_path) unlink (lock_path); - if (socket_path) - unlink (socket_path); + if (xserver) + g_object_unref (xserver); + if (xdmcp_client) + g_object_unref (xdmcp_client); } static void @@ -117,427 +52,169 @@ quit (int status) exit (status); } -static gsize -pad (gsize length) -{ - if (length % 4 == 0) - return 0; - return 4 - length % 4; -} - static void -read_padding (gsize length, gsize *offset) -{ - *offset += length; -} - -static guint8 -read_card8 (const guint8 *buffer, gsize buffer_length, gsize *offset) -{ - if (*offset >= buffer_length) - return 0; - (*offset)++; - return buffer[*offset - 1]; -} - -static guint16 -read_card16 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset) -{ - guint8 a, b; - - a = read_card8 (buffer, buffer_length, offset); - b = read_card8 (buffer, buffer_length, offset); - if (byte_order == BYTE_ORDER_MSB) - return a << 8 | b; - else - return b << 8 | a; -} - -static guint32 -read_card32 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset) -{ - guint8 a, b, c, d; - - a = read_card8 (buffer, buffer_length, offset); - b = read_card8 (buffer, buffer_length, offset); - c = read_card8 (buffer, buffer_length, offset); - d = read_card8 (buffer, buffer_length, offset); - if (byte_order == BYTE_ORDER_MSB) - return a << 24 | b << 16 | c << 8 | d; - else - return d << 24 | c << 16 | b << 8 | a; -} - -static guint8 * -read_string8 (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset) -{ - guint8 *string; - int i; - - string = g_malloc (string_length + 1); - for (i = 0; i < string_length; i++) - string[i] = read_card8 (buffer, buffer_length, offset); - string[i] = '\0'; - return string; -} - -static gchar * -read_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset) -{ - return (gchar *) read_string8 (buffer, buffer_length, string_length, offset); -} - -static gchar * -read_padded_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset) -{ - guint8 *value; - value = read_string8 (buffer, buffer_length, string_length, offset); - read_padding (pad (string_length), offset); - return (gchar *) value; -} - -static void -write_card8 (guint8 *buffer, gsize buffer_length, guint8 value, gsize *offset) -{ - if (*offset >= buffer_length) - return; - buffer[*offset] = value; - (*offset)++; -} - -static void -write_padding (guint8 *buffer, gsize buffer_length, gsize length, gsize *offset) -{ - gsize i; - for (i = 0; i < length; i++) - write_card8 (buffer, buffer_length, 0, offset); -} - -static void -write_card16 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint16 value, gsize *offset) +indicate_ready () { - if (byte_order == BYTE_ORDER_MSB) - { - write_card8 (buffer, buffer_length, value >> 8, offset); - write_card8 (buffer, buffer_length, value & 0xFF, offset); - } - else + void *handler; + handler = signal (SIGUSR1, SIG_IGN); + if (handler == SIG_IGN) { - write_card8 (buffer, buffer_length, value & 0xFF, offset); - write_card8 (buffer, buffer_length, value >> 8, offset); + notify_status ("XSERVER :%d INDICATE-READY", display_number); + kill (getppid (), SIGUSR1); } + signal (SIGUSR1, handler); } static void -write_card32 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint32 value, gsize *offset) +signal_cb (int signum) { - if (byte_order == BYTE_ORDER_MSB) + if (signum == SIGHUP) { - write_card8 (buffer, buffer_length, value >> 24, offset); - write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset); - write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset); - write_card8 (buffer, buffer_length, value & 0xFF, offset); + notify_status ("XSERVER :%d DISCONNECT-CLIENTS", display_number); + indicate_ready (); } else { - write_card8 (buffer, buffer_length, value & 0xFF, offset); - write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset); - write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset); - write_card8 (buffer, buffer_length, value >> 24, offset); + notify_status ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum); + quit (EXIT_SUCCESS); } } static void -write_string8 (guint8 *buffer, gsize buffer_length, const guint8 *value, gsize value_length, gsize *offset) -{ - gsize i; - for (i = 0; i < value_length; i++) - write_card8 (buffer, buffer_length, value[i], offset); -} - -static gsize -padded_string_length (const gchar *value) -{ - return (strlen (value) + pad (strlen (value))) / 4; -} - -static void -write_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset) +xdmcp_query_cb (XDMCPClient *client) { - write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset); -} + static gboolean notified_query = FALSE; -static void -write_padded_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset) -{ - write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset); - write_padding (buffer, buffer_length, pad (strlen (value)), offset); + if (!notified_query) + { + notify_status ("XSERVER :%d SEND-QUERY", display_number); + notified_query = TRUE; + } } static void -decode_connect (const guint8 *buffer, gsize buffer_length, - guint8 *byte_order, - guint16 *protocol_major_version, guint16 *protocol_minor_version, - gchar **authorization_protocol_name, - guint8 **authorization_protocol_data, guint16 *authorization_protocol_data_length) -{ - gsize offset = 0; - guint16 n; - - *byte_order = read_card8 (buffer, buffer_length, &offset); - read_padding (1, &offset); - *protocol_major_version = read_card16 (buffer, buffer_length, *byte_order, &offset); - *protocol_minor_version = read_card16 (buffer, buffer_length, *byte_order, &offset); - n = read_card16 (buffer, buffer_length, *byte_order, &offset); - *authorization_protocol_data_length = read_card16 (buffer, buffer_length, *byte_order, &offset); - read_padding (2, &offset); - *authorization_protocol_name = read_padded_string (buffer, buffer_length, n, &offset); - *authorization_protocol_data = read_string8 (buffer, buffer_length, *authorization_protocol_data_length, &offset); - read_padding (pad (*authorization_protocol_data_length), &offset); -} - -static gsize -encode_failed (guint8 *buffer, gsize buffer_length, - guint8 byte_order, const gchar *reason) +xdmcp_willing_cb (XDMCPClient *client, XDMCPWilling *message) { - gsize offset = 0; - guint8 additional_data_length; + gchar **authorization_names; + GInetAddress *addresses[2]; - write_card8 (buffer, buffer_length, Failed, &offset); - write_card8 (buffer, buffer_length, strlen (reason), &offset); - write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MAJOR_VERSION, &offset); - write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MINOR_VERSION, &offset); - additional_data_length = padded_string_length (reason); - write_card16 (buffer, buffer_length, byte_order, additional_data_length, &offset); + notify_status ("XSERVER :%d GOT-WILLING AUTHENTICATION-NAME=\"%s\" HOSTNAME=\"%s\" STATUS=\"%s\"", display_number, message->authentication_name, message->hostname, message->status); - /* Additional data */ - write_padded_string (buffer, buffer_length, reason, &offset); + notify_status ("XSERVER :%d SEND-REQUEST DISPLAY-NUMBER=%d AUTHORIZATION-NAME=\"%s\" MFID=\"%s\"", display_number, display_number, "MIT-MAGIC-COOKIE-1", "TEST XSERVER"); - return offset; + authorization_names = g_strsplit ("MIT-MAGIC-COOKIE-1", " ", -1); + addresses[0] = xdmcp_client_get_local_address (client); + addresses[1] = NULL; + xdmcp_client_send_request (client, display_number, + addresses, + "", NULL, 0, + authorization_names, "TEST XSERVER"); + g_strfreev (authorization_names); } -static gsize -encode_accept (guint8 *buffer, gsize buffer_length, - guint8 byte_order) +static void +xdmcp_accept_cb (XDMCPClient *client, XDMCPAccept *message) { - gsize offset = 0; - guint8 additional_data_length; + notify_status ("XSERVER :%d GOT-ACCEPT SESSION-ID=%d AUTHENTICATION-NAME=\"%s\" AUTHORIZATION-NAME=\"%s\"", display_number, message->session_id, message->authentication_name, message->authorization_name); - write_card8 (buffer, buffer_length, Success, &offset); - write_padding (buffer, buffer_length, 1, &offset); - write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MAJOR_VERSION, &offset); - write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MINOR_VERSION, &offset); - additional_data_length = 8 + padded_string_length (VENDOR); - write_card16 (buffer, buffer_length, byte_order, additional_data_length, &offset); + /* Ignore if haven't picked a valid authorization */ + if (strcmp (message->authorization_name, "MIT-MAGIC-COOKIE-1") != 0) + return; - /* Additional data */ - write_card32 (buffer, buffer_length, byte_order, RELEASE_NUMBER, &offset); - write_card32 (buffer, buffer_length, byte_order, RESOURCE_ID_BASE, &offset); - write_card32 (buffer, buffer_length, byte_order, RESOURCE_ID_MASK, &offset); - write_card32 (buffer, buffer_length, byte_order, MOTION_BUFFER_SIZE, &offset); - write_card16 (buffer, buffer_length, byte_order, strlen (VENDOR), &offset); - write_card16 (buffer, buffer_length, byte_order, MAXIMUM_REQUEST_LENGTH, &offset); - write_card8 (buffer, buffer_length, 0, &offset); // number of screens - write_card8 (buffer, buffer_length, 0, &offset); // number of pixmap formats - write_card8 (buffer, buffer_length, 0, &offset); // image-byte-order - write_card8 (buffer, buffer_length, 0, &offset); // bitmap-format-bit-order - write_card8 (buffer, buffer_length, BITMAP_FORMAT_SCANLINE_UNIT, &offset); - write_card8 (buffer, buffer_length, BITMAP_FORMAT_SCANLINE_PAD, &offset); - write_card8 (buffer, buffer_length, MIN_KEYCODE, &offset); - write_card8 (buffer, buffer_length, MAX_KEYCODE, &offset); - write_padding (buffer, buffer_length, 4, &offset); - write_padded_string (buffer, buffer_length, VENDOR, &offset); - // pixmap formats - // screens + g_free (xdmcp_cookie); + xdmcp_cookie_length = message->authorization_data_length; + xdmcp_cookie = g_malloc (message->authorization_data_length); + memcpy (xdmcp_cookie, message->authorization_data, message->authorization_data_length); - return offset; + notify_status ("XSERVER :%d SEND-MANAGE SESSION-ID=%d DISPLAY-NUMBER=%d DISPLAY-CLASS=\"%s\"", display_number, message->session_id, display_number, "DISPLAY CLASS"); + xdmcp_client_send_manage (client, message->session_id, display_number, "DISPLAY CLASS"); } -static gchar * -make_hex_string (const guint8 *buffer, gsize buffer_length) +static void +xdmcp_decline_cb (XDMCPClient *client, XDMCPDecline *message) { - GString *text; - gsize i; - gchar *result; - - text = g_string_new (""); - for (i = 0; i < buffer_length; i++) - { - if (i > 0) - g_string_append (text, " "); - g_string_append_printf (text, "%02X", buffer[i]); - } - - result = text->str; - g_string_free (text, FALSE); - return result; + notify_status ("XSERVER :%d GOT-DECLINE STATUS=\"%s\" AUTHENTICATION-NAME=\"%s\"", display_number, message->status, message->authentication_name); } static void -log_buffer (const gchar *text, const guint8 *buffer, gsize buffer_length) +xdmcp_failed_cb (XDMCPClient *client, XDMCPFailed *message) { - gchar *hex; - - hex = make_hex_string (buffer, buffer_length); - printf ("%s %s\n", text, hex); - g_free (hex); + notify_status ("XSERVER :%d GOT-FAILED SESSION-ID=%d STATUS=\"%s\"", display_number, message->session_id, message->status); } static void -decode_connection_request (Connection *connection, const guint8 *buffer, gssize buffer_length) +x_client_connect_cb (XClient *client, XConnect *message) { - guint8 byte_order; - guint16 protocol_major_version, protocol_minor_version; - gchar *authorization_protocol_name; - guint8 *authorization_protocol_data; - guint16 authorization_protocol_data_length; - gchar *hex; gchar *auth_error = NULL; - guint8 response_buffer[MAXIMUM_REQUEST_LENGTH]; - gsize n_written; - decode_connect (buffer, buffer_length, - &byte_order, - &protocol_major_version, &protocol_minor_version, - &authorization_protocol_name, - &authorization_protocol_data, &authorization_protocol_data_length); - hex = make_hex_string (authorization_protocol_data, authorization_protocol_data_length); - g_debug ("Got connect request using protocol %d.%d and authorization '%s' with data '%s'", protocol_major_version, protocol_minor_version, authorization_protocol_name, hex); - g_free (hex); - - if (connection->parent_channel == tcp_channel) + if (x_client_get_address (client)) notify_status ("XSERVER :%d TCP-ACCEPT-CONNECT", display_number); else notify_status ("XSERVER :%d ACCEPT-CONNECT", display_number); - if (do_xdmcp) + if (xdmcp_client) { - if (strcmp (xdmcp_authorization_name, "") == 0) - { - if (strcmp (authorization_protocol_name, "") != 0) - auth_error = g_strdup ("Authorization provided but none needed"); - } - else if (strcmp (xdmcp_authorization_name, "MIT-MAGIC-COOKIE-1") == 0) + if (!xdmcp_cookie) + auth_error = g_strdup ("Need to authenticate with XDMCP"); + else { gboolean matches = TRUE; - if (authorization_protocol_data_length == xdmcp_authorization_data_length) + if (message->authorization_protocol_data_length == xdmcp_cookie_length) { guint16 i; - for (i = 0; i < xdmcp_authorization_data_length && authorization_protocol_data[i] == xdmcp_authorization_data[i]; i++); - matches = i == xdmcp_authorization_data_length; + for (i = 0; i < xdmcp_cookie_length && message->authorization_protocol_data[i] == xdmcp_cookie[i]; i++); + matches = i == xdmcp_cookie_length; } else matches = FALSE; - if (strcmp (authorization_protocol_name, "MIT-MAGIC-COOKIE-1") != 0) + if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") != 0) auth_error = g_strdup ("Authorization required"); else if (!matches) auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key"); } - else - auth_error = g_strdup_printf ("Unknown authorization: '%s'", authorization_protocol_name); } else if (auth_path) { - gchar *xauth_data; - gsize xauth_length; + XAuthority *authority; + XAuthorityRecord *record = NULL; GError *error = NULL; - if (g_file_get_contents (auth_path, &xauth_data, &xauth_length, &error)) - { - gsize offset = 0; - guint16 family, length, data_length; - gchar *address, *number, *name; - guint8 *data; - - family = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset); - length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset); - address = (gchar *) read_string8 ((guint8 *) xauth_data, xauth_length, length, &offset); - length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset); - number = (gchar *) read_string8 ((guint8 *) xauth_data, xauth_length, length, &offset); - length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset); - name = (gchar *) read_string8 ((guint8 *) xauth_data, xauth_length, length, &offset); - data_length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset); - data = read_string8 ((guint8 *) xauth_data, xauth_length, data_length, &offset); + authority = x_authority_new (); + x_authority_load (authority, auth_path, &error); + if (error) + g_warning ("Error reading auth file: %s", error->message); + g_clear_error (&error); - if (connection->parent_channel == unix_channel && !(family == XAUTH_FAMILY_LOCAL || family == XAUTH_FAMILY_WILD)) - auth_error = g_strdup ("Authorization not valid for Unix connection"); - else if (connection->parent_channel == tcp_channel && !(family == XAUTH_FAMILY_INTERNET || family == XAUTH_FAMILY_WILD)) - auth_error = g_strdup ("Authorization not valid for TCP/IP connection"); - else if (strcmp (authorization_protocol_name, "") == 0) - auth_error = g_strdup ("Authorization required"); - else if (strcmp (authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0) + if (x_client_get_address (client)) + record = x_authority_match_localhost (authority, message->authorization_protocol_name); // FIXME: Should check if remote + else + record = x_authority_match_local (authority, message->authorization_protocol_name); + if (record) + { + if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0) { - gboolean matches = TRUE; - if (authorization_protocol_data_length == data_length) - { - guint16 i; - for (i = 0; i < data_length && authorization_protocol_data[i] == data[i]; i++); - matches = i == data_length; - } - else - matches = FALSE; - if (!matches) - { - gchar *hex1, *hex2; - hex1 = make_hex_string (authorization_protocol_data, authorization_protocol_data_length); - hex2 = make_hex_string (data, data_length); - g_debug ("MIT-MAGIC-COOKIE mismatch, got '%s', expect '%s'", hex1, hex2); - g_free (hex1); - g_free (hex2); + if (!x_authority_record_check_cookie (record, message->authorization_protocol_data, message->authorization_protocol_data_length)) auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key"); - } } else - auth_error = g_strdup_printf ("Unknown authorization: '%s'", authorization_protocol_name); - - g_free (address); - g_free (number); - g_free (name); - g_free (data); + auth_error = g_strdup_printf ("Unknown authorization: '%s'", message->authorization_protocol_name); } else - { - g_warning ("Error reading auth file: %s", error->message); - auth_error = g_strdup ("No authorization database"); - } - g_clear_error (&error); + auth_error = g_strdup ("No authorization record"); } if (auth_error) - { - n_written = encode_failed (response_buffer, MAXIMUM_REQUEST_LENGTH, byte_order, auth_error); - g_debug ("Sending Failed: %s", auth_error); - g_free (auth_error); - } + x_client_send_failed (client, auth_error); else - { - g_debug ("Sending Success"); - connection->connected = TRUE; - connection->byte_order = byte_order; - n_written = encode_accept (response_buffer, MAXIMUM_REQUEST_LENGTH, byte_order); - } - - send (g_io_channel_unix_get_fd (connection->channel), response_buffer, n_written, 0); - log_buffer ("Wrote X", response_buffer, n_written); + x_client_send_success (client); + g_free (auth_error); } static void -decode_intern_atom (Connection *connection, const guint8 *buffer, gssize buffer_length, gsize *offset) +x_client_intern_atom_cb (XClient *client, XInternAtom *message) { - gboolean only_if_exists; - guint16 name_length; - gchar *name; - - only_if_exists = read_card8 (buffer, buffer_length, offset) != 0; - read_padding (2, offset); - name_length = read_card16 (buffer, buffer_length, connection->byte_order, offset); - read_padding (2, offset); - name = read_padded_string (buffer, buffer_length, name_length, offset); - - g_debug ("InternAtom only-if-exits=%s name=%s", only_if_exists ? "True" : "False", name); - - if (strcmp (name, "SIGSEGV") == 0) + if (strcmp (message->name, "SIGSEGV") == 0) { notify_status ("XSERVER :%d CRASH", display_number); cleanup (); @@ -546,347 +223,18 @@ decode_intern_atom (Connection *connection, const guint8 *buffer, gssize buffer_ } static void -decode_request (Connection *connection, const guint8 *buffer, gssize buffer_length) +client_connected_cb (XServer *server, XClient *client) { - int opcode; - gsize offset = 0; - - opcode = read_card8 (buffer, buffer_length, &offset); - switch (opcode) - { - case 16: - decode_intern_atom (connection, buffer, buffer_length, &offset); - break; - default: - g_debug ("Ignoring unknown opcode %d", opcode); - break; - } + g_signal_connect (client, "connect", G_CALLBACK (x_client_connect_cb), NULL); + g_signal_connect (client, "intern-atom", G_CALLBACK (x_client_intern_atom_cb), NULL); } static void -indicate_ready () -{ - void *handler; - handler = signal (SIGUSR1, SIG_IGN); - if (handler == SIG_IGN) - { - notify_status ("XSERVER :%d INDICATE-READY", display_number); - kill (getppid (), SIGUSR1); - } - signal (SIGUSR1, handler); -} - -static gboolean -socket_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data) -{ - Connection *connection; - guint8 buffer[MAXIMUM_REQUEST_LENGTH]; - gssize n_read; - - connection = g_hash_table_lookup (connections, channel); - - n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0); - if (n_read < 0) - g_warning ("Error reading from socket: %s", strerror (errno)); - else if (n_read == 0) - { - if (connection->connected) - { - g_debug ("Client disconnected"); - g_hash_table_remove (connections, connection->channel); - if (g_hash_table_size (connections) == 0) - indicate_ready (); - } - else - g_debug ("EOF before connection made"); - return FALSE; - } - else - { - log_buffer ("Read X", buffer, n_read); - - if (connection->connected) - decode_request (connection, buffer, n_read); - else - decode_connection_request (connection, buffer, n_read); - } - - return TRUE; -} - -static gboolean -socket_connect_cb (GIOChannel *channel, GIOCondition condition, gpointer data) -{ - int s, data_socket; - - g_debug ("Got connection"); - - s = g_io_channel_unix_get_fd (channel); - data_socket = accept (s, NULL, NULL); - if (data_socket < 0) - g_warning ("Error accepting connection: %s", strerror (errno)); - else if (do_xdmcp && !xdmcp_authorization_name) - { - /* Decline if haven't connected yet */ - close (data_socket); - } - else - { - Connection *connection; - - connection = g_malloc0 (sizeof (Connection)); - connection->parent_channel = g_io_channel_ref (channel); - connection->channel = g_io_channel_unix_new (data_socket); - g_hash_table_insert (connections, connection->channel, connection); - g_io_add_watch (connection->channel, G_IO_IN, socket_data_cb, NULL); - } - - return TRUE; -} - -static void -signal_cb (int signum) -{ - if (signum == SIGHUP) - { - notify_status ("XSERVER :%d DISCONNECT-CLIENTS", display_number); +client_disconnected_cb (XServer *server, XClient *client) +{ + g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); + if (x_server_get_n_clients (server) == 0) indicate_ready (); - } - else - { - notify_status ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum); - quit (EXIT_SUCCESS); - } -} - -static void -xdmcp_write (const guint8 *buffer, gssize buffer_length) -{ - gssize n_written; - GError *error = NULL; - - n_written = g_socket_send (xdmcp_socket, (const gchar *) buffer, buffer_length, NULL, &error); - if (n_written < 0) - g_warning ("Failed to send XDMCP request: %s", error->message); - else if (n_written != buffer_length) - g_warning ("Partial write for XDMCP request, wrote %zi, expected %zi", n_written, buffer_length); - g_clear_error (&error); - - log_buffer ("Wrote XDMCP", buffer, buffer_length); -} - -static void -decode_willing (const guint8 *buffer, gssize buffer_length) -{ - gsize offset = 0; - guint16 length; - gchar *authentication_name, *hostname, *status; - GSocketAddress *local_address; - const guint8 *native_address; - gssize native_address_length; - guint8 response[MAXIMUM_REQUEST_LENGTH]; - - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - authentication_name = read_string (buffer, buffer_length, length, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - hostname = read_string (buffer, buffer_length, length, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - status = read_string (buffer, buffer_length, length, &offset); - - if (xdmcp_query_timer == 0) - { - g_debug ("Ignoring XDMCP unrequested/duplicate Willing"); - return; - } - - notify_status ("XSERVER :%d GOT-WILLING AUTHENTICATION-NAME=\"%s\" HOSTNAME=\"%s\" STATUS=\"%s\"", display_number, authentication_name, hostname, status); - - /* Stop sending queries */ - g_source_remove (xdmcp_query_timer); - xdmcp_query_timer = 0; - - local_address = g_socket_get_local_address (xdmcp_socket, NULL); - GInetAddress *inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (local_address)); - native_address_length = g_inet_address_get_native_size (inet_address); - native_address = g_inet_address_to_bytes (inet_address); - - offset = 0; - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); /* version = 1 */ - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, XDMCP_Request, &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 17 + native_address_length + strlen ("") + strlen ("MIT-MAGIC-COOKIE-1") + strlen ("TEST XSERVER"), &offset); - - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, display_number, &offset); - write_card8 (response, MAXIMUM_REQUEST_LENGTH, 1, &offset); /* 1 address */ - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 0, &offset); /* FamilyInternet */ - write_card8 (response, MAXIMUM_REQUEST_LENGTH, 1, &offset); /* 1 address */ - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, native_address_length, &offset); - write_string8 (response, MAXIMUM_REQUEST_LENGTH, native_address, native_address_length, &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen (""), &offset); - write_string (response, MAXIMUM_REQUEST_LENGTH, "", &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 0, &offset); /* No authentication data */ - write_card8 (response, MAXIMUM_REQUEST_LENGTH, 1, &offset); /* 1 authorization */ - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen ("MIT-MAGIC-COOKIE-1"), &offset); - write_string (response, MAXIMUM_REQUEST_LENGTH, "MIT-MAGIC-COOKIE-1", &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen ("TEST XSERVER"), &offset); - write_string (response, MAXIMUM_REQUEST_LENGTH, "TEST XSERVER", &offset); - - notify_status ("XSERVER :%d SEND-REQUEST DISPLAY-NUMBER=%d AUTHORIZATION-NAME=\"%s\" MFID=\"%s\"", display_number, display_number, "MIT-MAGIC-COOKIE-1", "TEST XSERVER"); - - xdmcp_write (response, offset); -} - -static void -decode_accept (const guint8 *buffer, gssize buffer_length) -{ - gsize offset = 0; - guint16 length; - guint32 session_id; - gchar *authentication_name; - guint8 response[MAXIMUM_REQUEST_LENGTH]; - - session_id = read_card32 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - authentication_name = read_string (buffer, buffer_length, length, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - read_string8 (buffer, buffer_length, length, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - xdmcp_authorization_name = read_string (buffer, buffer_length, length, &offset); - xdmcp_authorization_data_length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - xdmcp_authorization_data = read_string8 (buffer, buffer_length, length, &offset); - - notify_status ("XSERVER :%d GOT-ACCEPT SESSION-ID=%d AUTHENTICATION-NAME=\"%s\" AUTHORIZATION-NAME=\"%s\"", display_number, session_id, authentication_name, xdmcp_authorization_name); - - offset = 0; - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); /* version = 1 */ - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, XDMCP_Manage, &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 8 + strlen ("DISPLAY CLASS"), &offset); - - write_card32 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, session_id, &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, display_number, &offset); - write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen ("DISPLAY CLASS"), &offset); - write_string (response, MAXIMUM_REQUEST_LENGTH, "DISPLAY CLASS", &offset); - - notify_status ("XSERVER :%d SEND-MANAGE SESSION-ID=%d DISPLAY-NUMBER=%d DISPLAY-CLASS=\"%s\"", display_number, session_id, display_number, "DISPLAY CLASS"); - - xdmcp_write (response, offset); -} - -static void -decode_decline (const guint8 *buffer, gssize buffer_length) -{ - gsize offset = 0; - guint16 length; - gchar *authentication_name, *status; - - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - status = read_string (buffer, buffer_length, length, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - authentication_name = read_string (buffer, buffer_length, length, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - read_string8 (buffer, buffer_length, length, &offset); - - notify_status ("XSERVER :%d GOT-DECLINE STATUS=\"%s\" AUTHENTICATION-NAME=\"%s\"", display_number, status, authentication_name); -} - -static void -decode_failed (const guint8 *buffer, gssize buffer_length) -{ - gsize offset = 0; - guint16 length; - guint32 session_id; - gchar *status; - - session_id = read_card32 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset); - status = read_string (buffer, buffer_length, length, &offset); - - notify_status ("XSERVER :%d GOT-FAILED SESSION-ID=%d STATUS=\"%s\"", display_number, session_id, status); -} - -static gboolean -xdmcp_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data) -{ - guint8 buffer[MAXIMUM_REQUEST_LENGTH]; - gssize n_read; - - n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0); - if (n_read < 0) - g_warning ("Error reading from XDMCP socket: %s", strerror (errno)); - else if (n_read == 0) - { - g_debug ("EOF"); - return FALSE; - } - else - { - gsize offset = 0; - guint16 version, opcode, length; - - log_buffer ("Read XDMCP", buffer, n_read); - - version = read_card16 (buffer, n_read, BYTE_ORDER_MSB, &offset); - opcode = read_card16 (buffer, n_read, BYTE_ORDER_MSB, &offset); - length = read_card16 (buffer, n_read, BYTE_ORDER_MSB, &offset); - - if (version != 1) - { - g_debug ("Ignoring XDMCP version %d message", version); - return TRUE; - } - if (6 + length > n_read) - { - g_debug ("Ignoring XDMCP message of length %zi with invalid length field %d", n_read, length); - return TRUE; - } - switch (opcode) - { - case XDMCP_Willing: - decode_willing (buffer + offset, n_read - offset); - break; - - case XDMCP_Accept: - decode_accept (buffer + offset, n_read - offset); - break; - - case XDMCP_Decline: - decode_decline (buffer + offset, n_read - offset); - break; - - case XDMCP_Failed: - decode_failed (buffer + offset, n_read - offset); - break; - - default: - g_debug ("Ignoring unknown XDMCP opcode %d", opcode); - break; - } - } - - return TRUE; -} - -static gboolean -xdmcp_query_cb (gpointer data) -{ - guint8 buffer[MAXIMUM_REQUEST_LENGTH]; - gsize offset = 0; - static gboolean notified_query = FALSE; - - g_debug ("Send XDMCP Query"); - if (!notified_query) - { - notify_status ("XSERVER :%d SEND-QUERY", display_number); - notified_query = TRUE; - } - - write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); /* version = 1 */ - write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, XDMCP_Query, &offset); - write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); - write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &offset); - - xdmcp_write (buffer, offset); - - return TRUE; } int @@ -895,16 +243,20 @@ main (int argc, char **argv) int i; char *pid_string, *return_lock; GMainLoop *loop; + gboolean listen_tcp = TRUE; + gboolean listen_unix = TRUE; + gboolean do_xdmcp = FALSE; + guint xdmcp_port = 0; + gchar *xdmcp_host = NULL; int lock_file; FILE *f; - GError *error = NULL; signal (SIGINT, signal_cb); signal (SIGTERM, signal_cb); signal (SIGHUP, signal_cb); g_type_init (); - + for (i = 1; i < argc; i++) { char *arg = argv[i]; @@ -916,7 +268,6 @@ main (int argc, char **argv) else if (strcmp (arg, "-auth") == 0) { auth_path = argv[i+1]; - g_debug ("Loading authorization from %s", auth_path); i++; } else if (strcmp (arg, "-nolisten") == 0) @@ -964,7 +315,7 @@ main (int argc, char **argv) g_printerr ("Unrecognized option: %s\n" "Use: %s [:<display>] [option]\n" "-auth file Select authorization file\n" - "-nolisten string Don't listen on protocol\n" + "-nolisten protocol Don't listen on protocol\n" "-background [none] Create root window with no background\n" "-nr (Ubuntu-specific) Synonym for -background none\n" "-query host-name Contact named host for XDMCP\n" @@ -976,6 +327,12 @@ main (int argc, char **argv) } } + xserver = x_server_new (display_number); + g_signal_connect (xserver, "client-connected", G_CALLBACK (client_connected_cb), NULL); + g_signal_connect (xserver, "client-disconnected", G_CALLBACK (client_disconnected_cb), NULL); + x_server_set_listen_unix (xserver, listen_unix); + x_server_set_listen_tcp (xserver, listen_tcp); + notify_status ("XSERVER :%d START", display_number); config = g_key_file_new (); @@ -1021,73 +378,30 @@ main (int argc, char **argv) } g_free (pid_string); - connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) g_io_channel_unref, g_free); - - if (listen_unix) - { - socket_path = g_strdup_printf ("/tmp/.X11-unix/X%d", display_number); - g_debug ("Opening Unix socket %s", socket_path); - - unix_socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); - if (!unix_socket || - !g_socket_bind (unix_socket, g_unix_socket_address_new (socket_path), TRUE, &error) || - !g_socket_listen (unix_socket, &error)) - { - g_warning ("Error creating Unix X socket: %s", error->message); - quit (EXIT_FAILURE); - } - unix_channel = g_io_channel_unix_new (g_socket_get_fd (unix_socket)); - g_io_add_watch (unix_channel, G_IO_IN, socket_connect_cb, NULL); - } - - if (listen_tcp) - { - gint port = 6000 + display_number; - - g_debug ("Opening TCP/IP socket on port %d", port); - - tcp_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error); - if (!tcp_socket || - !g_socket_bind (tcp_socket, g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), port), TRUE, &error) || - !g_socket_listen (tcp_socket, &error)) - { - g_warning ("Error creating TCP/IP X socket: %s", error->message); - quit (EXIT_FAILURE); - } - tcp_channel = g_io_channel_unix_new (g_socket_get_fd (tcp_socket)); - g_io_add_watch (tcp_channel, G_IO_IN, socket_connect_cb, NULL); - } + if (!x_server_start (xserver)) + quit (EXIT_FAILURE); /* Enable XDMCP */ if (do_xdmcp) { - GSocketConnectable *address; - GSocketAddress *socket_address; - - xdmcp_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error); - - address = g_network_address_new (xdmcp_host, xdmcp_port); - socket_address = g_socket_address_enumerator_next (g_socket_connectable_enumerate (address), NULL, NULL); - - if (!xdmcp_socket || - !g_socket_connect (xdmcp_socket, socket_address, NULL, NULL) || - !g_io_add_watch (g_io_channel_unix_new (g_socket_get_fd (xdmcp_socket)), G_IO_IN, xdmcp_data_cb, &error)) - { - g_warning ("Error creating XDMCP socket: %s", error->message); + xdmcp_client = xdmcp_client_new (); + if (xdmcp_host > 0) + xdmcp_client_set_hostname (xdmcp_client, xdmcp_host); + if (xdmcp_port > 0) + xdmcp_client_set_port (xdmcp_client, xdmcp_port); + g_signal_connect (xdmcp_client, "query", G_CALLBACK (xdmcp_query_cb), NULL); + g_signal_connect (xdmcp_client, "willing", G_CALLBACK (xdmcp_willing_cb), NULL); + g_signal_connect (xdmcp_client, "accept", G_CALLBACK (xdmcp_accept_cb), NULL); + g_signal_connect (xdmcp_client, "decline", G_CALLBACK (xdmcp_decline_cb), NULL); + g_signal_connect (xdmcp_client, "failed", G_CALLBACK (xdmcp_failed_cb), NULL); + + if (!xdmcp_client_start (xdmcp_client)) quit (EXIT_FAILURE); - } } /* Indicate ready if parent process has requested it */ indicate_ready (); - /* Send first query */ - if (do_xdmcp) - { - xdmcp_query_timer = g_timeout_add (2000, xdmcp_query_cb, NULL); - xdmcp_query_cb (NULL); - } - g_main_loop_run (loop); return EXIT_SUCCESS; diff --git a/tests/src/Xvnc.c b/tests/src/Xvnc.c new file mode 100644 index 00000000..2098b52a --- /dev/null +++ b/tests/src/Xvnc.c @@ -0,0 +1,286 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <gio/gio.h> + +#include "status.h" +#include "x-server.h" +#include "x-authority.h" + +static GKeyFile *config; + +/* Path to lock file */ +static gchar *lock_path = NULL; + +/* Path to authority database to use */ +static gchar *auth_path = NULL; + +/* Display number being served */ +static int display_number = 0; + +/* X server */ +static XServer *xserver = NULL; + +static void +indicate_ready () +{ + void *handler; + handler = signal (SIGUSR1, SIG_IGN); + if (handler == SIG_IGN) + { + notify_status ("XSERVER :%d INDICATE-READY", display_number); + kill (getppid (), SIGUSR1); + } + signal (SIGUSR1, handler); +} + +static void +cleanup () +{ + if (lock_path) + unlink (lock_path); + if (xserver) + g_object_unref (xserver); +} + +static void +quit (int status) +{ + cleanup (); + exit (status); +} + +static void +signal_cb (int signum) +{ + if (signum == SIGHUP) + { + notify_status ("XSERVER :%d DISCONNECT-CLIENTS", display_number); + indicate_ready (); + } + else + { + notify_status ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum); + quit (EXIT_SUCCESS); + } +} + +static void +x_client_connect_cb (XClient *client, XConnect *message) +{ + gchar *auth_error = NULL; + + if (x_client_get_address (client)) + notify_status ("XSERVER :%d TCP-ACCEPT-CONNECT", display_number); + else + notify_status ("XSERVER :%d ACCEPT-CONNECT", display_number); + + if (auth_path) + { + XAuthority *authority; + XAuthorityRecord *record = NULL; + GError *error = NULL; + + authority = x_authority_new (); + x_authority_load (authority, auth_path, &error); + if (error) + g_warning ("Error reading auth file: %s", error->message); + g_clear_error (&error); + + if (x_client_get_address (client)) + record = x_authority_match_localhost (authority, message->authorization_protocol_name); // FIXME: Should check if remote + else + record = x_authority_match_local (authority, message->authorization_protocol_name); + if (record) + { + if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0) + { + if (!x_authority_record_check_cookie (record, message->authorization_protocol_data, message->authorization_protocol_data_length)) + auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key"); + } + else + auth_error = g_strdup_printf ("Unknown authorization: '%s'", message->authorization_protocol_name); + } + else + auth_error = g_strdup ("No authorization record"); + } + + if (auth_error) + x_client_send_failed (client, auth_error); + else + x_client_send_success (client); + g_free (auth_error); +} + +static void +client_connected_cb (XServer *server, XClient *client) +{ + g_signal_connect (client, "connect", G_CALLBACK (x_client_connect_cb), NULL); +} + +static void +client_disconnected_cb (XServer *server, XClient *client) +{ + g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); + if (x_server_get_n_clients (server) == 0) + indicate_ready (); +} + +static gboolean +vnc_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data) +{ + gchar buffer[1024]; + gsize n_read; + GIOStatus status; + GError *error = NULL; + + status = g_io_channel_read_chars (channel, buffer, 1023, &n_read, &error); + if (error) + g_warning ("Error reading from VNC client: %s", error->message); + g_clear_error (&error); + + if (status == G_IO_STATUS_NORMAL) + { + buffer[n_read] = '\0'; + if (g_str_has_suffix (buffer, "\n")) + buffer[n_read-1] = '\0'; + notify_status ("XSERVER :%d VNC-CLIENT-CONNECT VERSION=\"%s\"", display_number, buffer); + } + + return TRUE; +} + +int +main (int argc, char **argv) +{ + GMainLoop *loop; + char *pid_string; + gboolean listen_tcp = TRUE; + gboolean listen_unix = TRUE; + gboolean use_inetd = FALSE; + gchar *geometry = g_strdup ("640x480"); + gint depth = 8; + int lock_file; + int i; + + signal (SIGINT, signal_cb); + signal (SIGTERM, signal_cb); + signal (SIGHUP, signal_cb); + + g_type_init (); + + for (i = 1; i < argc; i++) + { + char *arg = argv[i]; + + if (arg[0] == ':') + { + display_number = atoi (arg + 1); + } + else if (strcmp (arg, "-auth") == 0) + { + auth_path = argv[i+1]; + i++; + } + else if (strcmp (arg, "-nolisten") == 0) + { + char *protocol = argv[i+1]; + i++; + if (strcmp (protocol, "tcp") == 0) + listen_tcp = FALSE; + else if (strcmp (protocol, "unix") == 0) + listen_unix = FALSE; + } + else if (strcmp (arg, "-geometry") == 0) + { + g_free (geometry); + geometry = g_strdup (argv[i+1]); + i++; + } + else if (strcmp (arg, "-depth") == 0) + { + depth = atoi (argv[i+1]); + i++; + } + else if (strcmp (arg, "-inetd") == 0) + { + use_inetd = TRUE; + } + else + { + g_printerr ("Unrecognized option: %s\n" + "Use: %s [:<display>] [option]\n" + "-auth file Select authorization file\n" + "-nolisten protocol Don't listen on protocol\n" + "-geometry WxH Set framebuffer width & height\n" + "-depth D Set framebuffer depth\n" + "-inetd Xvnc is launched by inetd\n", + arg, argv[0]); + return EXIT_FAILURE; + } + } + + xserver = x_server_new (display_number); + g_signal_connect (xserver, "client-connected", G_CALLBACK (client_connected_cb), NULL); + g_signal_connect (xserver, "client-disconnected", G_CALLBACK (client_disconnected_cb), NULL); + x_server_set_listen_unix (xserver, listen_unix); + x_server_set_listen_tcp (xserver, listen_tcp); + + notify_status ("XSERVER :%d START GEOMETRY=%s DEPTH=%d", display_number, geometry, depth); + + config = g_key_file_new (); + if (g_getenv ("LIGHTDM_TEST_CONFIG")) + g_key_file_load_from_file (config, g_getenv ("LIGHTDM_TEST_CONFIG"), G_KEY_FILE_NONE, NULL); + + if (use_inetd) + { + /* Send server protocol version to client */ + g_print ("RFB 003.007\n"); + + if (!g_io_add_watch (g_io_channel_unix_new (STDIN_FILENO), G_IO_IN, vnc_data_cb, NULL)) + return EXIT_FAILURE; + } + else + { + g_printerr ("Only supported in -inetd mode\n"); + return EXIT_FAILURE; + } + + loop = g_main_loop_new (NULL, FALSE); + + lock_path = g_strdup_printf ("/tmp/.X%d-lock", display_number); + lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444); + if (lock_file < 0) + { + fprintf (stderr, + "Fatal server error:\n" + "Server is already active for display %d\n" + " If this server is no longer running, remove %s\n" + " and start again.\n", display_number, lock_path); + g_free (lock_path); + lock_path = NULL; + quit (EXIT_FAILURE); + } + pid_string = g_strdup_printf ("%10ld", (long) getpid ()); + if (write (lock_file, pid_string, strlen (pid_string)) < 0) + { + g_warning ("Error writing PID file: %s", strerror (errno)); + quit (EXIT_FAILURE); + } + g_free (pid_string); + + if (!x_server_start (xserver)) + quit (EXIT_FAILURE); + + /* Indicate ready if parent process has requested it */ + indicate_ready (); + + g_main_loop_run (loop); + + return EXIT_SUCCESS; +} diff --git a/tests/src/status.c b/tests/src/status.c index 0beee6e6..2822a605 100644 --- a/tests/src/status.c +++ b/tests/src/status.c @@ -36,7 +36,14 @@ notify_status (const char *format, ...) socket_name = getenv ("LIGHTDM_TEST_STATUS_SOCKET"); if (!socket_name) { - fprintf (stderr, "LIGHTDM_TEST_STATUS_SOCKET not defined\n"); + static int warned = 0; + + if (!warned) + { + fprintf (stderr, "LIGHTDM_TEST_STATUS_SOCKET not defined\n"); + warned = 1; + } + fprintf (stderr, "%s", status); fprintf (stderr, "\n"); return; diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c index c6325f43..a917dc66 100644 --- a/tests/src/test-runner.c +++ b/tests/src/test-runner.c @@ -277,6 +277,7 @@ run_commands () expect_exit = TRUE; stop_daemon (); } + // FIXME: Make generic RUN-COMMAND else if (strcmp (name, "START-XSERVER") == 0) { gchar *xserver_args, *command_line; @@ -289,7 +290,6 @@ run_commands () xserver_args = ""; command_line = g_strdup_printf ("%s/tests/src/X %s", BUILDDIR, xserver_args); - g_debug ("Run %s", command_line); if (!g_shell_parse_argv (command_line, NULL, &argv, &error) || !g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, &error)) { @@ -299,6 +299,27 @@ run_commands () } children = g_list_append (children, GINT_TO_POINTER (pid)); } + else if (strcmp (name, "START-VNC-CLIENT") == 0) + { + gchar *vnc_client_args, *command_line; + gchar **argv; + GPid pid; + GError *error = NULL; + + vnc_client_args = g_hash_table_lookup (params, "ARGS"); + if (!vnc_client_args) + vnc_client_args = ""; + command_line = g_strdup_printf ("%s/tests/src/vnc-client %s", BUILDDIR, vnc_client_args); + + if (!g_shell_parse_argv (command_line, NULL, &argv, &error) || + !g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, &error)) + { + g_printerr ("Error starting VNC client: %s", error->message); + quit (EXIT_FAILURE); + return; + } + children = g_list_append (children, GINT_TO_POINTER (pid)); + } else { g_printerr ("Unknown command '%s'\n", name); diff --git a/tests/src/vnc-client.c b/tests/src/vnc-client.c new file mode 100644 index 00000000..fc1ec74c --- /dev/null +++ b/tests/src/vnc-client.c @@ -0,0 +1,98 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <glib.h> +#include <gio/gio.h> + +#include "status.h" + +static GKeyFile *config; + +int +main (int argc, char **argv) +{ + gchar *server_address; + gchar *hostname, *c; + gint port; + GError *error = NULL; + GSocket *socket; + GSocketConnectable *address; + GSocketAddress *socket_address; + gboolean result; + gchar buffer[1024]; + gssize n_read, n_sent; + + g_type_init (); + + notify_status ("VNC-CLIENT START"); + + config = g_key_file_new (); + if (g_getenv ("LIGHTDM_TEST_CONFIG")) + g_key_file_load_from_file (config, g_getenv ("LIGHTDM_TEST_CONFIG"), G_KEY_FILE_NONE, NULL); + + if (argc > 1) + server_address = g_strdup (argv[1]); + else + server_address = g_strdup (":0"); + + notify_status ("VNC-CLIENT CONNECT SERVER=%s", server_address); + + socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error); + if (error) + g_warning ("Unable to make VNC socket: %s", error->message); + g_clear_error (&error); + if (!socket) + return EXIT_FAILURE; + + hostname = g_strdup (server_address); + c = strchr (hostname, ':'); + if (c) + { + *c = '\0'; + gchar *port_string = c + 1; + if (g_str_has_prefix (port_string, ":")) + port = atoi (port_string + 1); + else + port = 5900 + atoi (port_string); + } + else + port = 5900; + if (strcmp (hostname, "") == 0) + { + g_free (hostname); + hostname = g_strdup ("localhost"); + } + + address = g_network_address_new (hostname, port); + socket_address = g_socket_address_enumerator_next (g_socket_connectable_enumerate (address), NULL, NULL); + + result = g_socket_connect (socket, socket_address, NULL, &error); + if (error) + g_warning ("Unable to connect VNC socket: %s", error->message); + g_clear_error (&error); + if (!result) + return EXIT_FAILURE; + + n_read = g_socket_receive (socket, buffer, 1023, NULL, &error); + if (error) + g_warning ("Unable to receive on VNC socket: %s", error->message); + g_clear_error (&error); + if (n_read < 0) + return EXIT_FAILURE; + + buffer[n_read] = '\0'; + if (g_str_has_suffix (buffer, "\n")) + buffer[n_read-1] = '\0'; + notify_status ("VNC-CLIENT CONNECTED VERSION=\"%s\"", buffer); + + snprintf (buffer, 1024, "RFB 003.003\n"); + n_sent = g_socket_send (socket, buffer, strlen (buffer), NULL, &error); + if (error) + g_warning ("Unable to send on VNC socket: %s", error->message); + g_clear_error (&error); + if (n_sent != strlen (buffer)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/tests/src/x-authority.c b/tests/src/x-authority.c new file mode 100644 index 00000000..5f692fe0 --- /dev/null +++ b/tests/src/x-authority.c @@ -0,0 +1,229 @@ +#include <string.h> + +#include "x-authority.h" +#include "x-common.h" + +struct XAuthorityPrivate +{ + GList *records; +}; + +struct XAuthorityRecordPrivate +{ + guint16 family; + guint16 address_length; + guint8 *address; + gchar *number; + gchar *authorization_name; + guint16 authorization_data_length; + guint8 *authorization_data; +}; + +G_DEFINE_TYPE (XAuthority, x_authority, G_TYPE_OBJECT); +G_DEFINE_TYPE (XAuthorityRecord, x_authority_record, G_TYPE_OBJECT); + +XAuthority * +x_authority_new (void) +{ + return g_object_new (x_authority_get_type (), NULL); +} + +gboolean +x_authority_load (XAuthority *authority, const gchar *filename, GError **error) +{ + guint8 *xauth_data; + gsize xauth_length; + gsize offset = 0; + + if (!g_file_get_contents (filename, (gchar **) &xauth_data, &xauth_length, error)) + return FALSE; + + while (offset < xauth_length) + { + XAuthorityRecord *record; + guint16 length; + + record = g_object_new (x_authority_record_get_type (), NULL); + record->priv->family = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset); + record->priv->address_length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset); + record->priv->address = read_string8 (xauth_data, xauth_length, record->priv->address_length, &offset); + length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset); + record->priv->number = (gchar *) read_string8 (xauth_data, xauth_length, length, &offset); + length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset); + record->priv->authorization_name = (gchar *) read_string8 (xauth_data, xauth_length, length, &offset); + record->priv->authorization_data_length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset); + record->priv->authorization_data = read_string8 (xauth_data, xauth_length, record->priv->authorization_data_length, &offset); + + authority->priv->records = g_list_append (authority->priv->records, record); + } + + return TRUE; +} + +XAuthorityRecord * +x_authority_match_local (XAuthority *authority, const gchar *authorization_name) +{ + GList *link; + + for (link = authority->priv->records; link; link = link->next) + { + XAuthorityRecord *record = link->data; + + if (strcmp (record->priv->authorization_name, authorization_name) != 0) + continue; + + if (record->priv->family == XAUTH_FAMILY_WILD || record->priv->family == XAUTH_FAMILY_LOCAL) + return record; + } + + return NULL; +} + +XAuthorityRecord * +x_authority_match_localhost (XAuthority *authority, const gchar *authorization_name) +{ + GList *link; + + for (link = authority->priv->records; link; link = link->next) + { + XAuthorityRecord *record = link->data; + + if (strcmp (record->priv->authorization_name, authorization_name) != 0) + continue; + + if (record->priv->family == XAUTH_FAMILY_WILD || record->priv->family == XAUTH_FAMILY_LOCALHOST) + return record; + } + + return NULL; +} + +XAuthorityRecord * +x_authority_match_inet (XAuthority *authority, GInetAddress *address, const gchar *authorization_name) +{ + GList *link; + guint16 family; + gssize address_data_length; + const guint8 *address_data; + + switch (g_inet_address_get_family (address)) + { + case G_SOCKET_FAMILY_IPV4: + family = XAUTH_FAMILY_INTERNET; + break; + case G_SOCKET_FAMILY_IPV6: + family = XAUTH_FAMILY_INTERNET6; + break; + default: + return NULL; + } + + address_data_length = g_inet_address_get_native_size (address); + address_data = g_inet_address_to_bytes (address); + for (link = authority->priv->records; link; link = link->next) + { + XAuthorityRecord *record = link->data; + int i; + gboolean matches = TRUE; + + if (strcmp (record->priv->authorization_name, authorization_name) != 0) + continue; + + if (record->priv->family == XAUTH_FAMILY_WILD) + return record; + + if (record->priv->family != family) + continue; + + if (record->priv->address_length != address_data_length) + continue; + + for (i = 0; i < address_data_length; i++) + { + if (address_data[i] != record->priv->address[i]) + { + matches = FALSE; + break; + } + } + if (matches) + return record; + } + + return NULL; +} + +static void +x_authority_init (XAuthority *authority) +{ + authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority, x_authority_get_type (), XAuthorityPrivate); +} + +static void +x_authority_finalize (GObject *object) +{ + XAuthority *authority = (XAuthority *) object; + g_list_free_full (authority->priv->records, g_object_unref); +} + +static void +x_authority_class_init (XAuthorityClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = x_authority_finalize; + g_type_class_add_private (klass, sizeof (XAuthorityPrivate)); +} + +guint16 +x_authority_record_get_authorization_data_length (XAuthorityRecord *record) +{ + return record->priv->authorization_data_length; +} + +const guint8 * +x_authority_record_get_authorization_data (XAuthorityRecord *record) +{ + return record->priv->authorization_data; +} + +gboolean +x_authority_record_check_cookie (XAuthorityRecord *record, const guint8 *cookie_data, guint16 cookie_data_length) +{ + guint16 i; + + if (strcmp (record->priv->authorization_name, "MIT-MAGIC-COOKIE-1") != 0) + return FALSE; + + if (cookie_data_length != record->priv->authorization_data_length) + return FALSE; + + for (i = 0; i < cookie_data_length; i++) + if (cookie_data[i] != record->priv->authorization_data[i]) + return FALSE; + + return TRUE; +} + +static void +x_authority_record_init (XAuthorityRecord *record) +{ + record->priv = G_TYPE_INSTANCE_GET_PRIVATE (record, x_authority_record_get_type (), XAuthorityRecordPrivate); +} + +static void +x_authority_record_finalize (GObject *object) +{ + XAuthorityRecord *record = (XAuthorityRecord *) object; + g_free (record->priv->address); + g_free (record->priv->number); + g_free (record->priv->authorization_name); + g_free (record->priv->authorization_data); +} + +static void +x_authority_record_class_init (XAuthorityRecordClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = x_authority_record_finalize; + g_type_class_add_private (klass, sizeof (XAuthorityRecordPrivate)); +} diff --git a/tests/src/x-authority.h b/tests/src/x-authority.h new file mode 100644 index 00000000..56497fb3 --- /dev/null +++ b/tests/src/x-authority.h @@ -0,0 +1,71 @@ +#ifndef _X_AUTHORITY_H_ +#define _X_AUTHORITY_H_ + +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +enum +{ + XAUTH_FAMILY_INTERNET = 0, + XAUTH_FAMILY_DECNET = 1, + XAUTH_FAMILY_CHAOS = 2, + XAUTH_FAMILY_SERVER_INTERPRETED = 5, + XAUTH_FAMILY_INTERNET6 = 6, + XAUTH_FAMILY_LOCALHOST = 252, + XAUTH_FAMILY_KRB5_PRINCIPAL = 253, + XAUTH_FAMILY_NETNAME = 254, + XAUTH_FAMILY_LOCAL = 256, + XAUTH_FAMILY_WILD = 65535 +}; + +typedef struct XAuthorityPrivate XAuthorityPrivate; + +typedef struct +{ + GObjectClass parent_instance; + XAuthorityPrivate *priv; +} XAuthority; + +typedef struct +{ + GObjectClass parent_class; +} XAuthorityClass; + +typedef struct XAuthorityRecordPrivate XAuthorityRecordPrivate; + +typedef struct +{ + GObjectClass parent_instance; + XAuthorityRecordPrivate *priv; +} XAuthorityRecord; + +typedef struct +{ + GObjectClass parent_class; +} XAuthorityRecordClass; + +GType x_authority_get_type (void); + +GType x_authority_record_get_type (void); + +XAuthority *x_authority_new (void); + +gboolean x_authority_load (XAuthority *authority, const gchar *filename, GError **error); + +XAuthorityRecord *x_authority_match_local (XAuthority *authority, const gchar *authorization_name); + +XAuthorityRecord *x_authority_match_localhost (XAuthority *authority, const gchar *authorization_name); + +XAuthorityRecord *x_authority_match_inet (XAuthority *authority, GInetAddress *address, const gchar *authorization_name); + +guint16 x_authority_record_get_authorization_data_length (XAuthorityRecord *record); + +const guint8 *x_authority_record_get_authorization_data (XAuthorityRecord *record); + +gboolean x_authority_record_check_cookie (XAuthorityRecord *record, const guint8 *cookie_data, guint16 cookie_data_length); + +G_END_DECLS + +#endif /* _X_AUTHORITY_H_ */ diff --git a/tests/src/x-common.c b/tests/src/x-common.c new file mode 100644 index 00000000..82249cd5 --- /dev/null +++ b/tests/src/x-common.c @@ -0,0 +1,160 @@ +#include <string.h> + +#include "x-common.h" + +gsize +pad (gsize length) +{ + if (length % 4 == 0) + return 0; + return 4 - length % 4; +} + +void +read_padding (gsize length, gsize *offset) +{ + *offset += length; +} + +guint8 +read_card8 (const guint8 *buffer, gsize buffer_length, gsize *offset) +{ + if (*offset >= buffer_length) + return 0; + (*offset)++; + return buffer[*offset - 1]; +} + +guint16 +read_card16 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset) +{ + guint8 a, b; + + a = read_card8 (buffer, buffer_length, offset); + b = read_card8 (buffer, buffer_length, offset); + if (byte_order == X_BYTE_ORDER_MSB) + return a << 8 | b; + else + return b << 8 | a; +} + +guint32 +read_card32 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset) +{ + guint8 a, b, c, d; + + a = read_card8 (buffer, buffer_length, offset); + b = read_card8 (buffer, buffer_length, offset); + c = read_card8 (buffer, buffer_length, offset); + d = read_card8 (buffer, buffer_length, offset); + if (byte_order == X_BYTE_ORDER_MSB) + return a << 24 | b << 16 | c << 8 | d; + else + return d << 24 | c << 16 | b << 8 | a; +} + +guint8 * +read_string8 (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset) +{ + guint8 *string; + int i; + + string = g_malloc (string_length + 1); + for (i = 0; i < string_length; i++) + string[i] = read_card8 (buffer, buffer_length, offset); + string[i] = '\0'; + return string; +} + +gchar * +read_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset) +{ + return (gchar *) read_string8 (buffer, buffer_length, string_length, offset); +} + +gchar * +read_padded_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset) +{ + guint8 *value; + value = read_string8 (buffer, buffer_length, string_length, offset); + read_padding (pad (string_length), offset); + return (gchar *) value; +} + +void +write_card8 (guint8 *buffer, gsize buffer_length, guint8 value, gsize *offset) +{ + if (*offset >= buffer_length) + return; + buffer[*offset] = value; + (*offset)++; +} + +void +write_padding (guint8 *buffer, gsize buffer_length, gsize length, gsize *offset) +{ + gsize i; + for (i = 0; i < length; i++) + write_card8 (buffer, buffer_length, 0, offset); +} + +void +write_card16 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint16 value, gsize *offset) +{ + if (byte_order == X_BYTE_ORDER_MSB) + { + write_card8 (buffer, buffer_length, value >> 8, offset); + write_card8 (buffer, buffer_length, value & 0xFF, offset); + } + else + { + write_card8 (buffer, buffer_length, value & 0xFF, offset); + write_card8 (buffer, buffer_length, value >> 8, offset); + } +} + +void +write_card32 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint32 value, gsize *offset) +{ + if (byte_order == X_BYTE_ORDER_MSB) + { + write_card8 (buffer, buffer_length, value >> 24, offset); + write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset); + write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset); + write_card8 (buffer, buffer_length, value & 0xFF, offset); + } + else + { + write_card8 (buffer, buffer_length, value & 0xFF, offset); + write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset); + write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset); + write_card8 (buffer, buffer_length, value >> 24, offset); + } +} + +void +write_string8 (guint8 *buffer, gsize buffer_length, const guint8 *value, gsize value_length, gsize *offset) +{ + gsize i; + for (i = 0; i < value_length; i++) + write_card8 (buffer, buffer_length, value[i], offset); +} + +gsize +padded_string_length (const gchar *value) +{ + return (strlen (value) + pad (strlen (value))) / 4; +} + +void +write_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset) +{ + write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset); +} + +void +write_padded_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset) +{ + write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset); + write_padding (buffer, buffer_length, pad (strlen (value)), offset); +} diff --git a/tests/src/x-common.h b/tests/src/x-common.h new file mode 100644 index 00000000..2904b254 --- /dev/null +++ b/tests/src/x-common.h @@ -0,0 +1,48 @@ +#ifndef _X_COMMON_H_ +#define _X_COMMON_H_ + +#include <glib-object.h> + +G_BEGIN_DECLS + +enum +{ + X_BYTE_ORDER_MSB, + X_BYTE_ORDER_LSB +}; + +gsize pad (gsize length); + +void read_padding (gsize length, gsize *offset); + +guint8 read_card8 (const guint8 *buffer, gsize buffer_length, gsize *offset); + +guint16 read_card16 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset); + +guint32 read_card32 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset); + +guint8 *read_string8 (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset); + +gchar *read_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset); + +gchar *read_padded_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset); + +void write_card8 (guint8 *buffer, gsize buffer_length, guint8 value, gsize *offset); + +void write_padding (guint8 *buffer, gsize buffer_length, gsize length, gsize *offset); + +void write_card16 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint16 value, gsize *offset); + +void write_card32 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint32 value, gsize *offset); + +void write_string8 (guint8 *buffer, gsize buffer_length, const guint8 *value, gsize value_length, gsize *offset); + +gsize padded_string_length (const gchar *value); + +void write_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset); + +void write_padded_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset); + +G_END_DECLS + +#endif /* _X_COMMON_H_ */ diff --git a/tests/src/x-server.c b/tests/src/x-server.c new file mode 100644 index 00000000..c773c21b --- /dev/null +++ b/tests/src/x-server.c @@ -0,0 +1,783 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <gio/gio.h> +#include <gio/gunixsocketaddress.h> + +#include "x-common.h" +#include "x-server.h" + +G_DEFINE_TYPE (XServer, x_server, G_TYPE_OBJECT); +G_DEFINE_TYPE (XClient, x_client, G_TYPE_OBJECT); + +#define MAXIMUM_REQUEST_LENGTH 65535 +#define VENDOR "LightDM" + +enum +{ + Failed = 0, + Success = 1, + Authenticate = 2 +}; + +enum +{ + Reply = 1, +}; + +enum { + X_SERVER_CLIENT_CONNECTED, + X_SERVER_CLIENT_DISCONNECTED, + X_SERVER_LAST_SIGNAL +}; +static guint x_server_signals[X_SERVER_LAST_SIGNAL] = { 0 }; + +struct XServerPrivate +{ + gint display_number; + gboolean listen_unix; + gboolean listen_tcp; + gint tcp_port; + gchar *socket_path; + GSocket *unix_socket; + GIOChannel *unix_channel; + GSocket *tcp_socket; + GIOChannel *tcp_channel; + GHashTable *clients; + + GList *screens; +}; + +struct XClientPrivate +{ + GSocket *socket; + GIOChannel *channel; + guint8 byte_order; + gboolean connected; + guint16 sequence_number; +}; + +enum +{ + X_CLIENT_CONNECT, + X_CLIENT_INTERN_ATOM, + X_CLIENT_GET_PROPERTY, + X_CLIENT_CREATE_GC, + X_CLIENT_QUERY_EXTENSION, + X_CLIENT_DISCONNECTED, + X_CLIENT_LAST_SIGNAL +}; +static guint x_client_signals[X_CLIENT_LAST_SIGNAL] = { 0 }; + +GInetAddress * +x_client_get_address (XClient *client) +{ + GSocketAddress *socket_address; + GError *error = NULL; + + socket_address = g_socket_get_remote_address (client->priv->socket, &error); + if (error) + g_warning ("Error getting remote socket address"); + g_clear_error (&error); + if (!socket_address) + return NULL; + + if (G_IS_INET_SOCKET_ADDRESS (socket_address)) + return g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address)); + else + return NULL; +} + +void +x_client_send_failed (XClient *client, const gchar *reason) +{ + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gsize n_written = 0, length_offset; + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, Failed, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, strlen (reason), &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MAJOR_VERSION, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MINOR_VERSION, &n_written); + length_offset = n_written; + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); + write_padded_string (buffer, MAXIMUM_REQUEST_LENGTH, reason, &n_written); + + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, (n_written - length_offset) / 4, &length_offset); + + send (g_io_channel_unix_get_fd (client->priv->channel), buffer, n_written, 0); +} + +void +x_client_send_success (XClient *client) +{ + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gsize n_written = 0, length_offset; + int i; + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, Success, &n_written); + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MAJOR_VERSION, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MINOR_VERSION, &n_written); + length_offset = n_written; + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_RELEASE_NUMBER, &n_written); + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00a00000, &n_written); // resource-id-base + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x001fffff, &n_written); // resource-id-mask + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // motion-buffer-size + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, strlen (VENDOR), &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, MAXIMUM_REQUEST_LENGTH, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // number of screens + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 7, &n_written); // number of pixmap formats + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // image-byte-order + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // bitmap-format-bit-order + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bitmap-format-scanline-unit + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bitmap-format-scanline-pad + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // min-keycode + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 255, &n_written); // max-keycode + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + write_padded_string (buffer, MAXIMUM_REQUEST_LENGTH, VENDOR, &n_written); + + // LISTofFORMAT + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 15, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 24, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bits-per-pixel + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written); + + // LISTofSCREEN + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 87, &n_written); // root + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 32, &n_written); // default-colormap + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00FFFFFF, &n_written); // white-pixel + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00000000, &n_written); // black-pixel + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_EVENT_StructureNotify | X_EVENT_SubstructureNotify | X_EVENT_SubstructureRedirect, &n_written); // SETofEVENT + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1680, &n_written); // width-in-pixels + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1050, &n_written); // height-in-pixels + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 569, &n_written); // width-in-millimeters + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 356, &n_written); // height-in-millimeters + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1, &n_written); // min-installed-maps + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1, &n_written); // max-installed-maps + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 34, &n_written); // root-visual + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // backing-stores + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // save-unders + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 24, &n_written); // root-depth + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 7, &n_written); // number of depths + + // LISTofDEPTH + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 24, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 32, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + // LISTofVISUALTYPE + for (i = 0; i < 32; i++) + { + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 34 + i, &n_written); // visual-id + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); // class + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // bits-per-rgb-value + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1, &n_written); // colormap-entries + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00FF0000, &n_written); // red-mask + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x0000FF00, &n_written); // green-mask + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x000000FF, &n_written); // blue-mask + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + } + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 15, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // depth + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); + + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, (n_written - length_offset) / 4, &length_offset); + + send (g_io_channel_unix_get_fd (client->priv->channel), buffer, n_written, 0); +} + +void +x_client_send_query_extension_response (XClient *client, guint16 sequence_number, gboolean present, guint8 major_opcode, guint8 first_event, guint8 first_error) +{ + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gsize n_written = 0; + + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, Reply, &n_written); + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, sequence_number, &n_written); // sequence-number + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, present ? 1 : 0, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, major_opcode, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, first_event, &n_written); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, first_error, &n_written); + write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 20, &n_written); + + send (g_io_channel_unix_get_fd (client->priv->channel), buffer, n_written, 0); +} + +void +x_client_disconnect (XClient *client) +{ + g_io_channel_shutdown (client->priv->channel, TRUE, NULL); +} + +static void +x_client_init (XClient *client) +{ + client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, x_client_get_type (), XClientPrivate); + client->priv->sequence_number = 1; +} + +static void +x_client_finalize (GObject *object) +{ +} + +static void +x_client_class_init (XClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = x_client_finalize; + g_type_class_add_private (klass, sizeof (XClientPrivate)); + + x_client_signals[X_CLIENT_CONNECT] = + g_signal_new ("connect", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XClientClass, connect), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + x_client_signals[X_CLIENT_INTERN_ATOM] = + g_signal_new ("intern-atom", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XClientClass, intern_atom), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + x_client_signals[X_CLIENT_GET_PROPERTY] = + g_signal_new ("get_property", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XClientClass, get_property), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + x_client_signals[X_CLIENT_CREATE_GC] = + g_signal_new ("create-gc", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XClientClass, create_gc), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + x_client_signals[X_CLIENT_QUERY_EXTENSION] = + g_signal_new ("query-extension", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XClientClass, query_extension), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + x_client_signals[X_CLIENT_DISCONNECTED] = + g_signal_new ("disconnected", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XClientClass, disconnected), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +XServer * +x_server_new (gint display_number) +{ + XServer *server = g_object_new (x_server_get_type (), NULL); + server->priv->display_number = display_number; + server->priv->tcp_port = 6000 + display_number; + return server; +} + +void +x_server_set_listen_unix (XServer *server, gboolean listen_unix) +{ + server->priv->listen_unix = listen_unix; +} + +void +x_server_set_listen_tcp (XServer *server, gboolean listen_tcp) +{ + server->priv->listen_tcp = listen_tcp; +} + +static void +decode_connection_request (XClient *client, const guint8 *buffer, gssize buffer_length) +{ + guint8 byte_order; + gsize offset = 0; + guint16 n; + XConnect *message; + + byte_order = read_card8 (buffer, buffer_length, &offset); + if (!(byte_order == 'B' || byte_order == 'l')) + { + g_warning ("Invalid byte order"); + return; + } + + message = g_malloc0 (sizeof (XConnect)); + + message->byte_order = byte_order == 'B' ? X_BYTE_ORDER_MSB : X_BYTE_ORDER_LSB; + read_padding (1, &offset); + message->protocol_major_version = read_card16 (buffer, buffer_length, message->byte_order, &offset); + message->protocol_minor_version = read_card16 (buffer, buffer_length, message->byte_order, &offset); + n = read_card16 (buffer, buffer_length, message->byte_order, &offset); + message->authorization_protocol_data_length = read_card16 (buffer, buffer_length, message->byte_order, &offset); + read_padding (2, &offset); + message->authorization_protocol_name = read_padded_string (buffer, buffer_length, n, &offset); + message->authorization_protocol_data = read_string8 (buffer, buffer_length, message->authorization_protocol_data_length, &offset); + read_padding (pad (message->authorization_protocol_data_length), &offset); + + /* Store information about the client */ + client->priv->byte_order = message->byte_order; + client->priv->connected = TRUE; + + g_signal_emit (client, x_client_signals[X_CLIENT_CONNECT], 0, message); + + g_free (message->authorization_protocol_name); + g_free (message->authorization_protocol_data); + g_free (message); +} + +static void +decode_intern_atom (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset) +{ + XInternAtom *message; + guint16 name_length; + + message = g_malloc0 (sizeof (XInternAtom)); + + message->only_if_exists = data != 0; + name_length = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + message->name = read_padded_string (buffer, buffer_length, name_length, offset); + + g_signal_emit (client, x_client_signals[X_CLIENT_INTERN_ATOM], 0, message); + + g_free (message->name); + g_free (message); +} + +static void +decode_get_property (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset) +{ + XGetProperty *message; + + message = g_malloc0 (sizeof (XGetProperty)); + + message->delete = data != 0; + message->window = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + message->property = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + message->type = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + message->long_offset = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + message->long_length = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + + g_signal_emit (client, x_client_signals[X_CLIENT_GET_PROPERTY], 0, message); + + g_free (message); +} + +static void +decode_create_gc (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset) +{ + XCreateGC *message; + + message = g_malloc0 (sizeof (XCreateGC)); + + message->cid = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + message->drawable = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + message->value_mask = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_function) != 0) + { + message->function = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_plane_mask) != 0) + message->plane_mask = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_foreground) != 0) + message->foreground = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_background) != 0) + message->background = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_line_width) != 0) + { + message->line_width = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_line_style) != 0) + { + message->line_style = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_cap_style) != 0) + { + message->cap_style = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_join_style) != 0) + { + message->join_style = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_fill_style) != 0) + { + message->fill_style = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_fill_rule) != 0) + { + message->fill_rule = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_tile) != 0) + message->tile = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_stipple) != 0) + message->stipple = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_tile_stipple_x_origin) != 0) + { + message->tile_stipple_x_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_tile_stipple_y_origin) != 0) + { + message->tile_stipple_y_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_font) != 0) + message->font = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_subwindow_mode) != 0) + { + message->subwindow_mode = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_graphics_exposures) != 0) + { + message->graphics_exposures = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_clip_x_origin) != 0) + { + message->clip_x_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_clip_y_origin) != 0) + { + message->clip_y_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_clip_mask) != 0) + message->clip_mask = read_card32 (buffer, buffer_length, client->priv->byte_order, offset); + if ((message->value_mask & X_GC_VALUE_MASK_dash_offset) != 0) + { + message->dash_offset = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_dashes) != 0) + { + message->dashes = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + if ((message->value_mask & X_GC_VALUE_MASK_arc_mode) != 0) + { + message->arc_mode = read_card8 (buffer, buffer_length, offset); + read_padding (3, offset); + } + + g_signal_emit (client, x_client_signals[X_CLIENT_CREATE_GC], 0, message); + + g_free (message); +} + +static void +decode_query_extension (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset) +{ + XQueryExtension *message; + guint16 name_length; + + message = g_malloc0 (sizeof (XQueryExtension)); + + name_length = read_card16 (buffer, buffer_length, client->priv->byte_order, offset); + read_padding (2, offset); + message->name = read_padded_string (buffer, buffer_length, name_length, offset); + + g_signal_emit (client, x_client_signals[X_CLIENT_QUERY_EXTENSION], 0, message); + + g_free (message->name); + g_free (message); +} + +static void +decode_big_req_enable (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset) +{ +} + +static void +decode_request (XClient *client, guint16 sequence_number, const guint8 *buffer, gssize buffer_length) +{ + int opcode; + gsize offset = 0; + + while (offset < buffer_length) + { + guint8 data; + guint16 length, remaining; + gsize start_offset; + + start_offset = offset; + opcode = read_card8 (buffer, buffer_length, &offset); + data = read_card8 (buffer, buffer_length, &offset); + length = read_card16 (buffer, buffer_length, client->priv->byte_order, &offset) * 4; + remaining = start_offset + length; + + g_debug ("Got opcode=%d length=%d", opcode, length); + + switch (opcode) + { + /*case 1: + decode_create_window (client, sequence_number, data, buffer, remaining, &offset); + break;*/ + /*case 4: + decode_destroy_window (client, sequence_number, data, buffer, remaining, &offset); + break;*/ + /*case 8: + decode_map_window (client, sequence_number, data, buffer, remaining, &offset); + break;*/ + /*case 10: + decode_unmap_window (client, sequence_number, data, buffer, remaining, &offset); + break;*/ + /*case 12: + decode_configure_window (client, sequence_number, data, buffer, remaining, &offset); + break;*/ + case 16: + decode_intern_atom (client, sequence_number, data, buffer, remaining, &offset); + break; + case 20: + decode_get_property (client, sequence_number, data, buffer, remaining, &offset); + break; + case 55: + decode_create_gc (client, sequence_number, data, buffer, remaining, &offset); + break; + case 98: + decode_query_extension (client, sequence_number, data, buffer, remaining, &offset); + break; + case 135: + decode_big_req_enable (client, sequence_number, data, buffer, remaining, &offset); + break; + default: + g_debug ("Ignoring unknown opcode %d", opcode); + break; + } + + offset = start_offset + length; + } +} + +static gboolean +socket_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data) +{ + XClient *client = data; + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gssize n_read; + + n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0); + if (n_read < 0) + g_warning ("Error reading from socket: %s", strerror (errno)); + else if (n_read == 0) + { + g_signal_emit (client, x_client_signals[X_CLIENT_DISCONNECTED], 0); + return FALSE; + } + else + { + if (client->priv->connected) + { + decode_request (client, client->priv->sequence_number, buffer, n_read); + client->priv->sequence_number++; + } + else + decode_connection_request (client, buffer, n_read); + } + + return TRUE; +} + +static void +x_client_disconnected_cb (XClient *client, XServer *server) +{ + g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, server); + g_hash_table_remove (server->priv->clients, client->priv->channel); + g_signal_emit (server, x_server_signals[X_SERVER_CLIENT_DISCONNECTED], 0, client); +} + +static gboolean +socket_connect_cb (GIOChannel *channel, GIOCondition condition, gpointer data) +{ + XServer *server = data; + GSocket *data_socket; + XClient *client; + GError *error = NULL; + + if (channel == server->priv->unix_channel) + data_socket = g_socket_accept (server->priv->unix_socket, NULL, &error); + else + data_socket = g_socket_accept (server->priv->tcp_socket, NULL, &error); + if (error) + g_warning ("Error accepting connection: %s", strerror (errno)); + g_clear_error (&error); + if (!data_socket) + return FALSE; + + client = g_object_new (x_client_get_type (), NULL); + g_signal_connect (client, "disconnected", G_CALLBACK (x_client_disconnected_cb), server); + client->priv->socket = data_socket; + client->priv->channel = g_io_channel_unix_new (g_socket_get_fd (data_socket)); + g_hash_table_insert (server->priv->clients, client->priv->channel, client); + g_io_add_watch (client->priv->channel, G_IO_IN, socket_data_cb, client); + + g_signal_emit (server, x_server_signals[X_SERVER_CLIENT_CONNECTED], 0, client); + + return TRUE; +} + +gboolean +x_server_start (XServer *server) +{ + if (server->priv->listen_unix) + { + GError *error = NULL; + + server->priv->socket_path = g_strdup_printf ("/tmp/.X11-unix/X%d", server->priv->display_number); + + server->priv->unix_socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); + if (!server->priv->unix_socket || + !g_socket_bind (server->priv->unix_socket, g_unix_socket_address_new (server->priv->socket_path), TRUE, &error) || + !g_socket_listen (server->priv->unix_socket, &error)) + { + g_warning ("Error creating Unix X socket: %s", error->message); + return FALSE; + } + server->priv->unix_channel = g_io_channel_unix_new (g_socket_get_fd (server->priv->unix_socket)); + g_io_add_watch (server->priv->unix_channel, G_IO_IN, socket_connect_cb, server); + } + + if (server->priv->listen_tcp) + { + GError *error = NULL; + + server->priv->tcp_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error); + if (!server->priv->tcp_socket || + !g_socket_bind (server->priv->tcp_socket, g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), server->priv->tcp_port), TRUE, &error) || + !g_socket_listen (server->priv->tcp_socket, &error)) + { + g_warning ("Error creating TCP/IP X socket: %s", error->message); + return FALSE; + } + server->priv->tcp_channel = g_io_channel_unix_new (g_socket_get_fd (server->priv->tcp_socket)); + g_io_add_watch (server->priv->tcp_channel, G_IO_IN, socket_connect_cb, server); + } + + return TRUE; +} + +gsize +x_server_get_n_clients (XServer *server) +{ + return g_hash_table_size (server->priv->clients); +} + +static void +x_server_init (XServer *server) +{ + server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, x_server_get_type (), XServerPrivate); + server->priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) g_io_channel_unref, g_object_unref); + server->priv->listen_unix = TRUE; + server->priv->listen_tcp = TRUE; +} + +static void +x_server_finalize (GObject *object) +{ + XServer *server = (XServer *) object; + if (server->priv->socket_path) + unlink (server->priv->socket_path); +} + +static void +x_server_class_init (XServerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = x_server_finalize; + g_type_class_add_private (klass, sizeof (XServerPrivate)); + x_server_signals[X_SERVER_CLIENT_CONNECTED] = + g_signal_new ("client-connected", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XServerClass, client_connected), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, x_client_get_type ()); + x_server_signals[X_SERVER_CLIENT_DISCONNECTED] = + g_signal_new ("client-disconnected", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XServerClass, client_disconnected), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, x_client_get_type ()); +} diff --git a/tests/src/x-server.h b/tests/src/x-server.h new file mode 100644 index 00000000..955a8d52 --- /dev/null +++ b/tests/src/x-server.h @@ -0,0 +1,190 @@ +#ifndef _X_SERVER_H_ +#define _X_SERVER_H_ + +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define X_PROTOCOL_MAJOR_VERSION 11 +#define X_PROTOCOL_MINOR_VERSION 0 + +#define X_RELEASE_NUMBER 0 + +typedef enum +{ + X_EVENT_KeyPress = 0x00000001, + X_EVENT_KeyRelease = 0x00000002, + X_EVENT_ButtonPress = 0x00000004, + X_EVENT_ButtonRelease = 0x00000008, + X_EVENT_EnterWindow = 0x00000010, + X_EVENT_LeaveWindow = 0x00000020, + X_EVENT_PointerMotion = 0x00000040, + X_EVENT_PointerMotionHint = 0x00000080, + X_EVENT_Button1Motion = 0x00000100, + X_EVENT_Button2Motion = 0x00000200, + X_EVENT_Button3Motion = 0x00000400, + X_EVENT_Button4Motion = 0x00000800, + X_EVENT_Button5Motion = 0x00001000, + X_EVENT_ButtonMotion = 0x00002000, + X_EVENT_KeymapState = 0x00004000, + X_EVENT_Exposure = 0x00008000, + X_EVENT_VisibilityChange = 0x00010000, + X_EVENT_StructureNotify = 0x00020000, + X_EVENT_ResizeRedirect = 0x00040000, + X_EVENT_SubstructureNotify = 0x00080000, + X_EVENT_SubstructureRedirect = 0x00100000, + X_EVENT_FocusChange = 0x00200000, + X_EVENT_PropertyChange = 0x00400000, + X_EVENT_ColormapChange = 0x00800000, + X_EVENT_OwnerGrabButton = 0x01000000 +} XEvent; + +enum +{ + X_GC_VALUE_MASK_function = 0x00000001, + X_GC_VALUE_MASK_plane_mask = 0x00000002, + X_GC_VALUE_MASK_foreground = 0x00000004, + X_GC_VALUE_MASK_background = 0x00000008, + X_GC_VALUE_MASK_line_width = 0x00000010, + X_GC_VALUE_MASK_line_style = 0x00000020, + X_GC_VALUE_MASK_cap_style = 0x00000040, + X_GC_VALUE_MASK_join_style = 0x00000080, + X_GC_VALUE_MASK_fill_style = 0x00000100, + X_GC_VALUE_MASK_fill_rule = 0x00000200, + X_GC_VALUE_MASK_tile = 0x00000400, + X_GC_VALUE_MASK_stipple = 0x00000800, + X_GC_VALUE_MASK_tile_stipple_x_origin = 0x00001000, + X_GC_VALUE_MASK_tile_stipple_y_origin = 0x00002000, + X_GC_VALUE_MASK_font = 0x00004000, + X_GC_VALUE_MASK_subwindow_mode = 0x00008000, + X_GC_VALUE_MASK_graphics_exposures = 0x00010000, + X_GC_VALUE_MASK_clip_x_origin = 0x00020000, + X_GC_VALUE_MASK_clip_y_origin = 0x00040000, + X_GC_VALUE_MASK_clip_mask = 0x00080000, + X_GC_VALUE_MASK_dash_offset = 0x00100000, + X_GC_VALUE_MASK_dashes = 0x00200000, + X_GC_VALUE_MASK_arc_mode = 0x00400000 +} XGCValueMask; + +typedef struct +{ + guint8 byte_order; + guint16 protocol_major_version, protocol_minor_version; + gchar *authorization_protocol_name; + guint8 *authorization_protocol_data; + guint16 authorization_protocol_data_length; +} XConnect; + +typedef struct +{ + gboolean only_if_exists; + gchar *name; +} XInternAtom; + +typedef struct +{ + gboolean delete; + guint32 window; + guint32 property; + guint32 type; + guint32 long_offset; + guint32 long_length; +} XGetProperty; + +typedef struct +{ + guint32 cid; + guint32 drawable; + guint32 value_mask; + guint8 function; + guint32 plane_mask; + guint32 foreground; + guint32 background; + guint16 line_width; + guint8 line_style; + guint8 cap_style; + guint8 join_style; + guint8 fill_style; + guint8 fill_rule; + guint32 tile; + guint32 stipple; + guint16 tile_stipple_x_origin; + guint16 tile_stipple_y_origin; + guint32 font; + guint8 subwindow_mode; + gboolean graphics_exposures; + guint16 clip_x_origin; + guint16 clip_y_origin; + guint32 clip_mask; + guint16 dash_offset; + guint8 dashes; + guint8 arc_mode; +} XCreateGC; + +typedef struct +{ + gchar *name; +} XQueryExtension; + +typedef struct XClientPrivate XClientPrivate; + +typedef struct +{ + GObject parent_instance; + XClientPrivate *priv; +} XClient; + +typedef struct +{ + GObjectClass parent_class; + void (*connect)(XClient *client, XConnect *message); + void (*intern_atom)(XClient *client, XInternAtom *message); + void (*get_property)(XClient *client, XGetProperty *message); + void (*create_gc)(XClient *client, XCreateGC *message); + void (*query_extension)(XClient *client, XQueryExtension *message); + void (*disconnected)(XClient *client); +} XClientClass; + +typedef struct XServerPrivate XServerPrivate; + +typedef struct +{ + GObject parent_instance; + XServerPrivate *priv; +} XServer; + +typedef struct +{ + GObjectClass parent_class; + void (*client_connected)(XServer *server, XClient *client); + void (*client_disconnected)(XServer *server, XClient *client); +} XServerClass; + +GType x_server_get_type (void); + +GType x_client_get_type (void); + +XServer *x_server_new (gint display_number); + +void x_server_set_listen_unix (XServer *server, gboolean listen_unix); + +void x_server_set_listen_tcp (XServer *server, gboolean listen_tcp); + +gboolean x_server_start (XServer *server); + +gsize x_server_get_n_clients (XServer *server); + +GInetAddress *x_client_get_address (XClient *client); + +void x_client_send_failed (XClient *client, const gchar *reason); + +void x_client_send_success (XClient *client); + +void x_client_send_query_extension_response (XClient *client, guint16 sequence_number, gboolean present, guint8 major_opcode, guint8 first_event, guint8 first_error); + +void x_client_disconnect (XClient *client); + +G_END_DECLS + +#endif /* _X_SERVER_H_ */ diff --git a/tests/src/xdmcp-client.c b/tests/src/xdmcp-client.c new file mode 100644 index 00000000..dd352c8b --- /dev/null +++ b/tests/src/xdmcp-client.c @@ -0,0 +1,456 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <gio/gio.h> + +#include "x-common.h" +#include "xdmcp-client.h" + +G_DEFINE_TYPE (XDMCPClient, xdmcp_client, G_TYPE_OBJECT); + +#define MAXIMUM_REQUEST_LENGTH 65535 + +typedef enum +{ + XDMCP_BroadcastQuery = 1, + XDMCP_Query = 2, + XDMCP_IndirectQuery = 3, + XDMCP_ForwardQuery = 4, + XDMCP_Willing = 5, + XDMCP_Unwilling = 6, + XDMCP_Request = 7, + XDMCP_Accept = 8, + XDMCP_Decline = 9, + XDMCP_Manage = 10, + XDMCP_Refuse = 11, + XDMCP_Failed = 12, + XDMCP_KeepAlive = 13, + XDMCP_Alive = 14 +} XDMCPOpcode; + +struct XDMCPClientPrivate +{ + gchar *host; + gint port; + GSocket *socket; + guint query_timer; + gchar *authorization_name; + gint authorization_data_length; + guint8 *authorization_data; +}; + +enum { + XDMCP_CLIENT_QUERY, + XDMCP_CLIENT_WILLING, + XDMCP_CLIENT_ACCEPT, + XDMCP_CLIENT_DECLINE, + XDMCP_CLIENT_FAILED, + XDMCP_CLIENT_LAST_SIGNAL +}; +static guint xdmcp_client_signals[XDMCP_CLIENT_LAST_SIGNAL] = { 0 }; + +static void +xdmcp_write (XDMCPClient *client, const guint8 *buffer, gssize buffer_length) +{ + gssize n_written; + GError *error = NULL; + + n_written = g_socket_send (client->priv->socket, (const gchar *) buffer, buffer_length, NULL, &error); + if (n_written < 0) + g_warning ("Failed to send XDMCP request: %s", error->message); + else if (n_written != buffer_length) + g_warning ("Partial write for XDMCP request, wrote %zi, expected %zi", n_written, buffer_length); + g_clear_error (&error); +} + +static void +decode_willing (XDMCPClient *client, const guint8 *buffer, gssize buffer_length) +{ + XDMCPWilling *message; + gsize offset = 0; + guint16 length; + + if (client->priv->query_timer == 0) + { + g_debug ("Ignoring XDMCP unrequested/duplicate Willing"); + return; + } + + /* Stop sending queries */ + g_source_remove (client->priv->query_timer); + client->priv->query_timer = 0; + + message = g_malloc0 (sizeof (XDMCPWilling)); + + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->authentication_name = read_string (buffer, buffer_length, length, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->hostname = read_string (buffer, buffer_length, length, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->status = read_string (buffer, buffer_length, length, &offset); + + g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_WILLING], 0, message); + + g_free (message->authentication_name); + g_free (message->hostname); + g_free (message->status); + g_free (message); +} + +static void +decode_accept (XDMCPClient *client, const guint8 *buffer, gssize buffer_length) +{ + XDMCPAccept *message; + gsize offset = 0; + guint16 length; + + message = g_malloc (sizeof (XDMCPAccept)); + + message->session_id = read_card32 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->authentication_name = read_string (buffer, buffer_length, length, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + read_string8 (buffer, buffer_length, length, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->authorization_name = read_string (buffer, buffer_length, length, &offset); + message->authorization_data_length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->authorization_data = read_string8 (buffer, buffer_length, length, &offset); + + g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_ACCEPT], 0, message); + + g_free (message->authentication_name); + g_free (message->authorization_name); + g_free (message->authorization_data); + g_free (message); +} + +static void +decode_decline (XDMCPClient *client, const guint8 *buffer, gssize buffer_length) +{ + XDMCPDecline *message; + gsize offset = 0; + guint16 length; + + message = g_malloc0 (sizeof (XDMCPDecline)); + + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->status = read_string (buffer, buffer_length, length, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->authentication_name = read_string (buffer, buffer_length, length, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + read_string8 (buffer, buffer_length, length, &offset); + + g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_DECLINE], 0, message); + + g_free (message->status); + g_free (message->authentication_name); + g_free (message); +} + +static void +decode_failed (XDMCPClient *client, const guint8 *buffer, gssize buffer_length) +{ + XDMCPFailed *message; + gsize offset = 0; + guint16 length; + + message = g_malloc0 (sizeof (XDMCPFailed)); + + message->session_id = read_card32 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset); + message->status = read_string (buffer, buffer_length, length, &offset); + + g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_FAILED], 0, message); + + g_free (message->status); + g_free (message); +} + +static gboolean +xdmcp_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data) +{ + XDMCPClient *client = data; + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gssize n_read; + + n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0); + if (n_read < 0) + g_warning ("Error reading from XDMCP socket: %s", strerror (errno)); + else if (n_read == 0) + { + g_debug ("EOF"); + return FALSE; + } + else + { + gsize offset = 0; + guint16 version, opcode, length; + + version = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset); + opcode = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset); + length = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset); + + if (version != 1) + { + g_debug ("Ignoring XDMCP version %d message", version); + return TRUE; + } + if (6 + length > n_read) + { + g_debug ("Ignoring XDMCP message of length %zi with invalid length field %d", n_read, length); + return TRUE; + } + switch (opcode) + { + case XDMCP_Willing: + decode_willing (client, buffer + offset, n_read - offset); + break; + + case XDMCP_Accept: + decode_accept (client, buffer + offset, n_read - offset); + break; + + case XDMCP_Decline: + decode_decline (client, buffer + offset, n_read - offset); + break; + + case XDMCP_Failed: + decode_failed (client, buffer + offset, n_read - offset); + break; + + default: + g_debug ("Ignoring unknown XDMCP opcode %d", opcode); + break; + } + } + + return TRUE; +} + +static gboolean +xdmcp_query_cb (gpointer data) +{ + XDMCPClient *client = data; + g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_QUERY], 0); + xdmcp_client_send_query (client); + return TRUE; +} + +XDMCPClient * +xdmcp_client_new (void) +{ + return g_object_new (xdmcp_client_get_type (), NULL); +} + +void +xdmcp_client_set_hostname (XDMCPClient *client, const gchar *hostname) +{ + g_free (client->priv->host); + client->priv->host = g_strdup (hostname); +} + +void +xdmcp_client_set_port (XDMCPClient *client, guint16 port) +{ + client->priv->port = port; +} + +gboolean +xdmcp_client_start (XDMCPClient *client) +{ + GSocketConnectable *address; + GSocketAddress *socket_address; + GError *error = NULL; + + client->priv->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error); + + address = g_network_address_new (client->priv->host, client->priv->port); + socket_address = g_socket_address_enumerator_next (g_socket_connectable_enumerate (address), NULL, NULL); + + if (!client->priv->socket || + !g_socket_connect (client->priv->socket, socket_address, NULL, &error) || + !g_io_add_watch (g_io_channel_unix_new (g_socket_get_fd (client->priv->socket)), G_IO_IN, xdmcp_data_cb, client)) + { + g_warning ("Error creating XDMCP socket: %s", error->message); + return FALSE; + } + + client->priv->query_timer = g_timeout_add (2000, xdmcp_query_cb, client); + xdmcp_query_cb (client); + + return TRUE; +} + +GInetAddress * +xdmcp_client_get_local_address (XDMCPClient *client) +{ + GSocketAddress *socket_address; + + if (!client->priv->socket) + return NULL; + + socket_address = g_socket_get_local_address (client->priv->socket, NULL); + return g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address)); +} + +static void +xdmcp_client_init (XDMCPClient *client) +{ + client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, xdmcp_client_get_type (), XDMCPClientPrivate); + client->priv->port = XDMCP_PORT; +} + +void +xdmcp_client_send_query (XDMCPClient *client) +{ + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gsize offset = 0; + + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Query, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 1, &offset); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &offset); + + xdmcp_write (client, buffer, offset); +} + +void +xdmcp_client_send_request (XDMCPClient *client, + guint16 display_number, + GInetAddress **addresses, + const gchar *authentication_name, + const guint8 *authentication_data, guint16 authentication_data_length, + gchar **authorization_names, const gchar *mfid) +{ + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gsize length = 0, offset = 0, n_addresses = 0, n_names = 0; + GInetAddress **address; + gchar **name; + + length = 11 + strlen (authentication_name) + authentication_data_length + strlen (mfid); + for (address = addresses; *address; address++) + { + gssize native_address_length = g_inet_address_get_native_size (*address); + length += 4 + native_address_length; + n_addresses++; + } + for (name = authorization_names; *name; name++) + { + length += 2 + strlen (*name); + n_names++; + } + + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Request, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, length, &offset); + + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, display_number, &offset); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_addresses, &offset); + for (address = addresses; *address; address++) + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 0, &offset); /* FamilyInternet */ + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_addresses, &offset); + for (address = addresses; *address; address++) + { + gssize native_address_length; + const guint8 *native_address; + + native_address_length = g_inet_address_get_native_size (*address); + native_address = g_inet_address_to_bytes (*address); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, native_address_length, &offset); + write_string8 (buffer, MAXIMUM_REQUEST_LENGTH, native_address, native_address_length, &offset); + } + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (authentication_name), &offset); + write_string (buffer, MAXIMUM_REQUEST_LENGTH, authentication_name, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, authentication_data_length, &offset); + write_string8 (buffer, MAXIMUM_REQUEST_LENGTH, authentication_data, authentication_data_length, &offset); + write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_names, &offset); + for (name = authorization_names; *name; name++) + { + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (*name), &offset); + write_string (buffer, MAXIMUM_REQUEST_LENGTH, *name, &offset); + } + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (mfid), &offset); + write_string (buffer, MAXIMUM_REQUEST_LENGTH, mfid, &offset); + + xdmcp_write (client, buffer, offset); +} + +void +xdmcp_client_send_manage (XDMCPClient *client, guint32 session_id, guint16 display_number, gchar *display_class) +{ + guint8 buffer[MAXIMUM_REQUEST_LENGTH]; + gsize offset = 0; + + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Manage, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 8 + strlen (display_class), &offset); + + write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, session_id, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, display_number, &offset); + write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (display_class), &offset); + write_string (buffer, MAXIMUM_REQUEST_LENGTH, display_class, &offset); + + xdmcp_write (client, buffer, offset); +} + +static void +xdmcp_client_finalize (GObject *object) +{ + XDMCPClient *client = (XDMCPClient *) object; + g_free (client->priv->host); + if (client->priv->socket) + g_object_unref (client->priv->socket); + g_free (client->priv->authorization_name); + g_free (client->priv->authorization_data); +} + +static void +xdmcp_client_class_init (XDMCPClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = xdmcp_client_finalize; + g_type_class_add_private (klass, sizeof (XDMCPClientPrivate)); + xdmcp_client_signals[XDMCP_CLIENT_QUERY] = + g_signal_new ("query", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XDMCPClientClass, query), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + xdmcp_client_signals[XDMCP_CLIENT_WILLING] = + g_signal_new ("willing", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XDMCPClientClass, willing), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + xdmcp_client_signals[XDMCP_CLIENT_ACCEPT] = + g_signal_new ("accept", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XDMCPClientClass, accept), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + xdmcp_client_signals[XDMCP_CLIENT_DECLINE] = + g_signal_new ("decline", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XDMCPClientClass, decline), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + xdmcp_client_signals[XDMCP_CLIENT_FAILED] = + g_signal_new ("failed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (XDMCPClientClass, failed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} diff --git a/tests/src/xdmcp-client.h b/tests/src/xdmcp-client.h new file mode 100644 index 00000000..8cacbf83 --- /dev/null +++ b/tests/src/xdmcp-client.h @@ -0,0 +1,81 @@ +#ifndef _XDMCP_CLIENT_H_ +#define _XDMCP_CLIENT_H_ + +#include <glib-object.h> +#include <gio/gio.h> + +#define XDMCP_VERSION 1 +#define XDMCP_PORT 177 + +typedef struct +{ + gchar *authentication_name; + gchar *hostname; + gchar *status; +} XDMCPWilling; + +typedef struct +{ + guint32 session_id; + gchar *authentication_name; + gchar *authorization_name; + guint16 authorization_data_length; + guint8 *authorization_data; +} XDMCPAccept; + +typedef struct +{ + gchar *status; + gchar *authentication_name; +} XDMCPDecline; + +typedef struct +{ + guint32 session_id; + gchar *status; +} XDMCPFailed; + +typedef struct XDMCPClientPrivate XDMCPClientPrivate; + +typedef struct +{ + GObject parent_instance; + XDMCPClientPrivate *priv; +} XDMCPClient; + +typedef struct +{ + GObjectClass parent_class; + void (*query)(XDMCPClient *client); + void (*willing)(XDMCPClient *client, XDMCPWilling *message); + void (*accept)(XDMCPClient *client, XDMCPAccept *message); + void (*decline)(XDMCPClient *client, XDMCPDecline *message); + void (*failed)(XDMCPClient *client, XDMCPFailed *message); +} XDMCPClientClass; + +GType xdmcp_client_get_type (void); + +XDMCPClient *xdmcp_client_new (void); + +void xdmcp_client_set_hostname (XDMCPClient *client, const gchar *hostname); + +void xdmcp_client_set_port (XDMCPClient *client, guint16 port); + +gboolean xdmcp_client_start (XDMCPClient *client); + +GInetAddress *xdmcp_client_get_local_address (XDMCPClient *client); + +void xdmcp_client_send_query (XDMCPClient *client); + +void xdmcp_client_send_request (XDMCPClient *client, + guint16 display_number, + GInetAddress **addresses, + const gchar *authentication_name, + const guint8 *authentication_data, guint16 authentication_data_length, + gchar **authorization_names, const gchar *mfid); + +void xdmcp_client_send_manage (XDMCPClient *client, guint32 session_id, guint16 display_number, gchar *display_class); + +G_END_DECLS + +#endif /* _XDMCP_CLIENT_H_ */ diff --git a/tests/test-vnc-login b/tests/test-vnc-login new file mode 100755 index 00000000..243b7d45 --- /dev/null +++ b/tests/test-vnc-login @@ -0,0 +1,2 @@ +#!/bin/sh +./src/test-runner vnc-login test-gobject-greeter |