[kdbus] Add new library API for common tasks on dbus/kdbus
[platform/upstream/glib.git] / gio / gsocket.c
index 8e9e07e..4d863ce 100644 (file)
@@ -15,9 +15,7 @@
  * 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * 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>
  */
 
 #include "config.h"
-#include "glib.h"
+
+#include "gsocket.h"
+
+#ifdef G_OS_UNIX
+#include "glib-unix.h"
+#endif
 
 #include <errno.h>
 #include <signal.h>
 #ifndef G_OS_WIN32
 # include <fcntl.h>
 # include <unistd.h>
+# include <sys/ioctl.h>
 #endif
 
-#ifdef HAVE_SYS_UIO_H
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#ifdef G_OS_UNIX
 #include <sys/uio.h>
 #endif
 
-#include "gsocket.h"
 #include "gcancellable.h"
 #include "gioenumtypes.h"
+#include "ginetaddress.h"
 #include "ginitable.h"
-#include "gasynchelper.h"
 #include "gioerror.h"
 #include "gioenums.h"
 #include "gioerror.h"
 #include "gnetworkingprivate.h"
+#include "gsocketaddress.h"
+#include "gsocketcontrolmessage.h"
+#include "gcredentials.h"
+#include "gcredentialsprivate.h"
 #include "glibintl.h"
 
-#include "gioalias.h"
-
 /**
  * SECTION:gsocket
  * @short_description: Low-level socket object
  * @include: gio/gio.h
- * @see_also: #GInitable
+ * @see_also: #GInitable, [<gnetworking.h>][gio-gnetworking.h]
  *
  * A #GSocket is a low-level networking primitive. It is a more or less
  * direct mapping of the BSD socket API in a portable GObject based API.
  * reasons. For instance, on Windows a socket is always seen as writable
  * until a write returns %G_IO_ERROR_WOULD_BLOCK.
  *
- * #GSocket<!-- -->s can be either connection oriented or datagram based.
+ * #GSockets can be either connection oriented or datagram based.
  * For connection oriented types you must first establish a connection by
  * either connecting to an address or accepting a connection from another
  * address. For connectionless socket types the target/source address is
  * account the fact that your program will not automatically be killed
  * if it tries to write to %stdout after it has been closed.
  *
+ * Like most other APIs in GLib, #GSocket is not inherently thread safe. To use
+ * a #GSocket concurrently from multiple threads, you must implement your own
+ * locking.
+ *
  * Since: 2.22
  */
 
@@ -116,10 +129,6 @@ static gboolean g_socket_initable_init       (GInitable       *initable,
                                              GCancellable    *cancellable,
                                              GError         **error);
 
-G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT,
-                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
-                                               g_socket_initable_iface_init));
-
 enum
 {
   PROP_0,
@@ -131,9 +140,17 @@ enum
   PROP_LISTEN_BACKLOG,
   PROP_KEEPALIVE,
   PROP_LOCAL_ADDRESS,
-  PROP_REMOTE_ADDRESS
+  PROP_REMOTE_ADDRESS,
+  PROP_TIMEOUT,
+  PROP_TTL,
+  PROP_BROADCAST,
+  PROP_MULTICAST_LOOPBACK,
+  PROP_MULTICAST_TTL
 };
 
+/* Size of the receiver cache for g_socket_receive_from() */
+#define RECV_ADDR_CACHE_SIZE 8
+
 struct _GSocketPrivate
 {
   GSocketFamily   family;
@@ -141,22 +158,40 @@ struct _GSocketPrivate
   GSocketProtocol protocol;
   gint            fd;
   gint            listen_backlog;
+  guint           timeout;
   GError         *construct_error;
+  GSocketAddress *remote_address;
   guint           inited : 1;
   guint           blocking : 1;
   guint           keepalive : 1;
   guint           closed : 1;
   guint           connected : 1;
   guint           listening : 1;
+  guint           timed_out : 1;
+  guint           connect_pending : 1;
 #ifdef G_OS_WIN32
   WSAEVENT        event;
   int             current_events;
   int             current_errors;
   int             selected_events;
   GList          *requested_conditions; /* list of requested GIOCondition * */
+  GMutex          win32_source_lock;
 #endif
+
+  struct {
+    GSocketAddress *addr;
+    struct sockaddr *native;
+    gint native_len;
+    guint64 last_used;
+  } recv_addr_cache[RECV_ADDR_CACHE_SIZE];
 };
 
+G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT,
+                         G_ADD_PRIVATE (GSocket)
+                        g_networking_init ();
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                               g_socket_initable_iface_init));
+
 static int
 get_socket_errno (void)
 {
@@ -170,34 +205,10 @@ get_socket_errno (void)
 static GIOErrorEnum
 socket_io_error_from_errno (int err)
 {
-#ifndef G_OS_WIN32
-  return g_io_error_from_errno (err);
+#ifdef G_OS_WIN32
+  return g_io_error_from_win32_error (err);
 #else
-  switch (err)
-    {
-    case WSAEADDRINUSE:
-      return G_IO_ERROR_ADDRESS_IN_USE;
-    case WSAEWOULDBLOCK:
-      return G_IO_ERROR_WOULD_BLOCK;
-    case WSAEACCES:
-      return G_IO_ERROR_PERMISSION_DENIED;
-    case WSA_INVALID_HANDLE:
-    case WSA_INVALID_PARAMETER:
-    case WSAEBADF:
-    case WSAENOTSOCK:
-      return G_IO_ERROR_INVALID_ARGUMENT;
-    case WSAEPROTONOSUPPORT:
-      return G_IO_ERROR_NOT_SUPPORTED;
-    case WSAECANCELLED:
-      return G_IO_ERROR_CANCELLED;
-    case WSAESOCKTNOSUPPORT:
-    case WSAEOPNOTSUPP:
-    case WSAEPFNOSUPPORT:
-    case WSAEAFNOSUPPORT:
-      return G_IO_ERROR_NOT_SUPPORTED;
-    default:
-      return G_IO_ERROR_FAILED;
-    }
+  return g_io_error_from_errno (err);
 #endif
 }
 
@@ -207,20 +218,15 @@ socket_strerror (int err)
 #ifndef G_OS_WIN32
   return g_strerror (err);
 #else
-  static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
-  char *buf, *msg;
-
-  buf = g_static_private_get (&msg_private);
-  if (!buf)
-    {
-      buf = g_new (gchar, 128);
-      g_static_private_set (&msg_private, buf, g_free);
-    }
+  const char *msg_ret;
+  char *msg;
 
   msg = g_win32_error_message (err);
-  strncpy (buf, msg, 128);
+
+  msg_ret = g_intern_string (msg);
   g_free (msg);
-  return buf;
+
+  return msg_ret;
 #endif
 }
 
@@ -236,36 +242,19 @@ _win32_unset_event_mask (GSocket *socket, int mask)
 #define win32_unset_event_mask(_socket, _mask)
 #endif
 
-static void
-set_fd_nonblocking (int fd)
-{
-#ifndef G_OS_WIN32
-  glong arg;
-#else
-  gulong arg;
-#endif
-
-#ifndef G_OS_WIN32
-  if ((arg = fcntl (fd, F_GETFL, NULL)) < 0)
-    {
-      g_warning ("Error getting socket status flags: %s", socket_strerror (errno));
-      arg = 0;
-    }
-
-  arg = arg | O_NONBLOCK;
-
-  if (fcntl (fd, F_SETFL, arg) < 0)
-      g_warning ("Error setting socket status flags: %s", socket_strerror (errno));
-#else
-  arg = TRUE;
-
-  if (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR)
-    {
-      int errsv = get_socket_errno ();
-      g_warning ("Error setting socket status flags: %s", socket_strerror (errsv));
-    }
+/* Windows has broken prototypes... */
+#ifdef G_OS_WIN32
+#define getsockopt(sockfd, level, optname, optval, optlen) \
+  getsockopt (sockfd, level, optname, (gpointer) optval, (int*) optlen)
+#define setsockopt(sockfd, level, optname, optval, optlen) \
+  setsockopt (sockfd, level, optname, (gpointer) optval, optlen)
+#define getsockname(sockfd, addr, addrlen) \
+  getsockname (sockfd, addr, (int *)addrlen)
+#define getpeername(sockfd, addr, addrlen) \
+  getpeername (sockfd, addr, (int *)addrlen)
+#define recv(sockfd, buf, len, flags) \
+  recv (sockfd, (gpointer)buf, len, flags)
 #endif
-}
 
 static gboolean
 check_socket (GSocket *socket,
@@ -292,6 +281,22 @@ check_socket (GSocket *socket,
                           _("Socket is already closed"));
       return FALSE;
     }
+
+  return TRUE;
+}
+
+static gboolean
+check_timeout (GSocket *socket,
+              GError **error)
+{
+  if (socket->priv->timed_out)
+    {
+      socket->priv->timed_out = FALSE;
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                          _("Socket I/O timed out"));
+      return FALSE;
+    }
+
   return TRUE;
 }
 
@@ -301,18 +306,11 @@ g_socket_details_from_fd (GSocket *socket)
   struct sockaddr_storage address;
   gint fd;
   guint addrlen;
-  guint optlen;
-  int value;
+  int value, family;
   int errsv;
-#ifdef G_OS_WIN32
-  BOOL bool_val;
-#else
-  int bool_val;
-#endif
 
   fd = socket->priv->fd;
-  optlen = sizeof value;
-  if (getsockopt (fd, SOL_SOCKET, SO_TYPE, (void *)&value, &optlen) != 0)
+  if (!g_socket_get_option (socket, SOL_SOCKET, SO_TYPE, &value, NULL))
     {
       errsv = get_socket_errno ();
 
@@ -320,10 +318,11 @@ g_socket_details_from_fd (GSocket *socket)
        {
 #ifdef ENOTSOCK
         case ENOTSOCK:
-#endif
+#else
 #ifdef WSAENOTSOCK
         case WSAENOTSOCK:
 #endif
+#endif
         case EBADF:
          /* programmer error */
          g_error ("creating GSocket from fd %d: %s\n",
@@ -335,7 +334,6 @@ g_socket_details_from_fd (GSocket *socket)
       goto err;
     }
 
-  g_assert (optlen == sizeof value);
   switch (value)
     {
      case SOCK_STREAM:
@@ -362,19 +360,62 @@ g_socket_details_from_fd (GSocket *socket)
       goto err;
     }
 
-  g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) +
-           sizeof address.ss_family <= addrlen);
-  switch (address.ss_family)
+  if (addrlen > 0)
+    {
+      g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) +
+               sizeof address.ss_family <= addrlen);
+      family = address.ss_family;
+    }
+  else
+    {
+      /* On Solaris, this happens if the socket is not yet connected.
+       * But we can use SO_DOMAIN as a workaround there.
+       */
+#ifdef SO_DOMAIN
+      if (!g_socket_get_option (socket, SOL_SOCKET, SO_DOMAIN, &family, NULL))
+       {
+         errsv = get_socket_errno ();
+         goto err;
+       }
+#else
+      /* This will translate to G_IO_ERROR_FAILED on either unix or windows */
+      errsv = -1;
+      goto err;
+#endif
+    }
+
+  switch (family)
     {
      case G_SOCKET_FAMILY_IPV4:
      case G_SOCKET_FAMILY_IPV6:
+       socket->priv->family = address.ss_family;
+       switch (socket->priv->type)
+        {
+        case G_SOCKET_TYPE_STREAM:
+          socket->priv->protocol = G_SOCKET_PROTOCOL_TCP;
+          break;
+
+        case G_SOCKET_TYPE_DATAGRAM:
+          socket->priv->protocol = G_SOCKET_PROTOCOL_UDP;
+          break;
+
+        case G_SOCKET_TYPE_SEQPACKET:
+          socket->priv->protocol = G_SOCKET_PROTOCOL_SCTP;
+          break;
+
+        default:
+          break;
+        }
+       break;
+
      case G_SOCKET_FAMILY_UNIX:
-      socket->priv->family = address.ss_family;
-      break;
+       socket->priv->family = G_SOCKET_FAMILY_UNIX;
+       socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT;
+       break;
 
      default:
-      socket->priv->family = G_SOCKET_FAMILY_INVALID;
-      break;
+       socket->priv->family = G_SOCKET_FAMILY_INVALID;
+       break;
     }
 
   if (socket->priv->family != G_SOCKET_FAMILY_INVALID)
@@ -384,12 +425,9 @@ g_socket_details_from_fd (GSocket *socket)
        socket->priv->connected = TRUE;
     }
 
-  optlen = sizeof bool_val;
-  if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE,
-                 (void *)&bool_val, &optlen) == 0)
+  if (g_socket_get_option (socket, SOL_SOCKET, SO_KEEPALIVE, &value, NULL))
     {
-      g_assert (optlen == sizeof bool_val);
-      socket->priv->keepalive = !!bool_val;
+      socket->priv->keepalive = !!value;
     }
   else
     {
@@ -406,6 +444,55 @@ g_socket_details_from_fd (GSocket *socket)
               socket_strerror (errsv));
 }
 
