Back to index

lightdm  1.3.2
xdmcp-server.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 Robert Ancell.
00003  * Author: Robert Ancell <robert.ancell@canonical.com>
00004  * 
00005  * This program is free software: you can redistribute it and/or modify it under
00006  * the terms of the GNU General Public License as published by the Free Software
00007  * Foundation, either version 3 of the License, or (at your option) any later
00008  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
00009  * license.
00010  */
00011 
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <X11/X.h>
00015 #define HASXDMAUTH
00016 #include <X11/Xdmcp.h>
00017 #include <gio/gio.h>
00018 #include "ldm-marshal.h"
00019 
00020 #include "xdmcp-server.h"
00021 #include "xdmcp-protocol.h"
00022 #include "xdmcp-session-private.h"
00023 #include "xauthority.h"
00024 
00025 enum {
00026     NEW_SESSION,
00027     LAST_SIGNAL
00028 };
00029 static guint signals[LAST_SIGNAL] = { 0 };
00030 
00031 struct XDMCPServerPrivate
00032 {
00033     /* Port to listen on */
00034     guint port;
00035 
00036     /* Listening sockets */
00037     GSocket *socket, *socket6;
00038 
00039     /* Hostname to report to client */
00040     gchar *hostname;
00041 
00042     /* Status to report to clients */
00043     gchar *status;
00044 
00045     /* XDM-AUTHENTICATION-1 key */
00046     gchar *key;
00047 
00048     /* Active XDMCP sessions */
00049     GHashTable *sessions;
00050 };
00051 
00052 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
00053 
00054 /* Maximum number of milliseconds client will resend manage requests before giving up */
00055 #define MANAGE_TIMEOUT 126000
00056 
00057 XDMCPServer *
00058 xdmcp_server_new (void)
00059 {
00060     return g_object_new (XDMCP_SERVER_TYPE, NULL);
00061 }
00062 
00063 void
00064 xdmcp_server_set_port (XDMCPServer *server, guint port)
00065 {
00066     g_return_if_fail (server != NULL);
00067     server->priv->port = port;
00068 }
00069 
00070 guint
00071 xdmcp_server_get_port (XDMCPServer *server)
00072 {
00073     g_return_val_if_fail (server != NULL, 0);
00074     return server->priv->port;
00075 }
00076 
00077 void
00078 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
00079 {
00080     g_return_if_fail (server != NULL);
00081 
00082     g_free (server->priv->hostname);
00083     server->priv->hostname = g_strdup (hostname);
00084 }
00085 
00086 const gchar *
00087 xdmcp_server_get_hostname (XDMCPServer *server)
00088 {
00089     g_return_val_if_fail (server != NULL, NULL);
00090     return server->priv->hostname;
00091 }
00092 
00093 void
00094 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
00095 {
00096     g_return_if_fail (server != NULL);
00097 
00098     g_free (server->priv->status);
00099     server->priv->status = g_strdup (status);
00100 }
00101 
00102 const gchar *
00103 xdmcp_server_get_status (XDMCPServer *server)
00104 {
00105     g_return_val_if_fail (server != NULL, NULL);
00106     return server->priv->status;
00107 }
00108 
00109 void
00110 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
00111 {
00112     g_return_if_fail (server != NULL);
00113     g_free (server->priv->key);
00114     server->priv->key = g_strdup (key);
00115 }
00116 
00117 static gboolean
00118 session_timeout_cb (XDMCPSession *session)
00119 {
00120     g_debug ("Timing out unmanaged session %d", session->priv->id);
00121     g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
00122     return FALSE;
00123 }
00124 
00125 static XDMCPSession *
00126 add_session (XDMCPServer *server)
00127 {
00128     XDMCPSession *session;
00129     guint16 id;
00130 
00131     do
00132     {
00133         id = g_random_int () & 0xFFFFFFFF;
00134     } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
00135 
00136     session = xdmcp_session_new (id);
00137     session->priv->server = server;
00138     g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
00139     session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
00140 
00141     return session;
00142 }
00143 
00144 static XDMCPSession *
00145 get_session (XDMCPServer *server, guint16 id)
00146 {
00147     return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
00148 }
00149 
00150 static void
00151 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
00152 {
00153     guint8 data[1024];
00154     gssize n_written;
00155 
00156     g_debug ("Send %s", xdmcp_packet_tostring (packet));
00157               
00158     n_written = xdmcp_packet_encode (packet, data, 1024);
00159     if (n_written < 0)
00160       g_critical ("Failed to encode XDMCP packet");
00161     else
00162     {
00163         GError *error = NULL;
00164 
00165         g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
00166         if (error)
00167             g_warning ("Error sending packet: %s", error->message);
00168         g_clear_error (&error);
00169     }
00170 }
00171 
00172 static const gchar *
00173 get_authentication_name (XDMCPServer *server)
00174 {
00175     if (server->priv->key)
00176         return "XDM-AUTHENTICATION-1";
00177     else
00178         return "";
00179 }
00180 
00181 static void
00182 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
00183 {
00184     XDMCPPacket *response;
00185     gchar **i;
00186     gchar *authentication_name = NULL;
00187 
00188     /* If no authentication requested and we are configured for none then allow */
00189     if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
00190         authentication_name = "";
00191 
00192     for (i = packet->Query.authentication_names; *i; i++)
00193     {
00194         if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
00195         {
00196             authentication_name = *i;
00197             break;
00198         }
00199     }
00200 
00201     if (authentication_name)
00202     {
00203         response = xdmcp_packet_alloc (XDMCP_Willing);
00204         response->Willing.authentication_name = g_strdup (authentication_name);
00205         response->Willing.hostname = g_strdup (server->priv->hostname);
00206         response->Willing.status = g_strdup (server->priv->status);
00207     }
00208     else
00209     {
00210         response = xdmcp_packet_alloc (XDMCP_Unwilling);
00211         response->Unwilling.hostname = g_strdup (server->priv->hostname);
00212         if (server->priv->key)
00213             response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
00214         else
00215             response->Unwilling.status = g_strdup ("Server does not support authentication");
00216     }
00217   
00218     send_packet (socket, address, response);
00219 
00220     xdmcp_packet_free (response);
00221 }
00222 
00223 static guint8
00224 atox (char c)
00225 {
00226     if (c >= '0' && c <= '9')
00227         return c - '0';
00228     if (c >= 'a' && c <= 'f')
00229         return c - 'a' + 10;
00230     if (c >= 'A' && c <= 'F')
00231         return c - 'A' + 10;
00232     return 0;
00233 }
00234 
00235 static void
00236 decode_key (const gchar *key, guint8 *data)
00237 {
00238     gint i;
00239 
00240     memset (data, 0, sizeof (data));
00241     if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
00242     {
00243         for (i = 0; i < 8; i++)
00244         {
00245             if (key[i*2] == '\0')
00246                 break;
00247             data[i] |= atox (key[i*2]) << 8;
00248             if (key[i*2+1] == '\0')
00249                 break;
00250             data[i] |= atox (key[i*2+1]);
00251         }
00252     }
00253     else
00254     {
00255         for (i = 1; i < 8 && key[i-1]; i++)
00256            data[i] = key[i-1];
00257     }
00258 }
00259 
00260 static void
00261 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
00262 {
00263     int i;
00264     XDMCPPacket *response;
00265     XDMCPSession *session;
00266     guint8 *authentication_data = NULL;
00267     gsize authentication_data_length = 0;
00268     gboolean match_authorization = FALSE;
00269     gchar *authorization_name;
00270     guint8 *authorization_data = NULL;
00271     gsize authorization_data_length = 0;
00272     guint8 *session_authorization_data = NULL;
00273     gsize session_authorization_data_length = 0;
00274     gchar **j;
00275     guint16 family;
00276     GInetAddress *xserver_address = NULL;
00277     gchar *display_number;
00278     XdmAuthKeyRec rho;
00279 
00280     /* Try and find an IPv6 address */
00281     for (i = 0; i < packet->Request.n_connections; i++)
00282     {
00283         XDMCPConnection *connection = &packet->Request.connections[i];
00284         if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
00285         {
00286             family = connection->type;
00287             xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
00288 
00289             /* We can't use link-local addresses, as we need to know what interface it is on */
00290             if (g_inet_address_get_is_link_local (xserver_address))
00291             {
00292                 g_object_unref (xserver_address);
00293                 xserver_address = NULL;
00294             }
00295             else
00296                 break;
00297         }
00298     }
00299 
00300     /* If no IPv6 address, then try and find an IPv4 one */
00301     if (!xserver_address)
00302     {
00303         for (i = 0; i < packet->Request.n_connections; i++)
00304         {
00305             XDMCPConnection *connection = &packet->Request.connections[i];
00306             if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
00307             {
00308                 family = connection->type;
00309                 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
00310                 break;
00311             }
00312         }
00313     }
00314 
00315     /* Decline if haven't got an address we can connect on */
00316     if (!xserver_address)
00317     {
00318         response = xdmcp_packet_alloc (XDMCP_Decline);
00319         response->Decline.status = g_strdup ("No valid address found");
00320         response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
00321         response->Decline.authentication_data.data = authentication_data;
00322         response->Decline.authentication_data.length = authentication_data_length;
00323         send_packet (socket, address, response);
00324         xdmcp_packet_free (response);
00325         return;
00326     }
00327   
00328     /* Must be using our authentication scheme */
00329     if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
00330     {
00331         response = xdmcp_packet_alloc (XDMCP_Decline);
00332         if (server->priv->key)
00333             response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
00334         else
00335             response->Decline.status = g_strdup ("Server does not support authentication");
00336         response->Decline.authentication_name = g_strdup ("");
00337         send_packet (socket, address, response);
00338         xdmcp_packet_free (response);
00339         return;
00340     }
00341 
00342     /* Perform requested authentication */
00343     if (server->priv->key)
00344     {
00345         guint8 input[8], key[8];
00346 
00347         memset (input, 0, 8);
00348         memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
00349 
00350         /* Setup key */
00351         decode_key (server->priv->key, key);
00352 
00353         /* Decode message from server */
00354         authentication_data = g_malloc (sizeof (guint8) * 8);
00355         authentication_data_length = 8;
00356 
00357         XdmcpUnwrap (input, key, rho.data, authentication_data_length);
00358         XdmcpIncrementKey (&rho);
00359         XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
00360 
00361         authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
00362     }
00363     else
00364         authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
00365 
00366     /* Check if they support our authorization */
00367     for (j = packet->Request.authorization_names; *j; j++)
00368     {
00369         if (strcmp (*j, authorization_name) == 0)
00370         {
00371              match_authorization = TRUE;
00372              break;
00373         }
00374     }
00375 
00376     /* Decline if don't support out authorization */
00377     if (!match_authorization)
00378     {
00379         response = xdmcp_packet_alloc (XDMCP_Decline);
00380         response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
00381         g_free (authorization_name);
00382         response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
00383         response->Decline.authentication_data.data = authentication_data;
00384         response->Decline.authentication_data.length = authentication_data_length;
00385         send_packet (socket, address, response);
00386         xdmcp_packet_free (response);
00387         return;
00388     }
00389 
00390     /* Perform requested authorization */
00391     if (server->priv->key)
00392     {
00393         gint i;
00394         guint8 key[8], session_key[8];
00395 
00396         /* Setup key */
00397         decode_key (server->priv->key, key);
00398 
00399         /* Generate a private session key */
00400         // FIXME: Pick a good DES key?
00401         session_key[0] = 0;
00402         for (i = 1; i < 8; i++)
00403             session_key[i] = g_random_int () & 0xFF;
00404 
00405         /* Encrypt the session key and send it to the server */
00406         authorization_data = g_malloc (8);
00407         authorization_data_length = 8;
00408         XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
00409 
00410         /* Authorization data is the number received from the client followed by the private session key */
00411         session_authorization_data = g_malloc (16);
00412         session_authorization_data_length = 16;
00413         XdmcpDecrementKey (&rho);
00414         memcpy (session_authorization_data, rho.data, 8);
00415         memcpy (session_authorization_data + 8, session_key, 8);
00416     }
00417     else
00418     {
00419         XAuthority *auth;
00420 
00421         /* Data is the cookie */
00422         auth = xauth_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
00423         authorization_data = xauth_copy_authorization_data (auth);
00424         authorization_data_length = xauth_get_authorization_data_length (auth);
00425         session_authorization_data = xauth_copy_authorization_data (auth);
00426         session_authorization_data_length = xauth_get_authorization_data_length (auth);
00427 
00428         g_object_unref (auth);
00429     }
00430 
00431     session = add_session (server);
00432     session->priv->address = xserver_address;
00433     session->priv->display_number = packet->Request.display_number;
00434     display_number = g_strdup_printf ("%d", packet->Request.display_number);
00435 
00436     /* We need to check if this is the loopback address and set the authority
00437      * for a local connection if this is so as XCB treats "127.0.0.1" as local
00438      * always */
00439     if (g_inet_address_get_is_loopback (xserver_address))
00440     {
00441         gchar hostname[1024];
00442         gethostname (hostname, 1024);
00443 
00444         session->priv->authority = xauth_new (XAUTH_FAMILY_LOCAL,
00445                                               (guint8 *) hostname,
00446                                               strlen (hostname),
00447                                               display_number,
00448                                               authorization_name,
00449                                               session_authorization_data,
00450                                               session_authorization_data_length);
00451     }
00452     else
00453         session->priv->authority = xauth_new (family,
00454                                               g_inet_address_to_bytes (G_INET_ADDRESS (xserver_address)),
00455                                               g_inet_address_get_native_size (G_INET_ADDRESS (xserver_address)),
00456                                               display_number,
00457                                               authorization_name,
00458                                               session_authorization_data,
00459                                               session_authorization_data_length);
00460     g_free (display_number);
00461 
00462     response = xdmcp_packet_alloc (XDMCP_Accept);
00463     response->Accept.session_id = xdmcp_session_get_id (session);
00464     response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
00465     response->Accept.authentication_data.data = authentication_data;
00466     response->Accept.authentication_data.length = authentication_data_length;
00467     response->Accept.authorization_name = authorization_name;
00468     response->Accept.authorization_data.data = authorization_data;
00469     response->Accept.authorization_data.length = authorization_data_length;
00470     send_packet (socket, address, response);
00471     xdmcp_packet_free (response);
00472 }
00473 
00474 static void
00475 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
00476 {
00477     XDMCPSession *session;
00478     gboolean result;
00479 
00480     session = get_session (server, packet->Manage.session_id);
00481     if (!session)
00482     {
00483         XDMCPPacket *response;
00484 
00485         response = xdmcp_packet_alloc (XDMCP_Refuse);
00486         response->Refuse.session_id = packet->Manage.session_id;
00487         send_packet (socket, address, response);
00488         xdmcp_packet_free (response);
00489 
00490         return;
00491     }
00492 
00493     /* Ignore duplicate requests */
00494     if (session->priv->started)
00495     {
00496         if (session->priv->display_number != packet->Manage.display_number ||
00497             strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
00498             g_debug ("Ignoring duplicate Manage with different data");
00499         return;
00500     }
00501 
00502     /* Reject if has changed display number */
00503     if (packet->Manage.display_number != session->priv->display_number)
00504     {
00505         XDMCPPacket *response;
00506 
00507         g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
00508         response = xdmcp_packet_alloc (XDMCP_Refuse);
00509         response->Refuse.session_id = packet->Manage.session_id;
00510         send_packet (socket, address, response);
00511         xdmcp_packet_free (response);
00512     }
00513 
00514     session->priv->display_class = g_strdup (packet->Manage.display_class);
00515 
00516     g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
00517     if (result)
00518     {
00519         /* Cancel the inactive timer */
00520         g_source_remove (session->priv->inactive_timeout);
00521 
00522         session->priv->started = TRUE;
00523     }
00524     else
00525     {
00526         XDMCPPacket *response;
00527 
00528         response = xdmcp_packet_alloc (XDMCP_Failed);
00529         response->Failed.session_id = packet->Manage.session_id;
00530         response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
00531         send_packet (socket, address, response);
00532         xdmcp_packet_free (response);
00533     }
00534 }
00535 
00536 static void
00537 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
00538 {
00539     XDMCPPacket *response;
00540     XDMCPSession *session;
00541     gboolean alive = FALSE;
00542 
00543     session = get_session (server, packet->KeepAlive.session_id);
00544     if (session)
00545         alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
00546 
00547     response = xdmcp_packet_alloc (XDMCP_Alive);
00548     response->Alive.session_running = alive;
00549     response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
00550     send_packet (socket, address, response);
00551     xdmcp_packet_free (response);
00552 }
00553 
00554 static gboolean
00555 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
00556 {
00557     GSocketAddress *address;
00558     gchar data[1024];
00559     GError *error = NULL;
00560     gssize n_read;
00561 
00562     n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
00563     if (error)
00564         g_warning ("Failed to read from XDMCP socket: %s", error->message);
00565     g_clear_error (&error);
00566 
00567     if (n_read > 0)
00568     {
00569         XDMCPPacket *packet;
00570 
00571         packet = xdmcp_packet_decode ((guint8 *)data, n_read);
00572         if (packet)
00573         {        
00574             g_debug ("Got %s", xdmcp_packet_tostring (packet));
00575 
00576             switch (packet->opcode)
00577             {
00578             case XDMCP_BroadcastQuery:
00579             case XDMCP_Query:
00580             case XDMCP_IndirectQuery:
00581                 handle_query (server, socket, address, packet);
00582                 break;
00583             case XDMCP_Request:
00584                 handle_request (server, socket, address, packet);
00585                 break;
00586             case XDMCP_Manage:
00587                 handle_manage (server, socket, address, packet);              
00588                 break;
00589             case XDMCP_KeepAlive:
00590                 handle_keep_alive (server, socket, address, packet);
00591                 break;
00592             default:
00593                 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
00594                 break;
00595             }
00596 
00597             xdmcp_packet_free (packet);
00598         }
00599     }
00600 
00601     return TRUE;
00602 }
00603 
00604 static GSocket *
00605 open_udp_socket (GSocketFamily family, guint port, GError **error)
00606 {
00607     GSocket *socket;
00608     GSocketAddress *address;
00609     gboolean result;
00610   
00611     socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
00612     if (!socket)
00613         return NULL;
00614 
00615     address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
00616     result = g_socket_bind (socket, address, TRUE, error);
00617     if (!result)
00618     {
00619         g_object_unref (socket);
00620         return NULL;
00621     }
00622 
00623     return socket;
00624 }
00625 
00626 gboolean
00627 xdmcp_server_start (XDMCPServer *server)
00628 {
00629     GSource *source;
00630     GError *error = NULL;
00631 
00632     g_return_val_if_fail (server != NULL, FALSE);
00633   
00634     server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
00635     if (error)
00636         g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
00637     g_clear_error (&error);
00638   
00639     if (server->priv->socket)
00640     {
00641         source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
00642         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
00643         g_source_attach (source, NULL);
00644     }
00645     
00646     server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
00647     if (error)
00648         g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
00649     g_clear_error (&error);
00650 
00651     if (server->priv->socket6)
00652     {
00653         source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
00654         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
00655         g_source_attach (source, NULL);
00656     }
00657 
00658     if (!server->priv->socket && !server->priv->socket6)
00659         return FALSE;
00660 
00661     return TRUE;
00662 }
00663 
00664 static void
00665 xdmcp_server_init (XDMCPServer *server)
00666 {
00667     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
00668 
00669     server->priv->port = XDM_UDP_PORT;
00670     server->priv->hostname = g_strdup ("");
00671     server->priv->status = g_strdup ("");
00672     server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
00673 }
00674 
00675 static void
00676 xdmcp_server_finalize (GObject *object)
00677 {
00678     XDMCPServer *self;
00679 
00680     self = XDMCP_SERVER (object);
00681   
00682     if (self->priv->socket)
00683         g_object_unref (self->priv->socket);
00684     if (self->priv->socket6)
00685         g_object_unref (self->priv->socket6);
00686     g_free (self->priv->hostname);
00687     g_free (self->priv->status);
00688     g_free (self->priv->key);
00689     g_hash_table_unref (self->priv->sessions);
00690   
00691     G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);  
00692 }
00693 
00694 static void
00695 xdmcp_server_class_init (XDMCPServerClass *klass)
00696 {
00697     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00698 
00699     object_class->finalize = xdmcp_server_finalize;  
00700 
00701     g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
00702 
00703     signals[NEW_SESSION] =
00704         g_signal_new ("new-session",
00705                       G_TYPE_FROM_CLASS (klass),
00706                       G_SIGNAL_RUN_LAST,
00707                       G_STRUCT_OFFSET (XDMCPServerClass, new_session),
00708                       g_signal_accumulator_true_handled,
00709                       NULL,
00710                       ldm_marshal_BOOLEAN__OBJECT,
00711                       G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);
00712 }