gsocket: add getsockopt/setsockopt wrappers
authorDan Winship <danw@gnome.org>
Tue, 14 Feb 2012 02:12:34 +0000 (21:12 -0500)
committerDan Winship <danw@gnome.org>
Wed, 12 Dec 2012 14:20:22 +0000 (15:20 +0100)
Add g_socket_get_option() and g_socket_set_option(), wrapping
getsockopt/setsockopt for the case of integer-valued options. Update
code to use these instead of the underlying calls.

https://bugzilla.gnome.org/show_bug.cgi?id=623187

configure.ac
docs/reference/gio/gio-sections.txt
gio/gio.symbols
gio/gnetworkmonitornetlink.c
gio/gsocket.c
gio/gsocket.h
gio/gunixconnection.c

index e08b255..bf4d938 100644 (file)
@@ -1065,25 +1065,25 @@ fi
 
 AC_CHECK_FUNCS(getprotobyname_r endservent if_nametoindex)
 
-# <wspiapi.h> in the Windows SDK and in mingw-w64 has wrappers for
-# inline workarounds for getaddrinfo, getnameinfo and freeaddrinfo if
-# they aren't present at run-time (on Windows 2000).
-AC_CHECK_HEADER([wspiapi.h], [WSPIAPI_INCLUDE="\#include <wspiapi.h>"])
-AC_SUBST(WSPIAPI_INCLUDE)
-
-AC_MSG_CHECKING([if arpa/nameser_compat.h is needed])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <arpa/nameser.h>],
-                                   [int qclass = C_IN;])],
-                  [AC_MSG_RESULT([no])],
-                  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <arpa/nameser.h>
-                                                       #include <arpa/nameser_compat.h>],
-                                                      [int qclass = C_IN;])],
-                                     [AC_MSG_RESULT([yes])
-                                      NAMESER_COMPAT_INCLUDE="\#include <arpa/nameser_compat.h>"],
-                                     [AC_MSG_ERROR([could not compile test program either way])])])
-AC_SUBST(NAMESER_COMPAT_INCLUDE)
-
-AS_IF([test $glib_native_win32 = no], [
+AS_IF([test $glib_native_win32 = yes], [
+  # <wspiapi.h> in the Windows SDK and in mingw-w64 has wrappers for
+  # inline workarounds for getaddrinfo, getnameinfo and freeaddrinfo if
+  # they aren't present at run-time (on Windows 2000).
+  AC_CHECK_HEADER([wspiapi.h], [WSPIAPI_INCLUDE="#include <wspiapi.h>"])
+  AC_SUBST(WSPIAPI_INCLUDE)
+], [
+  AC_MSG_CHECKING([if arpa/nameser_compat.h is needed])
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <arpa/nameser.h>],
+                                     [int qclass = C_IN;])],
+                    [AC_MSG_RESULT([no])],
+                    [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <arpa/nameser.h>
+                                                         #include <arpa/nameser_compat.h>],
+                                                        [int qclass = C_IN;])],
+                                       [AC_MSG_RESULT([yes])
+                                        NAMESER_COMPAT_INCLUDE="\#include <arpa/nameser_compat.h>"],
+                                       [AC_MSG_ERROR([could not compile test program either way])])])
+  AC_SUBST(NAMESER_COMPAT_INCLUDE)
+
   # We can't just use AC_CHECK_FUNC/AC_CHECK_LIB here. Bug 586150
   NETWORK_LIBS=""
   AC_MSG_CHECKING([for res_query])
index c9efdc5..81f613c 100644 (file)
@@ -1900,6 +1900,8 @@ g_socket_set_ttl
 g_socket_get_ttl
 g_socket_get_broadcast
 g_socket_set_broadcast
+g_socket_get_option
+g_socket_set_option
 g_socket_get_family
 g_socket_get_fd
 g_socket_get_local_address
index c97e2bd..b148333 100644 (file)
@@ -1001,6 +1001,7 @@ g_socket_get_listen_backlog
 g_socket_get_local_address
 g_socket_get_multicast_loopback
 g_socket_get_multicast_ttl
+g_socket_get_option
 g_socket_get_protocol
 g_socket_get_remote_address
 g_socket_get_socket_type
@@ -1027,6 +1028,7 @@ g_socket_set_keepalive
 g_socket_set_listen_backlog
 g_socket_set_multicast_loopback
 g_socket_set_multicast_ttl
+g_socket_set_option
 g_socket_speaks_ipv4
 g_socket_get_credentials
 g_socket_control_message_get_type