+/* Wrapper around socket() that is shared with gnetworkmonitornetlink.c */
+gint
+g_socket (gint     domain,
+          gint     type,
+          gint     protocol,
+          GError **error)
+{
+  int fd;
+
+#ifdef SOCK_CLOEXEC
+  fd = socket (domain, type | SOCK_CLOEXEC, protocol);
+  if (fd != -1)
+    return fd;
+
+  /* It's possible that libc has SOCK_CLOEXEC but the kernel does not */
+  if (fd < 0 && (errno == EINVAL || errno == EPROTOTYPE))
+#endif
+    fd = socket (domain, type, protocol);
+
+  if (fd < 0)
+    {
+      int errsv = get_socket_errno ();
+
+      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+                  _("Unable to create socket: %s"), socket_strerror (errsv));
+      errno = errsv;
+      return -1;
+    }
+
+#ifndef G_OS_WIN32
+  {
+    int flags;
+
+    /* We always want to set close-on-exec to protect users. If you
+       need to so some weird inheritance to exec you can re-enable this
+       using lower level hacks with g_socket_get_fd(). */
+    flags = fcntl (fd, F_GETFD, 0);
+    if (flags != -1 &&
+       (flags & FD_CLOEXEC) == 0)
+      {
+       flags |= FD_CLOEXEC;
+       fcntl (fd, F_SETFD, flags);
+      }
+  }
+#endif
+
+  return fd;
+}
+
 static gint
 g_socket_create_socket (GSocketFamily   family,
                        GSocketType     type,
@@ -413,7 +500,6 @@ g_socket_create_socket (GSocketFamily   family,
                        GError        **error)
 {
   gint native_type;
-  gint fd;
 
   switch (type)
     {
@@ -433,44 +519,21 @@ g_socket_create_socket (GSocketFamily   family,
       g_assert_not_reached ();
     }
 
-  if (protocol == -1)
+  if (family <= 0)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
-                  _("Unable to create socket: %s"), _("Unknown protocol was specified"));
+                   _("Unable to create socket: %s"), _("Unknown family was specified"));
       return -1;
     }
 
-#ifdef SOCK_CLOEXEC
-  native_type |= SOCK_CLOEXEC;
-#endif
-  fd = socket (family, native_type, protocol);
-
-  if (fd < 0)
+  if (protocol == -1)
     {
-      int errsv = get_socket_errno ();
-
-      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
-                  _("Unable to create socket: %s"), socket_strerror (errsv));
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Unable to create socket: %s"), _("Unknown protocol was specified"));
+      return -1;
     }
 
-#ifndef G_OS_WIN32
-  {
-    int flags;
-
-    /* We always want to set close-on-exec to protect users. If you
-       need to so some weird inheritance to exec you can re-enable this
-       using lower level hacks with g_socket_get_fd(). */
-    flags = fcntl (fd, F_GETFD, 0);
-    if (flags != -1 &&
-       (flags & FD_CLOEXEC) == 0)
-      {
-       flags |= FD_CLOEXEC;
-       fcntl (fd, F_SETFD, flags);
-      }
-  }
-#endif
-
-  return fd;
+  return g_socket (family, native_type, protocol, error);
 }
 
 static void
@@ -489,12 +552,39 @@ g_socket_constructed (GObject *object)
                                               socket->priv->protocol,
                                               &socket->priv->construct_error);
 
-  /* Always use native nonblocking sockets, as
-     windows sets sockets to nonblocking automatically
-     in certain operations. This way we make things work
-     the same on all platforms */
   if (socket->priv->fd != -1)
-    set_fd_nonblocking (socket->priv->fd);
+    {
+#ifndef G_OS_WIN32
+      GError *error = NULL;
+#else
+      gulong arg;
+#endif
+
+      /* Always use native nonblocking sockets, as Windows sets sockets to
+       * nonblocking automatically in certain operations. This way we make
+       * things work the same on all platforms.
+       */
+#ifndef G_OS_WIN32
+      if (!g_unix_set_fd_nonblocking (socket->priv->fd, TRUE, &error))
+        {
+          g_warning ("Error setting socket nonblocking: %s", error->message);
+          g_clear_error (&error);
+        }
+#else
+      arg = TRUE;
+
+      if (ioctlsocket (socket->priv->fd, FIONBIO, &arg) == SOCKET_ERROR)
+        {
+          int errsv = get_socket_errno ();
+          g_warning ("Error setting socket status flags: %s", socket_strerror (errsv));
+        }
+#endif
+
+#ifdef SO_NOSIGPIPE
+      /* See note about SIGPIPE below. */
+      g_socket_set_option (socket, SOL_SOCKET, SO_NOSIGPIPE, TRUE, NULL);
+#endif
+    }
 }
 
 static void
@@ -546,6 +636,26 @@ g_socket_get_property (GObject    *object,
        g_value_take_object (value, address);
        break;
 
+      case PROP_TIMEOUT:
+       g_value_set_uint (value, socket->priv->timeout);
+       break;
+
+      case PROP_TTL:
+       g_value_set_uint (value, g_socket_get_ttl (socket));
+       break;
+
+      case PROP_BROADCAST:
+       g_value_set_boolean (value, g_socket_get_broadcast (socket));
+       break;
+
+      case PROP_MULTICAST_LOOPBACK:
+       g_value_set_boolean (value, g_socket_get_multicast_loopback (socket));
+       break;
+
+      case PROP_MULTICAST_TTL:
+       g_value_set_uint (value, g_socket_get_multicast_ttl (socket));
+       break;
+
       default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -589,6 +699,26 @@ g_socket_set_property (GObject      *object,
        g_socket_set_keepalive (socket, g_value_get_boolean (value));
        break;
 
+      case PROP_TIMEOUT:
+       g_socket_set_timeout (socket, g_value_get_uint (value));
+       break;
+
+      case PROP_TTL:
+       g_socket_set_ttl (socket, g_value_get_uint (value));
+       break;
+
+      case PROP_BROADCAST:
+       g_socket_set_broadcast (socket, g_value_get_boolean (value));
+       break;
+
+      case PROP_MULTICAST_LOOPBACK:
+       g_socket_set_multicast_loopback (socket, g_value_get_boolean (value));
+       break;
+
+      case PROP_MULTICAST_TTL:
+       g_socket_set_multicast_ttl (socket, g_value_get_uint (value));
+       break;
+
       default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -598,6 +728,7 @@ static void
 g_socket_finalize (GObject *object)
 {
   GSocket *socket = G_SOCKET (object);
+  gint i;
 
   g_clear_error (&socket->priv->construct_error);
 
@@ -605,10 +736,29 @@ g_socket_finalize (GObject *object)
       !socket->priv->closed)
     g_socket_close (socket, NULL);
 
+  if (socket->priv->remote_address)
+    g_object_unref (socket->priv->remote_address);
+
 #ifdef G_OS_WIN32
+  if (socket->priv->event != WSA_INVALID_EVENT)
+    {
+      WSACloseEvent (socket->priv->event);
+      socket->priv->event = WSA_INVALID_EVENT;
+    }
+
   g_assert (socket->priv->requested_conditions == NULL);
+  g_mutex_clear (&socket->priv->win32_source_lock);
 #endif
 
+  for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++)
+    {
+      if (socket->priv->recv_addr_cache[i].addr)
+        {
+          g_object_unref (socket->priv->recv_addr_cache[i].addr);
+          g_free (socket->priv->recv_addr_cache[i].native);
+        }
+    }
+
   if (G_OBJECT_CLASS (g_socket_parent_class)->finalize)
     (*G_OBJECT_CLASS (g_socket_parent_class)->finalize) (object);
 }
