gsocket: fix g_socket_details_from_fd() on Solaris
authorDan Winship <danw@gnome.org>
Sat, 3 Sep 2011 23:14:16 +0000 (19:14 -0400)
committerDan Winship <danw@gnome.org>
Sat, 3 Sep 2011 23:58:40 +0000 (19:58 -0400)
On Solaris, getsockname() on an unconnected socket gives an addrlen of
0 and doesn't set the sockaddr. So use the SO_DOMAIN sockopt to find
the socket family in that case. (SO_DOMAIN doesn't exist everywhere,
so we can't use it unconditionally. Also, we have to only use it if
getsockname() fails, since SO_DOMAIN returns a bogus value for
accept()ed sockets on both Linux and Solaris...)

gio/gsocket.c

index 2ee0b5c..9d58c41 100644 (file)
@@ -309,7 +309,7 @@ g_socket_details_from_fd (GSocket *socket)
   gint fd;
   guint addrlen;
   guint optlen;
-  int value;
+  int value, family;
   int errsv;
 #ifdef G_OS_WIN32
   /* See bug #611756 */
@@ -370,9 +370,31 @@ 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
+      optlen = sizeof family;
+      if (getsockopt (fd, SOL_SOCKET, SO_DOMAIN, (void *)&family, &optlen) != 0)
+       {
+         errsv = get_socket_errno ();
+         goto err;
+       }
+#else
+      errsv = ENOTSUP;
+      goto err;
+#endif
+    }
+
+  switch (family)
     {
      case G_SOCKET_FAMILY_IPV4:
      case G_SOCKET_FAMILY_IPV6: