passfd: give nicer error for recvfd at eof
[gnulib.git] / lib / passfd.c
index 573b80e..5388ca5 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011 Free Software Foundation, Inc.
+/* Copyright (C) 2011-2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
-#include <sys/uio.h>
 #include <unistd.h>
 
 #include <sys/socket.h>
-#if HAVE_SYS_UN_H
-# include <sys/un.h>
-#endif
 
 #include "cloexec.h"
 
+/* The code that uses CMSG_FIRSTHDR is enabled on
+   Linux, Mac OS X, FreeBSD, OpenBSD, NetBSD, AIX, OSF/1, Cygwin.
+   The code that uses HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS is enabled on
+   HP-UX, IRIX, Solaris.  */
+
+/* MSG_CMSG_CLOEXEC is defined only on Linux, as of 2011.  */
+#ifndef MSG_CMSG_CLOEXEC
+# define MSG_CMSG_CLOEXEC 0
+#endif
+
+#if HAVE_SENDMSG
 /* sendfd sends the file descriptor fd along the socket
    to a process calling recvfd on the other end.
 
 int
 sendfd (int sock, int fd)
 {
-  char send = 0;
-  struct iovec iov[1];
+  char byte = 0;
+  struct iovec iov;
   struct msghdr msg;
+# ifdef CMSG_FIRSTHDR
+  struct cmsghdr *cmsg;
+  char buf[CMSG_SPACE (sizeof fd)];
+# endif
 
   /* send at least one char */
-  iov[0].iov_base = &send;
-  iov[0].iov_len = 1;
-  msg.msg_iov = iov;
+  memset (&msg, 0, sizeof msg);
+  iov.iov_base = &byte;
+  iov.iov_len = 1;
+  msg.msg_iov = &iov;
   msg.msg_iovlen = 1;
-  msg.msg_name = 0;
+  msg.msg_name = NULL;
   msg.msg_namelen = 0;
 
-  {
-#if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
-    struct cmsghdr *cmsg;
-    char buf[CMSG_SPACE (sizeof (fd))];
-
-    msg.msg_control = buf;
-    msg.msg_controllen = sizeof (buf);
-    cmsg = CMSG_FIRSTHDR (&msg);
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    cmsg->cmsg_len = CMSG_LEN (sizeof (int));
-    /* Initialize the payload: */
-    memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
-    msg.msg_controllen = cmsg->cmsg_len;
-#elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
-    msg.msg_accrights = &fd;
-    msg.msg_accrightslen = sizeof (fd);
-#else
-    errno = ENOSYS;
-    return -1;
-#endif
-  }
+# ifdef CMSG_FIRSTHDR
+  msg.msg_control = buf;
+  msg.msg_controllen = sizeof buf;
+  cmsg = CMSG_FIRSTHDR (&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN (sizeof fd);
+  /* Initialize the payload: */
+  memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);
+# elif HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
+  msg.msg_accrights = &fd;
+  msg.msg_accrightslen = sizeof fd;
+# else
+  errno = ENOSYS;
+  return -1;
+# endif
 
-  if (sendmsg (sock, &msg, 0) != iov[0].iov_len)
+  if (sendmsg (sock, &msg, 0) != iov.iov_len)
     return -1;
   return 0;
 }