index a276c58..3f67654 100644 (file)
@@ -83,7 +83,7 @@ g_network_monitor_netlink_initable_init (GInitable     *initable,
                                          GError       **error)
 {
   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
-  gint sockfd, val;
+  gint sockfd;
   struct sockaddr_nl snl;
 
   /* We create the socket the old-school way because sockaddr_netlink
@@ -112,22 +112,21 @@ g_network_monitor_netlink_initable_init (GInitable     *initable,
       return FALSE;
     }
 
-  val = 1;
-  if (setsockopt (sockfd, SOL_SOCKET, SO_PASSCRED, &val, sizeof (val)) != 0)
+  nl->priv->sock = g_socket_new_from_fd (sockfd, error);
+  if (error)
     {
-      int errsv = errno;
-      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
-                   _("Could not create network monitor: %s"),
-                   g_strerror (errno));
+      g_prefix_error (error, "%s", _("Could not create network monitor: "));
       close (sockfd);
       return FALSE;
     }
 
-  nl->priv->sock = g_socket_new_from_fd (sockfd, error);
-  if (error)
+  if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED,
+                           TRUE, NULL))
     {
-      g_prefix_error (error, "%s", _("Could not create network monitor: "));
-      close (sockfd);
+      int errsv = errno;
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+                   _("Could not create network monitor: %s"),
+                   g_strerror (errno));
       return FALSE;
     }
 
index 548b348..e2fec92 100644 (file)
@@ -344,19 +344,11 @@ g_socket_details_from_fd (GSocket *socket)
   struct sockaddr_storage address;
   gint fd;
   guint addrlen;
-  guint optlen;
   int value, family;
   int errsv;
-#ifdef G_OS_WIN32
-  /* See bug #611756 */
-  BOOL bool_val = FALSE;
-#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 ();
 
@@ -380,7 +372,6 @@ g_socket_details_from_fd (GSocket *socket)
       goto err;
     }
 
-  g_assert (optlen == sizeof value);
   switch (value)
     {
      case SOCK_STREAM:
@@ -419,8 +410,7 @@ g_socket_details_from_fd (GSocket *socket)
        * But we can use SO_DOMAIN as a workaround there.
        */
 #ifdef SO_DOMAIN
-      optlen = sizeof family;
-      if (getsockopt (fd, SOL_SOCKET, SO_DOMAIN, (void *)&family, &optlen) != 0)
+      if (!g_socket_get_option (socket, SOL_SOCKET, SO_DOMAIN, &family, NULL))
        {
          errsv = get_socket_errno ();
          goto err;
@@ -473,19 +463,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))
     {
-#ifndef G_OS_WIN32
-      /* Experimentation indicates that the SO_KEEPALIVE value is
-       * actually a char on Windows, even if documentation claims it
-       * to be a BOOL which is a typedef for int. So this g_assert()
-       * fails. See bug #611756.
-       */
-      g_assert (optlen == sizeof bool_val);
-#endif
-      socket->priv->keepalive = !!bool_val;
+      socket->priv->keepalive = !!value;
     }
   else
     {
@@ -1154,7 +1134,7 @@ void
 g_socket_set_keepalive (GSocket  *socket,
                        gboolean  keepalive)
 {
-  int value;
+  GError *error = NULL;
 
   g_return_if_fail (G_IS_SOCKET (socket));
 
@@ -1162,12 +1142,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;
     }
 
@@ -1316,34 +1295,29 @@ g_socket_set_timeout (GSocket *socket,
 guint
 g_socket_get_ttl (GSocket *socket)
 {
-  int result;
-  guint value, optlen;
+  GError *error = NULL;
+  gint value;
 
-  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+  g_return_val_if_fail (G_IS_SOCKET (socket), 0);
 
   if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
     {
-      guchar optval;
-
-      optlen = sizeof (optval);
-      result = getsockopt (socket->priv->fd, IPPROTO_IP, IP_TTL,
-                          &optval, &optlen);
-      value = optval;
+      g_socket_get_option (socket, IPPROTO_IP, IP_TTL,
+                          &value, &error);
     }
   else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
     {
-      optlen = sizeof (value);
-      result = getsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
-                          &value, &optlen);
+      g_socket_get_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+                          &value, &error);
     }
   else
-    g_return_val_if_reached (FALSE);
+    g_return_val_if_reached (0);
 
-  if (result < 0)
+  if (error)
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error getting unicast ttl: %s", socket_strerror (errsv));
-      return FALSE;
+      g_warning ("error getting unicast ttl: %s", error->message);
+      g_error_free (error);
+      return 0;
     }
 
   return value;