@@ -617,21 +767,20 @@ static void
 g_socket_class_init (GSocketClass *klass)
 {
   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
-  volatile GType type;
-
-  /* Make sure winsock has been initialized */
-  type = g_inet_address_get_type ();
 
 #ifdef SIGPIPE
   /* There is no portable, thread-safe way to avoid having the process
    * be killed by SIGPIPE when calling send() or sendmsg(), so we are
    * forced to simply ignore the signal process-wide.
+   *
+   * Even if we ignore it though, gdb will still stop if the app
+   * receives a SIGPIPE, which can be confusing and annoying. So when
+   * possible, we also use MSG_NOSIGNAL / SO_NOSIGPIPE elsewhere to
+   * prevent the signal from occurring at all.
    */
   signal (SIGPIPE, SIG_IGN);
 #endif
 
-  g_type_class_add_private (klass, sizeof (GSocketPrivate));
-
   gobject_class->finalize = g_socket_finalize;
   gobject_class->constructed = g_socket_constructed;
   gobject_class->set_property = g_socket_set_property;
@@ -719,6 +868,83 @@ g_socket_class_init (GSocketClass *klass)
                                                        G_TYPE_SOCKET_ADDRESS,
                                                        G_PARAM_READABLE |
                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GSocket:timeout:
+   *
+   * The timeout in seconds on socket I/O
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+                                  g_param_spec_uint ("timeout",
+                                                     P_("Timeout"),
+                                                     P_("The timeout in seconds on socket I/O"),
+                                                     0,
+                                                     G_MAXUINT,
+                                                     0,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GSocket:broadcast:
+   *
+   * Whether the socket should allow sending to broadcast addresses.
+   *
+   * Since: 2.32
+   */
+  g_object_class_install_property (gobject_class, PROP_BROADCAST,
+                                  g_param_spec_boolean ("broadcast",
+                                                        P_("Broadcast"),
+                                                        P_("Whether to allow sending to broadcast addresses"),
+                                                        FALSE,
+                                                        G_PARAM_READWRITE |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GSocket:ttl:
+   *
+   * Time-to-live for outgoing unicast packets
+   *
+   * Since: 2.32
+   */
+  g_object_class_install_property (gobject_class, PROP_TTL,
+                                  g_param_spec_uint ("ttl",
+                                                     P_("TTL"),
+                                                     P_("Time-to-live of outgoing unicast packets"),
+                                                     0, G_MAXUINT, 0,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GSocket:multicast-loopback:
+   *
+   * Whether outgoing multicast packets loop back to the local host.
+   *
+   * Since: 2.32
+   */
+  g_object_class_install_property (gobject_class, PROP_MULTICAST_LOOPBACK,
+                                  g_param_spec_boolean ("multicast-loopback",
+                                                        P_("Multicast loopback"),
+                                                        P_("Whether outgoing multicast packets loop back to the local host"),
+                                                        TRUE,
+                                                        G_PARAM_READWRITE |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GSocket:multicast-ttl:
+   *
+   * Time-to-live out outgoing multicast packets
+   *
+   * Since: 2.32
+   */
+  g_object_class_install_property (gobject_class, PROP_MULTICAST_TTL,
+                                  g_param_spec_uint ("multicast-ttl",
+                                                     P_("Multicast TTL"),
+                                                     P_("Time-to-live of outgoing multicast packets"),
+                                                     0, G_MAXUINT, 1,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -730,7 +956,7 @@ g_socket_initable_iface_init (GInitableIface *iface)
 static void
 g_socket_init (GSocket *socket)
 {
-  socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket, G_TYPE_SOCKET, GSocketPrivate);
+  socket->priv = g_socket_get_instance_private (socket);
 
   socket->priv->fd = -1;
   socket->priv->blocking = TRUE;
@@ -738,6 +964,7 @@ g_socket_init (GSocket *socket)
   socket->priv->construct_error = NULL;
 #ifdef G_OS_WIN32
   socket->priv->event = WSA_INVALID_EVENT;
+  g_mutex_init (&socket->priv->win32_source_lock);
 #endif
 }
 
@@ -917,7 +1144,7 @@ void
 g_socket_set_keepalive (GSocket  *socket,
                        gboolean  keepalive)
 {
-  int value;
+  GError *error = NULL;
 
   g_return_if_fail (G_IS_SOCKET (socket));
 
@@ -925,12 +1152,11 @@ g_socket_set_keepalive (GSocket  *socket,
   if (socket->priv->keepalive == keepalive)
     return;
 
-  value = (gint) keepalive;
-  if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_KEEPALIVE,
-                 (gpointer) &value, sizeof (value)) < 0)
+  if (!g_socket_set_option (socket, SOL_SOCKET, SO_KEEPALIVE,
+                           keepalive, &error))
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error setting keepalive: %s", socket_strerror (errsv));
+      g_warning ("error setting keepalive: %s", error->message);
+      g_error_free (error);
       return;
     }
 
@@ -1006,66 +1232,450 @@ g_socket_set_listen_backlog (GSocket *socket,
 }
 
 /**
- * g_socket_get_family:
+ * g_socket_get_timeout:
  * @socket: a #GSocket.
  *
- * Gets the socket family of the socket.
+ * Gets the timeout setting of the socket. For details on this, see
+ * g_socket_set_timeout().
  *
- * Returns: a #GSocketFamily
+ * Returns: the timeout in seconds
  *
- * Since: 2.22
+ * Since: 2.26
  */
-GSocketFamily
-g_socket_get_family (GSocket *socket)
+guint
+g_socket_get_timeout (GSocket *socket)
 {
-  g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_FAMILY_INVALID);
+  g_return_val_if_fail (G_IS_SOCKET (socket), 0);
 
-  return socket->priv->family;
+  return socket->priv->timeout;
 }
 
 /**
- * g_socket_get_socket_type:
+ * g_socket_set_timeout:
  * @socket: a #GSocket.
+ * @timeout: the timeout for @socket, in seconds, or 0 for none
  *
- * Gets the socket type of the socket.
+ * Sets the time in seconds after which I/O operations on @socket will
+ * time out if they have not yet completed.
  *
- * Returns: a #GSocketType
+ * On a blocking socket, this means that any blocking #GSocket
+ * operation will time out after @timeout seconds of inactivity,
+ * returning %G_IO_ERROR_TIMED_OUT.
  *
- * Since: 2.22
+ * On a non-blocking socket, calls to g_socket_condition_wait() will
+ * also fail with %G_IO_ERROR_TIMED_OUT after the given time. Sources
+ * created with g_socket_create_source() will trigger after
+ * @timeout seconds of inactivity, with the requested condition
+ * set, at which point calling g_socket_receive(), g_socket_send(),
+ * g_socket_check_connect_result(), etc, will fail with
+ * %G_IO_ERROR_TIMED_OUT.
+ *
+ * If @timeout is 0 (the default), operations will never time out
+ * on their own.
+ *
+ * Note that if an I/O operation is interrupted by a signal, this may
+ * cause the timeout to be reset.
+ *
+ * Since: 2.26
  */
-GSocketType
-g_socket_get_socket_type (GSocket *socket)
+void
+g_socket_set_timeout (GSocket *socket,
+                     guint    timeout)
 {
-  g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_TYPE_INVALID);
+  g_return_if_fail (G_IS_SOCKET (socket));
 
-  return socket->priv->type;
+  if (timeout != socket->priv->timeout)
+    {
+      socket->priv->timeout = timeout;
+      g_object_notify (G_OBJECT (socket), "timeout");
+    }
 }
 
 /**
- * g_socket_get_protocol:
+ * g_socket_get_ttl:
  * @socket: a #GSocket.
  *
- * Gets the socket protocol id the socket was created with.
- * In case the protocol is unknown, -1 is returned.
+ * Gets the unicast time-to-live setting on @socket; see
+ * g_socket_set_ttl() for more details.
  *
- * Returns: a protocol id, or -1 if unknown
+ * Returns: the time-to-live setting on @socket
  *
- * Since: 2.22
+ * Since: 2.32
  */
-GSocketProtocol
-g_socket_get_protocol (GSocket *socket)
+guint
+g_socket_get_ttl (GSocket *socket)
 {
-  g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+  GError *error = NULL;
+  gint value;
 
-  return socket->priv->protocol;
-}
+  g_return_val_if_fail (G_IS_SOCKET (socket), 0);
 
-/**
- * g_socket_get_fd:
- * @socket: a #GSocket.
- *
- * Returns the underlying OS socket object. On unix this
- * is a socket file descriptor, and on windows this is
+  if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+    {
+      g_socket_get_option (socket, IPPROTO_IP, IP_TTL,
+                          &value, &error);
+    }
+  else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+    {
+      g_socket_get_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+                          &value, &error);
+    }
+  else
+    g_return_val_if_reached (0);
+
+  if (error)
+    {
+      g_warning ("error getting unicast ttl: %s", error->message);
+      g_error_free (error);
+      return 0;
+    }
+
+  return value;
+}
+
+/**
+ * g_socket_set_ttl:
+ * @socket: a #GSocket.
+ * @ttl: the time-to-live value for all unicast packets on @socket
+ *
+ * Sets the time-to-live for outgoing unicast packets on @socket.
+ * By default the platform-specific default value is used.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_ttl (GSocket  *socket,
+                  guint     ttl)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_SOCKET (socket));
+
+  if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+    {
+      g_socket_set_option (socket, IPPROTO_IP, IP_TTL,
+                          ttl, &error);
+    }
+  else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+    {
+      g_socket_set_option (socket, IPPROTO_IP, IP_TTL,
+                          ttl, NULL);
+      g_socket_set_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+                          ttl, &error);
+    }
+  else
+    g_return_if_reached ();
+
+  if (error)
+    {
+      g_warning ("error setting unicast ttl: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_object_notify (G_OBJECT (socket), "ttl");
+}
+
+/**
+ * g_socket_get_broadcast:
+ * @socket: a #GSocket.
+ *
+ * Gets the broadcast setting on @socket; if %TRUE,
+ * it is possible to send packets to broadcast
+ * addresses.
+ *
+ * Returns: the broadcast setting on @socket
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_get_broadcast (GSocket *socket)
+{
+  GError *error = NULL;
+  gint value;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+  if (!g_socket_get_option (socket, SOL_SOCKET, SO_BROADCAST,
+                           &value, &error))
+    {
+      g_warning ("error getting broadcast: %s", error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+
+  return !!value;
+}
+
+/**
+ * g_socket_set_broadcast:
+ * @socket: a #GSocket.
+ * @broadcast: whether @socket should allow sending to broadcast
+ *     addresses
+ *
+ * Sets whether @socket should allow sending to broadcast addresses.
+ * This is %FALSE by default.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_broadcast (GSocket    *socket,
+                               gboolean    broadcast)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_SOCKET (socket));
+
+  broadcast = !!broadcast;
+
+  if (!g_socket_set_option (socket, SOL_SOCKET, SO_BROADCAST,
+                           broadcast, &error))
+    {
+      g_warning ("error setting broadcast: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_object_notify (G_OBJECT (socket), "broadcast");
+}
+
+/**
+ * g_socket_get_multicast_loopback:
+ * @socket: a #GSocket.
+ *
+ * Gets the multicast loopback setting on @socket; if %TRUE (the
+ * default), outgoing multicast packets will be looped back to
+ * multicast listeners on the same host.
+ *
+ * Returns: the multicast loopback setting on @socket
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_get_multicast_loopback (GSocket *socket)
+{
+  GError *error = NULL;
+  gint value;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+  if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+    {
+      g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+                          &value, &error);
+    }
+  else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+    {
+      g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                          &value, &error);
+    }
+  else
+    g_return_val_if_reached (FALSE);
+
+  if (error)
+    {
+      g_warning ("error getting multicast loopback: %s", error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+
+  return !!value;
+}
+
+/**
+ * g_socket_set_multicast_loopback:
+ * @socket: a #GSocket.
+ * @loopback: whether @socket should receive messages sent to its
+ *   multicast groups from the local host
+ *
+ * Sets whether outgoing multicast packets will be received by sockets
+ * listening on that multicast address on the same host. This is %TRUE
+ * by default.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_multicast_loopback (GSocket    *socket,
+                                gboolean    loopback)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_SOCKET (socket));
+
+  loopback = !!loopback;
+
+  if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+    {
+      g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+                          loopback, &error);
+    }
+  else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+    {
+      g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+                          loopback, NULL);
+      g_socket_set_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                          loopback, &error);
+    }
+  else
+    g_return_if_reached ();
+
+  if (error)
+    {
+      g_warning ("error setting multicast loopback: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_object_notify (G_OBJECT (socket), "multicast-loopback");
+}
+
+/**
+ * g_socket_get_multicast_ttl:
+ * @socket: a #GSocket.
+ *
+ * Gets the multicast time-to-live setting on @socket; see
+ * g_socket_set_multicast_ttl() for more details.
+ *
+ * Returns: the multicast time-to-live setting on @socket
+ *
+ * Since: 2.32
+ */
+guint
+g_socket_get_multicast_ttl (GSocket *socket)
+{
+  GError *error = NULL;
+  gint value;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), 0);
+
+  if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+    {
+      g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+                          &value, &error);
+    }
+  else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+    {
+      g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                          &value, &error);
+    }
+  else
+    g_return_val_if_reached (FALSE);
+
+  if (error)
+    {
+      g_warning ("error getting multicast ttl: %s", error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+
+  return value;
+}
+
+/**
+ * g_socket_set_multicast_ttl:
+ * @socket: a #GSocket.
+ * @ttl: the time-to-live value for all multicast datagrams on @socket
+ *
+ * Sets the time-to-live for outgoing multicast datagrams on @socket.
+ * By default, this is 1, meaning that multicast packets will not leave
+ * the local network.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_multicast_ttl (GSocket  *socket,
+                            guint     ttl)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_SOCKET (socket));
+
+  if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+    {
+      g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+                          ttl, &error);
+    }
+  else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+    {
+      g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+                          ttl, NULL);
+      g_socket_set_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                          ttl, &error);
+    }
+  else
+    g_return_if_reached ();
+
+  if (error)
+    {
+      g_warning ("error setting multicast ttl: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_object_notify (G_OBJECT (socket), "multicast-ttl");
+}
+
+/**
+ * g_socket_get_family:
+ * @socket: a #GSocket.
+ *
+ * Gets the socket family of the socket.
+ *
+ * Returns: a #GSocketFamily
+ *
+ * Since: 2.22
+ */
+GSocketFamily
+g_socket_get_family (GSocket *socket)
+{
+  g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_FAMILY_INVALID);
+
+  return socket->priv->family;
+}
+
+/**
+ * g_socket_get_socket_type:
+ * @socket: a #GSocket.
+ *
+ * Gets the socket type of the socket.
+ *
+ * Returns: a #GSocketType
+ *
+ * Since: 2.22
+ */
+GSocketType
+g_socket_get_socket_type (GSocket *socket)
+{
+  g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_TYPE_INVALID);
+
+  return socket->priv->type;
+}
+
+/**
+ * g_socket_get_protocol:
+ * @socket: a #GSocket.
+ *
+ * Gets the socket protocol id the socket was created with.
+ * In case the protocol is unknown, -1 is returned.
+ *
+ * Returns: a protocol id, or -1 if unknown
+ *
+ * Since: 2.22
+ */
+GSocketProtocol
+g_socket_get_protocol (GSocket *socket)
+{
+  g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+
+  return socket->priv->protocol;
+}
+
+/**
+ * g_socket_get_fd:
+ * @socket: a #GSocket.
+ *
+ * Returns the underlying OS socket object. On unix this
+ * is a socket file descriptor, and on Windows this is
  * a Winsock2 SOCKET handle. This may be useful for
  * doing platform specific or otherwise unusual operations
  * on the socket.
@@ -1091,7 +1701,7 @@ g_socket_get_fd (GSocket *socket)
  * useful if the socket has been bound to a local address,
  * either explicitly or implicitly when connecting.
  *
- * Returns: a #GSocketAddress or %NULL on error.
+ * Returns: (transfer full): a #GSocketAddress or %NULL on error.
  *     Free the returned object with g_object_unref().
  *
  * Since: 2.22
@@ -1101,7 +1711,7 @@ g_socket_get_local_address (GSocket  *socket,
                            GError  **error)
 {
   struct sockaddr_storage buffer;
-  guint32 len = sizeof (buffer);
+  guint len = sizeof (buffer);
 
   g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
 
@@ -1124,7 +1734,7 @@ g_socket_get_local_address (GSocket  *socket,
  * Try to get the remove address of a connected socket. This is only
  * useful for connection oriented sockets that have been connected.
  *
- * Returns: a #GSocketAddress or %NULL on error.
+ * Returns: (transfer full): a #GSocketAddress or %NULL on error.
  *     Free the returned object with g_object_unref().
  *
  * Since: 2.22
@@ -1134,19 +1744,32 @@ g_socket_get_remote_address (GSocket  *socket,
                             GError  **error)
 {
   struct sockaddr_storage buffer;
-  guint32 len = sizeof (buffer);
+  guint len = sizeof (buffer);
 
   g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
 
-  if (getpeername (socket->priv->fd, (struct sockaddr *) &buffer, &len) < 0)
+  if (socket->priv->connect_pending)
     {
-      int errsv = get_socket_errno ();
-      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
-                  _("could not get remote address: %s"), socket_strerror (errsv));
-      return NULL;
+      if (!g_socket_check_connect_result (socket, error))
+        return NULL;
+      else
+        socket->priv->connect_pending = FALSE;
     }
 
-  return g_socket_address_new_from_native (&buffer, len);
+  if (!socket->priv->remote_address)
+    {
+      if (getpeername (socket->priv->fd, (struct sockaddr *) &buffer, &len) < 0)
+       {
+         int errsv = get_socket_errno ();
+         g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+                      _("could not get remote address: %s"), socket_strerror (errsv));
+         return NULL;
+       }
+
+      socket->priv->remote_address = g_socket_address_new_from_native (&buffer, len);
+    }
+
+  return g_object_ref (socket->priv->remote_address);
 }
 
 /**
@@ -1225,14 +1848,20 @@ g_socket_listen (GSocket  *socket,
  * In certain situations, you may also want to bind a socket that will be
  * used to initiate connections, though this is not normally required.
  *
- * @allow_reuse should be %TRUE for server sockets (sockets that you will
- * eventually call g_socket_accept() on), and %FALSE for client sockets.
- * (Specifically, if it is %TRUE, then g_socket_bind() will set the
- * %SO_REUSEADDR flag on the socket, allowing it to bind @address even if
- * that address was previously used by another socket that has not yet been
- * fully cleaned-up by the kernel. Failing to set this flag on a server
- * socket may cause the bind call to return %G_IO_ERROR_ADDRESS_IN_USE if
- * the server program is stopped and then immediately restarted.)
+ * If @socket is a TCP socket, then @allow_reuse controls the setting
+ * of the `SO_REUSEADDR` socket option; normally it should be %TRUE for
+ * server sockets (sockets that you will eventually call
+ * g_socket_accept() on), and %FALSE for client sockets. (Failing to
+ * set this flag on a server socket may cause g_socket_bind() to return
+ * %G_IO_ERROR_ADDRESS_IN_USE if the server program is stopped and then
+ * immediately restarted.)
+ *
+ * If @socket is a UDP socket, then @allow_reuse determines whether or
+ * not other UDP sockets can be bound to the same address at the same
+ * time. In particular, you can have several UDP sockets bound to the
+ * same address, and they will all receive all of the multicast and
+ * broadcast packets sent to that address. (The behavior of unicast
+ * UDP packets to an address with multiple listeners is not defined.)
  *
  * Returns: %TRUE on success, %FALSE on error.
  *
@@ -1245,28 +1874,47 @@ g_socket_bind (GSocket         *socket,
               GError         **error)
 {
   struct sockaddr_storage addr;
+  gboolean so_reuseaddr;
+#ifdef SO_REUSEPORT
+  gboolean so_reuseport;
+#endif
 
   g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
 
   if (!check_socket (socket, error))
     return FALSE;
 
-  /* SO_REUSEADDR on windows means something else and is not what we want.
-     It always allows the unix variant of SO_REUSEADDR anyway */
-#ifndef G_OS_WIN32
-  {
-    int value;
+  if (!g_socket_address_to_native (address, &addr, sizeof addr, error))
+    return FALSE;
 
-    value = (int) !!reuse_address;
-    /* Ignore errors here, the only likely error is "not supported", and
-       this is a "best effort" thing mainly */
-    setsockopt (socket->priv->fd, SOL_SOCKET, SO_REUSEADDR,
-               (gpointer) &value, sizeof (value));
-  }
+  /* On Windows, SO_REUSEADDR has the semantics we want for UDP
+   * sockets, but has nasty side effects we don't want for TCP
+   * sockets.
+   *
+   * On other platforms, we set SO_REUSEPORT, if it exists, for
+   * UDP sockets, and SO_REUSEADDR for all sockets, hoping that
+   * if SO_REUSEPORT doesn't exist, then SO_REUSEADDR will have
+   * the desired semantics on UDP (as it does on Linux, although
+   * Linux has SO_REUSEPORT too as of 3.9).
+   */
+
+#ifdef G_OS_WIN32
+  so_reuseaddr = reuse_address && (socket->priv->type == G_SOCKET_TYPE_DATAGRAM);
+#else
+  so_reuseaddr = !!reuse_address;
 #endif
 
-  if (!g_socket_address_to_native (address, &addr, sizeof addr, error))
-    return FALSE;
+#ifdef SO_REUSEPORT
+  so_reuseport = reuse_address && (socket->priv->type == G_SOCKET_TYPE_DATAGRAM);
+#endif
+
+  /* Ignore errors here, the only likely error is "not supported", and
+   * this is a "best effort" thing mainly.
+   */
+  g_socket_set_option (socket, SOL_SOCKET, SO_REUSEADDR, so_reuseaddr, NULL);
+#ifdef SO_REUSEPORT
+  g_socket_set_option (socket, SOL_SOCKET, SO_REUSEPORT, so_reuseport, NULL);
+#endif
 
   if (bind (socket->priv->fd, (struct sockaddr *) &addr,
            g_socket_address_get_native_size (address)) < 0)
@@ -1281,6 +1929,220 @@ g_socket_bind (GSocket         *socket,
   return TRUE;
 }
 
+#if !defined(HAVE_IF_NAMETOINDEX) && defined(G_OS_WIN32)
+static guint
+if_nametoindex (const gchar *iface)
+{
+  PIP_ADAPTER_ADDRESSES addresses = NULL, p;
+  gulong addresses_len = 0;
+  guint idx = 0;
+  DWORD res;
+
+  res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len);
+  if (res != NO_ERROR && res != ERROR_BUFFER_OVERFLOW)
+    {
+      if (res == ERROR_NO_DATA)
+        errno = ENXIO;
+      else
+        errno = EINVAL;
+      return 0;
+    }
+
+  addresses = g_malloc (addresses_len);
+  res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, addresses, &addresses_len);
+
+  if (res != NO_ERROR)
+    {
+      g_free (addresses);
+      if (res == ERROR_NO_DATA)
+        errno = ENXIO;
+      else
+        errno = EINVAL;
+      return 0;
+    }
+
+  p = addresses;
+  while (p)
+    {
+      if (strcmp (p->AdapterName, iface) == 0)
+        {
+          idx = p->IfIndex;
+          break;
+        }
+      p = p->Next;
+    }
+
+  if (p == NULL)
+    errno = ENXIO;
+
+  g_free (addresses);
+
+  return idx;
+}
+
+#define HAVE_IF_NAMETOINDEX 1
+#endif
+
+static gboolean
+g_socket_multicast_group_operation (GSocket       *socket,
+                                   GInetAddress  *group,
+                                    gboolean       source_specific,
+                                    const gchar   *iface,
+                                   gboolean       join_group,
+                                   GError       **error)
+{
+  const guint8 *native_addr;
+  gint optname, result;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+  g_return_val_if_fail (socket->priv->type == G_SOCKET_TYPE_DATAGRAM, FALSE);
+  g_return_val_if_fail (G_IS_INET_ADDRESS (group), FALSE);
+
+  if (!check_socket (socket, error))
+    return FALSE;
+
+  native_addr = g_inet_address_to_bytes (group);
+  if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4)
+    {
+#ifdef HAVE_IP_MREQN
+      struct ip_mreqn mc_req;
+#else
+      struct ip_mreq mc_req;
+#endif
+
+      memset (&mc_req, 0, sizeof (mc_req));
+      memcpy (&mc_req.imr_multiaddr, native_addr, sizeof (struct in_addr));
+
+#ifdef HAVE_IP_MREQN
+      if (iface)
+        mc_req.imr_ifindex = if_nametoindex (iface);
+      else
+        mc_req.imr_ifindex = 0;  /* Pick any.  */
+#elif defined(G_OS_WIN32)
+      if (iface)
+        mc_req.imr_interface.s_addr = g_htonl (if_nametoindex (iface));
+      else
+        mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
+#else
+      mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
+#endif
+
+      if (source_specific)
+       {
+#ifdef IP_ADD_SOURCE_MEMBERSHIP
+         optname = join_group ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP;
+#else
+         g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                      join_group ?
+                      _("Error joining multicast group: %s") :
+                      _("Error leaving multicast group: %s"),
+                      _("No support for source-specific multicast"));
+         return FALSE;
+#endif
+       }
+      else
+        optname = join_group ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
+      result = setsockopt (socket->priv->fd, IPPROTO_IP, optname,
+                          &mc_req, sizeof (mc_req));
+    }
+  else if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV6)
+    {
+      struct ipv6_mreq mc_req_ipv6;
+
+      memset (&mc_req_ipv6, 0, sizeof (mc_req_ipv6));
+      memcpy (&mc_req_ipv6.ipv6mr_multiaddr, native_addr, sizeof (struct in6_addr));
+#ifdef HAVE_IF_NAMETOINDEX
+      if (iface)
+        mc_req_ipv6.ipv6mr_interface = if_nametoindex (iface);
+      else
+#endif
+        mc_req_ipv6.ipv6mr_interface = 0;
+
+      optname = join_group ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
+      result = setsockopt (socket->priv->fd, IPPROTO_IPV6, optname,
+                          &mc_req_ipv6, sizeof (mc_req_ipv6));
+    }
+  else
+    g_return_val_if_reached (FALSE);
+
+  if (result < 0)
+    {
+      int errsv = get_socket_errno ();
+
+      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+                  join_group ?
+                  _("Error joining multicast group: %s") :
+                  _("Error leaving multicast group: %s"),
+                  socket_strerror (errsv));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * g_socket_join_multicast_group:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to join.
+ * @iface: (allow-none): Name of the interface to use, or %NULL
+ * @source_specific: %TRUE if source-specific multicast should be used
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Registers @socket to receive multicast messages sent to @group.
+ * @socket must be a %G_SOCKET_TYPE_DATAGRAM socket, and must have
+ * been bound to an appropriate interface and port with
+ * g_socket_bind().
+ *
+ * If @iface is %NULL, the system will automatically pick an interface
+ * to bind to based on @group.
+ *
+ * If @source_specific is %TRUE, source-specific multicast as defined
+ * in RFC 4604 is used. Note that on older platforms this may fail
+ * with a %G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_join_multicast_group (GSocket       *socket,
+                              GInetAddress  *group,
+                               gboolean       source_specific,
+                               const gchar   *iface,
+                              GError       **error)
+{
+  return g_socket_multicast_group_operation (socket, group, source_specific, iface, TRUE, error);
+}
+
+/**
+ * g_socket_leave_multicast_group:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to leave.
+ * @iface: (allow-none): Interface used
+ * @source_specific: %TRUE if source-specific multicast was used
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Removes @socket from the multicast group defined by @group, @iface,
+ * and @source_specific (which must all have the same values they had
+ * when you joined the group).
+ *
+ * @socket remains bound to its address and port, and can still receive
+ * unicast messages after calling this.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_leave_multicast_group (GSocket       *socket,
+                               GInetAddress  *group,
+                                gboolean       source_specific,
+                                const gchar   *iface,
+                               GError       **error)
+{
+  return g_socket_multicast_group_operation (socket, group, source_specific, iface, FALSE, error);
+}
+
 /**
  * g_socket_speaks_ipv4:
  * @socket: a #GSocket
@@ -1297,7 +2159,7 @@ g_socket_bind (GSocket         *socket,
  *
  * Returns: %TRUE if this socket can be used with IPv4.
  *
- * Since: 2.22.
+ * Since: 2.22
  **/
 gboolean
 g_socket_speaks_ipv4 (GSocket *socket)
