1309554Ssobomax/*-
2309554Ssobomax * Copyright (c) 2005 Andrey Simonenko
3309554Ssobomax * All rights reserved.
4309554Ssobomax *
5309554Ssobomax * Redistribution and use in source and binary forms, with or without
6309554Ssobomax * modification, are permitted provided that the following conditions
7309554Ssobomax * are met:
8309554Ssobomax * 1. Redistributions of source code must retain the above copyright
9309554Ssobomax *    notice, this list of conditions and the following disclaimer.
10309554Ssobomax * 2. Redistributions in binary form must reproduce the above copyright
11309554Ssobomax *    notice, this list of conditions and the following disclaimer in the
12309554Ssobomax *    documentation and/or other materials provided with the distribution.
13309554Ssobomax *
14309554Ssobomax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15309554Ssobomax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16309554Ssobomax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17309554Ssobomax * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18309554Ssobomax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19309554Ssobomax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20309554Ssobomax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21309554Ssobomax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22309554Ssobomax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23309554Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24309554Ssobomax * SUCH DAMAGE.
25309554Ssobomax */
26309554Ssobomax
27309554Ssobomax#include <sys/cdefs.h>
28309554Ssobomax__FBSDID("$FreeBSD: stable/11/tools/regression/sockets/unix_cmsg/uc_common.c 339066 2018-10-01 17:26:41Z sobomax $");
29309554Ssobomax
30309554Ssobomax#include <sys/socket.h>
31309554Ssobomax#include <sys/un.h>
32309554Ssobomax#include <err.h>
33309554Ssobomax#include <fcntl.h>
34309554Ssobomax#include <errno.h>
35309554Ssobomax#include <inttypes.h>
36309554Ssobomax#include <stdarg.h>
37309554Ssobomax#include <stdbool.h>
38309554Ssobomax#include <stdlib.h>
39309554Ssobomax#include <stdio.h>
40309554Ssobomax#include <string.h>
41309554Ssobomax#include <unistd.h>
42309554Ssobomax#include <sys/wait.h>
43309554Ssobomax
44309554Ssobomax#include "uc_common.h"
45309554Ssobomax
46309554Ssobomax#ifndef LISTENQ
47309554Ssobomax# define LISTENQ	1
48309554Ssobomax#endif
49309554Ssobomax
50309554Ssobomax#ifndef	TIMEOUT
51309554Ssobomax# define TIMEOUT	2
52309554Ssobomax#endif
53309554Ssobomax
54309554Ssobomax#define	SYNC_SERVER	0
55309554Ssobomax#define	SYNC_CLIENT	1
56309554Ssobomax#define	SYNC_RECV	0
57309554Ssobomax#define	SYNC_SEND	1
58309554Ssobomax
59309554Ssobomax#define	LOGMSG_SIZE	128
60309554Ssobomax
61309554Ssobomaxvoid
62309554Ssobomaxuc_output(const char *format, ...)
63309554Ssobomax{
64309554Ssobomax	char buf[LOGMSG_SIZE];
65309554Ssobomax	va_list ap;
66309554Ssobomax
67309554Ssobomax	va_start(ap, format);
68309554Ssobomax	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
69309554Ssobomax		err(EXIT_FAILURE, "output: vsnprintf failed");
70309554Ssobomax	write(STDOUT_FILENO, buf, strlen(buf));
71309554Ssobomax	va_end(ap);
72309554Ssobomax}
73309554Ssobomax
74309554Ssobomaxvoid
75309554Ssobomaxuc_logmsg(const char *format, ...)
76309554Ssobomax{
77309554Ssobomax	char buf[LOGMSG_SIZE];
78309554Ssobomax	va_list ap;
79309554Ssobomax	int errno_save;
80309554Ssobomax
81309554Ssobomax	errno_save = errno;
82309554Ssobomax	va_start(ap, format);
83309554Ssobomax	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
84309554Ssobomax		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
85309554Ssobomax	if (errno_save == 0)
86309554Ssobomax		uc_output("%s: %s\n", uc_cfg.proc_name, buf);
87309554Ssobomax	else
88309554Ssobomax		uc_output("%s: %s: %s\n", uc_cfg.proc_name, buf,
89309554Ssobomax		    strerror(errno_save));
90309554Ssobomax	va_end(ap);
91309554Ssobomax	errno = errno_save;
92309554Ssobomax}
93309554Ssobomax
94309554Ssobomaxvoid
95309554Ssobomaxuc_vlogmsgx(const char *format, va_list ap)
96309554Ssobomax{
97309554Ssobomax	char buf[LOGMSG_SIZE];
98309554Ssobomax
99309554Ssobomax	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
100309554Ssobomax		err(EXIT_FAILURE, "uc_logmsgx: vsnprintf failed");
101309554Ssobomax	uc_output("%s: %s\n", uc_cfg.proc_name, buf);
102309554Ssobomax}
103309554Ssobomax
104309554Ssobomaxvoid
105309554Ssobomaxuc_logmsgx(const char *format, ...)
106309554Ssobomax{
107309554Ssobomax	va_list ap;
108309554Ssobomax
109309554Ssobomax	va_start(ap, format);
110309554Ssobomax	uc_vlogmsgx(format, ap);
111309554Ssobomax	va_end(ap);
112309554Ssobomax}
113309554Ssobomax
114309554Ssobomaxvoid
115309554Ssobomaxuc_dbgmsg(const char *format, ...)
116309554Ssobomax{
117309554Ssobomax	va_list ap;
118309554Ssobomax
119309554Ssobomax	if (uc_cfg.debug) {
120309554Ssobomax		va_start(ap, format);
121309554Ssobomax		uc_vlogmsgx(format, ap);
122309554Ssobomax		va_end(ap);
123309554Ssobomax	}
124309554Ssobomax}
125309554Ssobomax
126309554Ssobomaxint
127309554Ssobomaxuc_socket_create(void)
128309554Ssobomax{
129309554Ssobomax	struct timeval tv;
130309554Ssobomax	int fd;
131309554Ssobomax
132309554Ssobomax	fd = socket(PF_LOCAL, uc_cfg.sock_type, 0);
133309554Ssobomax	if (fd < 0) {
134309554Ssobomax		uc_logmsg("socket_create: socket(PF_LOCAL, %s, 0)", uc_cfg.sock_type_str);
135309554Ssobomax		return (-1);
136309554Ssobomax	}
137309554Ssobomax	if (uc_cfg.server_flag)
138309554Ssobomax		uc_cfg.serv_sock_fd = fd;
139309554Ssobomax
140309554Ssobomax	tv.tv_sec = TIMEOUT;
141309554Ssobomax	tv.tv_usec = 0;
142309554Ssobomax	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
143309554Ssobomax	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
144309554Ssobomax		uc_logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
145309554Ssobomax		goto failed;
146309554Ssobomax	}
147309554Ssobomax
148309554Ssobomax	if (uc_cfg.server_flag) {
149309554Ssobomax		if (bind(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
150309554Ssobomax		    uc_cfg.serv_addr_sun.sun_len) < 0) {
151309554Ssobomax			uc_logmsg("socket_create: bind(%s)",
152309554Ssobomax			    uc_cfg.serv_addr_sun.sun_path);
153309554Ssobomax			goto failed;
154309554Ssobomax		}
155309554Ssobomax		if (uc_cfg.sock_type == SOCK_STREAM) {
156309554Ssobomax			int val;
157309554Ssobomax
158309554Ssobomax			if (listen(fd, LISTENQ) < 0) {
159309554Ssobomax				uc_logmsg("socket_create: listen");
160309554Ssobomax				goto failed;
161309554Ssobomax			}
162309554Ssobomax			val = fcntl(fd, F_GETFL, 0);
163309554Ssobomax			if (val < 0) {
164309554Ssobomax				uc_logmsg("socket_create: fcntl(F_GETFL)");
165309554Ssobomax				goto failed;
166309554Ssobomax			}
167309554Ssobomax			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
168309554Ssobomax				uc_logmsg("socket_create: fcntl(F_SETFL)");
169309554Ssobomax				goto failed;
170309554Ssobomax			}
171309554Ssobomax		}
172309554Ssobomax	}
173309554Ssobomax
174309554Ssobomax	return (fd);
175309554Ssobomax
176309554Ssobomaxfailed:
177309554Ssobomax	if (close(fd) < 0)
178309554Ssobomax		uc_logmsg("socket_create: close");
179309554Ssobomax	if (uc_cfg.server_flag)
180309554Ssobomax		if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0)
181309554Ssobomax			uc_logmsg("socket_close: unlink(%s)",
182309554Ssobomax			    uc_cfg.serv_addr_sun.sun_path);
183309554Ssobomax	return (-1);
184309554Ssobomax}
185309554Ssobomax
186309554Ssobomaxint
187309554Ssobomaxuc_socket_close(int fd)
188309554Ssobomax{
189309554Ssobomax	int rv;
190309554Ssobomax
191309554Ssobomax	rv = 0;
192309554Ssobomax	if (close(fd) < 0) {
193309554Ssobomax		uc_logmsg("socket_close: close");
194309554Ssobomax		rv = -1;
195309554Ssobomax	}
196309554Ssobomax	if (uc_cfg.server_flag && fd == uc_cfg.serv_sock_fd)
197309554Ssobomax		if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) {
198309554Ssobomax			uc_logmsg("socket_close: unlink(%s)",
199309554Ssobomax			    uc_cfg.serv_addr_sun.sun_path);
200309554Ssobomax			rv = -1;
201309554Ssobomax		}
202309554Ssobomax	return (rv);
203309554Ssobomax}
204309554Ssobomax
205309554Ssobomaxint
206309554Ssobomaxuc_socket_connect(int fd)
207309554Ssobomax{
208309554Ssobomax	uc_dbgmsg("connect");
209309554Ssobomax
210309554Ssobomax	if (connect(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
211309554Ssobomax	    uc_cfg.serv_addr_sun.sun_len) < 0) {
212309554Ssobomax		uc_logmsg("socket_connect: connect(%s)", uc_cfg.serv_addr_sun.sun_path);
213309554Ssobomax		return (-1);
214309554Ssobomax	}
215309554Ssobomax	return (0);
216309554Ssobomax}
217309554Ssobomax
218309554Ssobomaxint
219309554Ssobomaxuc_sync_recv(void)
220309554Ssobomax{
221309554Ssobomax	ssize_t ssize;
222309554Ssobomax	int fd;
223309554Ssobomax	char buf;
224309554Ssobomax
225309554Ssobomax	uc_dbgmsg("sync: wait");
226309554Ssobomax
227309554Ssobomax	fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
228309554Ssobomax
229309554Ssobomax	ssize = read(fd, &buf, 1);
230309554Ssobomax	if (ssize < 0) {
231309554Ssobomax		uc_logmsg("sync_recv: read");
232309554Ssobomax		return (-1);
233309554Ssobomax	}
234309554Ssobomax	if (ssize < 1) {
235309554Ssobomax		uc_logmsgx("sync_recv: read %zd of 1 byte", ssize);
236309554Ssobomax		return (-1);
237309554Ssobomax	}
238309554Ssobomax
239309554Ssobomax	uc_dbgmsg("sync: received");
240309554Ssobomax
241309554Ssobomax	return (0);
242309554Ssobomax}
243309554Ssobomax
244309554Ssobomaxint
245309554Ssobomaxuc_sync_send(void)
246309554Ssobomax{
247309554Ssobomax	ssize_t ssize;
248309554Ssobomax	int fd;
249309554Ssobomax
250309554Ssobomax	uc_dbgmsg("sync: send");
251309554Ssobomax
252309554Ssobomax	fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
253309554Ssobomax
254309554Ssobomax	ssize = write(fd, "", 1);
255309554Ssobomax	if (ssize < 0) {
256309554Ssobomax		uc_logmsg("uc_sync_send: write");
257309554Ssobomax		return (-1);
258309554Ssobomax	}
259309554Ssobomax	if (ssize < 1) {
260309554Ssobomax		uc_logmsgx("uc_sync_send: sent %zd of 1 byte", ssize);
261309554Ssobomax		return (-1);
262309554Ssobomax	}
263309554Ssobomax
264309554Ssobomax	return (0);
265309554Ssobomax}
266309554Ssobomax
267309554Ssobomaxint
268309554Ssobomaxuc_message_send(int fd, const struct msghdr *msghdr)
269309554Ssobomax{
270309554Ssobomax	const struct cmsghdr *cmsghdr;
271309554Ssobomax	size_t size;
272309554Ssobomax	ssize_t ssize;
273309554Ssobomax
274309554Ssobomax	size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
275309554Ssobomax	uc_dbgmsg("send: data size %zu", size);
276309554Ssobomax	uc_dbgmsg("send: msghdr.msg_controllen %u",
277309554Ssobomax	    (u_int)msghdr->msg_controllen);
278309554Ssobomax	cmsghdr = CMSG_FIRSTHDR(msghdr);
279309554Ssobomax	if (cmsghdr != NULL)
280309554Ssobomax		uc_dbgmsg("send: cmsghdr.cmsg_len %u",
281309554Ssobomax		    (u_int)cmsghdr->cmsg_len);
282309554Ssobomax
283309554Ssobomax	ssize = sendmsg(fd, msghdr, 0);
284309554Ssobomax	if (ssize < 0) {
285309554Ssobomax		uc_logmsg("message_send: sendmsg");
286309554Ssobomax		return (-1);
287309554Ssobomax	}
288309554Ssobomax	if ((size_t)ssize != size) {
289309554Ssobomax		uc_logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
290309554Ssobomax		    ssize, size);
291309554Ssobomax		return (-1);
292309554Ssobomax	}
293309554Ssobomax
294309554Ssobomax	if (!uc_cfg.send_data_flag)
295309554Ssobomax		if (uc_sync_send() < 0)
296309554Ssobomax			return (-1);
297309554Ssobomax
298309554Ssobomax	return (0);
299309554Ssobomax}
300309554Ssobomax
301309554Ssobomaxint
302309554Ssobomaxuc_message_sendn(int fd, struct msghdr *msghdr)
303309554Ssobomax{
304309554Ssobomax	u_int i;
305309554Ssobomax
306309554Ssobomax	for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
307309554Ssobomax		uc_dbgmsg("message #%u", i);
308309554Ssobomax		if (uc_message_send(fd, msghdr) < 0)
309309554Ssobomax			return (-1);
310309554Ssobomax	}
311309554Ssobomax	return (0);
312309554Ssobomax}
313309554Ssobomax
314309554Ssobomaxint
315309554Ssobomaxuc_message_recv(int fd, struct msghdr *msghdr)
316309554Ssobomax{
317309554Ssobomax	const struct cmsghdr *cmsghdr;
318309554Ssobomax	size_t size;
319309554Ssobomax	ssize_t ssize;
320309554Ssobomax
321309554Ssobomax	if (!uc_cfg.send_data_flag)
322309554Ssobomax		if (uc_sync_recv() < 0)
323309554Ssobomax			return (-1);
324309554Ssobomax
325309554Ssobomax	size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
326309554Ssobomax	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
327309554Ssobomax	if (ssize < 0) {
328309554Ssobomax		uc_logmsg("message_recv: recvmsg");
329309554Ssobomax		return (-1);
330309554Ssobomax	}
331309554Ssobomax	if ((size_t)ssize != size) {
332309554Ssobomax		uc_logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
333309554Ssobomax		    ssize, size);
334309554Ssobomax		return (-1);
335309554Ssobomax	}
336309554Ssobomax
337309554Ssobomax	uc_dbgmsg("recv: data size %zd", ssize);
338309554Ssobomax	uc_dbgmsg("recv: msghdr.msg_controllen %u",
339309554Ssobomax	    (u_int)msghdr->msg_controllen);
340309554Ssobomax	cmsghdr = CMSG_FIRSTHDR(msghdr);
341309554Ssobomax	if (cmsghdr != NULL)
342309554Ssobomax		uc_dbgmsg("recv: cmsghdr.cmsg_len %u",
343309554Ssobomax		    (u_int)cmsghdr->cmsg_len);
344309554Ssobomax
345309554Ssobomax	if (memcmp(uc_cfg.ipc_msg.buf_recv, uc_cfg.ipc_msg.buf_send, size) != 0) {
346309554Ssobomax		uc_logmsgx("message_recv: received message has wrong content");
347309554Ssobomax		return (-1);
348309554Ssobomax	}
349309554Ssobomax
350309554Ssobomax	return (0);
351309554Ssobomax}
352309554Ssobomax
353309554Ssobomaxint
354309554Ssobomaxuc_socket_accept(int listenfd)
355309554Ssobomax{
356309554Ssobomax	fd_set rset;
357309554Ssobomax	struct timeval tv;
358309554Ssobomax	int fd, rv, val;
359309554Ssobomax
360309554Ssobomax	uc_dbgmsg("accept");
361309554Ssobomax
362309554Ssobomax	FD_ZERO(&rset);
363309554Ssobomax	FD_SET(listenfd, &rset);
364309554Ssobomax	tv.tv_sec = TIMEOUT;
365309554Ssobomax	tv.tv_usec = 0;
366309554Ssobomax	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
367309554Ssobomax	if (rv < 0) {
368309554Ssobomax		uc_logmsg("socket_accept: select");
369309554Ssobomax		return (-1);
370309554Ssobomax	}
371309554Ssobomax	if (rv == 0) {
372309554Ssobomax		uc_logmsgx("socket_accept: select timeout");
373309554Ssobomax		return (-1);
374309554Ssobomax	}
375309554Ssobomax
376309554Ssobomax	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
377309554Ssobomax	if (fd < 0) {
378309554Ssobomax		uc_logmsg("socket_accept: accept");
379309554Ssobomax		return (-1);
380309554Ssobomax	}
381309554Ssobomax
382309554Ssobomax	val = fcntl(fd, F_GETFL, 0);
383309554Ssobomax	if (val < 0) {
384309554Ssobomax		uc_logmsg("socket_accept: fcntl(F_GETFL)");
385309554Ssobomax		goto failed;
386309554Ssobomax	}
387309554Ssobomax	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
388309554Ssobomax		uc_logmsg("socket_accept: fcntl(F_SETFL)");
389309554Ssobomax		goto failed;
390309554Ssobomax	}
391309554Ssobomax
392309554Ssobomax	return (fd);
393309554Ssobomax
394309554Ssobomaxfailed:
395309554Ssobomax	if (close(fd) < 0)
396309554Ssobomax		uc_logmsg("socket_accept: close");
397309554Ssobomax	return (-1);
398309554Ssobomax}
399309554Ssobomax
400309554Ssobomaxint
401309554Ssobomaxuc_check_msghdr(const struct msghdr *msghdr, size_t size)
402309554Ssobomax{
403309554Ssobomax	if (msghdr->msg_flags & MSG_TRUNC) {
404309554Ssobomax		uc_logmsgx("msghdr.msg_flags has MSG_TRUNC");
405309554Ssobomax		return (-1);
406309554Ssobomax	}
407309554Ssobomax	if (msghdr->msg_flags & MSG_CTRUNC) {
408309554Ssobomax		uc_logmsgx("msghdr.msg_flags has MSG_CTRUNC");
409309554Ssobomax		return (-1);
410309554Ssobomax	}
411309554Ssobomax	if (msghdr->msg_controllen < size) {
412309554Ssobomax		uc_logmsgx("msghdr.msg_controllen %u < %zu",
413309554Ssobomax		    (u_int)msghdr->msg_controllen, size);
414309554Ssobomax		return (-1);
415309554Ssobomax	}
416309554Ssobomax	if (msghdr->msg_controllen > 0 && size == 0) {
417309554Ssobomax		uc_logmsgx("msghdr.msg_controllen %u > 0",
418309554Ssobomax		    (u_int)msghdr->msg_controllen);
419309554Ssobomax		return (-1);
420309554Ssobomax	}
421309554Ssobomax	return (0);
422309554Ssobomax}
423309554Ssobomax
424309554Ssobomaxint
425309554Ssobomaxuc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
426309554Ssobomax{
427309554Ssobomax	if (cmsghdr == NULL) {
428309554Ssobomax		uc_logmsgx("cmsghdr is NULL");
429309554Ssobomax		return (-1);
430309554Ssobomax	}
431309554Ssobomax	if (cmsghdr->cmsg_level != SOL_SOCKET) {
432309554Ssobomax		uc_logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
433309554Ssobomax		    cmsghdr->cmsg_level);
434309554Ssobomax		return (-1);
435309554Ssobomax	}
436309554Ssobomax	if (cmsghdr->cmsg_type != type) {
437309554Ssobomax		uc_logmsgx("cmsghdr.cmsg_type %d != %d",
438309554Ssobomax		    cmsghdr->cmsg_type, type);
439309554Ssobomax		return (-1);
440309554Ssobomax	}
441309554Ssobomax	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
442309554Ssobomax		uc_logmsgx("cmsghdr.cmsg_len %u != %zu",
443309554Ssobomax		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
444309554Ssobomax		return (-1);
445309554Ssobomax	}
446309554Ssobomax	return (0);
447309554Ssobomax}
448309554Ssobomax
449309554Ssobomaxstatic void
450309554Ssobomaxuc_msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
451309554Ssobomax{
452309554Ssobomax	msghdr->msg_name = NULL;
453309554Ssobomax	msghdr->msg_namelen = 0;
454309554Ssobomax	if (uc_cfg.send_data_flag) {
455309554Ssobomax		iov->iov_base = uc_cfg.server_flag ?
456309554Ssobomax		    uc_cfg.ipc_msg.buf_recv : uc_cfg.ipc_msg.buf_send;
457309554Ssobomax		iov->iov_len = uc_cfg.ipc_msg.buf_size;
458309554Ssobomax		msghdr->msg_iov = iov;
459309554Ssobomax		msghdr->msg_iovlen = 1;
460309554Ssobomax	} else {
461309554Ssobomax		msghdr->msg_iov = NULL;
462309554Ssobomax		msghdr->msg_iovlen = 0;
463309554Ssobomax	}
464309554Ssobomax	msghdr->msg_control = cmsg_data;
465309554Ssobomax	msghdr->msg_flags = 0;
466309554Ssobomax}
467309554Ssobomax
468309554Ssobomaxvoid
469309554Ssobomaxuc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
470309554Ssobomax    void *cmsg_data, size_t cmsg_size)
471309554Ssobomax{
472309554Ssobomax	uc_msghdr_init_generic(msghdr, iov, cmsg_data);
473309554Ssobomax	msghdr->msg_controllen = cmsg_size;
474309554Ssobomax	uc_dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
475309554Ssobomax	    msghdr->msg_iov->iov_len : (size_t)0);
476309554Ssobomax	uc_dbgmsg("init: msghdr.msg_controllen %u",
477309554Ssobomax	    (u_int)msghdr->msg_controllen);
478309554Ssobomax}
479309554Ssobomax
480309554Ssobomaxvoid
481309554Ssobomaxuc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
482309554Ssobomax    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
483309554Ssobomax{
484309554Ssobomax	struct cmsghdr *cmsghdr;
485309554Ssobomax
486309554Ssobomax	uc_msghdr_init_generic(msghdr, iov, cmsg_data);
487309554Ssobomax	if (cmsg_data != NULL) {
488309554Ssobomax		if (uc_cfg.send_array_flag)
489309554Ssobomax			uc_dbgmsg("sending an array");
490309554Ssobomax		else
491309554Ssobomax			uc_dbgmsg("sending a scalar");
492309554Ssobomax		msghdr->msg_controllen = uc_cfg.send_array_flag ?
493309554Ssobomax		    cmsg_size : CMSG_SPACE(0);
494309554Ssobomax		cmsghdr = CMSG_FIRSTHDR(msghdr);
495309554Ssobomax		cmsghdr->cmsg_level = SOL_SOCKET;
496309554Ssobomax		cmsghdr->cmsg_type = type;
497309554Ssobomax		cmsghdr->cmsg_len = CMSG_LEN(uc_cfg.send_array_flag ? arr_size : 0);
498309554Ssobomax	} else
499309554Ssobomax		msghdr->msg_controllen = 0;
500309554Ssobomax}
501309554Ssobomax
502309554Ssobomaxint
503309554Ssobomaxuc_client_fork(void)
504309554Ssobomax{
505309554Ssobomax	int fd1, fd2;
506309554Ssobomax
507309554Ssobomax	if (pipe(uc_cfg.sync_fd[SYNC_SERVER]) < 0 ||
508309554Ssobomax	    pipe(uc_cfg.sync_fd[SYNC_CLIENT]) < 0) {
509309554Ssobomax		uc_logmsg("client_fork: pipe");
510309554Ssobomax		return (-1);
511309554Ssobomax	}
512309554Ssobomax	uc_cfg.client_pid = fork();
513309554Ssobomax	if (uc_cfg.client_pid == (pid_t)-1) {
514309554Ssobomax		uc_logmsg("client_fork: fork");
515309554Ssobomax		return (-1);
516309554Ssobomax	}
517309554Ssobomax	if (uc_cfg.client_pid == 0) {
518309554Ssobomax		uc_cfg.proc_name = "CLIENT";
519309554Ssobomax		uc_cfg.server_flag = false;
520309554Ssobomax		fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV];
521309554Ssobomax		fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND];
522309554Ssobomax	} else {
523309554Ssobomax		fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND];
524309554Ssobomax		fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV];
525309554Ssobomax	}
526309554Ssobomax	if (close(fd1) < 0 || close(fd2) < 0) {
527309554Ssobomax		uc_logmsg("client_fork: close");
528309554Ssobomax		return (-1);
529309554Ssobomax	}
530309554Ssobomax	return (uc_cfg.client_pid != 0);
531309554Ssobomax}
532309554Ssobomax
533309554Ssobomaxvoid
534309554Ssobomaxuc_client_exit(int rv)
535309554Ssobomax{
536309554Ssobomax	if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
537309554Ssobomax	    close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
538309554Ssobomax		uc_logmsg("client_exit: close");
539309554Ssobomax		rv = -1;
540309554Ssobomax	}
541309554Ssobomax	rv = rv == 0 ? EXIT_SUCCESS : -rv;
542309554Ssobomax	uc_dbgmsg("exit: code %d", rv);
543309554Ssobomax	_exit(rv);
544309554Ssobomax}
545309554Ssobomax
546309554Ssobomaxint
547309554Ssobomaxuc_client_wait(void)
548309554Ssobomax{
549309554Ssobomax	int status;
550309554Ssobomax	pid_t pid;
551309554Ssobomax
552309554Ssobomax	uc_dbgmsg("waiting for client");
553309554Ssobomax
554309554Ssobomax	if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
555309554Ssobomax	    close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
556309554Ssobomax		uc_logmsg("client_wait: close");
557309554Ssobomax		return (-1);
558309554Ssobomax	}
559309554Ssobomax
560309554Ssobomax	pid = waitpid(uc_cfg.client_pid, &status, 0);
561309554Ssobomax	if (pid == (pid_t)-1) {
562309554Ssobomax		uc_logmsg("client_wait: waitpid");
563309554Ssobomax		return (-1);
564309554Ssobomax	}
565309554Ssobomax
566309554Ssobomax	if (WIFEXITED(status)) {
567309554Ssobomax		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
568309554Ssobomax			uc_logmsgx("client exit status is %d",
569309554Ssobomax			    WEXITSTATUS(status));
570309554Ssobomax			return (-WEXITSTATUS(status));
571309554Ssobomax		}
572309554Ssobomax	} else {
573309554Ssobomax		if (WIFSIGNALED(status))
574309554Ssobomax			uc_logmsgx("abnormal termination of client, signal %d%s",
575309554Ssobomax			    WTERMSIG(status), WCOREDUMP(status) ?
576309554Ssobomax			    " (core file generated)" : "");
577309554Ssobomax		else
578309554Ssobomax			uc_logmsgx("termination of client, unknown status");
579309554Ssobomax		return (-1);
580309554Ssobomax	}
581309554Ssobomax
582309554Ssobomax	return (0);
583309554Ssobomax}
584309554Ssobomax
585309554Ssobomaxint
586309554Ssobomaxuc_check_groups(const char *gid_arr_str, const gid_t *gid_arr,
587309554Ssobomax    const char *gid_num_str, int gid_num, bool all_gids)
588309554Ssobomax{
589309554Ssobomax	int i;
590309554Ssobomax
591309554Ssobomax	for (i = 0; i < gid_num; ++i)
592309554Ssobomax		uc_dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
593309554Ssobomax
594309554Ssobomax	if (all_gids) {
595309554Ssobomax		if (gid_num != uc_cfg.proc_cred.gid_num) {
596309554Ssobomax			uc_logmsgx("%s %d != %d", gid_num_str, gid_num,
597309554Ssobomax			    uc_cfg.proc_cred.gid_num);
598309554Ssobomax			return (-1);
599309554Ssobomax		}
600309554Ssobomax	} else {
601309554Ssobomax		if (gid_num > uc_cfg.proc_cred.gid_num) {
602309554Ssobomax			uc_logmsgx("%s %d > %d", gid_num_str, gid_num,
603309554Ssobomax			    uc_cfg.proc_cred.gid_num);
604309554Ssobomax			return (-1);
605309554Ssobomax		}
606309554Ssobomax	}
607309554Ssobomax	if (memcmp(gid_arr, uc_cfg.proc_cred.gid_arr,
608309554Ssobomax	    gid_num * sizeof(*gid_arr)) != 0) {
609309554Ssobomax		uc_logmsgx("%s content is wrong", gid_arr_str);
610309554Ssobomax		for (i = 0; i < gid_num; ++i)
611309554Ssobomax			if (gid_arr[i] != uc_cfg.proc_cred.gid_arr[i]) {
612309554Ssobomax				uc_logmsgx("%s[%d] %lu != %lu",
613309554Ssobomax				    gid_arr_str, i, (u_long)gid_arr[i],
614309554Ssobomax				    (u_long)uc_cfg.proc_cred.gid_arr[i]);
615309554Ssobomax				break;
616309554Ssobomax			}
617309554Ssobomax		return (-1);
618309554Ssobomax	}
619309554Ssobomax	return (0);
620309554Ssobomax}
621309554Ssobomax
622309554Ssobomaxint
623309554Ssobomaxuc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
624309554Ssobomax{
625309554Ssobomax	const struct cmsgcred *cmcred;
626309554Ssobomax	int rc;
627309554Ssobomax
628309554Ssobomax	if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0)
629309554Ssobomax		return (-1);
630309554Ssobomax
631309554Ssobomax	cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
632309554Ssobomax
633309554Ssobomax	uc_dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid);
634309554Ssobomax	uc_dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid);
635309554Ssobomax	uc_dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid);
636309554Ssobomax	uc_dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid);
637309554Ssobomax	uc_dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups);
638309554Ssobomax
639309554Ssobomax	rc = 0;
640309554Ssobomax
641309554Ssobomax	if (cmcred->cmcred_pid != uc_cfg.client_pid) {
642309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_pid %ld != %ld",
643309554Ssobomax		    (long)cmcred->cmcred_pid, (long)uc_cfg.client_pid);
644309554Ssobomax		rc = -1;
645309554Ssobomax	}
646309554Ssobomax	if (cmcred->cmcred_uid != uc_cfg.proc_cred.uid) {
647309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_uid %lu != %lu",
648309554Ssobomax		    (u_long)cmcred->cmcred_uid, (u_long)uc_cfg.proc_cred.uid);
649309554Ssobomax		rc = -1;
650309554Ssobomax	}
651309554Ssobomax	if (cmcred->cmcred_euid != uc_cfg.proc_cred.euid) {
652309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_euid %lu != %lu",
653309554Ssobomax		    (u_long)cmcred->cmcred_euid, (u_long)uc_cfg.proc_cred.euid);
654309554Ssobomax		rc = -1;
655309554Ssobomax	}
656309554Ssobomax	if (cmcred->cmcred_gid != uc_cfg.proc_cred.gid) {
657309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_gid %lu != %lu",
658309554Ssobomax		    (u_long)cmcred->cmcred_gid, (u_long)uc_cfg.proc_cred.gid);
659309554Ssobomax		rc = -1;
660309554Ssobomax	}
661309554Ssobomax	if (cmcred->cmcred_ngroups == 0) {
662309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_ngroups == 0");
663309554Ssobomax		rc = -1;
664309554Ssobomax	}
665309554Ssobomax	if (cmcred->cmcred_ngroups < 0) {
666309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_ngroups %d < 0",
667309554Ssobomax		    cmcred->cmcred_ngroups);
668309554Ssobomax		rc = -1;
669309554Ssobomax	}
670309554Ssobomax	if (cmcred->cmcred_ngroups > CMGROUP_MAX) {
671309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_ngroups %d > %d",
672309554Ssobomax		    cmcred->cmcred_ngroups, CMGROUP_MAX);
673309554Ssobomax		rc = -1;
674309554Ssobomax	}
675309554Ssobomax	if (cmcred->cmcred_groups[0] != uc_cfg.proc_cred.egid) {
676309554Ssobomax		uc_logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
677309554Ssobomax		    (u_long)cmcred->cmcred_groups[0], (u_long)uc_cfg.proc_cred.egid);
678309554Ssobomax		rc = -1;
679309554Ssobomax	}
680309554Ssobomax	if (uc_check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups,
681309554Ssobomax	    "cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0)
682309554Ssobomax		rc = -1;
683309554Ssobomax	return (rc);
684309554Ssobomax}
685309554Ssobomax
686309554Ssobomaxint
687309554Ssobomaxuc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
688309554Ssobomax{
689309554Ssobomax	const struct sockcred *sc;
690309554Ssobomax	int rc;
691309554Ssobomax
692309554Ssobomax	if (uc_check_cmsghdr(cmsghdr, SCM_CREDS,
693309554Ssobomax	    SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)) < 0)
694309554Ssobomax		return (-1);
695309554Ssobomax
696309554Ssobomax	sc = (struct sockcred *)CMSG_DATA(cmsghdr);
697309554Ssobomax
698309554Ssobomax	rc = 0;
699309554Ssobomax
700309554Ssobomax	uc_dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid);
701309554Ssobomax	uc_dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid);
702309554Ssobomax	uc_dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid);
703309554Ssobomax	uc_dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid);
704309554Ssobomax	uc_dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups);
705309554Ssobomax
706309554Ssobomax	if (sc->sc_uid != uc_cfg.proc_cred.uid) {
707309554Ssobomax		uc_logmsgx("sockcred.sc_uid %lu != %lu",
708309554Ssobomax		    (u_long)sc->sc_uid, (u_long)uc_cfg.proc_cred.uid);
709309554Ssobomax		rc = -1;
710309554Ssobomax	}
711309554Ssobomax	if (sc->sc_euid != uc_cfg.proc_cred.euid) {
712309554Ssobomax		uc_logmsgx("sockcred.sc_euid %lu != %lu",
713309554Ssobomax		    (u_long)sc->sc_euid, (u_long)uc_cfg.proc_cred.euid);
714309554Ssobomax		rc = -1;
715309554Ssobomax	}
716309554Ssobomax	if (sc->sc_gid != uc_cfg.proc_cred.gid) {
717309554Ssobomax		uc_logmsgx("sockcred.sc_gid %lu != %lu",
718309554Ssobomax		    (u_long)sc->sc_gid, (u_long)uc_cfg.proc_cred.gid);
719309554Ssobomax		rc = -1;
720309554Ssobomax	}
721309554Ssobomax	if (sc->sc_egid != uc_cfg.proc_cred.egid) {
722309554Ssobomax		uc_logmsgx("sockcred.sc_egid %lu != %lu",
723309554Ssobomax		    (u_long)sc->sc_egid, (u_long)uc_cfg.proc_cred.egid);
724309554Ssobomax		rc = -1;
725309554Ssobomax	}
726309554Ssobomax	if (sc->sc_ngroups == 0) {
727309554Ssobomax		uc_logmsgx("sockcred.sc_ngroups == 0");
728309554Ssobomax		rc = -1;
729309554Ssobomax	}
730309554Ssobomax	if (sc->sc_ngroups < 0) {
731309554Ssobomax		uc_logmsgx("sockcred.sc_ngroups %d < 0",
732309554Ssobomax		    sc->sc_ngroups);
733309554Ssobomax		rc = -1;
734309554Ssobomax	}
735309554Ssobomax	if (sc->sc_ngroups != uc_cfg.proc_cred.gid_num) {
736309554Ssobomax		uc_logmsgx("sockcred.sc_ngroups %d != %u",
737309554Ssobomax		    sc->sc_ngroups, uc_cfg.proc_cred.gid_num);
738309554Ssobomax		rc = -1;
739309554Ssobomax	}
740309554Ssobomax	if (uc_check_groups("sockcred.sc_groups", sc->sc_groups,
741309554Ssobomax	    "sockcred.sc_ngroups", sc->sc_ngroups, true) < 0)
742309554Ssobomax		rc = -1;
743309554Ssobomax	return (rc);
744309554Ssobomax}
745