@@ -1363,29 +1337,27 @@ void
 g_socket_set_ttl (GSocket  *socket,
                   guint     ttl)
 {
-  int result;
+  GError *error = NULL;
 
   g_return_if_fail (G_IS_SOCKET (socket));
 
   if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
     {
-      guchar optval = (guchar)ttl;
-
-      result = setsockopt (socket->priv->fd, IPPROTO_IP, IP_TTL,
-                          &optval, sizeof (optval));
+      g_socket_set_option (socket, IPPROTO_IP, IP_TTL,
+                          ttl, &error);
     }
   else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
     {
-      result = setsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
-                          &ttl, sizeof (ttl));
+      g_socket_set_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+                          ttl, &error);
     }
   else
     g_return_if_reached ();
 
-  if (result < 0)
+  if (error)
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error setting unicast ttl: %s", socket_strerror (errsv));
+      g_warning ("error setting unicast ttl: %s", error->message);
+      g_error_free (error);
       return;
     }
 
@@ -1407,19 +1379,16 @@ g_socket_set_ttl (GSocket  *socket,
 gboolean
 g_socket_get_broadcast (GSocket *socket)
 {
-  int result;
-  guint value = 0, optlen;
+  GError *error = NULL;
+  gint value;
 
   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
 
-  optlen = sizeof (guchar);
-  result = getsockopt (socket->priv->fd, SOL_SOCKET, SO_BROADCAST,
-                       &value, &optlen);
-
-  if (result < 0)
+  if (!g_socket_get_option (socket, SOL_SOCKET, SO_BROADCAST,
+                           &value, &error))
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error getting broadcast: %s", socket_strerror (errsv));
+      g_warning ("error getting broadcast: %s", error->message);
+      g_error_free (error);
       return FALSE;
     }
 
@@ -1441,21 +1410,17 @@ void
 g_socket_set_broadcast (GSocket    *socket,
                                gboolean    broadcast)
 {
-  int result;
-  gint value;
+  GError *error = NULL;
 
   g_return_if_fail (G_IS_SOCKET (socket));
 
   broadcast = !!broadcast;
-  value = (guchar)broadcast;
-
-  result = setsockopt (socket->priv->fd, SOL_SOCKET, SO_BROADCAST,
-                      &value, sizeof (value));
 
-  if (result < 0)
+  if (!g_socket_set_option (socket, SOL_SOCKET, SO_BROADCAST,
+                           broadcast, &error))
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error setting broadcast: %s", socket_strerror (errsv));
+      g_warning ("error setting broadcast: %s", error->message);
+      g_error_free (error);
       return;
     }
 
@@ -1477,30 +1442,28 @@ g_socket_set_broadcast (GSocket    *socket,
 gboolean
 g_socket_get_multicast_loopback (GSocket *socket)
 {
-  int result;
-  guint value = 0, optlen;
+  GError *error = NULL;
+  gint value;
 
   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
 
   if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
     {
-      optlen = sizeof (guchar);
-      result = getsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
-                          &value, &optlen);
+      g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+                          &value, &error);
     }
   else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
     {
-      optlen = sizeof (guint);
-      result = getsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
-                          &value, &optlen);
+      g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                          &value, &error);
     }
   else
     g_return_val_if_reached (FALSE);
 
-  if (result < 0)
+  if (error)
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error getting multicast loopback: %s", socket_strerror (errsv));
+      g_warning ("error getting multicast loopback: %s", error->message);
+      g_error_free (error);
       return FALSE;
     }
 
@@ -1523,7 +1486,7 @@ void
 g_socket_set_multicast_loopback (GSocket    *socket,
                                 gboolean    loopback)
 {
-  int result;
+  GError *error = NULL;
 
   g_return_if_fail (G_IS_SOCKET (socket));
 
@@ -1531,25 +1494,21 @@ g_socket_set_multicast_loopback (GSocket    *socket,
 
   if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
     {
-      guchar value = (guchar)loopback;
-
-      result = setsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
-                          &value, sizeof (value));
+      g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+                          loopback, &error);
     }
   else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
     {
-      guint value = (guint)loopback;
-
-      result = setsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
-                          &value, sizeof (value));
+      g_socket_set_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                          loopback, &error);
     }
   else
     g_return_if_reached ();
 