@@ -1310,12 +2172,11 @@ g_socket_speaks_ipv4 (GSocket *socket)
     case G_SOCKET_FAMILY_IPV6:
 #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY)
       {
-        guint sizeof_int = sizeof (int);
         gint v6_only;
 
-        if (getsockopt (socket->priv->fd,
-                        IPPROTO_IPV6, IPV6_V6ONLY,
-                        &v6_only, &sizeof_int) != 0)
+        if (!g_socket_get_option (socket,
+                                 IPPROTO_IPV6, IPV6_V6ONLY,
+                                 &v6_only, NULL))
           return FALSE;
 
         return !v6_only;
@@ -1332,7 +2193,7 @@ g_socket_speaks_ipv4 (GSocket *socket)
 /**
  * g_socket_accept:
  * @socket: a #GSocket.
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Accept incoming connections on a connection-based socket. This removes
@@ -1346,7 +2207,7 @@ g_socket_speaks_ipv4 (GSocket *socket)
  * or return %G_IO_ERROR_WOULD_BLOCK if non-blocking I/O is enabled.
  * To be notified of an incoming connection, wait for the %G_IO_IN condition.
  *
- * Returns: a new #GSocket, or %NULL on error.
+ * Returns: (transfer full): a new #GSocket, or %NULL on error.
  *     Free the returned object with g_object_unref().
  *
  * Since: 2.22
@@ -1364,6 +2225,9 @@ g_socket_accept (GSocket       *socket,
   if (!check_socket (socket, error))
     return NULL;
 
+  if (!check_timeout (socket, error))
+    return NULL;
+
   while (TRUE)
     {
       if (socket->priv->blocking &&
@@ -1444,7 +2308,7 @@ g_socket_accept (GSocket       *socket,
  * g_socket_connect:
  * @socket: a #GSocket.
  * @address: a #GSocketAddress specifying the remote address.
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Connect the socket to the specified remote address.
@@ -1461,7 +2325,7 @@ g_socket_accept (GSocket       *socket,
  * If the connect call needs to do network I/O it will block, unless
  * non-blocking I/O is enabled. Then %G_IO_ERROR_PENDING is returned
  * and the user can be notified of the connection finishing by waiting
- * for the G_IO_OUT condition. The result of the connection can then be
+ * for the G_IO_OUT condition. The result of the connection must then be
  * checked with g_socket_check_connect_result().
  *
  * Returns: %TRUE if connected, %FALSE on error.
@@ -1484,6 +2348,10 @@ g_socket_connect (GSocket         *socket,
   if (!g_socket_address_to_native (address, &buffer, sizeof buffer, error))
     return FALSE;
 
+  if (socket->priv->remote_address)
+    g_object_unref (socket->priv->remote_address);
+  socket->priv->remote_address = g_object_ref (address);
+
   while (1)
     {
       if (connect (socket->priv->fd, (struct sockaddr *) &buffer,
@@ -1507,16 +2375,18 @@ g_socket_connect (GSocket         *socket,
                      if (g_socket_check_connect_result (socket, error))
                        break;
                    }
-                 g_prefix_error (error, _("Error connecting: "));
                }
              else
-                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING,
-                                     _("Connection in progress"));
+                {
+                  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                                       _("Connection in progress"));
+                  socket->priv->connect_pending = TRUE;
+                }
            }
          else
-           g_set_error (error, G_IO_ERROR,
-                        socket_io_error_from_errno (errsv),
-                        _("Error connecting: %s"), socket_strerror (errsv));
+           g_set_error_literal (error, G_IO_ERROR,
+                                socket_io_error_from_errno (errsv),
+                                socket_strerror (errsv));
 
          return FALSE;
        }
@@ -1547,16 +2417,19 @@ gboolean
 g_socket_check_connect_result (GSocket  *socket,
                               GError  **error)
 {
-  guint optlen;
   int value;
 
-  optlen = sizeof (value);
-  if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_ERROR, (void *)&value, &optlen) != 0)
-    {
-      int errsv = get_socket_errno ();
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
 
-      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
-                  _("Unable to get pending error: %s"), socket_strerror (errsv));
+  if (!check_socket (socket, error))
+    return FALSE;
+
+  if (!check_timeout (socket, error))
+    return FALSE;
+
+  if (!g_socket_get_option (socket, SOL_SOCKET, SO_ERROR, &value, error))
+    {
+      g_prefix_error (error, _("Unable to get pending error: "));
       return FALSE;
     }
 
@@ -1564,18 +2437,86 @@ g_socket_check_connect_result (GSocket  *socket,
     {
       g_set_error_literal (error, G_IO_ERROR, socket_io_error_from_errno (value),
                            socket_strerror (value));
+      if (socket->priv->remote_address)
+        {
+          g_object_unref (socket->priv->remote_address);
+          socket->priv->remote_address = NULL;
+        }
       return FALSE;
     }
+
+  socket->priv->connected = TRUE;
   return TRUE;
 }
 
 /**
+ * g_socket_get_available_bytes:
+ * @socket: a #GSocket
+ *
+ * Get the amount of data pending in the OS input buffer.
+ *
+ * If @socket is a UDP or SCTP socket, this will return the size of
+ * just the next packet, even if additional packets are buffered after
+ * that one.
+ *
+ * Note that on Windows, this function is rather inefficient in the
+ * UDP case, and so if you know any plausible upper bound on the size
+ * of the incoming packet, it is better to just do a
+ * g_socket_receive() with a buffer of that size, rather than calling
+ * g_socket_get_available_bytes() first and then doing a receive of
+ * exactly the right size.
+ *
+ * Returns: the number of bytes that can be read from the socket
+ * without blocking or truncating, or -1 on error.
+ *
+ * Since: 2.32
+ */
+gssize
+g_socket_get_available_bytes (GSocket *socket)
+{
+#ifdef G_OS_WIN32
+  const gint bufsize = 64 * 1024;
+  static guchar *buf = NULL;
+  u_long avail;
+#else
+  gint avail;
+#endif
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+
+#if defined (SO_NREAD)
+  if (!g_socket_get_option (socket, SOL_SOCKET, SO_NREAD, &avail, NULL))
+      return -1;
+#elif !defined (G_OS_WIN32)
+  if (ioctl (socket->priv->fd, FIONREAD, &avail) < 0)
+    avail = -1;
+#else
+  if (socket->priv->type == G_SOCKET_TYPE_DATAGRAM)
+    {
+      if (G_UNLIKELY (g_once_init_enter (&buf)))
+        g_once_init_leave (&buf, g_malloc (bufsize));
+
+      avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK);
+      if (avail == -1 && get_socket_errno () == WSAEWOULDBLOCK)
+        avail = 0;
+    }
+  else
+    {
+      if (ioctlsocket (socket->priv->fd, FIONREAD, &avail) < 0)
+        avail = -1;
+    }
+#endif
+
+  return avail;
+}
+
+/**
  * g_socket_receive:
  * @socket: a #GSocket
- * @buffer: a buffer to read data into (which should be at least @size
- *     bytes long).
+ * @buffer: (array length=size) (element-type guint8): a buffer to
+ *     read data into (which should be at least @size bytes long).
  * @size: the number of bytes you want to read from the socket
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Receive data (up to @size bytes) from a socket. This is mainly used by
@@ -1593,15 +2534,17 @@ g_socket_check_connect_result (GSocket  *socket,
  * received, the additional data will be returned in future calls to
  * g_socket_receive().
  *
- * If the socket is in blocking mode the call will block until there is
- * some data to receive or there is an error. If there is no data available
- * and the socket is in non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified when data is available, wait for the
+ * If the socket is in blocking mode the call will block until there
+ * is some data to receive, the connection is closed, or there is an
+ * error. If there is no data available and the socket is in
+ * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned. To be notified when data is available, wait for the
  * %G_IO_IN condition.
  *
  * On error -1 is returned and @error is set accordingly.
  *
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.22
  */
