]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/xdmcp-client.c
Remove trailing whitespace
[sojka/lightdm.git] / tests / src / xdmcp-client.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <gio/gio.h>
8
9 #include "x-common.h"
10 #include "xdmcp-client.h"
11
12 G_DEFINE_TYPE (XDMCPClient, xdmcp_client, G_TYPE_OBJECT);
13
14 #define MAXIMUM_REQUEST_LENGTH 65535
15
16 typedef enum
17 {
18     XDMCP_BroadcastQuery = 1,
19     XDMCP_Query          = 2,
20     XDMCP_IndirectQuery  = 3,
21     XDMCP_ForwardQuery   = 4,
22     XDMCP_Willing        = 5,
23     XDMCP_Unwilling      = 6,
24     XDMCP_Request        = 7,
25     XDMCP_Accept         = 8,
26     XDMCP_Decline        = 9,
27     XDMCP_Manage         = 10,
28     XDMCP_Refuse         = 11,
29     XDMCP_Failed         = 12,
30     XDMCP_KeepAlive      = 13,
31     XDMCP_Alive          = 14
32 } XDMCPOpcode;
33
34 struct XDMCPClientPrivate
35 {
36     gchar *host;
37     gint port;
38     GSocket *socket;
39     guint query_timer;
40     gchar *authorization_name;
41     gint authorization_data_length;
42     guint8 *authorization_data;
43 };
44
45 enum {
46     XDMCP_CLIENT_QUERY,
47     XDMCP_CLIENT_WILLING,
48     XDMCP_CLIENT_ACCEPT,
49     XDMCP_CLIENT_DECLINE,
50     XDMCP_CLIENT_FAILED,
51     XDMCP_CLIENT_LAST_SIGNAL
52 };
53 static guint xdmcp_client_signals[XDMCP_CLIENT_LAST_SIGNAL] = { 0 };
54
55 static void
56 xdmcp_write (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
57 {
58     gssize n_written;
59     GError *error = NULL;
60
61     n_written = g_socket_send (client->priv->socket, (const gchar *) buffer, buffer_length, NULL, &error);
62     if (n_written < 0)
63         g_warning ("Failed to send XDMCP request: %s", error->message);
64     else if (n_written != buffer_length)
65         g_warning ("Partial write for XDMCP request, wrote %zi, expected %zi", n_written, buffer_length);
66     g_clear_error (&error);
67 }
68
69 static void
70 decode_willing (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
71 {
72     XDMCPWilling *message;
73     gsize offset = 0;
74     guint16 length;
75
76     if (client->priv->query_timer == 0)
77     {
78         g_debug ("Ignoring XDMCP unrequested/duplicate Willing");
79         return;
80     }
81
82     /* Stop sending queries */
83     g_source_remove (client->priv->query_timer);
84     client->priv->query_timer = 0;
85
86     message = g_malloc0 (sizeof (XDMCPWilling));
87
88     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
89     message->authentication_name = read_string (buffer, buffer_length, length, &offset);
90     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
91     message->hostname = read_string (buffer, buffer_length, length, &offset);
92     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
93     message->status = read_string (buffer, buffer_length, length, &offset);
94
95     g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_WILLING], 0, message);
96
97     g_free (message->authentication_name);
98     g_free (message->hostname);
99     g_free (message->status);
100     g_free (message);
101 }
102
103 static void
104 decode_accept (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
105 {
106     XDMCPAccept *message;
107     gsize offset = 0;
108     guint16 length;
109
110     message = g_malloc (sizeof (XDMCPAccept));
111
112     message->session_id = read_card32 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
113     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
114     message->authentication_name = read_string (buffer, buffer_length, length, &offset);
115     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
116     read_string8 (buffer, buffer_length, length, &offset);
117     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
118     message->authorization_name = read_string (buffer, buffer_length, length, &offset);
119     message->authorization_data_length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
120     message->authorization_data = read_string8 (buffer, buffer_length, length, &offset);
121
122     g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_ACCEPT], 0, message);
123
124     g_free (message->authentication_name);
125     g_free (message->authorization_name);
126     g_free (message->authorization_data);
127     g_free (message);
128 }
129
130 static void
131 decode_decline (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
132 {
133     XDMCPDecline *message;
134     gsize offset = 0;
135     guint16 length;
136
137     message = g_malloc0 (sizeof (XDMCPDecline));
138
139     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
140     message->status = read_string (buffer, buffer_length, length, &offset);
141     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
142     message->authentication_name = read_string (buffer, buffer_length, length, &offset);
143     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
144     read_string8 (buffer, buffer_length, length, &offset);
145
146     g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_DECLINE], 0, message);
147
148     g_free (message->status);
149     g_free (message->authentication_name);
150     g_free (message);
151 }
152
153 static void
154 decode_failed (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
155 {
156     XDMCPFailed *message;
157     gsize offset = 0;
158     guint16 length;
159
160     message = g_malloc0 (sizeof (XDMCPFailed));
161
162     message->session_id = read_card32 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
163     length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
164     message->status = read_string (buffer, buffer_length, length, &offset);
165
166     g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_FAILED], 0, message);
167
168     g_free (message->status);
169     g_free (message);
170 }
171
172 static gboolean
173 xdmcp_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
174 {
175     XDMCPClient *client = data;
176     guint8 buffer[MAXIMUM_REQUEST_LENGTH];
177     gssize n_read;
178
179     n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0);
180     if (n_read < 0)
181         g_warning ("Error reading from XDMCP socket: %s", strerror (errno));
182     else if (n_read == 0)
183     {
184         g_debug ("EOF");
185         return FALSE;
186     }
187     else
188     {
189         gsize offset = 0;
190         guint16 version, opcode, length;
191
192         version = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset);
193         opcode = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset);
194         length = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset);
195
196         if (version != 1)
197         {
198             g_debug ("Ignoring XDMCP version %d message", version);
199             return TRUE;
200         }
201         if (6 + length > n_read)
202         {
203             g_debug ("Ignoring XDMCP message of length %zi with invalid length field %d", n_read, length);
204             return TRUE;
205         }
206         switch (opcode)
207         {
208         case XDMCP_Willing:
209             decode_willing (client, buffer + offset, n_read - offset);
210             break;
211
212         case XDMCP_Accept:
213             decode_accept (client, buffer + offset, n_read - offset);
214             break;
215
216         case XDMCP_Decline:
217             decode_decline (client, buffer + offset, n_read - offset);
218             break;
219
220         case XDMCP_Failed:
221             decode_failed (client, buffer + offset, n_read - offset);
222             break;
223
224         default:
225             g_debug ("Ignoring unknown XDMCP opcode %d", opcode);
226             break;
227         }
228     }
229
230     return TRUE;
231 }
232
233 static gboolean
234 xdmcp_query_cb (gpointer data)
235 {
236     XDMCPClient *client = data;
237     g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_QUERY], 0);
238     xdmcp_client_send_query (client);
239     return TRUE;
240 }
241
242 XDMCPClient *
243 xdmcp_client_new (void)
244 {
245     return g_object_new (xdmcp_client_get_type (), NULL);
246 }
247
248 void
249 xdmcp_client_set_hostname (XDMCPClient *client, const gchar *hostname)
250 {
251     g_free (client->priv->host);
252     client->priv->host = g_strdup (hostname);
253 }
254
255 void
256 xdmcp_client_set_port (XDMCPClient *client, guint16 port)
257 {
258     client->priv->port = port;
259 }
260
261 gboolean
262 xdmcp_client_start (XDMCPClient *client)
263 {
264     GSocketConnectable *address;
265     GSocketAddressEnumerator *enumerator;
266     gboolean result;
267     GError *error = NULL;
268
269     client->priv->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error);
270     if (error)
271         g_warning ("Error creating XDMCP socket: %s", error->message);
272     if (!client->priv->socket)
273         return FALSE;
274
275     address = g_network_address_new (client->priv->host, client->priv->port);
276     enumerator = g_socket_connectable_enumerate (address);
277     result = FALSE;
278     while (TRUE)
279     {
280         GSocketAddress *socket_address;
281         GError *e = NULL;
282
283         socket_address = g_socket_address_enumerator_next (enumerator, NULL, &e);
284         if (e)
285             g_warning ("Failed to get socket address: %s", e->message);
286         g_clear_error (&e);
287         if (!socket_address)
288             break;
289
290         result = g_socket_connect (client->priv->socket, socket_address, NULL, error ? NULL : &error);
291         g_object_unref (socket_address);
292         if (result)
293         {
294             g_clear_error (&error);
295             break;
296         }
297     }
298     if (error)
299         g_warning ("Unable to connect XDMCP socket: %s", error->message);
300     if (!result)
301         return FALSE;
302
303     g_io_add_watch (g_io_channel_unix_new (g_socket_get_fd (client->priv->socket)), G_IO_IN, xdmcp_data_cb, client);
304
305     client->priv->query_timer = g_timeout_add (2000, xdmcp_query_cb, client);
306     xdmcp_query_cb (client);
307
308     return TRUE;
309 }
310
311 GInetAddress *
312 xdmcp_client_get_local_address (XDMCPClient *client)
313 {
314     GSocketAddress *socket_address;
315
316     if (!client->priv->socket)
317         return NULL;
318
319     socket_address = g_socket_get_local_address (client->priv->socket, NULL);
320     return g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address));
321 }
322
323 static void
324 xdmcp_client_init (XDMCPClient *client)
325 {
326     client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, xdmcp_client_get_type (), XDMCPClientPrivate);
327     client->priv->port = XDMCP_PORT;
328 }
329
330 void
331 xdmcp_client_send_query (XDMCPClient *client)
332 {
333     guint8 buffer[MAXIMUM_REQUEST_LENGTH];
334     gsize offset = 0;
335
336     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset);
337     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Query, &offset);
338     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 1, &offset);
339     write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &offset);
340
341     xdmcp_write (client, buffer, offset);
342 }
343
344 void
345 xdmcp_client_send_request (XDMCPClient *client,
346                            guint16 display_number,
347                            GInetAddress **addresses,
348                            const gchar *authentication_name,
349                            const guint8 *authentication_data, guint16 authentication_data_length,
350                            gchar **authorization_names, const gchar *mfid)
351 {
352     guint8 buffer[MAXIMUM_REQUEST_LENGTH];
353     gsize length = 0, offset = 0, n_addresses = 0, n_names = 0;
354     GInetAddress **address;
355     gchar **name;
356
357     length = 11 + strlen (authentication_name) + authentication_data_length + strlen (mfid);
358     for (address = addresses; *address; address++)
359     {
360         gssize native_address_length = g_inet_address_get_native_size (*address);
361         length += 4 + native_address_length;
362         n_addresses++;
363     }
364     for (name = authorization_names; *name; name++)
365     {
366         length += 2 + strlen (*name);
367         n_names++;
368     }
369
370     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset);
371     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Request, &offset);
372     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, length, &offset);
373
374     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, display_number, &offset);
375     write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_addresses, &offset);
376     for (address = addresses; *address; address++)
377         write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 0, &offset); /* FamilyInternet */
378     write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_addresses, &offset);
379     for (address = addresses; *address; address++)
380     {
381         gssize native_address_length;
382         const guint8 *native_address;
383
384         native_address_length = g_inet_address_get_native_size (*address);
385         native_address = g_inet_address_to_bytes (*address);
386         write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, native_address_length, &offset);
387         write_string8 (buffer, MAXIMUM_REQUEST_LENGTH, native_address, native_address_length, &offset);
388     }
389     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (authentication_name), &offset);
390     write_string (buffer, MAXIMUM_REQUEST_LENGTH, authentication_name, &offset);
391     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, authentication_data_length, &offset);
392     write_string8 (buffer, MAXIMUM_REQUEST_LENGTH, authentication_data, authentication_data_length, &offset);
393     write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_names, &offset);
394     for (name = authorization_names; *name; name++)
395     {
396         write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (*name), &offset);
397         write_string (buffer, MAXIMUM_REQUEST_LENGTH, *name, &offset);
398     }
399     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (mfid), &offset);
400     write_string (buffer, MAXIMUM_REQUEST_LENGTH, mfid, &offset);
401
402     xdmcp_write (client, buffer, offset);
403 }
404
405 void
406 xdmcp_client_send_manage (XDMCPClient *client, guint32 session_id, guint16 display_number, gchar *display_class)
407 {
408     guint8 buffer[MAXIMUM_REQUEST_LENGTH];
409     gsize offset = 0;
410
411     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset);
412     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Manage, &offset);
413     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 8 + strlen (display_class), &offset);
414
415     write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, session_id, &offset);
416     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, display_number, &offset);
417     write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (display_class), &offset);
418     write_string (buffer, MAXIMUM_REQUEST_LENGTH, display_class, &offset);
419
420     xdmcp_write (client, buffer, offset);
421 }
422
423 static void
424 xdmcp_client_finalize (GObject *object)
425 {
426     XDMCPClient *client = (XDMCPClient *) object;
427     g_free (client->priv->host);
428     if (client->priv->socket)
429         g_object_unref (client->priv->socket);
430     g_free (client->priv->authorization_name);
431     g_free (client->priv->authorization_data);
432 }
433
434 static void
435 xdmcp_client_class_init (XDMCPClientClass *klass)
436 {
437     GObjectClass *object_class = G_OBJECT_CLASS (klass);
438     object_class->finalize = xdmcp_client_finalize;
439     g_type_class_add_private (klass, sizeof (XDMCPClientPrivate));
440     xdmcp_client_signals[XDMCP_CLIENT_QUERY] =
441         g_signal_new ("query",
442                       G_TYPE_FROM_CLASS (klass),
443                       G_SIGNAL_RUN_LAST,
444                       G_STRUCT_OFFSET (XDMCPClientClass, query),
445                       NULL, NULL,
446                       NULL,
447                       G_TYPE_NONE, 0);
448     xdmcp_client_signals[XDMCP_CLIENT_WILLING] =
449         g_signal_new ("willing",
450                       G_TYPE_FROM_CLASS (klass),
451                       G_SIGNAL_RUN_LAST,
452                       G_STRUCT_OFFSET (XDMCPClientClass, willing),
453                       NULL, NULL,
454                       NULL,
455                       G_TYPE_NONE, 1, G_TYPE_POINTER);
456     xdmcp_client_signals[XDMCP_CLIENT_ACCEPT] =
457         g_signal_new ("accept",
458                       G_TYPE_FROM_CLASS (klass),
459                       G_SIGNAL_RUN_LAST,
460                       G_STRUCT_OFFSET (XDMCPClientClass, accept),
461                       NULL, NULL,
462                       NULL,
463                       G_TYPE_NONE, 1, G_TYPE_POINTER);
464     xdmcp_client_signals[XDMCP_CLIENT_DECLINE] =
465         g_signal_new ("decline",
466                       G_TYPE_FROM_CLASS (klass),
467                       G_SIGNAL_RUN_LAST,
468                       G_STRUCT_OFFSET (XDMCPClientClass, decline),
469                       NULL, NULL,
470                       NULL,
471                       G_TYPE_NONE, 1, G_TYPE_POINTER);
472     xdmcp_client_signals[XDMCP_CLIENT_FAILED] =
473         g_signal_new ("failed",
474                       G_TYPE_FROM_CLASS (klass),
475                       G_SIGNAL_RUN_LAST,
476                       G_STRUCT_OFFSET (XDMCPClientClass, failed),
477                       NULL, NULL,
478                       NULL,
479                       G_TYPE_NONE, 1, G_TYPE_POINTER);
480 }