+#else
+int
+sendfd (int sock _GL_UNUSED, int fd _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+#endif
 
+
+#if HAVE_RECVMSG
 /* recvfd receives a file descriptor through the socket.
    The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>).
 
-   Return 0 on success, or -1 with errno set in case of error.
+   Return the fd on success, or -1 with errno set in case of error.
 */
 int
 recvfd (int sock, int flags)
 {
-  char recv = 0;
-  struct iovec iov[1];
+  char byte = 0;
+  struct iovec iov;
   struct msghdr msg;
+  int fd = -1;
+  ssize_t len;
+# ifdef CMSG_FIRSTHDR
+  struct cmsghdr *cmsg;
+  char buf[CMSG_SPACE (sizeof fd)];
+  int flags_recvmsg = flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0;
+# endif
 
   if ((flags & ~O_CLOEXEC) != 0)
     {
@@ -101,91 +124,81 @@ recvfd (int sock, int flags)
     }
 
   /* send at least one char */
-  iov[0].iov_base = &recv;
-  iov[0].iov_len = 1;
-  msg.msg_iov = iov;
+  memset (&msg, 0, sizeof msg);
+  iov.iov_base = &byte;
+  iov.iov_len = 1;
+  msg.msg_iov = &iov;
   msg.msg_iovlen = 1;
-  msg.msg_name = 0;
+  msg.msg_name = NULL;
   msg.msg_namelen = 0;
 
-  {
-#if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
-    int fd;
-    struct cmsghdr *cmsg;
-    char buf[CMSG_SPACE (sizeof (fd))];
-    const int mone = -1;
-# if HAVE_MSG_CMSG_CLOEXEC
-    int flags_recvmsg = (flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0);
-# else
-    int flags_recvmsg = 0;
-# endif
+# ifdef CMSG_FIRSTHDR
+  msg.msg_control = buf;
+  msg.msg_controllen = sizeof buf;
+  cmsg = CMSG_FIRSTHDR (&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN (sizeof fd);
+  /* Initialize the payload: */
+  memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  len = recvmsg (sock, &msg, flags_recvmsg);
+  if (len < 0)
+    return -1;
 
-    msg.msg_control = buf;
-    msg.msg_controllen = sizeof (buf);
-    cmsg = CMSG_FIRSTHDR (&msg);
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    cmsg->cmsg_len = CMSG_LEN (sizeof (int));
-    /* Initialize the payload: */
-    memcpy (CMSG_DATA (cmsg), &mone, sizeof (mone));
-    msg.msg_controllen = cmsg->cmsg_len;
-
-    if (recvmsg (sock, &msg, flags_recvmsg) < 0)
+  cmsg = CMSG_FIRSTHDR (&msg);
+  /* be paranoiac */
+  if (len == 0 || cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof fd)
+      || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+    {
+      /* fake errno: at end the file is not available */
+      errno = len ? EACCES : ENOTCONN;
       return -1;
+    }
 
-    cmsg = CMSG_FIRSTHDR (&msg);
-    /* be paranoiac */
-    if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof (int))
-        || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
-      {
-        /* fake errno: at end the file is not available */
-        errno = EACCES;
-        return -1;
-      }
-
-    memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
-
-# if !HAVE_MSG_CMSG_CLOEXEC
-    /* set close-on-exec flag */
-    if (flags & O_CLOEXEC)
-      {
-        if (set_cloexec_flag (fd, true) < 0)
-          {
-            int saved_errno = errno;
-            (void) close (fd);
-            errno = saved_errno;
-            return -1;
-          }
-      }
-# endif
-
-    return fd;
-
-#elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
-    int fd;
+  memcpy (&fd, CMSG_DATA (cmsg), sizeof fd);
 
-    msg.msg_accrights = &fd;
-    msg.msg_accrightslen = sizeof (fd);
-    if (recvmsg (sock, &msg, 0) < 0)
-      return -1;
+  /* set close-on-exec flag */
+  if (!MSG_CMSG_CLOEXEC && (flags & O_CLOEXEC))
+    {
+      if (set_cloexec_flag (fd, true) < 0)
+        {
+          int saved_errno = errno;
+          (void) close (fd);
+          errno = saved_errno;
+          return -1;
+        }
+    }
 
-    /* set close-on-exec flag */
-    if (flags & O_CLOEXEC)
-      {
-        if (set_cloexec_flag (fd, true) < 0)
-          {
-            int saved_errno = errno;
-            (void) close (fd);
-            errno = saved_errno;
-            return -1;
-          }
-      }
+# elif HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
+  msg.msg_accrights = &fd;
+  msg.msg_accrightslen = sizeof fd;
+  if (recvmsg (sock, &msg, 0) < 0)
+    return -1;
 
-    return fd;
+  /* set close-on-exec flag */
+  if (flags & O_CLOEXEC)
+    {
+      if (set_cloexec_flag (fd, true) < 0)
+        {
+          int saved_errno = errno;
+          close (fd);
+          errno = saved_errno;
+          return -1;
+        }
+    }
+# else
+  errno = ENOSYS;
+# endif
 
+  return fd;
+}
 #else
-    errno = ENOSYS;
-    return -1;
-#endif
-  }
+int
+recvfd (int sock _GL_UNUSED, int flags _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
 }
+#endif