mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 08:22:16 +01:00 
			
		
		
		
	Nicks and blurbs don't have any practical use for gio/gobject libraries. Leaving tests untouched since this features is still used by other libraries. Closes #2991
		
			
				
	
	
		
			1287 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1287 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GIO - GLib Input, Output and Streaming Library
 | ||
|  *
 | ||
|  * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima
 | ||
|  * Copyright © 2009 codethink
 | ||
|  * Copyright © 2009 Red Hat, Inc
 | ||
|  *
 | ||
|  * SPDX-License-Identifier: LGPL-2.1-or-later
 | ||
|  *
 | ||
|  * This library is free software; you can redistribute it and/or
 | ||
|  * modify it under the terms of the GNU Lesser General Public
 | ||
|  * License as published by the Free Software Foundation; either
 | ||
|  * version 2.1 of the License, or (at your option) any later version.
 | ||
|  *
 | ||
|  * This library is distributed in the hope that it will be useful,
 | ||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||
|  * Lesser General Public License for more details.
 | ||
|  *
 | ||
|  * You should have received a copy of the GNU Lesser General
 | ||
|  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | ||
|  *
 | ||
|  * Authors: Christian Kellner <gicmo@gnome.org>
 | ||
|  *          Samuel Cormier-Iijima <sciyoshi@gmail.com>
 | ||
|  *          Ryan Lortie <desrt@desrt.ca>
 | ||
|  *          Alexander Larsson <alexl@redhat.com>
 | ||
|  */
 | ||
| 
 | ||
| #include "config.h"
 | ||
| #include "gsocketlistener.h"
 | ||
| 
 | ||
| #include <gio/gioenumtypes.h>
 | ||
| #include <gio/gtask.h>
 | ||
| #include <gio/gcancellable.h>
 | ||
| #include <gio/gsocketaddress.h>
 | ||
| #include <gio/ginetaddress.h>
 | ||
| #include <gio/gioerror.h>
 | ||
| #include <gio/gsocket.h>
 | ||
| #include <gio/gsocketconnection.h>
 | ||
| #include <gio/ginetsocketaddress.h>
 | ||
| #include "glibintl.h"
 | ||
| #include "gmarshal-internal.h"
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
|  * GSocketListener:
 | ||
|  *
 | ||
|  * A `GSocketListener` is an object that keeps track of a set
 | ||
|  * of server sockets and helps you accept sockets from any of the
 | ||
|  * socket, either sync or async.
 | ||
|  *
 | ||
|  * Add addresses and ports to listen on using
 | ||
|  * [method@Gio.SocketListener.add_address] and
 | ||
|  * [method@Gio.SocketListener.add_inet_port]. These will be listened on until
 | ||
|  * [method@Gio.SocketListener.close] is called. Dropping your final reference to
 | ||
|  * the `GSocketListener` will not cause [method@Gio.SocketListener.close] to be
 | ||
|  * called implicitly, as some references to the `GSocketListener` may be held
 | ||
|  * internally.
 | ||
|  *
 | ||
|  * If you want to implement a network server, also look at
 | ||
|  * [class@Gio.SocketService] and [class@Gio.ThreadedSocketService] which are
 | ||
|  * subclasses of `GSocketListener` that make this even easier.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| 
 | ||
| enum
 | ||
| {
 | ||
|   PROP_0,
 | ||
|   PROP_LISTEN_BACKLOG
 | ||
| };
 | ||
| 
 | ||
| enum
 | ||
| {
 | ||
|   EVENT,
 | ||
|   LAST_SIGNAL
 | ||
| };
 | ||
| 
 | ||
| static guint signals[LAST_SIGNAL] = { 0 };
 | ||
| 
 | ||
| static GQuark source_quark = 0;
 | ||
| 
 | ||
| struct _GSocketListenerPrivate
 | ||
| {
 | ||
|   GPtrArray           *sockets;
 | ||
|   GMainContext        *main_context;
 | ||
|   int                 listen_backlog;
 | ||
|   guint               closed : 1;
 | ||
| };
 | ||
| 
 | ||
| G_DEFINE_TYPE_WITH_PRIVATE (GSocketListener, g_socket_listener, G_TYPE_OBJECT)
 | ||
| 
 | ||
| static void
 | ||
| g_socket_listener_finalize (GObject *object)
 | ||
