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