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