msgio.c revision 261407
1/*-
2 * Copyright (c) 2013 The FreeBSD Foundation
3 * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
4 * All rights reserved.
5 *
6 * This software was developed by Pawel Jakub Dawidek under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/lib/libnv/msgio.c 261407 2014-02-02 19:03:52Z pjd $");
33
34#include <sys/types.h>
35#include <sys/socket.h>
36
37#include <errno.h>
38#include <fcntl.h>
39#include <stdbool.h>
40#include <stdint.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#ifdef HAVE_PJDLOG
46#include <pjdlog.h>
47#endif
48
49#include "common_impl.h"
50#include "msgio.h"
51
52#ifndef	HAVE_PJDLOG
53#include <assert.h>
54#define	PJDLOG_ASSERT(...)		assert(__VA_ARGS__)
55#define	PJDLOG_RASSERT(expr, ...)	assert(expr)
56#define	PJDLOG_ABORT(...)		abort()
57#endif
58
59static int
60msghdr_add_fd(struct cmsghdr *cmsg, int fd)
61{
62
63	PJDLOG_ASSERT(fd >= 0);
64
65	if (!fd_is_valid(fd)) {
66		errno = EBADF;
67		return (-1);
68	}
69
70	cmsg->cmsg_level = SOL_SOCKET;
71	cmsg->cmsg_type = SCM_RIGHTS;
72	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
73	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
74
75	return (0);
76}
77
78static int
79msghdr_get_fd(struct cmsghdr *cmsg)
80{
81	int fd;
82
83	if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
84	    cmsg->cmsg_type != SCM_RIGHTS ||
85	    cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) {
86		errno = EINVAL;
87		return (-1);
88	}
89
90	bcopy(CMSG_DATA(cmsg), &fd, sizeof(fd));
91#ifndef MSG_CMSG_CLOEXEC
92	/*
93	 * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the
94	 * close-on-exec flag atomically, but we still want to set it for
95	 * consistency.
96	 */
97	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
98#endif
99
100	return (fd);
101}
102
103static void
104fd_wait(int fd, bool doread)
105{
106	fd_set fds;
107
108	PJDLOG_ASSERT(fd >= 0);
109
110	FD_ZERO(&fds);
111	FD_SET(fd, &fds);
112	(void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
113	    NULL, NULL);
114}
115
116static int
117msg_recv(int sock, struct msghdr *msg)
118{
119	int flags;
120
121	PJDLOG_ASSERT(sock >= 0);
122
123#ifdef MSG_CMSG_CLOEXEC
124	flags = MSG_CMSG_CLOEXEC;
125#else
126	flags = 0;
127#endif
128
129	for (;;) {
130		fd_wait(sock, true);
131		if (recvmsg(sock, msg, flags) == -1) {
132			if (errno == EINTR)
133				continue;
134			return (-1);
135		}
136		break;
137	}
138
139	return (0);
140}
141
142static int
143msg_send(int sock, const struct msghdr *msg)
144{
145
146	PJDLOG_ASSERT(sock >= 0);
147
148	for (;;) {
149		fd_wait(sock, false);
150		if (sendmsg(sock, msg, 0) == -1) {
151			if (errno == EINTR)
152				continue;
153			return (-1);
154		}
155		break;
156	}
157
158	return (0);
159}
160
161int
162cred_send(int sock)
163{
164	unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
165	struct msghdr msg;
166	struct cmsghdr *cmsg;
167	struct iovec iov;
168	uint8_t dummy;
169
170	bzero(credbuf, sizeof(credbuf));
171	bzero(&msg, sizeof(msg));
172	bzero(&iov, sizeof(iov));
173
174	/*
175	 * XXX: We send one byte along with the control message, because
176	 *      setting msg_iov to NULL only works if this is the first
177	 *      packet send over the socket. Once we send some data we
178	 *      won't be able to send credentials anymore. This is most
179	 *      likely a kernel bug.
180	 */
181	dummy = 0;
182	iov.iov_base = &dummy;
183	iov.iov_len = sizeof(dummy);
184
185	msg.msg_iov = &iov;
186	msg.msg_iovlen = 1;
187	msg.msg_control = credbuf;
188	msg.msg_controllen = sizeof(credbuf);
189
190	cmsg = CMSG_FIRSTHDR(&msg);
191	cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
192	cmsg->cmsg_level = SOL_SOCKET;
193	cmsg->cmsg_type = SCM_CREDS;
194
195	if (msg_send(sock, &msg) == -1)
196		return (-1);
197
198	return (0);
199}
200
201int
202cred_recv(int sock, struct cmsgcred *cred)
203{
204	unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
205	struct msghdr msg;
206	struct cmsghdr *cmsg;
207	struct iovec iov;
208	uint8_t dummy;
209
210	bzero(credbuf, sizeof(credbuf));
211	bzero(&msg, sizeof(msg));
212	bzero(&iov, sizeof(iov));
213
214	iov.iov_base = &dummy;
215	iov.iov_len = sizeof(dummy);
216
217	msg.msg_iov = &iov;
218	msg.msg_iovlen = 1;
219	msg.msg_control = credbuf;
220	msg.msg_controllen = sizeof(credbuf);
221
222	if (msg_recv(sock, &msg) == -1)
223		return (-1);
224
225	cmsg = CMSG_FIRSTHDR(&msg);
226	if (cmsg == NULL ||
227	    cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) ||
228	    cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) {
229		errno = EINVAL;
230		return (-1);
231	}
232	bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred));
233
234	return (0);
235}
236
237int
238fd_send(int sock, const int *fds, size_t nfds)
239{
240	struct msghdr msg;
241	struct cmsghdr *cmsg;
242	unsigned int i;
243	int serrno, ret;
244
245	if (nfds == 0 || fds == NULL) {
246		errno = EINVAL;
247		return (-1);
248	}
249
250	bzero(&msg, sizeof(msg));
251	msg.msg_iov = NULL;
252	msg.msg_iovlen = 0;
253	msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
254	msg.msg_control = calloc(1, msg.msg_controllen);
255	if (msg.msg_control == NULL)
256		return (-1);
257
258	ret = -1;
259
260	for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
261	    i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
262		if (msghdr_add_fd(cmsg, fds[i]) == -1)
263			goto end;
264	}
265
266	if (msg_send(sock, &msg) == -1)
267		goto end;
268
269	ret = 0;
270end:
271	serrno = errno;
272	free(msg.msg_control);
273	errno = serrno;
274	return (ret);
275}
276
277int
278fd_recv(int sock, int *fds, size_t nfds)
279{
280	struct msghdr msg;
281	struct cmsghdr *cmsg;
282	unsigned int i;
283	int serrno, ret;
284
285	if (nfds == 0 || fds == NULL) {
286		errno = EINVAL;
287		return (-1);
288	}
289
290	bzero(&msg, sizeof(msg));
291	msg.msg_iov = NULL;
292	msg.msg_iovlen = 0;
293	msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
294	msg.msg_control = calloc(1, msg.msg_controllen);
295	if (msg.msg_control == NULL)
296		return (-1);
297
298	ret = -1;
299
300	if (msg_recv(sock, &msg) == -1)
301		goto end;
302
303	for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
304	    i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
305		fds[i] = msghdr_get_fd(cmsg);
306		if (fds[i] < 0)
307			break;
308	}
309
310	if (cmsg != NULL || i < nfds) {
311		int fd;
312
313		/*
314		 * We need to close all received descriptors, even if we have
315		 * different control message (eg. SCM_CREDS) in between.
316		 */
317		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
318		    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
319			fd = msghdr_get_fd(cmsg);
320			if (fd >= 0)
321				close(fd);
322		}
323		errno = EINVAL;
324		goto end;
325	}
326
327	ret = 0;
328end:
329	serrno = errno;
330	free(msg.msg_control);
331	errno = serrno;
332	return (ret);
333}
334
335int
336buf_send(int sock, void *buf, size_t size)
337{
338	ssize_t done;
339	unsigned char *ptr;
340
341	ptr = buf;
342	do {
343		fd_wait(sock, false);
344		done = send(sock, ptr, size, 0);
345		if (done == -1) {
346			if (errno == EINTR)
347				continue;
348			return (-1);
349		} else if (done == 0) {
350			errno = ENOTCONN;
351			return (-1);
352		}
353		size -= done;
354		ptr += done;
355	} while (size > 0);
356
357	return (0);
358}
359
360int
361buf_recv(int sock, void *buf, size_t size)
362{
363	ssize_t done;
364	unsigned char *ptr;
365
366	ptr = buf;
367	while (size > 0) {
368		fd_wait(sock, true);
369		done = recv(sock, ptr, size, 0);
370		if (done == -1) {
371			if (errno == EINTR)
372				continue;
373			return (-1);
374		} else if (done == 0) {
375			errno = ENOTCONN;
376			return (-1);
377		}
378		size -= done;
379		ptr += done;
380	}
381
382	return (0);
383}
384