diff -Naurp lightdm-1.10.6.old/src/xdmcp-server.c lightdm-1.10.6.new/src/xdmcp-server.c --- lightdm-1.10.6.old/src/xdmcp-server.c 2015-11-20 01:44:04.000000000 +0100 +++ lightdm-1.10.6.new/src/xdmcp-server.c 2016-04-07 18:35:05.133837199 +0200 @@ -56,6 +56,12 @@ G_DEFINE_TYPE (XDMCPServer, xdmcp_server /* Maximum number of milliseconds client will resend manage requests before giving up */ #define MANAGE_TIMEOUT 126000 +/* Address sort support structure */ +struct AddrSortItem { + gsize index; + GInetAddress *address; +}; + XDMCPServer * xdmcp_server_new (void) { @@ -355,54 +361,87 @@ connection_to_address (XDMCPConnection * } } -static gssize -find_address (GInetAddress **addresses, gsize length, GSocketFamily family) -{ - int i; - - for (i = 0; i < length; i++) - { - GInetAddress *address = addresses[i]; - if (address && g_inet_address_get_family (address) == family) - return i; +/* Compare 2 addresses according to our preferences */ +static gint +compare_addresses (gconstpointer a, gconstpointer b, gpointer user_data) +{ + GInetAddress *addr1 = ((const struct AddrSortItem *) a)->address; + GInetAddress *addr2 = ((const struct AddrSortItem *) b)->address; + GInetAddress *source_address = (GInetAddress *) user_data; + GSocketFamily sf1; + GSocketFamily sf2; + GSocketFamily ssf; + gboolean ll1; + + /* Prefer non link-local addresses */ + ll1 = g_inet_address_get_is_link_local (addr1); + if (ll1 != g_inet_address_get_is_link_local (addr2)) + return !ll1? -1: 1; + + /* Prefer the source address family */ + sf1 = g_inet_address_get_family (addr1); + sf2 = g_inet_address_get_family (addr2); + if (sf1 != sf2) { + ssf = g_inet_address_get_family (source_address); + if (sf1 == ssf) + return -1; + if (sf2 == ssf) + return 1; + return sf1 < sf2? -1: 1; } - return -1; + /* Check equality */ + if (g_inet_address_equal (addr1, addr2)) + return 0; + + /* Prefer the source address */ + if (g_inet_address_equal (source_address, addr1)) + return -1; + if (g_inet_address_equal (source_address, addr2)) + return 1; + + /* Addresses are not equal, but preferences are: order is undefined */ + return 0; } static XDMCPConnection * choose_connection (XDMCPPacket *packet, GInetAddress *source_address) { - GInetAddress **addresses; gsize addresses_length, i; + GArray *addresses; gssize index = -1; + struct AddrSortItem addr; addresses_length = packet->Request.n_connections; if (addresses_length == 0) return NULL; - addresses = malloc (sizeof (GInetAddress *) * addresses_length); - for (i = 0; i < addresses_length; i++) - addresses[i] = connection_to_address (&packet->Request.connections[i]); - - /* Use the address the request came in on as this is the least likely to have firewall / routing issues */ - for (i = 0; i < addresses_length && index < 0; i++) - if (g_inet_address_equal (source_address, addresses[i])) - index = i; - - /* Otherwise try and find an address that matches the incoming type */ - if (index < 0) - index = find_address (addresses, addresses_length, g_inet_address_get_family (source_address)); - - /* Otherwise use the first available */ - if (index < 0) - index = 0; - - for (i = 0; i < addresses_length; i++) - g_object_unref (addresses[i]); - g_free (addresses); + addresses = g_array_sized_new (FALSE, FALSE, sizeof addr, addresses_length); + if (!addresses) + return NULL; + + for (i = 0; i < addresses_length; i++) { + addr.address = connection_to_address (&packet->Request.connections[i]); + if (addr.address) { + addr.index = i; + g_array_append_val (addresses, addr); + } + } + + /* Sort the addresses according to our preferences */ + g_array_sort_with_data (addresses, compare_addresses, source_address); + + /* Use the best address */ + if (addresses->len) + index = g_array_index (addresses, struct AddrSortItem, 0).index; + + /* Free the local sort array and items */ + for (i = 0; i < addresses->len; i++) + g_object_unref (g_array_index (addresses, + struct AddrSortItem, i).address); + g_object_unref (addresses); - return &packet->Request.connections[index]; + return index >= 0? &packet->Request.connections[index]: NULL; } static gboolean