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