-  if (result < 0)
+  if (error)
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error setting multicast loopback: %s", socket_strerror (errsv));
+      g_warning ("error setting multicast loopback: %s", error->message);
+      g_error_free (error);
       return;
     }
 
@@ -1570,33 +1529,28 @@ g_socket_set_multicast_loopback (GSocket    *socket,
 guint
 g_socket_get_multicast_ttl (GSocket *socket)
 {
-  int result;
-  guint value, optlen;
+  GError *error = NULL;
+  gint value;
 
   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
 
   if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
     {
-      guchar optval;
-
-      optlen = sizeof (optval);
-      result = getsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_TTL,
-                          &optval, &optlen);
-      value = optval;
+      g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+                          &value, &error);
     }
   else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
     {
-      optlen = sizeof (value);
-      result = getsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-                          &value, &optlen);
+      g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                          &value, &error);
     }
   else
     g_return_val_if_reached (FALSE);
 
-  if (result < 0)
+  if (error)
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error getting multicast ttl: %s", socket_strerror (errsv));
+      g_warning ("error getting multicast ttl: %s", error->message);
+      g_error_free (error);
       return FALSE;
     }
 
@@ -1618,29 +1572,27 @@ void
 g_socket_set_multicast_ttl (GSocket  *socket,
                             guint     ttl)
 {
-  int result;
+  GError *error = NULL;
 
   g_return_if_fail (G_IS_SOCKET (socket));
 
   if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
     {
-      guchar optval = (guchar)ttl;
-
-      result = setsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_TTL,
-                          &optval, sizeof (optval));
+      g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+                          ttl, &error);
     }
   else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
     {
-      result = setsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-                          &ttl, sizeof (ttl));
+      g_socket_set_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                          ttl, &error);
     }
   else
     g_return_if_reached ();
 
-  if (result < 0)
+  if (error)
     {
-      int errsv = get_socket_errno ();
-      g_warning ("error setting multicast ttl: %s", socket_strerror (errsv));
+      g_warning ("error setting multicast ttl: %s", error->message);
+      g_error_free (error);
       return;
     }
 
@@ -1910,13 +1862,11 @@ g_socket_bind (GSocket         *socket,
      It always allows the unix variant of SO_REUSEADDR anyway */
 #ifndef G_OS_WIN32
   {
-    int value;
-
-    value = (int) !!reuse_address;
+    reuse_address = !!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));
+    g_socket_set_option (socket, SOL_SOCKET, SO_REUSEADDR,
+                        reuse_address, NULL);
   }
 #endif
 
@@ -2121,12 +2071,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;
@@ -2364,7 +2313,6 @@ gboolean
 g_socket_check_connect_result (GSocket  *socket,
                               GError  **error)
 {
-  guint optlen;
   int value;
 
   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
@@ -2372,13 +2320,9 @@ g_socket_check_connect_result (GSocket  *socket,
   if (!check_socket (socket, error))
     return FALSE;
 
-  optlen = sizeof (value);
-  if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_ERROR, (void *)&value, &optlen) != 0)
+  if (!g_socket_get_option (socket, SOL_SOCKET, SO_ERROR, &value, error))
     {
-      int errsv = get_socket_errno ();
-
-      g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
-                  _("Unable to get pending error: %s"), socket_strerror (errsv));
+      g_prefix_error (error, _("Unable to get pending error: "));
       return FALSE;
     }
 
@@ -4425,3 +4369,139 @@ g_socket_get_credentials (GSocket   *socket,
 
   return ret;
 }
