]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/xdmcp-server.c
Remove warning about Mir IDs when not using Mir
[sojka/lightdm.git] / src / xdmcp-server.c
1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <X11/X.h>
15 #define HASXDMAUTH
16 #include <X11/Xdmcp.h>
17 #include <gio/gio.h>
18
19 #include "xdmcp-server.h"
20 #include "xdmcp-protocol.h"
21 #include "xdmcp-session-private.h"
22 #include "x-authority.h"
23
24 enum {
25     NEW_SESSION,
26     LAST_SIGNAL
27 };
28 static guint signals[LAST_SIGNAL] = { 0 };
29
30 struct XDMCPServerPrivate
31 {
32     /* Port to listen on */
33     guint port;
34
35     /* Address to listen on */
36     gchar *listen_address;
37
38     /* Listening sockets */
39     GSocket *socket, *socket6;
40
41     /* Hostname to report to client */
42     gchar *hostname;
43
44     /* Status to report to clients */
45     gchar *status;
46
47     /* XDM-AUTHENTICATION-1 key */
48     gchar *key;
49
50     /* Active XDMCP sessions */
51     GHashTable *sessions;
52 };
53
54 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
55
56 /* Maximum number of milliseconds client will resend manage requests before giving up */
57 #define MANAGE_TIMEOUT 126000
58
59 /* Address sort support structure */
60 typedef struct
61 {
62     gsize index;
63     GInetAddress *address;
64 } AddrSortItem;
65
66 XDMCPServer *
67 xdmcp_server_new (void)
68 {
69     return g_object_new (XDMCP_SERVER_TYPE, NULL);
70 }
71
72 void
73 xdmcp_server_set_port (XDMCPServer *server, guint port)
74 {
75     g_return_if_fail (server != NULL);
76     server->priv->port = port;
77 }
78
79 guint
80 xdmcp_server_get_port (XDMCPServer *server)
81 {
82     g_return_val_if_fail (server != NULL, 0);
83     return server->priv->port;
84 }
85
86 void
87 xdmcp_server_set_listen_address (XDMCPServer *server, const gchar *listen_address)
88 {
89     g_return_if_fail (server != NULL);
90
91     g_free (server->priv->listen_address);
92     server->priv->listen_address = g_strdup (listen_address);
93 }
94
95 const gchar *
96 xdmcp_server_get_listen_address (XDMCPServer *server)
97 {
98     g_return_val_if_fail (server != NULL, NULL);
99     return server->priv->listen_address;
100 }
101
102 void
103 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
104 {
105     g_return_if_fail (server != NULL);
106
107     g_free (server->priv->hostname);
108     server->priv->hostname = g_strdup (hostname);
109 }
110
111 const gchar *
112 xdmcp_server_get_hostname (XDMCPServer *server)
113 {
114     g_return_val_if_fail (server != NULL, NULL);
115     return server->priv->hostname;
116 }
117
118 void
119 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
120 {
121     g_return_if_fail (server != NULL);
122
123     g_free (server->priv->status);
124     server->priv->status = g_strdup (status);
125 }
126
127 const gchar *
128 xdmcp_server_get_status (XDMCPServer *server)
129 {
130     g_return_val_if_fail (server != NULL, NULL);
131     return server->priv->status;
132 }
133
134 void
135 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
136 {
137     g_return_if_fail (server != NULL);
138     g_free (server->priv->key);
139     server->priv->key = g_strdup (key);
140 }
141
142 static gboolean
143 session_timeout_cb (XDMCPSession *session)
144 {
145     session->priv->inactive_timeout = 0;
146
147     g_debug ("Timing out unmanaged session %d", session->priv->id);
148     g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
149     return FALSE;
150 }
151
152 static XDMCPSession *
153 add_session (XDMCPServer *server)
154 {
155     XDMCPSession *session;
156     guint16 id;
157
158     do
159     {
160         id = g_random_int () & 0xFFFFFFFF;
161     } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
162
163     session = xdmcp_session_new (id);
164     session->priv->server = server;
165     g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
166     session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
167
168     return session;
169 }
170
171 static XDMCPSession *
172 get_session (XDMCPServer *server, guint16 id)
173 {
174     return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
175 }
176
177 static gchar *
178 socket_address_to_string (GSocketAddress *address)
179 {
180     gchar *inet_text, *text;
181
182     inet_text = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
183     text = g_strdup_printf ("%s:%d", inet_text, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)));
184     g_free (inet_text);
185
186     return text;
187 }
188
189 static void
190 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
191 {
192     gchar *address_string;
193     guint8 data[1024];
194     gssize n_written;
195
196     address_string = socket_address_to_string (address);
197     g_debug ("Send %s to %s", xdmcp_packet_tostring (packet), address_string);
198     g_free (address_string);
199
200     n_written = xdmcp_packet_encode (packet, data, 1024);
201     if (n_written < 0)
202       g_critical ("Failed to encode XDMCP packet");
203     else
204     {
205         GError *error = NULL;
206
207         g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
208         if (error)
209             g_warning ("Error sending packet: %s", error->message);
210         g_clear_error (&error);
211     }
212 }
213
214 static const gchar *
215 get_authentication_name (XDMCPServer *server)
216 {
217     if (server->priv->key)
218         return "XDM-AUTHENTICATION-1";
219     else
220         return "";
221 }
222
223 static void
224 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, gchar **authentication_names)
225 {
226     XDMCPPacket *response;
227     gchar **i;
228     gchar *authentication_name = NULL;
229
230     /* If no authentication requested and we are configured for none then allow */
231     if (authentication_names[0] == NULL && server->priv->key == NULL)
232         authentication_name = "";
233
234     for (i = authentication_names; *i; i++)
235     {
236         if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
237         {
238             authentication_name = *i;
239             break;
240         }
241     }
242
243     if (authentication_name)
244     {
245         response = xdmcp_packet_alloc (XDMCP_Willing);
246         response->Willing.authentication_name = g_strdup (authentication_name);
247         response->Willing.hostname = g_strdup (server->priv->hostname);
248         response->Willing.status = g_strdup (server->priv->status);
249     }
250     else
251     {
252         response = xdmcp_packet_alloc (XDMCP_Unwilling);
253         response->Unwilling.hostname = g_strdup (server->priv->hostname);
254         if (server->priv->key)
255             response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
256         else
257             response->Unwilling.status = g_strdup ("No matching authentication");
258     }
259
260     send_packet (socket, address, response);
261
262     xdmcp_packet_free (response);
263 }
264
265 static void
266 handle_forward_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
267 {
268     GSocketFamily family;
269     GInetAddress *client_inet_address;
270     GSocketAddress *client_address;
271     gint i;
272     guint16 port = 0;
273
274     family = g_socket_get_family (socket);
275     switch (family)
276     {
277     case G_SOCKET_FAMILY_IPV4:
278         if (packet->ForwardQuery.client_address.length != 4)
279         {
280             g_warning ("Ignoring IPv4 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
281             return;
282         }
283         break;
284     case G_SOCKET_FAMILY_IPV6:
285         if (packet->ForwardQuery.client_address.length != 16)
286         {
287             g_warning ("Ignoring IPv6 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
288             return;
289         }
290         break;
291     default:
292         g_warning ("Unknown socket family %d", family);
293         return;
294     }
295
296     for (i = 0; i < packet->ForwardQuery.client_port.length; i++)
297         port = port << 8 | packet->ForwardQuery.client_port.data[i];
298
299     client_inet_address = g_inet_address_new_from_bytes (packet->ForwardQuery.client_address.data, family);
300     client_address = g_inet_socket_address_new (client_inet_address, port);
301     g_object_unref (client_inet_address);
302
303     handle_query (server, socket, client_address, packet->ForwardQuery.authentication_names);
304
305     g_object_unref (client_address);
306 }
307
308 static guint8
309 atox (char c)
310 {
311     if (c >= '0' && c <= '9')
312         return c - '0';
313     if (c >= 'a' && c <= 'f')
314         return c - 'a' + 10;
315     if (c >= 'A' && c <= 'F')
316         return c - 'A' + 10;
317     return 0;
318 }
319
320 static void
321 decode_key (const gchar *key, guint8 *data)
322 {
323     gint i;
324
325     memset (data, 0, 8);
326     if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
327     {
328         for (i = 0; i < 8; i++)
329         {
330             if (key[i*2] == '\0')
331                 break;
332             data[i] |= atox (key[i*2]) << 8;
333             if (key[i*2+1] == '\0')
334                 break;
335             data[i] |= atox (key[i*2+1]);
336         }
337     }
338     else
339     {
340         for (i = 1; i < 8 && key[i-1]; i++)
341            data[i] = key[i-1];
342     }
343 }
344
345 static GInetAddress *
346 connection_to_address (XDMCPConnection *connection)
347 {
348     switch (connection->type)
349     {
350     case XAUTH_FAMILY_INTERNET:
351         if (connection->address.length == 4)
352             return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
353         else
354             return NULL;
355     case XAUTH_FAMILY_INTERNET6:
356         if (connection->address.length == 16)
357             return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
358         else
359             return NULL;
360     default:
361         return NULL;
362     }
363 }
364
365 /* Sort function to order XDMCP addresses by which is best to connect to */
366 static gint
367 compare_addresses (gconstpointer a, gconstpointer b, gpointer user_data)
368 {
369     const AddrSortItem *item_a = a;
370     const AddrSortItem *item_b = b;
371     GInetAddress *source_address = user_data;
372     GSocketFamily family_a;
373     GSocketFamily family_b;
374     gboolean is_link_local;
375
376     /* Prefer non link-local addresses */
377     is_link_local = g_inet_address_get_is_link_local (item_a->address);
378     if (is_link_local != g_inet_address_get_is_link_local (item_b->address))
379         return is_link_local ? 1 : -1;
380
381     /* Prefer the source address family */
382     family_a = g_inet_address_get_family (item_a->address);
383     family_b = g_inet_address_get_family (item_b->address);
384     if (family_a != family_b)
385     {
386         GSocketFamily family;
387
388         family = g_inet_address_get_family (source_address);
389         if (family_a == family)
390             return -1;
391         if (family_b == family)
392             return 1;
393         return family_a < family_b ? -1 : 1;
394     }
395
396     /* Check equality */
397     if (g_inet_address_equal (item_a->address, item_b->address))
398         return 0;
399
400     /* Prefer the source address */
401     if (g_inet_address_equal (source_address, item_a->address))
402         return -1;
403     if (g_inet_address_equal (source_address, item_b->address))
404         return 1;
405
406     /* Addresses are not equal, but preferences are: order is undefined */
407     return 0;
408 }
409
410 static XDMCPConnection *
411 choose_connection (XDMCPPacket *packet, GInetAddress *source_address)
412 {
413     gsize addresses_length, i;
414     GArray *addresses;
415     gssize index = -1;
416     AddrSortItem addr;
417
418     addresses_length = packet->Request.n_connections;
419     if (addresses_length == 0)
420         return NULL;
421
422     addresses = g_array_sized_new (FALSE, FALSE, sizeof addr, addresses_length);
423     if (!addresses)
424         return NULL;
425
426     for (i = 0; i < addresses_length; i++)
427     {
428         addr.address = connection_to_address (&packet->Request.connections[i]);
429         if (addr.address)
430         {
431             addr.index = i;
432             g_array_append_val (addresses, addr);
433         }
434     }
435
436     /* Sort the addresses according to our preferences */
437     g_array_sort_with_data (addresses, compare_addresses, source_address);
438
439     /* Use the best address */
440     if (addresses->len)
441         index = g_array_index (addresses, AddrSortItem, 0).index;
442
443     /* Free the local sort array and items */
444     for (i = 0; i < addresses->len; i++)
445         g_object_unref (g_array_index (addresses, AddrSortItem, i).address);
446     g_object_unref (addresses);
447
448     return index >= 0 ? &packet->Request.connections[index] : NULL;
449 }
450
451 static gboolean
452 has_string (gchar **list, const gchar *text)
453 {
454     gchar **i;
455
456     for (i = list; *i; i++)
457         if (strcmp (*i, text) == 0)
458             return TRUE;
459   
460     return FALSE;
461 }
462
463 static void
464 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
465 {
466     XDMCPPacket *response;
467     XDMCPSession *session;
468     gchar *authentication_name = NULL, *decline_status = NULL, *authorization_name, *display_number;
469     guint8 *authentication_data = NULL, *authorization_data = NULL, *session_authorization_data = NULL;
470     gsize authentication_data_length = 0, authorization_data_length = 0, session_authorization_data_length = 0;
471     XDMCPConnection *connection;
472     XdmAuthKeyRec rho;
473
474     /* Check authentication */
475     if (strcmp (packet->Request.authentication_name, "") == 0)
476     {
477         if (!server->priv->key)
478         {
479             if (!has_string (packet->Request.authorization_names, "MIT-MAGIC-COOKIE-1"))
480                 decline_status = g_strdup ("No matching authorization, server requires MIT-MAGIC-COOKIE-1");
481         }
482         else
483             decline_status = g_strdup ("No matching authentication, server requires XDM-AUTHENTICATION-1");
484     }
485     else if (strcmp (packet->Request.authentication_name, "XDM-AUTHENTICATION-1") == 0 && server->priv->key)
486     {
487         if (packet->Request.authentication_data.length == 8)
488         {
489             guint8 input[8], key[8];
490
491             memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length);
492
493             /* Setup key */
494             decode_key (server->priv->key, key);
495
496             /* Decode message from server */
497             authentication_name = g_strdup ("XDM-AUTHENTICATION-1");
498             authentication_data = g_malloc (sizeof (guint8) * 8);
499             authentication_data_length = 8;
500     
501             XdmcpUnwrap (input, key, rho.data, authentication_data_length);
502             XdmcpIncrementKey (&rho);
503             XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
504
505             if (!has_string (packet->Request.authorization_names, "XDM-AUTHORIZATION-1"))
506                 decline_status = g_strdup ("No matching authorization, server requires XDM-AUTHORIZATION-1");
507         }
508         else
509             decline_status = g_strdup ("Invalid XDM-AUTHENTICATION-1 data provided");
510     }
511     else
512     {
513         if (strcmp (packet->Request.authentication_name, "") == 0)
514             decline_status = g_strdup_printf ("No matching authentication, server does not support unauthenticated connections");
515         else if (server->priv->key)
516             decline_status = g_strdup ("No matching authentication, server requires XDM-AUTHENTICATION-1");
517         else
518             decline_status = g_strdup ("No matching authentication, server only supports unauthenticated connections");
519     }
520
521     /* Choose an address to connect back on */
522     connection = choose_connection (packet, g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
523     if (!connection && !decline_status)
524         decline_status = g_strdup ("No valid address found");
525
526     if (!authentication_name)
527         authentication_name = g_strdup ("");
528
529     /* Decline if request was not valid */
530     if (decline_status)
531     {
532         response = xdmcp_packet_alloc (XDMCP_Decline);
533         response->Decline.status = decline_status;
534         response->Decline.authentication_name = authentication_name;
535         response->Decline.authentication_data.data = authentication_data;
536         response->Decline.authentication_data.length = authentication_data_length;
537         send_packet (socket, address, response);
538         xdmcp_packet_free (response);
539         return;
540     }
541
542     /* Generate authorization data */
543     if (server->priv->key)
544     {
545         gint i;
546         guint8 key[8], session_key[8];
547
548         /* Setup key */
549         decode_key (server->priv->key, key);
550
551         /* Generate a private session key */
552         // FIXME: Pick a good DES key?
553         session_key[0] = 0;
554         for (i = 1; i < 8; i++)
555             session_key[i] = g_random_int () & 0xFF;
556
557         /* Encrypt the session key and send it to the server */
558         authorization_data = g_malloc (8);
559         authorization_data_length = 8;
560         XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
561
562         /* Authorization data is the number received from the client followed by the private session key */
563         authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
564         session_authorization_data = g_malloc (16);
565         session_authorization_data_length = 16;
566         XdmcpDecrementKey (&rho);
567         memcpy (session_authorization_data, rho.data, 8);
568         memcpy (session_authorization_data + 8, session_key, 8);
569     }
570     else
571     {
572         XAuthority *auth;
573
574         /* Data is the cookie */
575         auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
576         authorization_data = x_authority_copy_authorization_data (auth);
577         authorization_data_length = x_authority_get_authorization_data_length (auth);
578         authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
579         session_authorization_data = x_authority_copy_authorization_data (auth);
580         session_authorization_data_length = x_authority_get_authorization_data_length (auth);
581
582         g_object_unref (auth);
583     }
584
585     session = add_session (server);
586     session->priv->address = connection_to_address (connection);
587     session->priv->display_number = packet->Request.display_number;
588     display_number = g_strdup_printf ("%d", packet->Request.display_number);
589
590     /* We need to check if this is the loopback address and set the authority
591      * for a local connection if this is so as XCB treats "127.0.0.1" as local
592      * always */
593     if (g_inet_address_get_is_loopback (session->priv->address))
594     {
595         gchar hostname[1024];
596         gethostname (hostname, 1024);
597
598         session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
599                                                     (guint8 *) hostname,
600                                                     strlen (hostname),
601                                                     display_number,
602                                                     authorization_name,
603                                                     session_authorization_data,
604                                                     session_authorization_data_length);
605     }
606     else
607         session->priv->authority = x_authority_new (connection->type,
608                                                     connection->address.data,
609                                                     connection->address.length,
610                                                     display_number,
611                                                     authorization_name,
612                                                     session_authorization_data,
613                                                     session_authorization_data_length);
614     g_free (display_number);
615
616     response = xdmcp_packet_alloc (XDMCP_Accept);
617     response->Accept.session_id = xdmcp_session_get_id (session);
618     response->Accept.authentication_name = authentication_name;
619     response->Accept.authentication_data.data = authentication_data;
620     response->Accept.authentication_data.length = authentication_data_length;
621     response->Accept.authorization_name = authorization_name;
622     response->Accept.authorization_data.data = authorization_data;
623     response->Accept.authorization_data.length = authorization_data_length;
624     send_packet (socket, address, response);
625     xdmcp_packet_free (response);
626 }
627
628 static void
629 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
630 {
631     XDMCPSession *session;
632     gboolean result;
633
634     session = get_session (server, packet->Manage.session_id);
635     if (!session)
636     {
637         XDMCPPacket *response;
638
639         response = xdmcp_packet_alloc (XDMCP_Refuse);
640         response->Refuse.session_id = packet->Manage.session_id;
641         send_packet (socket, address, response);
642         xdmcp_packet_free (response);
643
644         return;
645     }
646
647     /* Ignore duplicate requests */
648     if (session->priv->started)
649     {
650         if (session->priv->display_number != packet->Manage.display_number ||
651             strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
652             g_debug ("Ignoring duplicate Manage with different data");
653         return;
654     }
655
656     /* Reject if has changed display number */
657     if (packet->Manage.display_number != session->priv->display_number)
658     {
659         XDMCPPacket *response;
660
661         g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
662         response = xdmcp_packet_alloc (XDMCP_Refuse);
663         response->Refuse.session_id = packet->Manage.session_id;
664         send_packet (socket, address, response);
665         xdmcp_packet_free (response);
666     }
667
668     session->priv->display_class = g_strdup (packet->Manage.display_class);
669
670     g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
671     if (result)
672     {
673         /* Cancel the inactive timer */
674         if (session->priv->inactive_timeout)
675             g_source_remove (session->priv->inactive_timeout);
676
677         session->priv->started = TRUE;
678     }
679     else
680     {
681         XDMCPPacket *response;
682
683         response = xdmcp_packet_alloc (XDMCP_Failed);
684         response->Failed.session_id = packet->Manage.session_id;
685         response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
686         send_packet (socket, address, response);
687         xdmcp_packet_free (response);
688     }
689 }
690
691 static void
692 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
693 {
694     XDMCPPacket *response;
695     XDMCPSession *session;
696     gboolean alive = FALSE;
697
698     session = get_session (server, packet->KeepAlive.session_id);
699     if (session)
700         alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
701
702     response = xdmcp_packet_alloc (XDMCP_Alive);
703     response->Alive.session_running = alive;
704     response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
705     send_packet (socket, address, response);
706     xdmcp_packet_free (response);
707 }
708
709 static gboolean
710 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
711 {
712     GSocketAddress *address;
713     gchar data[1024];
714     GError *error = NULL;
715     gssize n_read;
716
717     n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
718     if (error)
719         g_warning ("Failed to read from XDMCP socket: %s", error->message);
720     g_clear_error (&error);
721
722     if (n_read > 0)
723     {
724         XDMCPPacket *packet;
725
726         packet = xdmcp_packet_decode ((guint8 *)data, n_read);
727         if (packet)
728         {
729             gchar *packet_string, *address_string;
730
731             packet_string = xdmcp_packet_tostring (packet);
732             address_string = socket_address_to_string (address);
733             g_debug ("Got %s from %s", packet_string, address_string);
734             g_free (packet_string);
735             g_free (address_string);
736
737             switch (packet->opcode)
738             {
739             case XDMCP_BroadcastQuery:
740             case XDMCP_Query:
741             case XDMCP_IndirectQuery:
742                 handle_query (server, socket, address, packet->Query.authentication_names);
743                 break;
744             case XDMCP_ForwardQuery:
745                 handle_forward_query (server, socket, address, packet);
746                 break;
747             case XDMCP_Request:
748                 handle_request (server, socket, address, packet);
749                 break;
750             case XDMCP_Manage:
751                 handle_manage (server, socket, address, packet);
752                 break;
753             case XDMCP_KeepAlive:
754                 handle_keep_alive (server, socket, address, packet);
755                 break;
756             default:
757                 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
758                 break;
759             }
760
761             xdmcp_packet_free (packet);
762         }
763     }
764
765     return TRUE;
766 }
767
768 static GSocket *
769 open_udp_socket (GSocketFamily family, guint port, const gchar *listen_address, GError **error)
770 {
771     GSocket *socket;
772     GSocketAddress *address;
773     gboolean result;
774
775     socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
776     if (!socket)
777         return NULL;
778
779     if (listen_address) 
780     {
781         GList *addresses;
782
783         addresses = g_resolver_lookup_by_name (g_resolver_get_default (), listen_address, NULL, error);
784         if (!addresses)
785         {
786             g_object_unref (socket);
787             return NULL;
788         }
789         address = g_inet_socket_address_new (addresses->data, port);
790         g_resolver_free_addresses (addresses);
791     }
792     else
793         address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
794     result = g_socket_bind (socket, address, TRUE, error);
795     if (!result)
796     {
797         g_object_unref (socket);
798         return NULL;
799     }
800
801     return socket;
802 }
803
804 gboolean
805 xdmcp_server_start (XDMCPServer *server)
806 {
807     GSource *source;
808     GError *error = NULL;
809
810     g_return_val_if_fail (server != NULL, FALSE);
811
812     server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, server->priv->listen_address, &error);
813     if (error)
814         g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
815     g_clear_error (&error);
816
817     if (server->priv->socket)
818     {
819         source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
820         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
821         g_source_attach (source, NULL);
822     }
823
824     server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, server->priv->listen_address, &error);
825     if (error)
826         g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
827     g_clear_error (&error);
828
829     if (server->priv->socket6)
830     {
831         source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
832         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
833         g_source_attach (source, NULL);
834     }
835
836     if (!server->priv->socket && !server->priv->socket6)
837         return FALSE;
838
839     return TRUE;
840 }
841
842 static void
843 xdmcp_server_init (XDMCPServer *server)
844 {
845     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
846
847     server->priv->port = XDM_UDP_PORT;
848     server->priv->hostname = g_strdup ("");
849     server->priv->status = g_strdup ("");
850     server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
851 }
852
853 static void
854 xdmcp_server_finalize (GObject *object)
855 {
856     XDMCPServer *self = XDMCP_SERVER (object);
857
858     g_clear_object (&self->priv->socket);
859     g_clear_object (&self->priv->socket6);
860     g_free (self->priv->listen_address);
861     g_free (self->priv->hostname);
862     g_free (self->priv->status);
863     g_free (self->priv->key);
864     g_hash_table_unref (self->priv->sessions);
865
866     G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
867 }
868
869 static void
870 xdmcp_server_class_init (XDMCPServerClass *klass)
871 {
872     GObjectClass *object_class = G_OBJECT_CLASS (klass);
873
874     object_class->finalize = xdmcp_server_finalize;
875
876     g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
877
878     signals[NEW_SESSION] =
879         g_signal_new (XDMCP_SERVER_SIGNAL_NEW_SESSION,
880                       G_TYPE_FROM_CLASS (klass),
881                       G_SIGNAL_RUN_LAST,
882                       G_STRUCT_OFFSET (XDMCPServerClass, new_session),
883                       g_signal_accumulator_true_handled,
884                       NULL,
885                       NULL,
886                       G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);
887 }