passfd: fix incorrect sendmsg arguments
[gnulib.git] / lib / passfd.c
1 /* Copyright (C) 2011 Free Software Foundation, Inc.
2
3    This program is free software: you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 3 of the License, or
6    (at your option) any later version.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
15
16 #include <config.h>
17
18 /* Specification.  */
19 #include "passfd.h"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/uio.h>
28 #include <unistd.h>
29
30 #include <sys/socket.h>
31 #if HAVE_SYS_UN_H
32 # include <sys/un.h>
33 #endif
34
35 #include "cloexec.h"
36
37 /* sendfd sends the file descriptor fd along the socket
38    to a process calling recvfd on the other end.
39
40    Return 0 on success, or -1 with errno set in case of error.
41 */
42 int
43 sendfd (int sock, int fd)
44 {
45   char send = 0;
46   struct iovec iov[1];
47   struct msghdr msg;
48
49   /* send at least one char */
50   memset (&msg, 0, sizeof msg);
51   iov[0].iov_base = &send;
52   iov[0].iov_len = 1;
53   msg.msg_iov = iov;
54   msg.msg_iovlen = 1;
55   msg.msg_name = 0;
56   msg.msg_namelen = 0;
57
58   {
59 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
60     struct cmsghdr *cmsg;
61     char buf[CMSG_SPACE (sizeof (fd))];
62
63     msg.msg_control = buf;
64     msg.msg_controllen = sizeof (buf);
65     cmsg = CMSG_FIRSTHDR (&msg);
66     cmsg->cmsg_level = SOL_SOCKET;
67     cmsg->cmsg_type = SCM_RIGHTS;
68     cmsg->cmsg_len = CMSG_LEN (sizeof (int));
69     /* Initialize the payload: */
70     memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
71 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
72     msg.msg_accrights = &fd;
73     msg.msg_accrightslen = sizeof (fd);
74 #else
75     errno = ENOSYS;
76     return -1;
77 #endif
78   }
79
80   if (sendmsg (sock, &msg, 0) != iov[0].iov_len)
81     return -1;
82   return 0;
83 }
84
85 /* recvfd receives a file descriptor through the socket.
86    The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>).
87
88    Return 0 on success, or -1 with errno set in case of error.
89 */
90 int
91 recvfd (int sock, int flags)
92 {
93   char recv = 0;
94   struct iovec iov[1];
95   struct msghdr msg;
96
97   if ((flags & ~O_CLOEXEC) != 0)
98     {
99       errno = EINVAL;
100       return -1;
101     }
102
103   /* send at least one char */
104   iov[0].iov_base = &recv;
105   iov[0].iov_len = 1;
106   msg.msg_iov = iov;
107   msg.msg_iovlen = 1;
108   msg.msg_name = 0;
109   msg.msg_namelen = 0;
110
111   {
112 #if HAVE_UNIXSOCKET_SCM_RIGHTS_BSD44_WAY
113     int fd;
114     struct cmsghdr *cmsg;
115     char buf[CMSG_SPACE (sizeof (fd))];
116     const int mone = -1;
117 # if HAVE_MSG_CMSG_CLOEXEC
118     int flags_recvmsg = (flags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0);
119 # else
120     int flags_recvmsg = 0;
121 # endif
122
123     msg.msg_control = buf;
124     msg.msg_controllen = sizeof (buf);
125     cmsg = CMSG_FIRSTHDR (&msg);
126     cmsg->cmsg_level = SOL_SOCKET;
127     cmsg->cmsg_type = SCM_RIGHTS;
128     cmsg->cmsg_len = CMSG_LEN (sizeof (int));
129     /* Initialize the payload: */
130     memcpy (CMSG_DATA (cmsg), &mone, sizeof (mone));
131     msg.msg_controllen = cmsg->cmsg_len;
132
133     if (recvmsg (sock, &msg, flags_recvmsg) < 0)
134       return -1;
135
136     cmsg = CMSG_FIRSTHDR (&msg);
137     /* be paranoiac */
138     if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN (sizeof (int))
139         || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
140       {
141         /* fake errno: at end the file is not available */
142         errno = EACCES;
143         return -1;
144       }
145
146     memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
147
148 # if !HAVE_MSG_CMSG_CLOEXEC
149     /* set close-on-exec flag */
150     if (flags & O_CLOEXEC)
151       {
152         if (set_cloexec_flag (fd, true) < 0)
153           {
154             int saved_errno = errno;
155             (void) close (fd);
156             errno = saved_errno;
157             return -1;
158           }
159       }
160 # endif
161
162     return fd;
163
164 #elif HAVE_UNIXSOCKET_SCM_RIGHTS_BSD43_WAY
165     int fd;
166
167     msg.msg_accrights = &fd;
168     msg.msg_accrightslen = sizeof (fd);
169     if (recvmsg (sock, &msg, 0) < 0)
170       return -1;
171
172     /* set close-on-exec flag */
173     if (flags & O_CLOEXEC)
174       {
175         if (set_cloexec_flag (fd, true) < 0)
176           {
177             int saved_errno = errno;
178             (void) close (fd);
179             errno = saved_errno;
180             return -1;
181           }
182       }
183
184     return fd;
185
186 #else
187     errno = ENOSYS;
188     return -1;
189 #endif
190   }
191 }