+
+/**
+ * g_socket_get_option:
+ * @socket: a #GSocket
+ * @level: the "API level" of the option (eg, <literal>SOL_SOCKET</literal>)
+ * @optname: the "name" of the option (eg, <literal>SO_BROADCAST</literal>)
+ * @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
+ * <literal>getsockopt ()</literal>. (If you need to fetch a
+ * non-integer-valued option, you will need to call
+ * <literal>getsockopt ()</literal> directly.)
+ *
+ * The <link linkend="gio-gnetworking.h"><literal>&lt;gio/gnetworking.h&gt;</literal></link>
+ * 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 (<literal>errno</literal> or
+ *   <literal>WSAGetLastError ()</literal>) will still be set to the
+ *   result of the <literal>getsockopt ()</literal> 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, <literal>SOL_SOCKET</literal>)
+ * @optname: the "name" of the option (eg, <literal>SO_BROADCAST</literal>)
+ * @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
+ * <literal>setsockopt ()</literal>. (If you need to set a
+ * non-integer-valued option, you will need to call
+ * <literal>setsockopt ()</literal> directly.)
+ *
+ * The <link linkend="gio-gnetworking.h"><literal>&lt;gio/gnetworking.h&gt;</literal></link>
+ * 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 (<literal>errno</literal> or
+ *   <literal>WSAGetLastError ()</literal>) will still be set to the
+ *   result of the <literal>setsockopt ()</literal> 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;
+}
+
index 29ba44f..6d54ef0 100644 (file)
@@ -233,6 +233,19 @@ gssize                 g_socket_send_with_blocking      (GSocket
                                                         GCancellable            *cancellable,
                                                         GError                 **error);
 
+GLIB_AVAILABLE_IN_2_36
+gboolean               g_socket_get_option              (GSocket                 *socket,
+                                                        gint                     level,
+                                                        gint                     optname,
+                                                        gint                    *value,
+                                                        GError                 **error);
+GLIB_AVAILABLE_IN_2_36
+gboolean               g_socket_set_option              (GSocket                 *socket,
+                                                        gint                     level,
+                                                        gint                     optname,
+                                                        gint                     value,
+                                                        GError                 **error);
+
 G_END_DECLS
 
 #endif /* __G_SOCKET_H__ */
index 8a0968e..1c94051 100644 (file)
  */
 
 #include "config.h"
+
 #include "gunixconnection.h"
+#include "gnetworking.h"
+#include "gsocket.h"
+#include "gsocketcontrolmessage.h"
 #include "gunixcredentialsmessage.h"
+#include "gunixfdmessage.h"
 #include "glibintl.h"
 
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
 /**
  * SECTION:gunixconnection
  * @title: GUnixConnection
  * Since: 2.22
  */
 
-#include <gio/gsocketcontrolmessage.h>
-#include <gio/gunixfdmessage.h>
-#include <gio/gnetworking.h>
-#include <gio/gsocket.h>
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-
 G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection,
                         G_TYPE_SOCKET_CONNECTION,
   g_socket_connection_factory_register_type (g_define_type_id,
@@ -476,16 +475,14 @@ g_unix_connection_receive_credentials (GUnixConnection      *connection,
 #ifdef __linux__
   {
     gint opt_val;
-    socklen_t opt_len;
 
     turn_off_so_passcreds = FALSE;
     opt_val = 0;
-    opt_len = sizeof (gint);
-    if (getsockopt (g_socket_get_fd (socket),
-                    SOL_SOCKET,
-                    SO_PASSCRED,
-                    &opt_val,
-                    &opt_len) != 0)
+    if (!g_socket_get_option (socket,
+                             SOL_SOCKET,
+                             SO_PASSCRED,
+                             &opt_val,
+                             NULL))
       {
         g_set_error (error,
                      G_IO_ERROR,
@@ -494,24 +491,13 @@ g_unix_connection_receive_credentials (GUnixConnection      *connection,
                      strerror (errno));
         goto out;
       }
-    if (opt_len != sizeof (gint))
-      {
-        g_set_error (error,
-                     G_IO_ERROR,
-                     G_IO_ERROR_FAILED,
-                     _("Unexpected option length while checking if SO_PASSCRED is enabled for socket. "
-                       "Expected %d bytes, got %d"),
-                     (gint) sizeof (gint), (gint) opt_len);
-        goto out;
-      }
     if (opt_val == 0)
       {
-        opt_val = 1;
-        if (setsockopt (g_socket_get_fd (socket),
-                        SOL_SOCKET,
-                        SO_PASSCRED,
-                        &opt_val,
-                        sizeof opt_val) != 0)
+        if (!g_socket_set_option (socket,
+                                 SOL_SOCKET,
+                                 SO_PASSCRED,
+                                 TRUE,
+                                 NULL))
           {
             g_set_error (error,
                          G_IO_ERROR,
@@ -598,13 +584,11 @@ g_unix_connection_receive_credentials (GUnixConnection      *connection,
 #ifdef __linux__
   if (turn_off_so_passcreds)
     {
-      gint opt_val;
-      opt_val = 0;
-      if (setsockopt (g_socket_get_fd (socket),
-                      SOL_SOCKET,
-                      SO_PASSCRED,
-                      &opt_val,
-                      sizeof opt_val) != 0)
+      if (!g_socket_set_option (socket,
+                               SOL_SOCKET,
+                               SO_PASSCRED,
+                               FALSE,
+                               NULL))
         {
           g_set_error (error,
                        G_IO_ERROR,