Blame gio/gsocketlistener.c

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