[glib] Bug 585575 – g_socket_listener_add_inet_port()
- From: Ryan Lortie <ryanl src gnome org>
- To: svn-commits-list gnome org
- Subject: [glib] Bug 585575 – g_socket_listener_add_inet_port()
- Date: Sun, 14 Jun 2009 13:14:49 -0400 (EDT)
commit cb1a6092405b2f6d9b82e7c1b757951d695c5fe6
Author: Ryan Lortie <desrt desrt ca>
Date: Fri Jun 12 13:01:04 2009 -0400
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.
gio/gsocketlistener.c | 164 +++++++++++++++++++++++++++++++++++++-----------
1 files changed, 126 insertions(+), 38 deletions(-)
---
diff --git a/gio/gsocketlistener.c b/gio/gsocketlistener.c
index c675ae8..ca96530 100644
--- a/gio/gsocketlistener.c
+++ b/gio/gsocketlistener.c
@@ -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
* 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
* to accept to identify this particular source, which is
* 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:
* @listener: a #GSocketListener
- * @port: an ip port number
+ * @port: an IP port number (non-zero)
* @source_object: Optional #GObject identifying this source
* @error: #GError for error reporting, or %NULL to ignore.
*
@@ -328,54 +333,137 @@ g_socket_listener_add_inet_port (GSocketListener *listener,
GObject *source_object,
GError **error)
{
- GSocketAddress *address4, *address6;
- GInetAddress *inet_address;
- gboolean res;
+ gboolean need_ipv4_socket = TRUE;
+ GSocket *socket4 = NULL;
+ GSocket *socket6;
+
+ g_return_val_if_fail (listener != NULL, FALSE);
+ g_return_val_if_fail (port != 0, FALSE);
if (!check_listener (listener, error))
return FALSE;
- inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
- address4 = g_inet_socket_address_new (inet_address, port);
- g_object_unref (inet_address);
+ /* first try to create an IPv6 socket */
+ socket6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ NULL);
+
+ if (socket6 != NULL)
+ /* IPv6 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_IPV6);
- address6 = g_inet_socket_address_new (inet_address, port);
- g_object_unref (inet_address);
+ inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
+ address = g_inet_socket_address_new (inet_address, port);
+ g_object_unref (inet_address);
- if (!g_socket_listener_add_address (listener,
- address6,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_DEFAULT,
- source_object,
- NULL))
- {
- /* Failed, to create ipv6, socket, just use ipv4,
- return any error */
- res = g_socket_listener_add_address (listener,
- address4,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_DEFAULT,
- source_object,
- error);
+ g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog);
+
+ result = g_socket_bind (socket6, address, TRUE, error) &&
+ g_socket_listen (socket6, error);
+
+ g_object_unref (address);
+
+ if (!result)
+ {
+ g_object_unref (socket6);
+
+ return FALSE;
+ }
+
+ if (source_object)
+ g_object_set_qdata_full (G_OBJECT (socket6), source_quark,
+ g_object_ref (source_object),
+ g_object_unref);
+
+ /* If this socket already speaks IPv4 then we are done. */
+ if (g_socket_speaks_ipv4 (socket6))
+ need_ipv4_socket = FALSE;
}
- else
+
+ if (need_ipv4_socket)
+ /* 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.
+ */
{
- /* Succeeded with ipv6, also try ipv4 in case its ipv6 only,
- but ignore errors here */
- res = TRUE;
- g_socket_listener_add_address (listener,
- address4,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_DEFAULT,
- source_object,
- NULL);
+ socket4 = g_socket_new (G_SOCKET_FAMILY_IPV4,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ error);
+
+ 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_object_unref (address4);
- g_object_unref (address6);
+ 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);
- return res;
+ if (G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
+ G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
+
+ return TRUE;
}
static GList *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]