mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-12 20:36:15 +01:00
Bug 585575 – g_socket_listener_add_inet_port()
Change the logic in g_socket_listener_add_inet_port() as per the reasoning in the bug report. - If the OS supports neither IPv6 or IPv4, fail. - If the OS supports only IPv6, do that. - If the OS supports only IPv4, do that. - If the OS supports IPv6 and IPv6 "speaks" IPv4 then bind it and be done. - If the OS supports IPv6 and IPv6 doesn't "speak" IPv4 then create an additional socket for IPv4. - If binding any socket fails then fail the entire call. Also, remove the ability to call this function with port == 0. This is a useless thing to do anyway since you have no way to know what port number was actually allocated. We should have a separate function to deal with this.
This commit is contained in:
parent
f7d756f5b6
commit
cb1a609240
@ -256,6 +256,11 @@ g_socket_listener_add_socket (GSocketListener *listener,
|
|||||||
* it to @address and adds it to the set of sockets we're accepting
|
* it to @address and adds it to the set of sockets we're accepting
|
||||||
* sockets from.
|
* sockets from.
|
||||||
*
|
*
|
||||||
|
* Note that adding an IPv6 address, depending on the platform,
|
||||||
|
* may or may not result in a listener that also accepts IPv4
|
||||||
|
* connections. For more determinstic behaviour, see
|
||||||
|
* g_socket_listener_add_inet_port().
|
||||||
|
*
|
||||||
* @source_object will be passed out in the various calls
|
* @source_object will be passed out in the various calls
|
||||||
* to accept to identify this particular source, which is
|
* to accept to identify this particular source, which is
|
||||||
* useful if you're listening on multiple addresses and do
|
* useful if you're listening on multiple addresses and do
|
||||||
@ -305,7 +310,7 @@ g_socket_listener_add_address (GSocketListener *listener,
|
|||||||
/**
|
/**
|
||||||
* g_socket_listener_add_inet_port:
|
* g_socket_listener_add_inet_port:
|
||||||
* @listener: a #GSocketListener
|
* @listener: a #GSocketListener
|
||||||
* @port: an ip port number
|
* @port: an IP port number (non-zero)
|
||||||
* @source_object: Optional #GObject identifying this source
|
* @source_object: Optional #GObject identifying this source
|
||||||
* @error: #GError for error reporting, or %NULL to ignore.
|
* @error: #GError for error reporting, or %NULL to ignore.
|
||||||
*
|
*
|
||||||
@ -328,54 +333,137 @@ g_socket_listener_add_inet_port (GSocketListener *listener,
|
|||||||
GObject *source_object,
|
GObject *source_object,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GSocketAddress *address4, *address6;
|
gboolean need_ipv4_socket = TRUE;
|
||||||
GInetAddress *inet_address;
|
GSocket *socket4 = NULL;
|
||||||
gboolean res;
|
GSocket *socket6;
|
||||||
|
|
||||||
|
g_return_val_if_fail (listener != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (port != 0, FALSE);
|
||||||
|
|
||||||
if (!check_listener (listener, error))
|
if (!check_listener (listener, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
|
/* first try to create an IPv6 socket */
|
||||||
address4 = g_inet_socket_address_new (inet_address, port);
|
socket6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
|
||||||
g_object_unref (inet_address);
|
G_SOCKET_TYPE_STREAM,
|
||||||
|
G_SOCKET_PROTOCOL_DEFAULT,
|
||||||
|
NULL);
|
||||||
|
|
||||||
inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
|
if (socket6 != NULL)
|
||||||
address6 = g_inet_socket_address_new (inet_address, port);
|
/* IPv6 is supported on this platform, so if we fail now it is
|
||||||
g_object_unref (inet_address);
|
* a result of being unable to bind to our port. Don't fail
|
||||||
|
* silently as a result of this!
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
GInetAddress *inet_address;
|
||||||
|
GSocketAddress *address;
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
if (!g_socket_listener_add_address (listener,
|
inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
|
||||||
address6,
|
address = g_inet_socket_address_new (inet_address, port);
|
||||||
G_SOCKET_TYPE_STREAM,
|
g_object_unref (inet_address);
|
||||||
G_SOCKET_PROTOCOL_DEFAULT,
|
|
||||||
source_object,
|
g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog);
|
||||||
NULL))
|
|
||||||
{
|
result = g_socket_bind (socket6, address, TRUE, error) &&
|
||||||
/* Failed, to create ipv6, socket, just use ipv4,
|
g_socket_listen (socket6, error);
|
||||||
return any error */
|
|
||||||
res = g_socket_listener_add_address (listener,
|
g_object_unref (address);
|
||||||
address4,
|
|
||||||
G_SOCKET_TYPE_STREAM,
|
if (!result)
|
||||||
G_SOCKET_PROTOCOL_DEFAULT,
|
{
|
||||||
source_object,
|
g_object_unref (socket6);
|
||||||
error);
|
|
||||||
}
|
return FALSE;
|
||||||
else
|
}
|
||||||
{
|
|
||||||
/* Succeeded with ipv6, also try ipv4 in case its ipv6 only,
|
if (source_object)
|
||||||
but ignore errors here */
|
g_object_set_qdata_full (G_OBJECT (socket6), source_quark,
|
||||||
res = TRUE;
|
g_object_ref (source_object),
|
||||||
g_socket_listener_add_address (listener,
|
g_object_unref);
|
||||||
address4,
|
|
||||||
G_SOCKET_TYPE_STREAM,
|
/* If this socket already speaks IPv4 then we are done. */
|
||||||
G_SOCKET_PROTOCOL_DEFAULT,
|
if (g_socket_speaks_ipv4 (socket6))
|
||||||
source_object,
|
need_ipv4_socket = FALSE;
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref (address4);
|
if (need_ipv4_socket)
|
||||||
g_object_unref (address6);
|
/* We are here for exactly one of the following reasons:
|
||||||
|
*
|
||||||
|
* - our platform doesn't support IPv6
|
||||||
|
* - we successfully created an IPv6 socket but it's V6ONLY
|
||||||
|
*
|
||||||
|
* In either case, we need to go ahead and create an IPv4 socket
|
||||||
|
* and fail the call if we can't bind to it.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
socket4 = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
||||||
|
G_SOCKET_TYPE_STREAM,
|
||||||
|
G_SOCKET_PROTOCOL_DEFAULT,
|
||||||
|
error);
|
||||||
|
|
||||||
return res;
|
if (socket4 != NULL)
|
||||||
|
/* IPv4 is supported on this platform, so if we fail now it is
|
||||||
|
* a result of being unable to bind to our port. Don't fail
|
||||||
|
* silently as a result of this!
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
GInetAddress *inet_address;
|
||||||
|
GSocketAddress *address;
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
|
||||||
|
address = g_inet_socket_address_new (inet_address, port);
|
||||||
|
g_object_unref (inet_address);
|
||||||
|
|
||||||
|
g_socket_set_listen_backlog (socket4,
|
||||||
|
listener->priv->listen_backlog);
|
||||||
|
|
||||||
|
result = g_socket_bind (socket4, address, TRUE, error) &&
|
||||||
|
g_socket_listen (socket4, error);
|
||||||
|
|
||||||
|
g_object_unref (address);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
g_object_unref (socket4);
|
||||||
|
|
||||||
|
if (socket6 != NULL)
|
||||||
|
g_object_unref (socket6);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source_object)
|
||||||
|
g_object_set_qdata_full (G_OBJECT (socket4), source_quark,
|
||||||
|
g_object_ref (source_object),
|
||||||
|
g_object_unref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Ok. So IPv4 is not supported on this platform. If we
|
||||||
|
* succeeded at creating an IPv6 socket then that's OK, but
|
||||||
|
* otherwise we need to tell the user we failed.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
if (socket6 != NULL)
|
||||||
|
g_clear_error (error);
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (socket6 != NULL || socket4 != NULL);
|
||||||
|
|
||||||
|
if (socket6 != NULL)
|
||||||
|
g_ptr_array_add (listener->priv->sockets, socket6);
|
||||||
|
|
||||||
|
if (socket4 != NULL)
|
||||||
|
g_ptr_array_add (listener->priv->sockets, socket4);
|
||||||
|
|
||||||
|
if (G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
|
||||||
|
G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *
|
static GList *
|
||||||
|
Loading…
Reference in New Issue
Block a user