| {
 | ||
|   GSocketListener *listener = G_SOCKET_LISTENER (object);
 | ||
| 
 | ||
|   if (listener->priv->main_context)
 | ||
|     g_main_context_unref (listener->priv->main_context);
 | ||
| 
 | ||
|   /* Do not explicitly close the sockets. Instead, let them close themselves if
 | ||
|    * their final reference is dropped, but keep them open if a reference is
 | ||
|    * held externally to the GSocketListener (which is possible if
 | ||
|    * g_socket_listener_add_socket() was used).
 | ||
|    */
 | ||
|   g_ptr_array_free (listener->priv->sockets, TRUE);
 | ||
| 
 | ||
|   G_OBJECT_CLASS (g_socket_listener_parent_class)
 | ||
|     ->finalize (object);
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| g_socket_listener_get_property (GObject    *object,
 | ||
| 				guint       prop_id,
 | ||
| 				GValue     *value,
 | ||
| 				GParamSpec *pspec)
 | ||
| {
 | ||
|   GSocketListener *listener = G_SOCKET_LISTENER (object);
 | ||
| 
 | ||
|   switch (prop_id)
 | ||
|     {
 | ||
|       case PROP_LISTEN_BACKLOG:
 | ||
|         g_value_set_int (value, listener->priv->listen_backlog);
 | ||
|         break;
 | ||
| 
 | ||
|       default:
 | ||
|         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| g_socket_listener_set_property (GObject      *object,
 | ||
| 				guint         prop_id,
 | ||
| 				const GValue *value,
 | ||
| 				GParamSpec   *pspec)
 | ||
| {
 | ||
|   GSocketListener *listener = G_SOCKET_LISTENER (object);
 | ||
| 
 | ||
|   switch (prop_id)
 | ||
|     {
 | ||
|       case PROP_LISTEN_BACKLOG:
 | ||
| 	g_socket_listener_set_backlog (listener, g_value_get_int (value));
 | ||
| 	break;
 | ||
| 
 | ||
|       default:
 | ||
|         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| g_socket_listener_class_init (GSocketListenerClass *klass)
 | ||
| {
 | ||
|   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
 | ||
| 
 | ||
|   gobject_class->finalize = g_socket_listener_finalize;
 | ||
|   gobject_class->set_property = g_socket_listener_set_property;
 | ||
|   gobject_class->get_property = g_socket_listener_get_property;
 | ||
| 
 | ||
|   /**
 | ||
|    * GSocketListener:listen-backlog:
 | ||
|    *
 | ||
|    * The number of outstanding connections in the listen queue.
 | ||
|    *
 | ||
|    * Since: 2.22
 | ||
|    */
 | ||
|   g_object_class_install_property (gobject_class, PROP_LISTEN_BACKLOG,
 | ||
|                                    g_param_spec_int ("listen-backlog", NULL, NULL,
 | ||
|                                                      0,
 | ||
|                                                      2000,
 | ||
|                                                      10,
 | ||
|                                                      G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 | ||
| 
 | ||
|   /**
 | ||
|    * GSocketListener::event:
 | ||
|    * @listener: the #GSocketListener
 | ||
|    * @event: the event that is occurring
 | ||
|    * @socket: the #GSocket the event is occurring on
 | ||
|    *
 | ||
|    * Emitted when @listener's activity on @socket changes state.
 | ||
|    * Note that when @listener is used to listen on both IPv4 and
 | ||
|    * IPv6, a separate set of signals will be emitted for each, and
 | ||
|    * the order they happen in is undefined.
 | ||
|    *
 | ||
|    * Since: 2.46
 | ||
|    */
 | ||
|   signals[EVENT] =
 | ||
|     g_signal_new (I_("event"),
 | ||
|                   G_TYPE_FROM_CLASS (gobject_class),
 | ||
|                   G_SIGNAL_RUN_LAST,
 | ||
|                   G_STRUCT_OFFSET (GSocketListenerClass, event),
 | ||
|                   NULL, NULL,
 | ||
|                   _g_cclosure_marshal_VOID__ENUM_OBJECT,
 | ||
|                   G_TYPE_NONE, 2,
 | ||
|                   G_TYPE_SOCKET_LISTENER_EVENT,
 | ||
|                   G_TYPE_SOCKET);
 | ||
|   g_signal_set_va_marshaller (signals[EVENT],
 | ||
|                               G_TYPE_FROM_CLASS (gobject_class),
 | ||
|                               _g_cclosure_marshal_VOID__ENUM_OBJECTv);
 | ||
| 
 | ||
|   source_quark = g_quark_from_static_string ("g-socket-listener-source");
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| g_socket_listener_init (GSocketListener *listener)
 | ||
| {
 | ||
|   listener->priv = g_socket_listener_get_instance_private (listener);
 | ||
|   listener->priv->sockets =
 | ||
|     g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
 | ||
|   listener->priv->listen_backlog = 10;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_new:
 | ||
|  *
 | ||
|  * Creates a new #GSocketListener with no sockets to listen for.
 | ||
|  * New listeners can be added with e.g. g_socket_listener_add_address()
 | ||
|  * or g_socket_listener_add_inet_port().
 | ||
|  *
 | ||
|  * Returns: a new #GSocketListener.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| GSocketListener *
 | ||
| g_socket_listener_new (void)
 | ||
| {
 | ||
|   return g_object_new (G_TYPE_SOCKET_LISTENER, NULL);
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| check_listener (GSocketListener *listener,
 | ||
| 		GError **error)
 | ||
| {
 | ||
|   if (listener->priv->closed)
 | ||
|     {
 | ||
|       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
 | ||
| 			   _("Listener is already closed"));
 | ||
|       return FALSE;
 | ||
|     }
 | ||
| 
 | ||
|   return TRUE;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_add_socket:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @socket: a listening #GSocket
 | ||
|  * @source_object: (nullable): Optional #GObject identifying this source
 | ||
|  * @error: #GError for error reporting, or %NULL to ignore.
 | ||
|  *
 | ||
|  * Adds @socket to the set of sockets that we try to accept
 | ||
|  * new clients from. The socket must be bound to a local
 | ||
|  * address and listened to.
 | ||
|  *
 | ||
|  * @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
 | ||
|  * different things depending on what address is connected to.
 | ||
|  *
 | ||
|  * The @socket will not be automatically closed when the @listener is finalized
 | ||
|  * unless the listener held the final reference to the socket. Before GLib 2.42,
 | ||
|  * the @socket was automatically closed on finalization of the @listener, even
 | ||
|  * if references to it were held elsewhere.
 | ||
|  *
 | ||
|  * Returns: %TRUE on success, %FALSE on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| gboolean
 | ||
| g_socket_listener_add_socket (GSocketListener  *listener,
 | ||
| 			      GSocket          *socket,
 | ||
| 			      GObject          *source_object,
 | ||
| 			      GError          **error)
 | ||
| {
 | ||
|   if (!check_listener (listener, error))
 | ||
|     return FALSE;
 | ||
| 
 | ||
|   /* TODO: Check that socket it is bound & not closed? */
 | ||
| 
 | ||
|   if (g_socket_is_closed (socket))
 | ||
|     {
 | ||
|       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
 | ||
| 			   _("Added socket is closed"));
 | ||
|       return FALSE;
 | ||
|     }
 | ||
| 
 | ||
|   g_object_ref (socket);
 | ||
|   g_ptr_array_add (listener->priv->sockets, socket);
 | ||
| 
 | ||
|   if (source_object)
 | ||
|     g_object_set_qdata_full (G_OBJECT (socket), source_quark,
 | ||
| 			     g_object_ref (source_object), g_object_unref);
 | ||
| 
 | ||
| 
 | ||
|   if (G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
 | ||
|     G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
 | ||
| 
 | ||
|   return TRUE;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_add_address:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @address: a #GSocketAddress
 | ||
|  * @type: a #GSocketType
 | ||
|  * @protocol: a #GSocketProtocol
 | ||
|  * @source_object: (nullable): Optional #GObject identifying this source
 | ||
|  * @effective_address: (out) (optional): location to store the address that was bound to, or %NULL.
 | ||
|  * @error: #GError for error reporting, or %NULL to ignore.
 | ||
|  *
 | ||
|  * Creates a socket of type @type and protocol @protocol, binds
 | ||
|  * 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 deterministic behavior, 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
 | ||
|  * different things depending on what address is connected to.
 | ||
|  *
 | ||
|  * If successful and @effective_address is non-%NULL then it will
 | ||
|  * be set to the address that the binding actually occurred at.  This
 | ||
|  * is helpful for determining the port number that was used for when
 | ||
|  * requesting a binding to port 0 (ie: "any port").  This address, if
 | ||
|  * requested, belongs to the caller and must be freed.
 | ||
|  *
 | ||
|  * Call g_socket_listener_close() to stop listening on @address; this will not
 | ||
|  * be done automatically when you drop your final reference to @listener, as
 | ||
|  * references may be held internally.
 | ||
|  *
 | ||
|  * Returns: %TRUE on success, %FALSE on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| gboolean
 | ||
| g_socket_listener_add_address (GSocketListener  *listener,
 | ||
| 			       GSocketAddress   *address,
 | ||
| 			       GSocketType       type,
 | ||
| 			       GSocketProtocol   protocol,
 | ||
| 			       GObject          *source_object,
 | ||
|                                GSocketAddress  **effective_address,
 | ||
| 			       GError          **error)
 | ||
| {
 | ||
|   GSocketAddress *local_address;
 | ||
|   GSocketFamily family;
 | ||
|   GSocket *socket;
 | ||
| 
 | ||
|   if (!check_listener (listener, error))
 | ||
|     return FALSE;
 | ||
| 
 | ||
|   family = g_socket_address_get_family (address);
 | ||
|   socket = g_socket_new (family, type, protocol, error);
 | ||
|   if (socket == NULL)
 | ||
|     return FALSE;
 | ||
| 
 | ||
|   g_socket_set_listen_backlog (socket, listener->priv->listen_backlog);
 | ||
| 
 | ||
|   g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                  G_SOCKET_LISTENER_BINDING, socket);
 | ||
| 
 | ||
|   if (!g_socket_bind (socket, address, TRUE, error))
 | ||
|     {
 | ||
|       g_object_unref (socket);
 | ||
|       return FALSE;
 | ||
|     }
 | ||
| 
 | ||
|   g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                  G_SOCKET_LISTENER_BOUND, socket);
 | ||
|   g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                  G_SOCKET_LISTENER_LISTENING, socket);
 | ||
| 
 | ||
|   if (!g_socket_listen (socket, error))
 | ||
|     {
 | ||
|       g_object_unref (socket);
 | ||
|       return FALSE;
 | ||
|     }
 | ||
| 
 | ||
|   g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                  G_SOCKET_LISTENER_LISTENED, socket);
 | ||
| 
 | ||
|   local_address = NULL;
 | ||
|   if (effective_address)
 | ||
|     {
 | ||
|       local_address = g_socket_get_local_address (socket, error);
 | ||
|       if (local_address == NULL)
 | ||
| 	{
 | ||
| 	  g_object_unref (socket);
 | ||
| 	  return FALSE;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_socket_listener_add_socket (listener, socket,
 | ||
| 				     source_object,
 | ||
| 				     error))
 | ||
|     {
 | ||
|       if (local_address)
 | ||
| 	g_object_unref (local_address);
 | ||
|       g_object_unref (socket);
 | ||
|       return FALSE;
 | ||
|     }
 | ||
| 
 | ||
|   if (effective_address)
 | ||
|     *effective_address = local_address;
 | ||
| 
 | ||
|   g_object_unref (socket); /* add_socket refs this */
 | ||
| 
 | ||
|   return TRUE;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_add_inet_port:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @port: an IP port number (non-zero)
 | ||
|  * @source_object: (nullable): Optional #GObject identifying this source
 | ||
|  * @error: #GError for error reporting, or %NULL to ignore.
 | ||
|  *
 | ||
|  * Helper function for g_socket_listener_add_address() that
 | ||
|  * creates a TCP/IP socket listening on IPv4 and IPv6 (if
 | ||
|  * supported) on the specified port on all interfaces.
 | ||
|  *
 | ||
|  * @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
 | ||
|  * different things depending on what address is connected to.
 | ||
|  *
 | ||
|  * Call g_socket_listener_close() to stop listening on @port; this will not
 | ||
|  * be done automatically when you drop your final reference to @listener, as
 | ||
|  * references may be held internally.
 | ||
|  *
 | ||
|  * Returns: %TRUE on success, %FALSE on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| gboolean
 | ||
| g_socket_listener_add_inet_port (GSocketListener  *listener,
 | ||
| 				 guint16           port,
 | ||
| 				 GObject          *source_object,
 | ||
| 				 GError          **error)
 | ||
| {
 | ||
|   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;
 | ||
| 
 | ||
|   /* 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;
 | ||
| 
 | ||
|       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);
 | ||
| 
 | ||
|       g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog);
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_BINDING, socket6);
 | ||
| 
 | ||
|       if (!g_socket_bind (socket6, address, TRUE, error))
 | ||
|         {
 | ||
|           g_object_unref (address);
 | ||
|           g_object_unref (socket6);
 | ||
|           return FALSE;
 | ||
|         }
 | ||
| 
 | ||
|       g_object_unref (address);
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_BOUND, socket6);
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_LISTENING, socket6);
 | ||
| 
 | ||
|       if (!g_socket_listen (socket6, error))
 | ||
|         {
 | ||
|           g_object_unref (socket6);
 | ||
|           return FALSE;
 | ||
|         }
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_LISTENED, socket6);
 | ||
| 
 | ||
|       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;
 | ||
|     }
 | ||
| 
 | ||
|   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.
 | ||
|      */
 | ||
|     {
 | ||
|       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;
 | ||
| 
 | ||
|           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);
 | ||
| 
 | ||
|           g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                          G_SOCKET_LISTENER_BINDING, socket4);
 | ||
| 
 | ||
|           if (!g_socket_bind (socket4, address, TRUE, error))
 | ||
|             {
 | ||
|               g_object_unref (address);
 | ||
|               g_object_unref (socket4);
 | ||
|               if (socket6 != NULL)
 | ||
|                 g_object_unref (socket6);
 | ||
| 
 | ||
|               return FALSE;
 | ||
|             }
 | ||
| 
 | ||
|           g_object_unref (address);
 | ||
| 
 | ||
|           g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                          G_SOCKET_LISTENER_BOUND, socket4);
 | ||
|           g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                          G_SOCKET_LISTENER_LISTENING, socket4);
 | ||
| 
 | ||
|           if (!g_socket_listen (socket4, error))
 | ||
|             {
 | ||
|               g_object_unref (socket4);
 | ||
|               if (socket6 != NULL)
 | ||
|                 g_object_unref (socket6);
 | ||
| 
 | ||
|               return FALSE;
 | ||
|             }
 | ||
| 
 | ||
|           g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                          G_SOCKET_LISTENER_LISTENED, socket4);
 | ||
| 
 | ||
|           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 *
 | ||
| add_sources (GSocketListener   *listener,
 | ||
| 	     GSocketSourceFunc  callback,
 | ||
| 	     gpointer           callback_data,
 | ||
| 	     GCancellable      *cancellable,
 | ||
| 	     GMainContext      *context)
 | ||
| {
 | ||
|   GSocket *socket;
 | ||
|   GSource *source;
 | ||
|   GList *sources;
 | ||
|   guint i;
 | ||
| 
 | ||
|   sources = NULL;
 | ||
|   for (i = 0; i < listener->priv->sockets->len; i++)
 | ||
|     {
 | ||
|       socket = listener->priv->sockets->pdata[i];
 | ||
| 
 | ||
|       source = g_socket_create_source (socket, G_IO_IN, cancellable);
 | ||
|       g_source_set_callback (source,
 | ||
|                              (GSourceFunc) callback,
 | ||
|                              callback_data, NULL);
 | ||
|       g_source_attach (source, context);
 | ||
| 
 | ||
|       sources = g_list_prepend (sources, source);
 | ||
|     }
 | ||
| 
 | ||
|   return sources;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| free_sources (GList *sources)
 | ||
| {
 | ||
|   GSource *source;
 | ||
|   while (sources != NULL)
 | ||
|     {
 | ||
|       source = sources->data;
 | ||
|       sources = g_list_delete_link (sources, sources);
 | ||
|       g_source_destroy (source);
 | ||
|       g_source_unref (source);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| struct AcceptData {
 | ||
|   GMainLoop *loop;
 | ||
|   GSocket *socket;
 | ||
| };
 | ||
| 
 | ||
| static gboolean
 | ||
| accept_callback (GSocket      *socket,
 | ||
| 		 GIOCondition  condition,
 | ||
| 		 gpointer      user_data)
 | ||
| {
 | ||
|   struct AcceptData *data = user_data;
 | ||
| 
 | ||
|   data->socket = socket;
 | ||
|   g_main_loop_quit (data->loop);
 | ||
| 
 | ||
|   return TRUE;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_accept_socket:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @source_object: (out) (transfer none) (optional) (nullable): location where #GObject pointer will be stored, or %NULL.
 | ||
|  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
 | ||
|  * @error: #GError for error reporting, or %NULL to ignore.
 | ||
|  *
 | ||
|  * Blocks waiting for a client to connect to any of the sockets added
 | ||
|  * to the listener. Returns the #GSocket that was accepted.
 | ||
|  *
 | ||
|  * If you want to accept the high-level #GSocketConnection, not a #GSocket,
 | ||
|  * which is often the case, then you should use g_socket_listener_accept()
 | ||
|  * instead.
 | ||
|  *
 | ||
|  * If @source_object is not %NULL it will be filled out with the source
 | ||
|  * object specified when the corresponding socket or address was added
 | ||
|  * to the listener.
 | ||
|  *
 | ||
|  * If @cancellable is not %NULL, then the operation can be cancelled by
 | ||
|  * triggering the cancellable object from another thread. If the operation
 | ||
|  * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
 | ||
|  *
 | ||
|  * Returns: (transfer full): a #GSocket on success, %NULL on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| GSocket *
 | ||
| g_socket_listener_accept_socket (GSocketListener  *listener,
 | ||
| 				 GObject         **source_object,
 | ||
| 				 GCancellable     *cancellable,
 | ||
| 				 GError          **error)
 | ||
| {
 | ||
|   GSocket *accept_socket, *socket;
 | ||
| 
 | ||
|   g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), NULL);
 | ||
| 
 | ||
|   if (!check_listener (listener, error))
 | ||
|     return NULL;
 | ||
| 
 | ||
|   if (listener->priv->sockets->len == 1)
 | ||
|     {
 | ||
|       accept_socket = listener->priv->sockets->pdata[0];
 | ||
|       if (!g_socket_condition_wait (accept_socket, G_IO_IN,
 | ||
| 				    cancellable, error))
 | ||
| 	return NULL;
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       GList *sources;
 | ||
|       struct AcceptData data;
 | ||
|       GMainLoop *loop;
 | ||
| 
 | ||
|       if (listener->priv->main_context == NULL)
 | ||
| 	listener->priv->main_context = g_main_context_new ();
 | ||
| 
 | ||
|       loop = g_main_loop_new (listener->priv->main_context, FALSE);
 | ||
|       data.loop = loop;
 | ||
|       sources = add_sources (listener,
 | ||
| 			     accept_callback,
 | ||
| 			     &data,
 | ||
| 			     cancellable,
 | ||
| 			     listener->priv->main_context);
 | ||
|       g_main_loop_run (loop);
 | ||
|       accept_socket = data.socket;
 | ||
|       free_sources (sources);
 | ||
|       g_main_loop_unref (loop);
 | ||
|     }
 | ||
| 
 | ||
|   if (!(socket = g_socket_accept (accept_socket, cancellable, error)))
 | ||
|     return NULL;
 | ||
| 
 | ||
|   if (source_object)
 | ||
|     *source_object = g_object_get_qdata (G_OBJECT (accept_socket), source_quark);
 | ||
| 
 | ||
|   return socket;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_accept:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @source_object: (out) (transfer none) (optional) (nullable): location where #GObject pointer will be stored, or %NULL
 | ||
|  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
 | ||
|  * @error: #GError for error reporting, or %NULL to ignore.
 | ||
|  *
 | ||
|  * Blocks waiting for a client to connect to any of the sockets added
 | ||
|  * to the listener. Returns a #GSocketConnection for the socket that was
 | ||
|  * accepted.
 | ||
|  *
 | ||
|  * If @source_object is not %NULL it will be filled out with the source
 | ||
|  * object specified when the corresponding socket or address was added
 | ||
|  * to the listener.
 | ||
|  *
 | ||
|  * If @cancellable is not %NULL, then the operation can be cancelled by
 | ||
|  * triggering the cancellable object from another thread. If the operation
 | ||
|  * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
 | ||
|  *
 | ||
|  * Returns: (transfer full): a #GSocketConnection on success, %NULL on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| GSocketConnection *
 | ||
| g_socket_listener_accept (GSocketListener  *listener,
 | ||
| 			  GObject         **source_object,
 | ||
| 			  GCancellable     *cancellable,
 | ||
| 			  GError          **error)
 | ||
| {
 | ||
|   GSocketConnection *connection;
 | ||
|   GSocket *socket;
 | ||
| 
 | ||
|   socket = g_socket_listener_accept_socket (listener,
 | ||
| 					    source_object,
 | ||
| 					    cancellable,
 | ||
| 					    error);
 | ||
|   if (socket == NULL)
 | ||
|     return NULL;
 | ||
| 
 | ||
|   connection = g_socket_connection_factory_create_connection (socket);
 | ||
|   g_object_unref (socket);
 | ||
| 
 | ||
|   return connection;
 | ||
| }
 | ||
| 
 | ||
| typedef struct
 | ||
| {
 | ||
|   GList *sources;  /* (element-type GSource) */
 | ||
|   gboolean returned_yet;
 | ||
| } AcceptSocketAsyncData;
 | ||
| 
 | ||
| static void
 | ||
| accept_socket_async_data_free (AcceptSocketAsyncData *data)
 | ||
| {
 | ||
|   free_sources (data->sources);
 | ||
|   g_free (data);
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| accept_ready (GSocket      *accept_socket,
 | ||
| 	      GIOCondition  condition,
 | ||
| 	      gpointer      user_data)
 | ||
| {
 | ||
|   GTask *task = user_data;
 | ||
|   GError *error = NULL;
 | ||
|   GSocket *socket;
 | ||
|   GObject *source_object;
 | ||
|   AcceptSocketAsyncData *data = g_task_get_task_data (task);
 | ||
| 
 | ||
|   /* Don’t call g_task_return_*() multiple times if we have multiple incoming
 | ||
|    * connections in the same #GMainContext iteration. */
 | ||
|   if (data->returned_yet)
 | ||
|     return G_SOURCE_REMOVE;
 | ||
| 
 | ||
|   socket = g_socket_accept (accept_socket, g_task_get_cancellable (task), &error);
 | ||
|   if (socket)
 | ||
|     {
 | ||
|       source_object = g_object_get_qdata (G_OBJECT (accept_socket), source_quark);
 | ||
|       if (source_object)
 | ||
| 	g_object_set_qdata_full (G_OBJECT (task),
 | ||
| 				 source_quark,
 | ||
| 				 g_object_ref (source_object), g_object_unref);
 | ||
|       g_task_return_pointer (task, socket, g_object_unref);
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       g_task_return_error (task, error);
 | ||
|     }
 | ||
| 
 | ||
|   data->returned_yet = TRUE;
 | ||
|   g_object_unref (task);
 | ||
| 
 | ||
|   return G_SOURCE_REMOVE;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_accept_socket_async:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @cancellable: (nullable): a #GCancellable, or %NULL
 | ||
|  * @callback: (scope async): a #GAsyncReadyCallback
 | ||
|  * @user_data: user data for the callback
 | ||
|  *
 | ||
|  * This is the asynchronous version of g_socket_listener_accept_socket().
 | ||
|  *
 | ||
|  * When the operation is finished @callback will be
 | ||
|  * called. You can then call g_socket_listener_accept_socket_finish()
 | ||
|  * to get the result of the operation.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| void
 | ||
| g_socket_listener_accept_socket_async (GSocketListener     *listener,
 | ||
| 				       GCancellable        *cancellable,
 | ||
| 				       GAsyncReadyCallback  callback,
 | ||
| 				       gpointer             user_data)
 | ||
| {
 | ||
|   GTask *task;
 | ||
|   GError *error = NULL;
 | ||
|   AcceptSocketAsyncData *data = NULL;
 | ||
| 
 | ||
|   task = g_task_new (listener, cancellable, callback, user_data);
 | ||
|   g_task_set_source_tag (task, g_socket_listener_accept_socket_async);
 | ||
| 
 | ||
|   if (!check_listener (listener, &error))
 | ||
|     {
 | ||
|       g_task_return_error (task, error);
 | ||
|       g_object_unref (task);
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   data = g_new0 (AcceptSocketAsyncData, 1);
 | ||
|   data->returned_yet = FALSE;
 | ||
|   data->sources = add_sources (listener,
 | ||
| 			 accept_ready,
 | ||
| 			 task,
 | ||
| 			 cancellable,
 | ||
| 			 g_main_context_get_thread_default ());
 | ||
|   g_task_set_task_data (task, g_steal_pointer (&data),
 | ||
|                         (GDestroyNotify) accept_socket_async_data_free);
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_accept_socket_finish:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @result: a #GAsyncResult.
 | ||
|  * @source_object: (out) (transfer none) (optional) (nullable): Optional #GObject identifying this source
 | ||
|  * @error: a #GError location to store the error occurring, or %NULL to
 | ||
|  * ignore.
 | ||
|  *
 | ||
|  * Finishes an async accept operation. See g_socket_listener_accept_socket_async()
 | ||
|  *
 | ||
|  * Returns: (transfer full): a #GSocket on success, %NULL on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| GSocket *
 | ||
| g_socket_listener_accept_socket_finish (GSocketListener  *listener,
 | ||
| 					GAsyncResult     *result,
 | ||
| 					GObject         **source_object,
 | ||
| 					GError          **error)
 | ||
| {
 | ||
|   g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), NULL);
 | ||
|   g_return_val_if_fail (g_task_is_valid (result, listener), NULL);
 | ||
| 
 | ||
|   if (source_object)
 | ||
|     *source_object = g_object_get_qdata (G_OBJECT (result), source_quark);
 | ||
| 
 | ||
|   return g_task_propagate_pointer (G_TASK (result), error);
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_accept_async:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @cancellable: (nullable): a #GCancellable, or %NULL
 | ||
|  * @callback: (scope async): a #GAsyncReadyCallback
 | ||
|  * @user_data: user data for the callback
 | ||
|  *
 | ||
|  * This is the asynchronous version of g_socket_listener_accept().
 | ||
|  *
 | ||
|  * When the operation is finished @callback will be
 | ||
|  * called. You can then call g_socket_listener_accept_finish()
 | ||
|  * to get the result of the operation.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| void
 | ||
| g_socket_listener_accept_async (GSocketListener     *listener,
 | ||
|                                 GCancellable        *cancellable,
 | ||
|                                 GAsyncReadyCallback  callback,
 | ||
|                                 gpointer             user_data)
 | ||
| {
 | ||
|   g_socket_listener_accept_socket_async (listener,
 | ||
| 					 cancellable,
 | ||
| 					 callback,
 | ||
| 					 user_data);
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_accept_finish:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @result: a #GAsyncResult.
 | ||
|  * @source_object: (out) (transfer none) (optional) (nullable): Optional #GObject identifying this source
 | ||
|  * @error: a #GError location to store the error occurring, or %NULL to
 | ||
|  * ignore.
 | ||
|  *
 | ||
|  * Finishes an async accept operation. See g_socket_listener_accept_async()
 | ||
|  *
 | ||
|  * Returns: (transfer full): a #GSocketConnection on success, %NULL on error.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| GSocketConnection *
 | ||
| g_socket_listener_accept_finish (GSocketListener  *listener,
 | ||
| 				 GAsyncResult     *result,
 | ||
| 				 GObject         **source_object,
 | ||
| 				 GError          **error)
 | ||
| {
 | ||
|   GSocket *socket;
 | ||
|   GSocketConnection *connection;
 | ||
| 
 | ||
|   socket = g_socket_listener_accept_socket_finish (listener,
 | ||
| 						   result,
 | ||
| 						   source_object,
 | ||
| 						   error);
 | ||
|   if (socket == NULL)
 | ||
|     return NULL;
 | ||
| 
 | ||
|   connection = g_socket_connection_factory_create_connection (socket);
 | ||
|   g_object_unref (socket);
 | ||
|   return connection;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_set_backlog:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @listen_backlog: an integer
 | ||
|  *
 | ||
|  * Sets the listen backlog on the sockets in the listener. This must be called
 | ||
|  * before adding any sockets, addresses or ports to the #GSocketListener (for
 | ||
|  * example, by calling g_socket_listener_add_inet_port()) to be effective.
 | ||
|  *
 | ||
|  * See g_socket_set_listen_backlog() for details
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| void
 | ||
| g_socket_listener_set_backlog (GSocketListener *listener,
 | ||
| 			       int              listen_backlog)
 | ||
| {
 | ||
|   GSocket *socket;
 | ||
|   guint i;
 | ||
| 
 | ||
|   if (listener->priv->closed)
 | ||
|     return;
 | ||
| 
 | ||
|   listener->priv->listen_backlog = listen_backlog;
 | ||
| 
 | ||
|   for (i = 0; i < listener->priv->sockets->len; i++)
 | ||
|     {
 | ||
|       socket = listener->priv->sockets->pdata[i];
 | ||
|       g_socket_set_listen_backlog (socket, listen_backlog);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_close:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  *
 | ||
|  * Closes all the sockets in the listener.
 | ||
|  *
 | ||
|  * Since: 2.22
 | ||
|  */
 | ||
| void
 | ||
| g_socket_listener_close (GSocketListener *listener)
 | ||
| {
 | ||
|   GSocket *socket;
 | ||
|   guint i;
 | ||
| 
 | ||
|   g_return_if_fail (G_IS_SOCKET_LISTENER (listener));
 | ||
| 
 | ||
|   if (listener->priv->closed)
 | ||
|     return;
 | ||
| 
 | ||
|   for (i = 0; i < listener->priv->sockets->len; i++)
 | ||
|     {
 | ||
|       socket = listener->priv->sockets->pdata[i];
 | ||
|       g_socket_close (socket, NULL);
 | ||
|     }
 | ||
|   listener->priv->closed = TRUE;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * g_socket_listener_add_any_inet_port:
 | ||
|  * @listener: a #GSocketListener
 | ||
|  * @source_object: (nullable): Optional #GObject identifying this source
 | ||
|  * @error: a #GError location to store the error occurring, or %NULL to
 | ||
|  * ignore.
 | ||
|  *
 | ||
|  * Listens for TCP connections on any available port number for both
 | ||
|  * IPv6 and IPv4 (if each is available).
 | ||
|  *
 | ||
|  * This is useful if you need to have a socket for incoming connections
 | ||
|  * but don't care about the specific port number.
 | ||
|  *
 | ||
|  * @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
 | ||
|  * different things depending on what address is connected to.
 | ||
|  *
 | ||
|  * Returns: the port number, or 0 in case of failure.
 | ||
|  *
 | ||
|  * Since: 2.24
 | ||
|  **/
 | ||
| guint16
 | ||
| g_socket_listener_add_any_inet_port (GSocketListener  *listener,
 | ||
| 				     GObject          *source_object,
 | ||
|                                      GError          **error)
 | ||
| {
 | ||
|   GSList *sockets_to_close = NULL;
 | ||
|   guint16 candidate_port = 0;
 | ||
|   GSocket *socket6 = NULL;
 | ||
|   GSocket *socket4 = NULL;
 | ||
|   gint attempts = 37;
 | ||
| 
 | ||
|   /*
 | ||
|    * multi-step process:
 | ||
|    *  - first, create an IPv6 socket.
 | ||
|    *  - if that fails, create an IPv4 socket and bind it to port 0 and
 | ||
|    *    that's it.  no retries if that fails (why would it?).
 | ||
|    *  - if our IPv6 socket also speaks IPv4 then we are done.
 | ||
|    *  - if not, then we need to create a IPv4 socket with the same port
 | ||
|    *    number.  this might fail, of course.  so we try this a bunch of
 | ||
|    *    times -- leaving the old IPv6 sockets open so that we get a
 | ||
|    *    different port number to try each time.
 | ||
|    *  - if all that fails then just give up.
 | ||
|    */
 | ||
| 
 | ||
|   while (attempts--)
 | ||
|     {
 | ||
|       GInetAddress *inet_address;
 | ||
|       GSocketAddress *address;
 | ||
|       gboolean result;
 | ||
| 
 | ||
|       g_assert (socket6 == NULL);
 | ||
|       socket6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
 | ||
|                               G_SOCKET_TYPE_STREAM,
 | ||
|                               G_SOCKET_PROTOCOL_DEFAULT,
 | ||
|                               NULL);
 | ||
| 
 | ||
|       if (socket6 != NULL)
 | ||
|         {
 | ||
|           inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
 | ||
|           address = g_inet_socket_address_new (inet_address, 0);
 | ||
|           g_object_unref (inet_address);
 | ||
| 
 | ||
|           g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                          G_SOCKET_LISTENER_BINDING, socket6);
 | ||
| 
 | ||
|           result = g_socket_bind (socket6, address, TRUE, error);
 | ||
|           g_object_unref (address);
 | ||
| 
 | ||
|           if (!result ||
 | ||
|               !(address = g_socket_get_local_address (socket6, error)))
 | ||
|             {
 | ||
|               g_object_unref (socket6);
 | ||
|               socket6 = NULL;
 | ||
|               break;
 | ||
|             }
 | ||
| 
 | ||
|           g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                          G_SOCKET_LISTENER_BOUND, socket6);
 | ||
| 
 | ||
|           g_assert (G_IS_INET_SOCKET_ADDRESS (address));
 | ||
|           candidate_port =
 | ||
|             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address));
 | ||
|           g_assert (candidate_port != 0);
 | ||
|           g_object_unref (address);
 | ||
| 
 | ||
|           if (g_socket_speaks_ipv4 (socket6))
 | ||
|             break;
 | ||
|         }
 | ||
| 
 | ||
|       g_assert (socket4 == NULL);
 | ||
|       socket4 = g_socket_new (G_SOCKET_FAMILY_IPV4,
 | ||
|                               G_SOCKET_TYPE_STREAM,
 | ||
|                               G_SOCKET_PROTOCOL_DEFAULT,
 | ||
|                               socket6 ? NULL : error);
 | ||
| 
 | ||
|       if (socket4 == NULL)
 | ||
|         /* IPv4 not supported.
 | ||
|          * if IPv6 is supported then candidate_port will be non-zero
 | ||
|          *   (and the error parameter above will have been NULL)
 | ||
|          * if IPv6 is unsupported then candidate_port will be zero
 | ||
|          *   (and error will have been set by the above call)
 | ||
|          */
 | ||
|         break;
 | ||
| 
 | ||
|       inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
 | ||
|       address = g_inet_socket_address_new (inet_address, candidate_port);
 | ||
|       g_object_unref (inet_address);
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_BINDING, socket4);
 | ||
| 
 | ||
|       /* a note on the 'error' clause below:
 | ||
|        *
 | ||
|        * if candidate_port is 0 then we report the error right away
 | ||
|        * since it is strange that this binding would fail at all.
 | ||
|        * otherwise, we ignore the error message (ie: NULL).
 | ||
|        *
 | ||
|        * the exception to this rule is the last time through the loop
 | ||
|        * (ie: attempts == 0) in which case we want to set the error
 | ||
|        * because failure here means that the entire call will fail and
 | ||
|        * we need something to show to the user.
 | ||
|        *
 | ||
|        * an english summary of the situation:  "if we gave a candidate
 | ||
|        * port number AND we have more attempts to try, then ignore the
 | ||
|        * error for now".
 | ||
|        */
 | ||
|       result = g_socket_bind (socket4, address, TRUE,
 | ||
|                               (candidate_port && attempts) ? NULL : error);
 | ||
|       g_object_unref (address);
 | ||
| 
 | ||
|       if (candidate_port)
 | ||
|         {
 | ||
|           g_assert (socket6 != NULL);
 | ||
| 
 | ||
|           if (result)
 | ||
|             /* got our candidate port successfully */
 | ||
|             {
 | ||
|               g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                              G_SOCKET_LISTENER_BOUND, socket4);
 | ||
|               break;
 | ||
|             }
 | ||
|           else
 | ||
|             /* we failed to bind to the specified port.  try again. */
 | ||
|             {
 | ||
|               g_object_unref (socket4);
 | ||
|               socket4 = NULL;
 | ||
| 
 | ||
|               /* keep this open so we get a different port number */
 | ||
|               sockets_to_close = g_slist_prepend (sockets_to_close,
 | ||
|                                                   socket6);
 | ||
|               candidate_port = 0;
 | ||
|               socket6 = NULL;
 | ||
|             }
 | ||
|         }
 | ||
|       else
 | ||
|         /* we didn't tell it a port.  this means two things.
 | ||
|          *  - if we failed, then something really bad happened.
 | ||
|          *  - if we succeeded, then we need to find out the port number.
 | ||
|          */
 | ||
|         {
 | ||
|           g_assert (socket6 == NULL);
 | ||
| 
 | ||
|           if (!result ||
 | ||
|               !(address = g_socket_get_local_address (socket4, error)))
 | ||
|             {
 | ||
|               g_object_unref (socket4);
 | ||
|               socket4 = NULL;
 | ||
|               break;
 | ||
|             }
 | ||
| 
 | ||
|             g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                            G_SOCKET_LISTENER_BOUND, socket4);
 | ||
| 
 | ||
|             g_assert (G_IS_INET_SOCKET_ADDRESS (address));
 | ||
|             candidate_port =
 | ||
|               g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address));
 | ||
|             g_assert (candidate_port != 0);
 | ||
|             g_object_unref (address);
 | ||
|             break;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   /* should only be non-zero if we have a socket */
 | ||
|   g_assert ((candidate_port != 0) == (socket4 || socket6));
 | ||
| 
 | ||
|   while (sockets_to_close)
 | ||
|     {
 | ||
|       g_object_unref (sockets_to_close->data);
 | ||
|       sockets_to_close = g_slist_delete_link (sockets_to_close,
 | ||
|                                               sockets_to_close);
 | ||
|     }
 | ||
| 
 | ||
|   /* now we actually listen() the sockets and add them to the listener */
 | ||
|   if (socket6 != NULL)
 | ||
|     {
 | ||
|       g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog);
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_LISTENING, socket6);
 | ||
| 
 | ||
|       if (!g_socket_listen (socket6, error))
 | ||
|         {
 | ||
|           g_object_unref (socket6);
 | ||
|           if (socket4)
 | ||
|             g_object_unref (socket4);
 | ||
| 
 | ||
|           return 0;
 | ||
|         }
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_LISTENED, socket6);
 | ||
| 
 | ||
|       if (source_object)
 | ||
|         g_object_set_qdata_full (G_OBJECT (socket6), source_quark,
 | ||
|                                  g_object_ref (source_object),
 | ||
|                                  g_object_unref);
 | ||
| 
 | ||
|       g_ptr_array_add (listener->priv->sockets, socket6);
 | ||
|     }
 | ||
| 
 | ||
|    if (socket4 != NULL)
 | ||
|     {
 | ||
|       g_socket_set_listen_backlog (socket4, listener->priv->listen_backlog);
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_LISTENING, socket4);
 | ||
| 
 | ||
|       if (!g_socket_listen (socket4, error))
 | ||
|         {
 | ||
|           g_object_unref (socket4);
 | ||
|           if (socket6)
 | ||
|             g_object_unref (socket6);
 | ||
| 
 | ||
|           return 0;
 | ||
|         }
 | ||
| 
 | ||
|       g_signal_emit (listener, signals[EVENT], 0,
 | ||
|                      G_SOCKET_LISTENER_LISTENED, socket4);
 | ||
| 
 | ||
|       if (source_object)
 | ||
|         g_object_set_qdata_full (G_OBJECT (socket4), source_quark,
 | ||
|                                  g_object_ref (source_object),
 | ||
|                                  g_object_unref);
 | ||
| 
 | ||
|       g_ptr_array_add (listener->priv->sockets, socket4);
 | ||
|     }
 | ||
| 
 | ||
|   if ((socket4 != NULL || socket6 != NULL) &&
 | ||
|       G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
 | ||
|     G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
 | ||
| 
 | ||
|   return candidate_port;
 | ||
| }
 |