@@ -1612,19 +2555,54 @@ g_socket_receive (GSocket       *socket,
                  GCancellable  *cancellable,
                  GError       **error)
 {
+  return g_socket_receive_with_blocking (socket, buffer, size,
+                                        socket->priv->blocking,
+                                        cancellable, error);
+}
+
+/**
+ * g_socket_receive_with_blocking:
+ * @socket: a #GSocket
+ * @buffer: (array length=size) (element-type guint8): a buffer to
+ *     read data into (which should be at least @size bytes long).
+ * @size: the number of bytes you want to read from the socket
+ * @blocking: whether to do blocking or non-blocking I/O
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * This behaves exactly the same as g_socket_receive(), except that
+ * the choice of blocking or non-blocking behavior is determined by
+ * the @blocking argument rather than by @socket's properties.
+ *
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
+ *
+ * Since: 2.26
+ */
+gssize
+g_socket_receive_with_blocking (GSocket       *socket,
+                               gchar         *buffer,
+                               gsize          size,
+                               gboolean       blocking,
+                               GCancellable  *cancellable,
+                               GError       **error)
+{
   gssize ret;
 
-  g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE);
+  g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1);
 
   if (!check_socket (socket, error))
     return -1;
 
+  if (!check_timeout (socket, error))
+    return -1;
+
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return -1;
 
   while (1)
     {
-      if (socket->priv->blocking &&
+      if (blocking &&
          !g_socket_condition_wait (socket,
                                    G_IO_IN, cancellable, error))
        return -1;
@@ -1636,7 +2614,7 @@ g_socket_receive (GSocket       *socket,
          if (errsv == EINTR)
            continue;
 
-         if (socket->priv->blocking)
+         if (blocking)
            {
 #ifdef WSAEWOULDBLOCK
              if (errsv == WSAEWOULDBLOCK)
@@ -1667,11 +2645,12 @@ g_socket_receive (GSocket       *socket,
 /**
  * g_socket_receive_from:
  * @socket: a #GSocket
- * @address: a pointer to a #GSocketAddress pointer, or %NULL
- * @buffer: a buffer to read data into (which should be at least @size
- *     bytes long).
+ * @address: (out) (allow-none): a pointer to a #GSocketAddress
+ *     pointer, or %NULL
+ * @buffer: (array length=size) (element-type guint8): a buffer to
+ *     read data into (which should be at least @size bytes long).
  * @size: the number of bytes you want to read from the socket
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Receive data (up to @size bytes) from a socket.
@@ -1682,7 +2661,8 @@ g_socket_receive (GSocket       *socket,
  *
  * See g_socket_receive() for additional information.
  *
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.22
  */
@@ -1707,10 +2687,7 @@ g_socket_receive_from (GSocket         *socket,
                                   error);
 }
 
-/* Although we ignore SIGPIPE, gdb will still stop if the app receives
- * one, which can be confusing and annoying. So if possible, we want
- * to suppress the signal entirely.
- */
+/* See the comment about SIGPIPE above. */
 #ifdef MSG_NOSIGNAL
 #define G_SOCKET_DEFAULT_SEND_FLAGS MSG_NOSIGNAL
 #else
@@ -1720,9 +2697,10 @@ g_socket_receive_from (GSocket         *socket,
 /**
  * g_socket_send:
  * @socket: a #GSocket
- * @buffer: the buffer containing the data to send.
+ * @buffer: (array length=size) (element-type guint8): the buffer
+ *     containing the data to send.
  * @size: the number of bytes to send
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Tries to send @size bytes from @buffer on the socket. This is
@@ -1752,19 +2730,54 @@ g_socket_send (GSocket       *socket,
               GCancellable  *cancellable,
               GError       **error)
 {
+  return g_socket_send_with_blocking (socket, buffer, size,
+                                     socket->priv->blocking,
+                                     cancellable, error);
+}
+
+/**
+ * g_socket_send_with_blocking:
+ * @socket: a #GSocket
+ * @buffer: (array length=size) (element-type guint8): the buffer
+ *     containing the data to send.
+ * @size: the number of bytes to send
+ * @blocking: whether to do blocking or non-blocking I/O
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * This behaves exactly the same as g_socket_send(), except that
+ * the choice of blocking or non-blocking behavior is determined by
+ * the @blocking argument rather than by @socket's properties.
+ *
+ * Returns: Number of bytes written (which may be less than @size), or -1
+ * on error
+ *
+ * Since: 2.26
+ */
+gssize
+g_socket_send_with_blocking (GSocket       *socket,
+                            const gchar   *buffer,
+                            gsize          size,
+                            gboolean       blocking,
+                            GCancellable  *cancellable,
+                            GError       **error)
+{
   gssize ret;
 
-  g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE);
+  g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1);
 
   if (!check_socket (socket, error))
     return -1;
 
+  if (!check_timeout (socket, error))
+    return -1;
+
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return -1;
 
   while (1)
     {
-      if (socket->priv->blocking &&
+      if (blocking &&
          !g_socket_condition_wait (socket,
                                    G_IO_OUT, cancellable, error))
        return -1;
@@ -1781,7 +2794,7 @@ g_socket_send (GSocket       *socket,
            win32_unset_event_mask (socket, FD_WRITE);
 #endif
 
-         if (socket->priv->blocking)
+         if (blocking)
            {
 #ifdef WSAEWOULDBLOCK
              if (errsv == WSAEWOULDBLOCK)
@@ -1807,10 +2820,11 @@ g_socket_send (GSocket       *socket,
 /**
  * g_socket_send_to:
  * @socket: a #GSocket
- * @address: a #GSocketAddress, or %NULL
- * @buffer: the buffer containing the data to send.
+ * @address: (allow-none): a #GSocketAddress, or %NULL
+ * @buffer: (array length=size) (element-type guint8): the buffer
+ *     containing the data to send.
  * @size: the number of bytes to send
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Tries to send @size bytes from @buffer to @address. If @address is
@@ -1855,7 +2869,7 @@ g_socket_send_to (GSocket         *socket,
  *
  * Shut down part of a full-duplex connection.
  *
- * If @shutdown_read is %TRUE then the recieving side of the connection
+ * If @shutdown_read is %TRUE then the receiving side of the connection
  * is shut down, and further reading is disallowed.
  *
  * If @shutdown_write is %TRUE then the sending side of the connection
@@ -1881,7 +2895,7 @@ g_socket_shutdown (GSocket   *socket,
 
   g_return_val_if_fail (G_IS_SOCKET (socket), TRUE);
 
-  if (!check_socket (socket, NULL))
+  if (!check_socket (socket, error))
     return FALSE;
 
   /* Do nothing? */
@@ -1908,7 +2922,7 @@ g_socket_shutdown (GSocket   *socket,
     {
       int errsv = get_socket_errno ();
       g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
-                  _("Unable to create socket: %s"), socket_strerror (errsv));
+                  _("Unable to shutdown socket: %s"), socket_strerror (errsv));
       return FALSE;
     }
 
@@ -1968,7 +2982,7 @@ g_socket_close (GSocket  *socket,
   if (socket->priv->closed)
     return TRUE; /* Multiple close not an error */
 
-  if (!check_socket (socket, NULL))
+  if (!check_socket (socket, error))
     return FALSE;
 
   while (1)
@@ -1994,16 +3008,13 @@ g_socket_close (GSocket  *socket,
       break;
     }
 
-#ifdef G_OS_WIN32
-  if (socket->priv->event != WSA_INVALID_EVENT)
-    {
-      WSACloseEvent (socket->priv->event);
-      socket->priv->event = WSA_INVALID_EVENT;
-    }
-#endif
-
   socket->priv->connected = FALSE;
   socket->priv->closed = TRUE;
+  if (socket->priv->remote_address)
+    {
+      g_object_unref (socket->priv->remote_address);
+      socket->priv->remote_address = NULL;
+    }
 
   return TRUE;
 }
@@ -2027,19 +3038,6 @@ g_socket_is_closed (GSocket *socket)
 #ifdef G_OS_WIN32
 /* Broken source, used on errors */
 static gboolean
-broken_prepare  (GSource *source,
-                gint    *timeout)
-{
-  return FALSE;
-}
-
-static gboolean
-broken_check (GSource *source)
-{
-  return FALSE;
-}
-
-static gboolean
 broken_dispatch (GSource     *source,
                 GSourceFunc  callback,
                 gpointer     user_data)
@@ -2049,8 +3047,8 @@ broken_dispatch (GSource     *source,
 
 static GSourceFuncs broken_funcs =
 {
-  broken_prepare,
-  broken_check,
+  NULL,
+  NULL,
   broken_dispatch,
   NULL
 };
@@ -2112,24 +3110,28 @@ static void
 add_condition_watch (GSocket      *socket,
                     GIOCondition *condition)
 {
+  g_mutex_lock (&socket->priv->win32_source_lock);
   g_assert (g_list_find (socket->priv->requested_conditions, condition) == NULL);
 
   socket->priv->requested_conditions =
     g_list_prepend (socket->priv->requested_conditions, condition);
 
   update_select_events (socket);
+  g_mutex_unlock (&socket->priv->win32_source_lock);
 }
 
 static void
 remove_condition_watch (GSocket      *socket,
                        GIOCondition *condition)
 {
+  g_mutex_lock (&socket->priv->win32_source_lock);
   g_assert (g_list_find (socket->priv->requested_conditions, condition) != NULL);
 
   socket->priv->requested_conditions =
     g_list_remove (socket->priv->requested_conditions, condition);
 
   update_select_events (socket);
+  g_mutex_unlock (&socket->priv->win32_source_lock);
 }
 
 static GIOCondition
@@ -2155,8 +3157,26 @@ update_condition (GSocket *socket)
   if (socket->priv->current_events & (FD_READ | FD_ACCEPT))
     condition |= G_IO_IN;
 
-  if (socket->priv->current_events & FD_CLOSE ||
-      socket->priv->closed)
+  if (socket->priv->current_events & FD_CLOSE)
+    {
+      int r, errsv, buffer;
+
+      r = recv (socket->priv->fd, &buffer, sizeof (buffer), MSG_PEEK);
+      if (r < 0)
+          errsv = get_socket_errno ();
+
+      if (r > 0 ||
+          (r < 0 && errsv == WSAENOTCONN))
+        condition |= G_IO_IN;
+      else if (r == 0 ||
+               (r < 0 && (errsv == WSAESHUTDOWN || errsv == WSAECONNRESET ||
+                          errsv == WSAECONNABORTED || errsv == WSAENETRESET)))
+        condition |= G_IO_HUP;
+      else
+        condition |= G_IO_ERR;
+    }
+
+  if (socket->priv->closed)
     condition |= G_IO_HUP;
 
   /* Never report both G_IO_OUT and HUP, these are
@@ -2182,111 +3202,140 @@ update_condition (GSocket *socket)
 
   return condition;
 }
+#endif
 
 typedef struct {
   GSource       source;
+#ifdef G_OS_WIN32
   GPollFD       pollfd;
+#else
+  gpointer      fd_tag;
+#endif
   GSocket      *socket;
   GIOCondition  condition;
-  GCancellable *cancellable;
-  GPollFD       cancel_pollfd;
-  GIOCondition  result_condition;
-} GWinsockSource;
+} GSocketSource;
 
+#ifdef G_OS_WIN32
 static gboolean
-winsock_prepare (GSource *source,
-                gint    *timeout)
+socket_source_prepare_win32 (GSource *source,
+                             gint    *timeout)
 {
-  GWinsockSource *winsock_source = (GWinsockSource *)source;
-  GIOCondition current_condition;
+  GSocketSource *socket_source = (GSocketSource *)source;
 
-  current_condition = update_condition (winsock_source->socket);
+  *timeout = -1;
 
-  if (g_cancellable_is_cancelled (winsock_source->cancellable))
-    {
-      winsock_source->result_condition = current_condition;
-      return TRUE;
-    }
+  return (update_condition (socket_source->socket) & socket_source->condition) != 0;
+}
 
-  if ((winsock_source->condition & current_condition) != 0)
-    {
-      winsock_source->result_condition = current_condition;
-      return TRUE;
-    }
+static gboolean
+socket_source_check_win32 (GSource *source)
+{
+  int timeout;
 
-  return FALSE;
+  return socket_source_prepare_win32 (source, &timeout);
 }
+#endif
 
 static gboolean
-winsock_check (GSource *source)
+socket_source_dispatch (GSource     *source,
+                       GSourceFunc  callback,
+                       gpointer     user_data)
 {
-  GWinsockSource *winsock_source = (GWinsockSource *)source;
-  GIOCondition current_condition;
-
-  current_condition = update_condition (winsock_source->socket);
+  GSocketSourceFunc func = (GSocketSourceFunc)callback;
+  GSocketSource *socket_source = (GSocketSource *)source;
+  GSocket *socket = socket_source->socket;
+  gint64 timeout;
+  guint events;
+  gboolean ret;
 
-  if (g_cancellable_is_cancelled (winsock_source->cancellable))
-    {
-      winsock_source->result_condition = current_condition;
-      return TRUE;
-    }
+#ifdef G_OS_WIN32
+  events = update_condition (socket_source->socket);
+#else
+  events = g_source_query_unix_fd (source, socket_source->fd_tag);
+#endif
 
-  if ((winsock_source->condition & current_condition) != 0)
+  timeout = g_source_get_ready_time (source);
+  if (timeout >= 0 && timeout < g_source_get_time (source))
     {
-      winsock_source->result_condition = current_condition;
-      return TRUE;
+      socket->priv->timed_out = TRUE;
+      events |= (G_IO_IN | G_IO_OUT);
     }
 
-  return FALSE;
-}
+  ret = (*func) (socket, events & socket_source->condition, user_data);
 
-static gboolean
-winsock_dispatch (GSource     *source,
-                 GSourceFunc  callback,
-                 gpointer     user_data)
-{
-  GSocketSourceFunc func = (GSocketSourceFunc)callback;
-  GWinsockSource *winsock_source = (GWinsockSource *)source;
+  if (socket->priv->timeout)
+    g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000);
+  else
+    g_source_set_ready_time (source, -1);
 
-  return (*func) (winsock_source->socket,
-                 winsock_source->result_condition & winsock_source->condition,
-                 user_data);
+  return ret;
 }
 
 static void
-winsock_finalize (GSource *source)
+socket_source_finalize (GSource *source)
 {
-  GWinsockSource *winsock_source = (GWinsockSource *)source;
+  GSocketSource *socket_source = (GSocketSource *)source;
   GSocket *socket;
 
-  socket = winsock_source->socket;
+  socket = socket_source->socket;
+
+#ifdef G_OS_WIN32
+  remove_condition_watch (socket, &socket_source->condition);
+#endif
 
-  remove_condition_watch (socket, &winsock_source->condition);
   g_object_unref (socket);
+}
 
-  if (winsock_source->cancellable)
-    {
-      g_cancellable_release_fd (winsock_source->cancellable);
-      g_object_unref (winsock_source->cancellable);
-    }
+static gboolean
+socket_source_closure_callback (GSocket      *socket,
+                               GIOCondition  condition,
+                               gpointer      data)
+{
+  GClosure *closure = data;
+
+  GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
+  GValue result_value = G_VALUE_INIT;
+  gboolean result;
+
+  g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+  g_value_init (&params[0], G_TYPE_SOCKET);
+  g_value_set_object (&params[0], socket);
+  g_value_init (&params[1], G_TYPE_IO_CONDITION);
+  g_value_set_flags (&params[1], condition);
+
+  g_closure_invoke (closure, &result_value, 2, params, NULL);
+
+  result = g_value_get_boolean (&result_value);
+  g_value_unset (&result_value);
+  g_value_unset (&params[0]);
+  g_value_unset (&params[1]);
+
+  return result;
 }
 
-static GSourceFuncs winsock_funcs =
+static GSourceFuncs socket_source_funcs =
 {
-  winsock_prepare,
-  winsock_check,
-  winsock_dispatch,
-  winsock_finalize
+#ifdef G_OS_WIN32
+  socket_source_prepare_win32,
+  socket_source_check_win32,
+#else
+  NULL, NULL, /* check, prepare */
+#endif
+  socket_source_dispatch,
+  socket_source_finalize,
+  (GSourceFunc)socket_source_closure_callback,
 };
 
 static GSource *
-winsock_source_new (GSocket      *socket,
-                   GIOCondition  condition,
-                   GCancellable *cancellable)
+socket_source_new (GSocket      *socket,
+                  GIOCondition  condition,
+                  GCancellable *cancellable)
 {
   GSource *source;
-  GWinsockSource *winsock_source;
+  GSocketSource *socket_source;
 
+#ifdef G_OS_WIN32
   ensure_event (socket);
 
   if (socket->priv->event == WSA_INVALID_EVENT)
@@ -2294,43 +3343,58 @@ winsock_source_new (GSocket      *socket,
       g_warning ("Failed to create WSAEvent");
       return g_source_new (&broken_funcs, sizeof (GSource));
     }
+#endif
 
-  condition |= G_IO_HUP | G_IO_ERR;
+  condition |= G_IO_HUP | G_IO_ERR | G_IO_NVAL;
 
-  source = g_source_new (&winsock_funcs, sizeof (GWinsockSource));
-  winsock_source = (GWinsockSource *)source;
+  source = g_source_new (&socket_source_funcs, sizeof (GSocketSource));
+  g_source_set_name (source, "GSocket");
+  socket_source = (GSocketSource *)source;
 
-  winsock_source->socket = g_object_ref (socket);
-  winsock_source->condition = condition;
-  add_condition_watch (socket, &winsock_source->condition);
+  socket_source->socket = g_object_ref (socket);
+  socket_source->condition = condition;
 
-  if (g_cancellable_make_pollfd (cancellable,
-                                 &winsock_source->cancel_pollfd))
+  if (cancellable)
     {
-      winsock_source->cancellable = g_object_ref (cancellable);
-      g_source_add_poll (source, &winsock_source->cancel_pollfd);
+      GSource *cancellable_source;
+
+      cancellable_source = g_cancellable_source_new (cancellable);
+      g_source_add_child_source (source, cancellable_source);
+      g_source_set_dummy_callback (cancellable_source);
+      g_source_unref (cancellable_source);
     }
 
-  winsock_source->pollfd.fd = (gintptr) socket->priv->event;
-  winsock_source->pollfd.events = condition;
-  g_source_add_poll (source, &winsock_source->pollfd);
+#ifdef G_OS_WIN32
+  add_condition_watch (socket, &socket_source->condition);
+  socket_source->pollfd.fd = (gintptr) socket->priv->event;
+  socket_source->pollfd.events = condition;
+  socket_source->pollfd.revents = 0;
+  g_source_add_poll (source, &socket_source->pollfd);
+#else
+  socket_source->fd_tag = g_source_add_unix_fd (source, socket->priv->fd, condition);
+#endif
+
+  if (socket->priv->timeout)
+    g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000);
+  else
+    g_source_set_ready_time (source, -1);
 
   return source;
 }
-#endif
 
 /**
- * g_socket_create_source:
+ * g_socket_create_source: (skip)
  * @socket: a #GSocket
  * @condition: a #GIOCondition mask to monitor
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  *
- * Creates a %GSource that can be attached to a %GMainContext to monitor
- * for the availibility of the specified @condition on the socket.
+ * Creates a #GSource that can be attached to a %GMainContext to monitor
+ * for the availability of the specified @condition on the socket. The #GSource
+ * keeps a reference to the @socket.
  *
  * The callback on the source is of the #GSocketSourceFunc type.
  *
- * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in condition;
+ * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition;
  * these conditions will always be reported output if they are true.
  *
  * @cancellable if not %NULL can be used to cancel the source, which will
@@ -2339,7 +3403,13 @@ winsock_source_new (GSocket      *socket,
  * condition change). You can check for this in the callback using
  * g_cancellable_is_cancelled().
  *
- * Returns: a newly allocated %GSource, free with g_source_unref().
+ * If @socket has a timeout set, and it is reached before @condition
+ * occurs, the source will then trigger anyway, reporting %G_IO_IN or
+ * %G_IO_OUT depending on @condition. However, @socket will have been
+ * marked as having had a timeout, and so the next #GSocket I/O method
+ * you call will then fail with a %G_IO_ERROR_TIMED_OUT.
+ *
+ * Returns: (transfer full): a newly allocated %GSource, free with g_source_unref().
  *
  * Since: 2.22
  */
@@ -2348,16 +3418,9 @@ g_socket_create_source (GSocket      *socket,
                        GIOCondition  condition,
                        GCancellable *cancellable)
 {
-  GSource *source;
   g_return_val_if_fail (G_IS_SOCKET (socket) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL);
 
-#ifdef G_OS_WIN32
-  source = winsock_source_new (socket, condition, cancellable);
-#else
-  source =_g_fd_source_new_with_object (G_OBJECT (socket), socket->priv->fd,
-                                       condition, cancellable);
-#endif
-  return source;
+  return socket_source_new (socket, condition, cancellable);
 }
 
 /**
@@ -2370,6 +3433,14 @@ g_socket_create_source (GSocket      *socket,
  * against the currently-satisfied conditions on @socket. The result
  * is returned.
  *
+ * Note that on Windows, it is possible for an operation to return
+ * %G_IO_ERROR_WOULD_BLOCK even immediately after
+ * g_socket_condition_check() has claimed that the socket is ready for
+ * writing. Rather than calling g_socket_condition_check() and then
+ * writing to the socket if it succeeds, it is generally better to
+ * simply try writing to the socket right away, and try again later if
+ * the initial attempt returns %G_IO_ERROR_WOULD_BLOCK.
+ *
  * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in condition;
  * these conditions will always be set in the output if they are true.
  *
@@ -2383,6 +3454,8 @@ GIOCondition
 g_socket_condition_check (GSocket      *socket,
                          GIOCondition  condition)
 {
+  g_return_val_if_fail (G_IS_SOCKET (socket), 0);
+
   if (!check_socket (socket, NULL))
     return 0;
 
@@ -2403,6 +3476,7 @@ g_socket_condition_check (GSocket      *socket,
     gint result;
     poll_fd.fd = socket->priv->fd;
     poll_fd.events = condition;
+    poll_fd.revents = 0;
 
     do
       result = g_poll (&poll_fd, 1, 0);
@@ -2417,14 +3491,19 @@ g_socket_condition_check (GSocket      *socket,
  * g_socket_condition_wait:
  * @socket: a #GSocket
  * @condition: a #GIOCondition mask to wait for
- * @cancellable: a #GCancellable, or %NULL
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
  * @error: a #GError pointer, or %NULL
  *
  * Waits for @condition to become true on @socket. When the condition
  * is met, %TRUE is returned.
  *
- * If @cancellable is cancelled before the condition is met then %FALSE
- * is returned and @error, if non-%NULL, is set to %G_IO_ERROR_CANCELLED.
+ * If @cancellable is cancelled before the condition is met, or if the
+ * socket has a timeout set and it is reached before the condition is
+ * met, then %FALSE is returned and @error, if non-%NULL, is set to
+ * the appropriate value (%G_IO_ERROR_CANCELLED or
+ * %G_IO_ERROR_TIMED_OUT).
+ *
+ * See also g_socket_condition_timed_wait().
  *
  * Returns: %TRUE if the condition was met, %FALSE otherwise
  *
@@ -2436,12 +3515,66 @@ g_socket_condition_wait (GSocket       *socket,
                         GCancellable  *cancellable,
                         GError       **error)
 {
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+  return g_socket_condition_timed_wait (socket, condition, -1,
+                                       cancellable, error);
+}
+
+/**
+ * g_socket_condition_timed_wait:
+ * @socket: a #GSocket
+ * @condition: a #GIOCondition mask to wait for
+ * @timeout: the maximum time (in microseconds) to wait, or -1
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: a #GError pointer, or %NULL
+ *
+ * Waits for up to @timeout microseconds for @condition to become true
+ * on @socket. If the condition is met, %TRUE is returned.
+ *
+ * If @cancellable is cancelled before the condition is met, or if
+ * @timeout (or the socket's #GSocket:timeout) is reached before the
+ * condition is met, then %FALSE is returned and @error, if non-%NULL,
+ * is set to the appropriate value (%G_IO_ERROR_CANCELLED or
+ * %G_IO_ERROR_TIMED_OUT).
+ *
+ * If you don't want a timeout, use g_socket_condition_wait().
+ * (Alternatively, you can pass -1 for @timeout.)
+ *
+ * Note that although @timeout is in microseconds for consistency with
+ * other GLib APIs, this function actually only has millisecond
+ * resolution, and the behavior is undefined if @timeout is not an
+ * exact number of milliseconds.
+ *
+ * Returns: %TRUE if the condition was met, %FALSE otherwise
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_condition_timed_wait (GSocket       *socket,
+                              GIOCondition   condition,
+                              gint64         timeout,
+                              GCancellable  *cancellable,
+                              GError       **error)
+{
+  gint64 start_time;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
   if (!check_socket (socket, error))
     return FALSE;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
 
+  if (socket->priv->timeout &&
+      (timeout < 0 || socket->priv->timeout < timeout / G_USEC_PER_SEC))
+    timeout = socket->priv->timeout * 1000;
+  else if (timeout != -1)
+    timeout = timeout / 1000;
+
+  start_time = g_get_monotonic_time ();
+
 #ifdef G_OS_WIN32
   {
     GIOCondition current_condition;
@@ -2461,11 +3594,14 @@ g_socket_condition_wait (GSocket       *socket,
     if (g_cancellable_make_pollfd (cancellable, &cancel_fd))
       events[num_events++] = (WSAEVENT)cancel_fd.fd;
 
+    if (timeout == -1)
+      timeout = WSA_INFINITE;
+
     current_condition = update_condition (socket);
     while ((condition & current_condition) == 0)
       {
-       res = WSAWaitForMultipleEvents(num_events, events,
-                                      FALSE, WSA_INFINITE, FALSE);
+       res = WSAWaitForMultipleEvents (num_events, events,
+                                       FALSE, timeout, FALSE);
        if (res == WSA_WAIT_FAILED)
          {
            int errsv = get_socket_errno ();
@@ -2476,11 +3612,24 @@ g_socket_condition_wait (GSocket       *socket,
                         socket_strerror (errsv));
            break;
          }
+       else if (res == WSA_WAIT_TIMEOUT)
+         {
+           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                                _("Socket I/O timed out"));
+           break;
+         }
 
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
          break;
 
        current_condition = update_condition (socket);
+
+       if (timeout != WSA_INFINITE)
+         {
+           timeout -= (g_get_monotonic_time () - start_time) * 1000;
+           if (timeout < 0)
+             timeout = 0;
+         }
       }
     remove_condition_watch (socket, &condition);
     if (num_events > 1)
@@ -2501,15 +3650,31 @@ g_socket_condition_wait (GSocket       *socket,
     if (g_cancellable_make_pollfd (cancellable, &poll_fd[1]))
       num++;
 
-    do
-      result = g_poll (poll_fd, num, -1);
-    while (result == -1 && get_socket_errno () == EINTR);
+    while (TRUE)
+      {
+       result = g_poll (poll_fd, num, timeout);
+       if (result != -1 || errno != EINTR)
+         break;
+
+       if (timeout != -1)
+         {
+           timeout -= (g_get_monotonic_time () - start_time) / 1000;
+           if (timeout < 0)
+             timeout = 0;
+         }
+      }
     
     if (num > 1)
       g_cancellable_release_fd (cancellable);
 
-    return cancellable == NULL ||
-      !g_cancellable_set_error_if_cancelled (cancellable, error);
+    if (result == 0)
+      {
+       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+                            _("Socket I/O timed out"));
+       return FALSE;
+      }
+
+    return !g_cancellable_set_error_if_cancelled (cancellable, error);
   }
   #endif
 }
@@ -2517,14 +3682,14 @@ g_socket_condition_wait (GSocket       *socket,
 /**
  * g_socket_send_message:
  * @socket: a #GSocket
- * @address: a #GSocketAddress, or %NULL
- * @vectors: an array of #GOutputVector structs
+ * @address: (allow-none): a #GSocketAddress, or %NULL
+ * @vectors: (array length=num_vectors): an array of #GOutputVector structs
  * @num_vectors: the number of elements in @vectors, or -1
- * @messages: a pointer to an array of #GSocketControlMessages, or
- *   %NULL.
+ * @messages: (array length=num_messages) (allow-none): a pointer to an
+ *   array of #GSocketControlMessages, or %NULL.
  * @num_messages: number of elements in @messages, or -1.
  * @flags: an int containing #GSocketMsgFlags flags
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * Send data to @address on @socket.  This is the most complicated and
@@ -2539,7 +3704,7 @@ g_socket_condition_wait (GSocket       *socket,
  * then @vectors is assumed to be terminated by a #GOutputVector with a
  * %NULL buffer pointer.) The #GOutputVector structs describe the buffers
  * that the sent data will be gathered from. Using multiple
- * #GOutputVector<!-- -->s is more memory-efficient than manually copying
+ * #GOutputVectors is more memory-efficient than manually copying
  * data from multiple sources into a single buffer, and more
  * network-efficient than making multiple calls to g_socket_send().
  *
@@ -2584,9 +3749,19 @@ g_socket_send_message (GSocket                *socket,
   GOutputVector one_vector;
   char zero;
 
+  g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+  g_return_val_if_fail (address == NULL || G_IS_SOCKET_ADDRESS (address), -1);
+  g_return_val_if_fail (num_vectors == 0 || vectors != NULL, -1);
+  g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), -1);
+  g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
   if (!check_socket (socket, error))
     return -1;
 
+  if (!check_timeout (socket, error))
+    return -1;
+
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return -1;
 
@@ -2621,6 +3796,8 @@ g_socket_send_message (GSocket                *socket,
     struct msghdr msg;
     gssize result;
 
+   msg.msg_flags = 0;
+
     /* name */
     if (address)
       {
@@ -2674,7 +3851,13 @@ g_socket_send_message (GSocket                *socket,
       for (i = 0; i < num_messages; i++)
        msg.msg_controllen += CMSG_SPACE (g_socket_control_message_get_size (messages[i]));
 
-      msg.msg_control = g_alloca (msg.msg_controllen);
+      if (msg.msg_controllen == 0)
+        msg.msg_control = NULL;
+      else
+        {
+          msg.msg_control = g_alloca (msg.msg_controllen);
+          memset (msg.msg_control, '\0', msg.msg_controllen);
+        }
 
       cmsg = CMSG_FIRSTHDR (&msg);
       for (i = 0; i < num_messages; i++)
@@ -2736,7 +3919,7 @@ g_socket_send_message (GSocket                *socket,
     if (num_messages != 0)
       {
         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                             _("GSocketControlMessage not supported on windows"));
+                             _("GSocketControlMessage not supported on Windows"));
        return -1;
       }
 
@@ -2804,18 +3987,73 @@ g_socket_send_message (GSocket                *socket,
 #endif
 }
 
+static GSocketAddress *
+cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len)
+{
+  GSocketAddress *saddr;
+  gint i;
+  guint64 oldest_time = G_MAXUINT64;
+  gint oldest_index = 0;
+
+  if (native_len <= 0)
+    return NULL;
+
+  saddr = NULL;
+  for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++)
+    {
+      GSocketAddress *tmp = socket->priv->recv_addr_cache[i].addr;
+      gpointer tmp_native = socket->priv->recv_addr_cache[i].native;
+      gint tmp_native_len = socket->priv->recv_addr_cache[i].native_len;
+
+      if (!tmp)
+        continue;
+
+      if (tmp_native_len != native_len)
+        continue;
+
+      if (memcmp (tmp_native, native, native_len) == 0)
+        {
+          saddr = g_object_ref (tmp);
+          socket->priv->recv_addr_cache[i].last_used = g_get_monotonic_time ();
+          return saddr;
+        }
+
+      if (socket->priv->recv_addr_cache[i].last_used < oldest_time)
+        {
+          oldest_time = socket->priv->recv_addr_cache[i].last_used;
+          oldest_index = i;
+        }
+    }
+
+  saddr = g_socket_address_new_from_native (native, native_len);
+
+  if (socket->priv->recv_addr_cache[oldest_index].addr)
+    {
+      g_object_unref (socket->priv->recv_addr_cache[oldest_index].addr);
+      g_free (socket->priv->recv_addr_cache[oldest_index].native);
+    }
+
+  socket->priv->recv_addr_cache[oldest_index].native = g_memdup (native, native_len);
+  socket->priv->recv_addr_cache[oldest_index].native_len = native_len;
+  socket->priv->recv_addr_cache[oldest_index].addr = g_object_ref (saddr);
+  socket->priv->recv_addr_cache[oldest_index].last_used = g_get_monotonic_time ();
+
+  return saddr;
+}
+
 /**
  * g_socket_receive_message:
  * @socket: a #GSocket
- * @address: a pointer to a #GSocketAddress pointer, or %NULL
- * @vectors: an array of #GInputVector structs
+ * @address: (out) (allow-none): a pointer to a #GSocketAddress
+ *     pointer, or %NULL
+ * @vectors: (array length=num_vectors): an array of #GInputVector structs
  * @num_vectors: the number of elements in @vectors, or -1
- * @messages: a pointer which will be filled with an array of
- *     #GSocketControlMessages, or %NULL
+ * @messages: (array length=num_messages) (allow-none): a pointer which
+ *    may be filled with an array of #GSocketControlMessages, or %NULL
  * @num_messages: a pointer which will be filled with the number of
  *    elements in @messages, or %NULL
  * @flags: a pointer to an int containing #GSocketMsgFlags flags
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
  * @error: a #GError pointer, or %NULL
  *
  * Receive data from a socket.  This is the most complicated and
@@ -2838,12 +4076,13 @@ g_socket_send_message (GSocket                *socket,
  * single '\0' byte for the purposes of transferring ancillary data.
  *
  * @messages, if non-%NULL, will be set to point to a newly-allocated
- * array of #GSocketControlMessage instances. These correspond to the
- * control messages received from the kernel, one
- * #GSocketControlMessage per message from the kernel. This array is
- * %NULL-terminated and must be freed by the caller using g_free(). If
- * @messages is %NULL, any control messages received will be
- * discarded.
+ * array of #GSocketControlMessage instances or %NULL if no such
+ * messages was received. These correspond to the control messages
+ * received from the kernel, one #GSocketControlMessage per message
+ * from the kernel. This array is %NULL-terminated and must be freed
+ * by the caller using g_free() after calling g_object_unref() on each
+ * element. If @messages is %NULL, any control messages received will
+ * be discarded.
  *
  * @num_messages, if non-%NULL, will be set to the number of control
  * messages received.
@@ -2867,14 +4106,16 @@ g_socket_send_message (GSocket                *socket,
  * sufficiently-large buffer.
  *
  * If the socket is in blocking mode the call will block until there
- * is some data to receive or there is an error. If there is no data
- * available and the socket is in non-blocking mode, a
- * %G_IO_ERROR_WOULD_BLOCK error will be returned. To be notified when
- * data is available, wait for the %G_IO_IN condition.
+ * is some data to receive, the connection is closed, or there is an
+ * error. If there is no data available and the socket is in
+ * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned. To be notified when data is available, wait for the
+ * %G_IO_IN condition.
  *
  * On error -1 is returned and @error is set accordingly.
  *
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
  *
  * Since: 2.22
  */
@@ -2892,9 +4133,14 @@ g_socket_receive_message (GSocket                 *socket,
   GInputVector one_vector;
   char one_byte;
 
+  g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+
   if (!check_socket (socket, error))
     return -1;
 
+  if (!check_timeout (socket, error))
+    return -1;
+
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return -1;
 
@@ -2970,6 +4216,14 @@ g_socket_receive_message (GSocket                 *socket,
     else
       msg.msg_flags = 0;
 
+    /* We always set the close-on-exec flag so we don't leak file
+     * descriptors into child processes.  Note that gunixfdmessage.c
+     * will later call fcntl (fd, FD_CLOEXEC), but that isn't atomic.
+     */
+#ifdef MSG_CMSG_CLOEXEC
+    msg.msg_flags |= MSG_CMSG_CLOEXEC;
+#endif
+
     /* do it */
     while (1)
       {
@@ -2979,6 +4233,14 @@ g_socket_receive_message (GSocket                 *socket,
          return -1;
 
        result = recvmsg (socket->priv->fd, &msg, msg.msg_flags);
+#ifdef MSG_CMSG_CLOEXEC        
+       if (result < 0 && get_socket_errno () == EINVAL)
+         {
+           /* We must be running on an old kernel.  Call without the flag. */
+           msg.msg_flags &= ~(MSG_CMSG_CLOEXEC);
+           result = recvmsg (socket->priv->fd, &msg, msg.msg_flags);
+         }
+#endif
 
        if (result < 0)
          {
@@ -3004,67 +4266,63 @@ g_socket_receive_message (GSocket                 *socket,
     /* decode address */
     if (address != NULL)
       {
-       if (msg.msg_namelen > 0)
-         *address = g_socket_address_new_from_native (msg.msg_name,
-                                                      msg.msg_namelen);
-       else
-         *address = NULL;
+        *address = cache_recv_address (socket, msg.msg_name, msg.msg_namelen);
       }
 
     /* decode control messages */
     {
-      GSocketControlMessage **my_messages = NULL;
-      gint allocated = 0, index = 0;
-      const gchar *scm_pointer;
+      GPtrArray *my_messages = NULL;
       struct cmsghdr *cmsg;
-      gsize scm_size;
-
-      scm_pointer = (const gchar *) msg.msg_control;
-      scm_size = msg.msg_controllen;
-
-      for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
-       {
-         GSocketControlMessage *message;
-
-         message = g_socket_control_message_deserialize (cmsg->cmsg_level,
-                                                         cmsg->cmsg_type,
-                                                         cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg),
-                                                         CMSG_DATA (cmsg));
-         if (message == NULL)
-           /* We've already spewed about the problem in the
-              deserialization code, so just continue */
-           continue;
 
-         if (index == allocated)
-           {
-             /* estimated 99% case: exactly 1 control message */
-             allocated = MIN (allocated * 2, 1);
-             my_messages = g_new (GSocketControlMessage *, (allocated + 1));
-             allocated = 1;
-           }
-
-         my_messages[index++] = message;
-       }
+      if (msg.msg_controllen >= sizeof (struct cmsghdr))
+        {
+          for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
+            {
+              GSocketControlMessage *message;
+
+              message = g_socket_control_message_deserialize (cmsg->cmsg_level,
+                                                              cmsg->cmsg_type,
+                                                              cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg),
+                                                              CMSG_DATA (cmsg));
+              if (message == NULL)
+                /* We've already spewed about the problem in the
+                   deserialization code, so just continue */
+                continue;
+
+              if (messages == NULL)
+                {
+                  /* we have to do it this way if the user ignores the
+                   * messages so that we will close any received fds.
+                   */
+                  g_object_unref (message);
+                }
+              else
+                {
+                  if (my_messages == NULL)
+                    my_messages = g_ptr_array_new ();
+                  g_ptr_array_add (my_messages, message);
+                }
+            }
+        }
 
       if (num_messages)
-       *num_messages = index;
+       *num_messages = my_messages != NULL ? my_messages->len : 0;
 
       if (messages)
        {
-         my_messages[index++] = NULL;
-         *messages = my_messages;
+         if (my_messages == NULL)
+           {
+             *messages = NULL;
+           }
+         else
+           {
+             g_ptr_array_add (my_messages, NULL);
+             *messages = (GSocketControlMessage **) g_ptr_array_free (my_messages, FALSE);
+           }
        }
       else
        {
-         gint i;
-
-         /* free all those messages we just constructed.
-          * we have to do it this way if the user ignores the
-          * messages so that we will close any received fds.
-          */
-         for (i = 0; i < index; i++)
-           g_object_unref (my_messages[i]);
-         g_free (my_messages);
+         g_assert (my_messages == NULL);
        }
     }
 
@@ -3144,20 +4402,260 @@ g_socket_receive_message (GSocket                 *socket,
     /* decode address */
     if (address != NULL)
       {
-       if (addrlen > 0)
-         *address = g_socket_address_new_from_native (&addr, addrlen);
-       else
-         *address = NULL;
+        *address = cache_recv_address (socket, (struct sockaddr *)&addr, addrlen);
       }
 
     /* capture the flags */
     if (flags != NULL)
       *flags = win_flags;
 
+    if (messages != NULL)
+      *messages = NULL;
+    if (num_messages != NULL)
+      *num_messages = 0;
+
     return bytes_received;
   }
 #endif
 }
 
-#define __G_SOCKET_C__
-#include "gioaliasdef.c"
+/**
+ * g_socket_get_credentials:
+ * @socket: a #GSocket.
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Returns the credentials of the foreign process connected to this
+ * socket, if any (e.g. it is only supported for %G_SOCKET_FAMILY_UNIX
+ * sockets).
+ *
+ * If this operation isn't supported on the OS, the method fails with
+ * the %G_IO_ERROR_NOT_SUPPORTED error. On Linux this is implemented
+ * by reading the %SO_PEERCRED option on the underlying socket.
+ *
+ * Other ways to obtain credentials from a foreign peer includes the
+ * #GUnixCredentialsMessage type and
+ * g_unix_connection_send_credentials() /
+ * g_unix_connection_receive_credentials() functions.
+ *
+ * Returns: (transfer full): %NULL if @error is set, otherwise a #GCredentials object
+ * that must be freed with g_object_unref().
+ *
+ * Since: 2.26
+ */
+GCredentials *
+g_socket_get_credentials (GSocket   *socket,
+                          GError   **error)
+{
+  GCredentials *ret;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  ret = NULL;
+
+#if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED
+
+#ifdef SO_PEERCRED
+  {
+    guint8 native_creds_buf[G_CREDENTIALS_NATIVE_SIZE];
+    socklen_t optlen = sizeof (native_creds_buf);
+
+    if (getsockopt (socket->priv->fd,
+                    SOL_SOCKET,
+                    SO_PEERCRED,
+                    native_creds_buf,
+                    &optlen) == 0)
+      {
+        ret = g_credentials_new ();
+        g_credentials_set_native (ret,
+                                  G_CREDENTIALS_NATIVE_TYPE,
+                                  native_creds_buf);
+      }
+  }
+#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
+  {
+    struct unpcbid cred;
+    socklen_t optlen = sizeof (cred);
+
+    if (getsockopt (socket->priv->fd,
+                    0,
+                    LOCAL_PEEREID,
+                    &cred,
+                    &optlen) == 0)
+      {
+        ret = g_credentials_new ();
+        g_credentials_set_native (ret,
+                                  G_CREDENTIALS_NATIVE_TYPE,
+                                  &cred);
+      }
+  }
+#elif G_CREDENTIALS_USE_SOLARIS_UCRED
+  {
+    ucred_t *ucred = NULL;
+
+    if (getpeerucred (socket->priv->fd, &ucred) == 0)
+      {
+        ret = g_credentials_new ();
+        g_credentials_set_native (ret,
+                                  G_CREDENTIALS_TYPE_SOLARIS_UCRED,
+                                  ucred);
+        ucred_free (ucred);
+      }
+  }
+#else
+  #error "G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED is set but this is no code for this platform"
+#endif
+
+  if (!ret)
+    {
+      int errsv = get_socket_errno ();
+
+      g_set_error (error,
+                   G_IO_ERROR,
+                   socket_io_error_from_errno (errsv),
+                   _("Unable to read socket credentials: %s"),
+                   socket_strerror (errsv));
+    }
+
+#else
+
+  g_set_error_literal (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_NOT_SUPPORTED,
+                       _("g_socket_get_credentials not implemented for this OS"));
+#endif
+
+  return ret;
+}
+
+/**
+ * g_socket_get_option:
+ * @socket: a #GSocket
+ * @level: the "API level" of the option (eg, `SOL_SOCKET`)
+ * @optname: the "name" of the option (eg, `SO_BROADCAST`)
+ * @value: (out): return location for the option value
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Gets the value of an integer-valued option on @socket, as with
+ * getsockopt(). (If you need to fetch a  non-integer-valued option,
+ * you will need to call getsockopt() directly.)
+ *
+ * The [<gio/gnetworking.h>][gio-gnetworking.h]
+ * header pulls in system headers that will define most of the
+ * standard/portable socket options. For unusual socket protocols or
+ * platform-dependent options, you may need to include additional
+ * headers.
+ *
+ * Note that even for socket options that are a single byte in size,
+ * @value is still a pointer to a #gint variable, not a #guchar;
+ * g_socket_get_option() will handle the conversion internally.
+ *
+ * Returns: success or failure. On failure, @error will be set, and
+ *   the system error value (`errno` or WSAGetLastError()) will still
+ *   be set to the result of the getsockopt() call.
+ *
+ * Since: 2.36
+ */
+gboolean
+g_socket_get_option (GSocket  *socket,
+                    gint      level,
+                    gint      optname,
+                    gint     *value,
+                    GError  **error)
+{
+  guint size;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+  *value = 0;
+  size = sizeof (gint);
+  if (getsockopt (socket->priv->fd, level, optname, value, &size) != 0)
+    {
+      int errsv = get_socket_errno ();
+
+      g_set_error_literal (error,
+                          G_IO_ERROR,
+                          socket_io_error_from_errno (errsv),
+                          socket_strerror (errsv));
+#ifndef G_OS_WIN32
+      /* Reset errno in case the caller wants to look at it */
+      errno = errsv;
+#endif
+      return FALSE;
+    }
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  /* If the returned value is smaller than an int then we need to
+   * slide it over into the low-order bytes of *value.
+   */
+  if (size != sizeof (gint))
+    *value = *value >> (8 * (sizeof (gint) - size));
+#endif
+
+  return TRUE;
+}
+
+/**
+ * g_socket_set_option:
+ * @socket: a #GSocket
+ * @level: the "API level" of the option (eg, `SOL_SOCKET`)
+ * @optname: the "name" of the option (eg, `SO_BROADCAST`)
+ * @value: the value to set the option to
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Sets the value of an integer-valued option on @socket, as with
+ * setsockopt(). (If you need to set a non-integer-valued option,
+ * you will need to call setsockopt() directly.)
+ *
+ * The [<gio/gnetworking.h>][gio-gnetworking.h]
+ * header pulls in system headers that will define most of the
+ * standard/portable socket options. For unusual socket protocols or
+ * platform-dependent options, you may need to include additional
+ * headers.
+ *
+ * Returns: success or failure. On failure, @error will be set, and
+ *   the system error value (`errno` or WSAGetLastError()) will still
+ *   be set to the result of the setsockopt() call.
+ *
+ * Since: 2.36
+ */
+gboolean
+g_socket_set_option (GSocket  *socket,
+                    gint      level,
+                    gint      optname,
+                    gint      value,
+                    GError  **error)
+{
+  gint errsv;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+  if (setsockopt (socket->priv->fd, level, optname, &value, sizeof (gint)) == 0)
+    return TRUE;
+
+#if !defined (__linux__) && !defined (G_OS_WIN32)
+  /* Linux and Windows let you set a single-byte value from an int,
+   * but most other platforms don't.
+   */
+  if (errno == EINVAL && value >= SCHAR_MIN && value <= CHAR_MAX)
+    {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+      value = value << (8 * (sizeof (gint) - 1));
+#endif
+      if (setsockopt (socket->priv->fd, level, optname, &value, 1) == 0)
+        return TRUE;
+    }
+#endif
+
+  errsv = get_socket_errno ();
+
+  g_set_error_literal (error,
+                       G_IO_ERROR,
+                       socket_io_error_from_errno (errsv),
+                       socket_strerror (errsv));
+#ifndef G_OS_WIN32
+  errno = errsv;
+#endif
+  return FALSE;
+}
+