1/*-
2 * Copyright (c) 2005 Andrey Simonenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/resource.h>
32#include <sys/time.h>
33#include <sys/select.h>
34#include <sys/socket.h>
35#include <sys/ucred.h>
36#include <sys/un.h>
37#include <sys/wait.h>
38
39#include <ctype.h>
40#include <err.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <inttypes.h>
44#include <limits.h>
45#include <paths.h>
46#include <signal.h>
47#include <stdarg.h>
48#include <stdbool.h>
49#include <stdint.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54
55/*
56 * There are tables with tests descriptions and pointers to test
57 * functions.  Each t_*() function returns 0 if its test passed,
58 * -1 if its test failed, -2 if some system error occurred.
59 * If a test function returns -2, then a program exits.
60 *
61 * If a test function forks a client process, then it waits for its
62 * termination.  If a return code of a client process is not equal
63 * to zero, or if a client process was terminated by a signal, then
64 * a test function returns -1 or -2 depending on exit status of
65 * a client process.
66 *
67 * Each function which can block, is run under TIMEOUT.  If timeout
68 * occurs, then a test function returns -2 or a client process exits
69 * with a non-zero return code.
70 */
71
72#ifndef LISTENQ
73# define LISTENQ	1
74#endif
75
76#ifndef TIMEOUT
77# define TIMEOUT	2
78#endif
79
80static int	t_cmsgcred(void);
81static int	t_sockcred_1(void);
82static int	t_sockcred_2(void);
83static int	t_cmsgcred_sockcred(void);
84static int	t_timeval(void);
85static int	t_bintime(void);
86static int	t_cmsg_len(void);
87static int	t_peercred(void);
88
89struct test_func {
90	int		(*func)(void);
91	const char	*desc;
92};
93
94static const struct test_func test_stream_tbl[] = {
95	{
96	  .func = NULL,
97	  .desc = "All tests"
98	},
99	{
100	  .func = t_cmsgcred,
101	  .desc = "Sending, receiving cmsgcred"
102	},
103	{
104	  .func = t_sockcred_1,
105	  .desc = "Receiving sockcred (listening socket)"
106	},
107	{
108	  .func = t_sockcred_2,
109	  .desc = "Receiving sockcred (accepted socket)"
110	},
111	{
112	  .func = t_cmsgcred_sockcred,
113	  .desc = "Sending cmsgcred, receiving sockcred"
114	},
115	{
116	  .func = t_timeval,
117	  .desc = "Sending, receiving timeval"
118	},
119	{
120	  .func = t_bintime,
121	  .desc = "Sending, receiving bintime"
122	},
123	{
124	  .func = t_cmsg_len,
125	  .desc = "Check cmsghdr.cmsg_len"
126	},
127	{
128	  .func = t_peercred,
129	  .desc = "Check LOCAL_PEERCRED socket option"
130	}
131};
132
133#define TEST_STREAM_TBL_SIZE \
134	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
135
136static const struct test_func test_dgram_tbl[] = {
137	{
138	  .func = NULL,
139	  .desc = "All tests"
140	},
141	{
142	  .func = t_cmsgcred,
143	  .desc = "Sending, receiving cmsgcred"
144	},
145	{
146	  .func = t_sockcred_2,
147	  .desc = "Receiving sockcred"
148	},
149	{
150	  .func = t_cmsgcred_sockcred,
151	  .desc = "Sending cmsgcred, receiving sockcred"
152	},
153	{
154	  .func = t_timeval,
155	  .desc = "Sending, receiving timeval"
156	},
157	{
158	  .func = t_bintime,
159	  .desc = "Sending, receiving bintime"
160	},
161	{
162	  .func = t_cmsg_len,
163	  .desc = "Check cmsghdr.cmsg_len"
164	}
165};
166
167#define TEST_DGRAM_TBL_SIZE \
168	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
169
170static bool	debug = false;
171static bool	server_flag = true;
172static bool	send_data_flag = true;
173static bool	send_array_flag = true;
174static bool	failed_flag = false;
175
176static int	sock_type;
177static const char *sock_type_str;
178
179static const char *proc_name;
180
181static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
182static int	serv_sock_fd;
183static struct sockaddr_un serv_addr_sun;
184
185static struct {
186	char		*buf_send;
187	char		*buf_recv;
188	size_t		buf_size;
189	u_int		msg_num;
190}		ipc_msg;
191
192#define IPC_MSG_NUM_DEF		5
193#define IPC_MSG_NUM_MAX		10
194#define IPC_MSG_SIZE_DEF	7
195#define IPC_MSG_SIZE_MAX	128
196
197static struct {
198	uid_t		uid;
199	uid_t		euid;
200	gid_t		gid;
201	gid_t		egid;
202	gid_t		*gid_arr;
203	int		gid_num;
204}		proc_cred;
205
206static pid_t	client_pid;
207
208#define SYNC_SERVER	0
209#define SYNC_CLIENT	1
210#define SYNC_RECV	0
211#define SYNC_SEND	1
212
213static int	sync_fd[2][2];
214
215#define LOGMSG_SIZE	128
216
217static void	logmsg(const char *, ...) __printflike(1, 2);
218static void	logmsgx(const char *, ...) __printflike(1, 2);
219static void	dbgmsg(const char *, ...) __printflike(1, 2);
220static void	output(const char *, ...) __printflike(1, 2);
221
222static void
223usage(bool verbose)
224{
225	u_int i;
226
227	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
228	    "[-z value] [testno]\n", getprogname());
229	if (!verbose)
230		return;
231	printf("\n Options are:\n\
232  -d            Output debugging information\n\
233  -h            Output the help message and exit\n\
234  -n num        Number of messages to send\n\
235  -s size       Specify size of data for IPC\n\
236  -t type       Specify socket type (stream, dgram) for tests\n\
237  -z value      Do not send data in a message (bit 0x1), do not send\n\
238                data array associated with a cmsghdr structure (bit 0x2)\n\
239  testno        Run one test by its number (require the -t option)\n\n");
240	printf(" Available tests for stream sockets:\n");
241	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
242		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
243	printf("\n Available tests for datagram sockets:\n");
244	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
245		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
246}
247
248static void
249output(const char *format, ...)
250{
251	char buf[LOGMSG_SIZE];
252	va_list ap;
253
254	va_start(ap, format);
255	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
256		err(EXIT_FAILURE, "output: vsnprintf failed");
257	write(STDOUT_FILENO, buf, strlen(buf));
258	va_end(ap);
259}
260
261static void
262logmsg(const char *format, ...)
263{
264	char buf[LOGMSG_SIZE];
265	va_list ap;
266	int errno_save;
267
268	errno_save = errno;
269	va_start(ap, format);
270	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
271		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
272	if (errno_save == 0)
273		output("%s: %s\n", proc_name, buf);
274	else
275		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
276	va_end(ap);
277	errno = errno_save;
278}
279
280static void
281vlogmsgx(const char *format, va_list ap)
282{
283	char buf[LOGMSG_SIZE];
284
285	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
286		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
287	output("%s: %s\n", proc_name, buf);
288
289}
290
291static void
292logmsgx(const char *format, ...)
293{
294	va_list ap;
295
296	va_start(ap, format);
297	vlogmsgx(format, ap);
298	va_end(ap);
299}
300
301static void
302dbgmsg(const char *format, ...)
303{
304	va_list ap;
305
306	if (debug) {
307		va_start(ap, format);
308		vlogmsgx(format, ap);
309		va_end(ap);
310	}
311}
312
313static int
314run_tests(int type, u_int testno1)
315{
316	const struct test_func *tf;
317	u_int i, testno2, failed_num;
318
319	sock_type = type;
320	if (type == SOCK_STREAM) {
321		sock_type_str = "SOCK_STREAM";
322		tf = test_stream_tbl;
323		i = TEST_STREAM_TBL_SIZE - 1;
324	} else {
325		sock_type_str = "SOCK_DGRAM";
326		tf = test_dgram_tbl;
327		i = TEST_DGRAM_TBL_SIZE - 1;
328	}
329	if (testno1 == 0) {
330		testno1 = 1;
331		testno2 = i;
332	} else
333		testno2 = testno1;
334
335	output("Running tests for %s sockets:\n", sock_type_str);
336	failed_num = 0;
337	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
338		output("  %u: %s\n", i, tf->desc);
339		switch (tf->func()) {
340		case -1:
341			++failed_num;
342			break;
343		case -2:
344			logmsgx("some system error or timeout occurred");
345			return (-1);
346		}
347	}
348
349	if (failed_num != 0)
350		failed_flag = true;
351
352	if (testno1 != testno2) {
353		if (failed_num == 0)
354			output("-- all tests passed!\n");
355		else
356			output("-- %u test%s failed!\n",
357			    failed_num, failed_num == 1 ? "" : "s");
358	} else {
359		if (failed_num == 0)
360			output("-- test passed!\n");
361		else
362			output("-- test failed!\n");
363	}
364
365	return (0);
366}
367
368static int
369init(void)
370{
371	struct sigaction sigact;
372	size_t idx;
373	int rv;
374
375	proc_name = "SERVER";
376
377	sigact.sa_handler = SIG_IGN;
378	sigact.sa_flags = 0;
379	sigemptyset(&sigact.sa_mask);
380	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
381		logmsg("init: sigaction");
382		return (-1);
383	}
384
385	if (ipc_msg.buf_size == 0)
386		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
387	else {
388		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
389		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
390		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
391			logmsg("init: malloc");
392			return (-1);
393		}
394		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
395			ipc_msg.buf_send[idx] = (char)idx;
396	}
397
398	proc_cred.uid = getuid();
399	proc_cred.euid = geteuid();
400	proc_cred.gid = getgid();
401	proc_cred.egid = getegid();
402	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
403	if (proc_cred.gid_num < 0) {
404		logmsg("init: getgroups");
405		return (-1);
406	}
407	proc_cred.gid_arr = malloc(proc_cred.gid_num *
408	    sizeof(*proc_cred.gid_arr));
409	if (proc_cred.gid_arr == NULL) {
410		logmsg("init: malloc");
411		return (-1);
412	}
413	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
414		logmsg("init: getgroups");
415		return (-1);
416	}
417
418	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
419	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
420	    "%s/%s", work_dir, proc_name);
421	if (rv < 0) {
422		logmsg("init: snprintf");
423		return (-1);
424	}
425	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
426		logmsgx("init: not enough space for socket pathname");
427		return (-1);
428	}
429	serv_addr_sun.sun_family = PF_LOCAL;
430	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
431
432	return (0);
433}
434
435static int
436client_fork(void)
437{
438	int fd1, fd2;
439
440	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
441	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
442		logmsg("client_fork: pipe");
443		return (-1);
444	}
445	client_pid = fork();
446	if (client_pid == (pid_t)-1) {
447		logmsg("client_fork: fork");
448		return (-1);
449	}
450	if (client_pid == 0) {
451		proc_name = "CLIENT";
452		server_flag = false;
453		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
454		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
455	} else {
456		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
457		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
458	}
459	if (close(fd1) < 0 || close(fd2) < 0) {
460		logmsg("client_fork: close");
461		return (-1);
462	}
463	return (client_pid != 0);
464}
465
466static void
467client_exit(int rv)
468{
469	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
470	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
471		logmsg("client_exit: close");
472		rv = -1;
473	}
474	rv = rv == 0 ? EXIT_SUCCESS : -rv;
475	dbgmsg("exit: code %d", rv);
476	_exit(rv);
477}
478
479static int
480client_wait(void)
481{
482	int status;
483	pid_t pid;
484
485	dbgmsg("waiting for client");
486
487	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
488	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
489		logmsg("client_wait: close");
490		return (-1);
491	}
492
493	pid = waitpid(client_pid, &status, 0);
494	if (pid == (pid_t)-1) {
495		logmsg("client_wait: waitpid");
496		return (-1);
497	}
498
499	if (WIFEXITED(status)) {
500		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
501			logmsgx("client exit status is %d",
502			    WEXITSTATUS(status));
503			return (-WEXITSTATUS(status));
504		}
505	} else {
506		if (WIFSIGNALED(status))
507			logmsgx("abnormal termination of client, signal %d%s",
508			    WTERMSIG(status), WCOREDUMP(status) ?
509			    " (core file generated)" : "");
510		else
511			logmsgx("termination of client, unknown status");
512		return (-1);
513	}
514
515	return (0);
516}
517
518int
519main(int argc, char *argv[])
520{
521	const char *errstr;
522	u_int testno, zvalue;
523	int opt, rv;
524	bool dgram_flag, stream_flag;
525
526	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
527	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
528	dgram_flag = stream_flag = false;
529	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
530		switch (opt) {
531		case 'd':
532			debug = true;
533			break;
534		case 'h':
535			usage(true);
536			return (EXIT_SUCCESS);
537		case 'n':
538			ipc_msg.msg_num = strtonum(optarg, 1,
539			    IPC_MSG_NUM_MAX, &errstr);
540			if (errstr != NULL)
541				errx(EXIT_FAILURE, "option -n: number is %s",
542				    errstr);
543			break;
544		case 's':
545			ipc_msg.buf_size = strtonum(optarg, 0,
546			    IPC_MSG_SIZE_MAX, &errstr);
547			if (errstr != NULL)
548				errx(EXIT_FAILURE, "option -s: number is %s",
549				    errstr);
550			break;
551		case 't':
552			if (strcmp(optarg, "stream") == 0)
553				stream_flag = true;
554			else if (strcmp(optarg, "dgram") == 0)
555				dgram_flag = true;
556			else
557				errx(EXIT_FAILURE, "option -t: "
558				    "wrong socket type");
559			break;
560		case 'z':
561			zvalue = strtonum(optarg, 0, 3, &errstr);
562			if (errstr != NULL)
563				errx(EXIT_FAILURE, "option -z: number is %s",
564				    errstr);
565			if (zvalue & 0x1)
566				send_data_flag = false;
567			if (zvalue & 0x2)
568				send_array_flag = false;
569			break;
570		default:
571			usage(false);
572			return (EXIT_FAILURE);
573		}
574
575	if (optind < argc) {
576		if (optind + 1 != argc)
577			errx(EXIT_FAILURE, "too many arguments");
578		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
579		if (errstr != NULL)
580			errx(EXIT_FAILURE, "test number is %s", errstr);
581		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
582			errx(EXIT_FAILURE, "given test %u for stream "
583			    "sockets does not exist", testno);
584		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
585			errx(EXIT_FAILURE, "given test %u for datagram "
586			    "sockets does not exist", testno);
587	} else
588		testno = 0;
589
590	if (!dgram_flag && !stream_flag) {
591		if (testno != 0)
592			errx(EXIT_FAILURE, "particular test number "
593			    "can be used with the -t option only");
594		dgram_flag = stream_flag = true;
595	}
596
597	if (mkdtemp(work_dir) == NULL)
598		err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
599
600	rv = EXIT_FAILURE;
601	if (init() < 0)
602		goto done;
603
604	if (stream_flag)
605		if (run_tests(SOCK_STREAM, testno) < 0)
606			goto done;
607	if (dgram_flag)
608		if (run_tests(SOCK_DGRAM, testno) < 0)
609			goto done;
610
611	rv = EXIT_SUCCESS;
612done:
613	if (rmdir(work_dir) < 0) {
614		logmsg("rmdir(%s)", work_dir);
615		rv = EXIT_FAILURE;
616	}
617	return (failed_flag ? EXIT_FAILURE : rv);
618}
619
620static int
621socket_close(int fd)
622{
623	int rv;
624
625	rv = 0;
626	if (close(fd) < 0) {
627		logmsg("socket_close: close");
628		rv = -1;
629	}
630	if (server_flag && fd == serv_sock_fd)
631		if (unlink(serv_addr_sun.sun_path) < 0) {
632			logmsg("socket_close: unlink(%s)",
633			    serv_addr_sun.sun_path);
634			rv = -1;
635		}
636	return (rv);
637}
638
639static int
640socket_create(void)
641{
642	struct timeval tv;
643	int fd;
644
645	fd = socket(PF_LOCAL, sock_type, 0);
646	if (fd < 0) {
647		logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str);
648		return (-1);
649	}
650	if (server_flag)
651		serv_sock_fd = fd;
652
653	tv.tv_sec = TIMEOUT;
654	tv.tv_usec = 0;
655	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
656	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
657		logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
658		goto failed;
659	}
660
661	if (server_flag) {
662		if (bind(fd, (struct sockaddr *)&serv_addr_sun,
663		    serv_addr_sun.sun_len) < 0) {
664			logmsg("socket_create: bind(%s)",
665			    serv_addr_sun.sun_path);
666			goto failed;
667		}
668		if (sock_type == SOCK_STREAM) {
669			int val;
670
671			if (listen(fd, LISTENQ) < 0) {
672				logmsg("socket_create: listen");
673				goto failed;
674			}
675			val = fcntl(fd, F_GETFL, 0);
676			if (val < 0) {
677				logmsg("socket_create: fcntl(F_GETFL)");
678				goto failed;
679			}
680			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
681				logmsg("socket_create: fcntl(F_SETFL)");
682				goto failed;
683			}
684		}
685	}
686
687	return (fd);
688
689failed:
690	if (close(fd) < 0)
691		logmsg("socket_create: close");
692	if (server_flag)
693		if (unlink(serv_addr_sun.sun_path) < 0)
694			logmsg("socket_close: unlink(%s)",
695			    serv_addr_sun.sun_path);
696	return (-1);
697}
698
699static int
700socket_connect(int fd)
701{
702	dbgmsg("connect");
703
704	if (connect(fd, (struct sockaddr *)&serv_addr_sun,
705	    serv_addr_sun.sun_len) < 0) {
706		logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path);
707		return (-1);
708	}
709	return (0);
710}
711
712static int
713sync_recv(void)
714{
715	ssize_t ssize;
716	int fd;
717	char buf;
718
719	dbgmsg("sync: wait");
720
721	fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
722
723	ssize = read(fd, &buf, 1);
724	if (ssize < 0) {
725		logmsg("sync_recv: read");
726		return (-1);
727	}
728	if (ssize < 1) {
729		logmsgx("sync_recv: read %zd of 1 byte", ssize);
730		return (-1);
731	}
732
733	dbgmsg("sync: received");
734
735	return (0);
736}
737
738static int
739sync_send(void)
740{
741	ssize_t ssize;
742	int fd;
743
744	dbgmsg("sync: send");
745
746	fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
747
748	ssize = write(fd, "", 1);
749	if (ssize < 0) {
750		logmsg("sync_send: write");
751		return (-1);
752	}
753	if (ssize < 1) {
754		logmsgx("sync_send: sent %zd of 1 byte", ssize);
755		return (-1);
756	}
757
758	return (0);
759}
760
761static int
762message_send(int fd, const struct msghdr *msghdr)
763{
764	const struct cmsghdr *cmsghdr;
765	size_t size;
766	ssize_t ssize;
767
768	size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
769	dbgmsg("send: data size %zu", size);
770	dbgmsg("send: msghdr.msg_controllen %u",
771	    (u_int)msghdr->msg_controllen);
772	cmsghdr = CMSG_FIRSTHDR(msghdr);
773	if (cmsghdr != NULL)
774		dbgmsg("send: cmsghdr.cmsg_len %u",
775		    (u_int)cmsghdr->cmsg_len);
776
777	ssize = sendmsg(fd, msghdr, 0);
778	if (ssize < 0) {
779		logmsg("message_send: sendmsg");
780		return (-1);
781	}
782	if ((size_t)ssize != size) {
783		logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
784		    ssize, size);
785		return (-1);
786	}
787
788	if (!send_data_flag)
789		if (sync_send() < 0)
790			return (-1);
791
792	return (0);
793}
794
795static int
796message_sendn(int fd, struct msghdr *msghdr)
797{
798	u_int i;
799
800	for (i = 1; i <= ipc_msg.msg_num; ++i) {
801		dbgmsg("message #%u", i);
802		if (message_send(fd, msghdr) < 0)
803			return (-1);
804	}
805	return (0);
806}
807
808static int
809message_recv(int fd, struct msghdr *msghdr)
810{
811	const struct cmsghdr *cmsghdr;
812	size_t size;
813	ssize_t ssize;
814
815	if (!send_data_flag)
816		if (sync_recv() < 0)
817			return (-1);
818
819	size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
820	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
821	if (ssize < 0) {
822		logmsg("message_recv: recvmsg");
823		return (-1);
824	}
825	if ((size_t)ssize != size) {
826		logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
827		    ssize, size);
828		return (-1);
829	}
830
831	dbgmsg("recv: data size %zd", ssize);
832	dbgmsg("recv: msghdr.msg_controllen %u",
833	    (u_int)msghdr->msg_controllen);
834	cmsghdr = CMSG_FIRSTHDR(msghdr);
835	if (cmsghdr != NULL)
836		dbgmsg("recv: cmsghdr.cmsg_len %u",
837		    (u_int)cmsghdr->cmsg_len);
838
839	if (memcmp(ipc_msg.buf_recv, ipc_msg.buf_send, size) != 0) {
840		logmsgx("message_recv: received message has wrong content");
841		return (-1);
842	}
843
844	return (0);
845}
846
847static int
848socket_accept(int listenfd)
849{
850	fd_set rset;
851	struct timeval tv;
852	int fd, rv, val;
853
854	dbgmsg("accept");
855
856	FD_ZERO(&rset);
857	FD_SET(listenfd, &rset);
858	tv.tv_sec = TIMEOUT;
859	tv.tv_usec = 0;
860	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
861	if (rv < 0) {
862		logmsg("socket_accept: select");
863		return (-1);
864	}
865	if (rv == 0) {
866		logmsgx("socket_accept: select timeout");
867		return (-1);
868	}
869
870	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
871	if (fd < 0) {
872		logmsg("socket_accept: accept");
873		return (-1);
874	}
875
876	val = fcntl(fd, F_GETFL, 0);
877	if (val < 0) {
878		logmsg("socket_accept: fcntl(F_GETFL)");
879		goto failed;
880	}
881	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
882		logmsg("socket_accept: fcntl(F_SETFL)");
883		goto failed;
884	}
885
886	return (fd);
887
888failed:
889	if (close(fd) < 0)
890		logmsg("socket_accept: close");
891	return (-1);
892}
893
894static int
895check_msghdr(const struct msghdr *msghdr, size_t size)
896{
897	if (msghdr->msg_flags & MSG_TRUNC) {
898		logmsgx("msghdr.msg_flags has MSG_TRUNC");
899		return (-1);
900	}
901	if (msghdr->msg_flags & MSG_CTRUNC) {
902		logmsgx("msghdr.msg_flags has MSG_CTRUNC");
903		return (-1);
904	}
905	if (msghdr->msg_controllen < size) {
906		logmsgx("msghdr.msg_controllen %u < %zu",
907		    (u_int)msghdr->msg_controllen, size);
908		return (-1);
909	}
910	if (msghdr->msg_controllen > 0 && size == 0) {
911		logmsgx("msghdr.msg_controllen %u > 0",
912		    (u_int)msghdr->msg_controllen);
913		return (-1);
914	}
915	return (0);
916}
917
918static int
919check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
920{
921	if (cmsghdr == NULL) {
922		logmsgx("cmsghdr is NULL");
923		return (-1);
924	}
925	if (cmsghdr->cmsg_level != SOL_SOCKET) {
926		logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
927		    cmsghdr->cmsg_level);
928		return (-1);
929	}
930	if (cmsghdr->cmsg_type != type) {
931		logmsgx("cmsghdr.cmsg_type %d != %d",
932		    cmsghdr->cmsg_type, type);
933		return (-1);
934	}
935	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
936		logmsgx("cmsghdr.cmsg_len %u != %zu",
937		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
938		return (-1);
939	}
940	return (0);
941}
942
943static int
944check_groups(const char *gid_arr_str, const gid_t *gid_arr,
945    const char *gid_num_str, int gid_num, bool all_gids)
946{
947	int i;
948
949	for (i = 0; i < gid_num; ++i)
950		dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
951
952	if (all_gids) {
953		if (gid_num != proc_cred.gid_num) {
954			logmsgx("%s %d != %d", gid_num_str, gid_num,
955			    proc_cred.gid_num);
956			return (-1);
957		}
958	} else {
959		if (gid_num > proc_cred.gid_num) {
960			logmsgx("%s %d > %d", gid_num_str, gid_num,
961			    proc_cred.gid_num);
962			return (-1);
963		}
964	}
965	if (memcmp(gid_arr, proc_cred.gid_arr,
966	    gid_num * sizeof(*gid_arr)) != 0) {
967		logmsgx("%s content is wrong", gid_arr_str);
968		for (i = 0; i < gid_num; ++i)
969			if (gid_arr[i] != proc_cred.gid_arr[i]) {
970				logmsgx("%s[%d] %lu != %lu",
971				    gid_arr_str, i, (u_long)gid_arr[i],
972				    (u_long)proc_cred.gid_arr[i]);
973				break;
974			}
975		return (-1);
976	}
977	return (0);
978}
979
980static int
981check_xucred(const struct xucred *xucred, socklen_t len)
982{
983	if (len != sizeof(*xucred)) {
984		logmsgx("option value size %zu != %zu",
985		    (size_t)len, sizeof(*xucred));
986		return (-1);
987	}
988
989	dbgmsg("xucred.cr_version %u", xucred->cr_version);
990	dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
991	dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
992
993	if (xucred->cr_version != XUCRED_VERSION) {
994		logmsgx("xucred.cr_version %u != %d",
995		    xucred->cr_version, XUCRED_VERSION);
996		return (-1);
997	}
998	if (xucred->cr_uid != proc_cred.euid) {
999		logmsgx("xucred.cr_uid %lu != %lu (EUID)",
1000		   (u_long)xucred->cr_uid, (u_long)proc_cred.euid);
1001		return (-1);
1002	}
1003	if (xucred->cr_ngroups == 0) {
1004		logmsgx("xucred.cr_ngroups == 0");
1005		return (-1);
1006	}
1007	if (xucred->cr_ngroups < 0) {
1008		logmsgx("xucred.cr_ngroups < 0");
1009		return (-1);
1010	}
1011	if (xucred->cr_ngroups > XU_NGROUPS) {
1012		logmsgx("xucred.cr_ngroups %hu > %u (max)",
1013		    xucred->cr_ngroups, XU_NGROUPS);
1014		return (-1);
1015	}
1016	if (xucred->cr_groups[0] != proc_cred.egid) {
1017		logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
1018		    (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid);
1019		return (-1);
1020	}
1021	if (check_groups("xucred.cr_groups", xucred->cr_groups,
1022	    "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
1023		return (-1);
1024	return (0);
1025}
1026
1027static int
1028check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
1029{
1030	const struct cmsgcred *cmsgcred;
1031
1032	if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0)
1033		return (-1);
1034
1035	cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
1036
1037	dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid);
1038	dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid);
1039	dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid);
1040	dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid);
1041	dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups);
1042
1043	if (cmsgcred->cmcred_pid != client_pid) {
1044		logmsgx("cmsgcred.cmcred_pid %ld != %ld",
1045		    (long)cmsgcred->cmcred_pid, (long)client_pid);
1046		return (-1);
1047	}
1048	if (cmsgcred->cmcred_uid != proc_cred.uid) {
1049		logmsgx("cmsgcred.cmcred_uid %lu != %lu",
1050		    (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid);
1051		return (-1);
1052	}
1053	if (cmsgcred->cmcred_euid != proc_cred.euid) {
1054		logmsgx("cmsgcred.cmcred_euid %lu != %lu",
1055		    (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid);
1056		return (-1);
1057	}
1058	if (cmsgcred->cmcred_gid != proc_cred.gid) {
1059		logmsgx("cmsgcred.cmcred_gid %lu != %lu",
1060		    (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid);
1061		return (-1);
1062	}
1063	if (cmsgcred->cmcred_ngroups == 0) {
1064		logmsgx("cmsgcred.cmcred_ngroups == 0");
1065		return (-1);
1066	}
1067	if (cmsgcred->cmcred_ngroups < 0) {
1068		logmsgx("cmsgcred.cmcred_ngroups %d < 0",
1069		    cmsgcred->cmcred_ngroups);
1070		return (-1);
1071	}
1072	if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) {
1073		logmsgx("cmsgcred.cmcred_ngroups %d > %d",
1074		    cmsgcred->cmcred_ngroups, CMGROUP_MAX);
1075		return (-1);
1076	}
1077	if (cmsgcred->cmcred_groups[0] != proc_cred.egid) {
1078		logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
1079		    (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid);
1080		return (-1);
1081	}
1082	if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups,
1083	    "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0)
1084		return (-1);
1085	return (0);
1086}
1087
1088static int
1089check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
1090{
1091	const struct sockcred *sockcred;
1092
1093	if (check_cmsghdr(cmsghdr, SCM_CREDS,
1094	    SOCKCREDSIZE(proc_cred.gid_num)) < 0)
1095		return (-1);
1096
1097	sockcred = (struct sockcred *)CMSG_DATA(cmsghdr);
1098
1099	dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid);
1100	dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid);
1101	dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid);
1102	dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid);
1103	dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups);
1104
1105	if (sockcred->sc_uid != proc_cred.uid) {
1106		logmsgx("sockcred.sc_uid %lu != %lu",
1107		    (u_long)sockcred->sc_uid, (u_long)proc_cred.uid);
1108		return (-1);
1109	}
1110	if (sockcred->sc_euid != proc_cred.euid) {
1111		logmsgx("sockcred.sc_euid %lu != %lu",
1112		    (u_long)sockcred->sc_euid, (u_long)proc_cred.euid);
1113		return (-1);
1114	}
1115	if (sockcred->sc_gid != proc_cred.gid) {
1116		logmsgx("sockcred.sc_gid %lu != %lu",
1117		    (u_long)sockcred->sc_gid, (u_long)proc_cred.gid);
1118		return (-1);
1119	}
1120	if (sockcred->sc_egid != proc_cred.egid) {
1121		logmsgx("sockcred.sc_egid %lu != %lu",
1122		    (u_long)sockcred->sc_egid, (u_long)proc_cred.egid);
1123		return (-1);
1124	}
1125	if (sockcred->sc_ngroups == 0) {
1126		logmsgx("sockcred.sc_ngroups == 0");
1127		return (-1);
1128	}
1129	if (sockcred->sc_ngroups < 0) {
1130		logmsgx("sockcred.sc_ngroups %d < 0",
1131		    sockcred->sc_ngroups);
1132		return (-1);
1133	}
1134	if (sockcred->sc_ngroups != proc_cred.gid_num) {
1135		logmsgx("sockcred.sc_ngroups %d != %u",
1136		    sockcred->sc_ngroups, proc_cred.gid_num);
1137		return (-1);
1138	}
1139	if (check_groups("sockcred.sc_groups", sockcred->sc_groups,
1140	    "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0)
1141		return (-1);
1142	return (0);
1143}
1144
1145static int
1146check_scm_timestamp(struct cmsghdr *cmsghdr)
1147{
1148	const struct timeval *timeval;
1149
1150	if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0)
1151		return (-1);
1152
1153	timeval = (struct timeval *)CMSG_DATA(cmsghdr);
1154
1155	dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX,
1156	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec);
1157
1158	return (0);
1159}
1160
1161static int
1162check_scm_bintime(struct cmsghdr *cmsghdr)
1163{
1164	const struct bintime *bintime;
1165
1166	if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0)
1167		return (-1);
1168
1169	bintime = (struct bintime *)CMSG_DATA(cmsghdr);
1170
1171	dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64,
1172	    (intmax_t)bintime->sec, bintime->frac);
1173
1174	return (0);
1175}
1176
1177static void
1178msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
1179{
1180	msghdr->msg_name = NULL;
1181	msghdr->msg_namelen = 0;
1182	if (send_data_flag) {
1183		iov->iov_base = server_flag ?
1184		    ipc_msg.buf_recv : ipc_msg.buf_send;
1185		iov->iov_len = ipc_msg.buf_size;
1186		msghdr->msg_iov = iov;
1187		msghdr->msg_iovlen = 1;
1188	} else {
1189		msghdr->msg_iov = NULL;
1190		msghdr->msg_iovlen = 0;
1191	}
1192	msghdr->msg_control = cmsg_data;
1193	msghdr->msg_flags = 0;
1194}
1195
1196static void
1197msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
1198    void *cmsg_data, size_t cmsg_size)
1199{
1200	msghdr_init_generic(msghdr, iov, cmsg_data);
1201	msghdr->msg_controllen = cmsg_size;
1202	dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
1203	    msghdr->msg_iov->iov_len : (size_t)0);
1204	dbgmsg("init: msghdr.msg_controllen %u",
1205	    (u_int)msghdr->msg_controllen);
1206}
1207
1208static void
1209msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
1210    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
1211{
1212	struct cmsghdr *cmsghdr;
1213
1214	msghdr_init_generic(msghdr, iov, cmsg_data);
1215	if (cmsg_data != NULL) {
1216		msghdr->msg_controllen = send_array_flag ?
1217		    cmsg_size : CMSG_SPACE(0);
1218		cmsghdr = CMSG_FIRSTHDR(msghdr);
1219		cmsghdr->cmsg_level = SOL_SOCKET;
1220		cmsghdr->cmsg_type = type;
1221		cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0);
1222	} else
1223		msghdr->msg_controllen = 0;
1224}
1225
1226static int
1227t_generic(int (*client_func)(int), int (*server_func)(int))
1228{
1229	int fd, rv, rv_client;
1230
1231	switch (client_fork()) {
1232	case 0:
1233		fd = socket_create();
1234		if (fd < 0)
1235			rv = -2;
1236		else {
1237			rv = client_func(fd);
1238			if (socket_close(fd) < 0)
1239				rv = -2;
1240		}
1241		client_exit(rv);
1242		break;
1243	case 1:
1244		fd = socket_create();
1245		if (fd < 0)
1246			rv = -2;
1247		else {
1248			rv = server_func(fd);
1249			rv_client = client_wait();
1250			if (rv == 0 || (rv == -2 && rv_client != 0))
1251				rv = rv_client;
1252			if (socket_close(fd) < 0)
1253				rv = -2;
1254		}
1255		break;
1256	default:
1257		rv = -2;
1258	}
1259	return (rv);
1260}
1261
1262static int
1263t_cmsgcred_client(int fd)
1264{
1265	struct msghdr msghdr;
1266	struct iovec iov[1];
1267	void *cmsg_data;
1268	size_t cmsg_size;
1269	int rv;
1270
1271	if (sync_recv() < 0)
1272		return (-2);
1273
1274	rv = -2;
1275
1276	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
1277	cmsg_data = malloc(cmsg_size);
1278	if (cmsg_data == NULL) {
1279		logmsg("malloc");
1280		goto done;
1281	}
1282	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1283	    SCM_CREDS, sizeof(struct cmsgcred));
1284
1285	if (socket_connect(fd) < 0)
1286		goto done;
1287
1288	if (message_sendn(fd, &msghdr) < 0)
1289		goto done;
1290
1291	rv = 0;
1292done:
1293	free(cmsg_data);
1294	return (rv);
1295}
1296
1297static int
1298t_cmsgcred_server(int fd1)
1299{
1300	struct msghdr msghdr;
1301	struct iovec iov[1];
1302	struct cmsghdr *cmsghdr;
1303	void *cmsg_data;
1304	size_t cmsg_size;
1305	u_int i;
1306	int fd2, rv;
1307
1308	if (sync_send() < 0)
1309		return (-2);
1310
1311	fd2 = -1;
1312	rv = -2;
1313
1314	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
1315	cmsg_data = malloc(cmsg_size);
1316	if (cmsg_data == NULL) {
1317		logmsg("malloc");
1318		goto done;
1319	}
1320
1321	if (sock_type == SOCK_STREAM) {
1322		fd2 = socket_accept(fd1);
1323		if (fd2 < 0)
1324			goto done;
1325	} else
1326		fd2 = fd1;
1327
1328	rv = -1;
1329	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1330		dbgmsg("message #%u", i);
1331
1332		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1333		if (message_recv(fd2, &msghdr) < 0) {
1334			rv = -2;
1335			break;
1336		}
1337
1338		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1339			break;
1340
1341		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1342		if (check_scm_creds_cmsgcred(cmsghdr) < 0)
1343			break;
1344	}
1345	if (i > ipc_msg.msg_num)
1346		rv = 0;
1347done:
1348	free(cmsg_data);
1349	if (sock_type == SOCK_STREAM && fd2 >= 0)
1350		if (socket_close(fd2) < 0)
1351			rv = -2;
1352	return (rv);
1353}
1354
1355static int
1356t_cmsgcred(void)
1357{
1358	return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
1359}
1360
1361static int
1362t_sockcred_client(int type, int fd)
1363{
1364	struct msghdr msghdr;
1365	struct iovec iov[1];
1366	int rv;
1367
1368	if (sync_recv() < 0)
1369		return (-2);
1370
1371	rv = -2;
1372
1373	msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
1374
1375	if (socket_connect(fd) < 0)
1376		goto done;
1377
1378	if (type == 2)
1379		if (sync_recv() < 0)
1380			goto done;
1381
1382	if (message_sendn(fd, &msghdr) < 0)
1383		goto done;
1384
1385	rv = 0;
1386done:
1387	return (rv);
1388}
1389
1390static int
1391t_sockcred_server(int type, int fd1)
1392{
1393	struct msghdr msghdr;
1394	struct iovec iov[1];
1395	struct cmsghdr *cmsghdr;
1396	void *cmsg_data;
1397	size_t cmsg_size;
1398	u_int i;
1399	int fd2, rv, val;
1400
1401	fd2 = -1;
1402	rv = -2;
1403
1404	cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
1405	cmsg_data = malloc(cmsg_size);
1406	if (cmsg_data == NULL) {
1407		logmsg("malloc");
1408		goto done;
1409	}
1410
1411	if (type == 1) {
1412		dbgmsg("setting LOCAL_CREDS");
1413		val = 1;
1414		if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
1415			logmsg("setsockopt(LOCAL_CREDS)");
1416			goto done;
1417		}
1418	}
1419
1420	if (sync_send() < 0)
1421		goto done;
1422
1423	if (sock_type == SOCK_STREAM) {
1424		fd2 = socket_accept(fd1);
1425		if (fd2 < 0)
1426			goto done;
1427	} else
1428		fd2 = fd1;
1429
1430	if (type == 2) {
1431		dbgmsg("setting LOCAL_CREDS");
1432		val = 1;
1433		if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
1434			logmsg("setsockopt(LOCAL_CREDS)");
1435			goto done;
1436		}
1437		if (sync_send() < 0)
1438			goto done;
1439	}
1440
1441	rv = -1;
1442	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1443		dbgmsg("message #%u", i);
1444
1445		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1446		if (message_recv(fd2, &msghdr) < 0) {
1447			rv = -2;
1448			break;
1449		}
1450
1451		if (i > 1 && sock_type == SOCK_STREAM) {
1452			if (check_msghdr(&msghdr, 0) < 0)
1453				break;
1454		} else {
1455			if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1456				break;
1457
1458			cmsghdr = CMSG_FIRSTHDR(&msghdr);
1459			if (check_scm_creds_sockcred(cmsghdr) < 0)
1460				break;
1461		}
1462	}
1463	if (i > ipc_msg.msg_num)
1464		rv = 0;
1465done:
1466	free(cmsg_data);
1467	if (sock_type == SOCK_STREAM && fd2 >= 0)
1468		if (socket_close(fd2) < 0)
1469			rv = -2;
1470	return (rv);
1471}
1472
1473static int
1474t_sockcred_1(void)
1475{
1476	u_int i;
1477	int fd, rv, rv_client;
1478
1479	switch (client_fork()) {
1480	case 0:
1481		for (i = 1; i <= 2; ++i) {
1482			dbgmsg("client #%u", i);
1483			fd = socket_create();
1484			if (fd < 0)
1485				rv = -2;
1486			else {
1487				rv = t_sockcred_client(1, fd);
1488				if (socket_close(fd) < 0)
1489					rv = -2;
1490			}
1491			if (rv != 0)
1492				break;
1493		}
1494		client_exit(rv);
1495		break;
1496	case 1:
1497		fd = socket_create();
1498		if (fd < 0)
1499			rv = -2;
1500		else {
1501			rv = t_sockcred_server(1, fd);
1502			if (rv == 0)
1503				rv = t_sockcred_server(3, fd);
1504			rv_client = client_wait();
1505			if (rv == 0 || (rv == -2 && rv_client != 0))
1506				rv = rv_client;
1507			if (socket_close(fd) < 0)
1508				rv = -2;
1509		}
1510		break;
1511	default:
1512		rv = -2;
1513	}
1514
1515	return (rv);
1516}
1517
1518static int
1519t_sockcred_2_client(int fd)
1520{
1521	return (t_sockcred_client(2, fd));
1522}
1523
1524static int
1525t_sockcred_2_server(int fd)
1526{
1527	return (t_sockcred_server(2, fd));
1528}
1529
1530static int
1531t_sockcred_2(void)
1532{
1533	return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
1534}
1535
1536static int
1537t_cmsgcred_sockcred_server(int fd1)
1538{
1539	struct msghdr msghdr;
1540	struct iovec iov[1];
1541	struct cmsghdr *cmsghdr;
1542	void *cmsg_data, *cmsg1_data, *cmsg2_data;
1543	size_t cmsg_size, cmsg1_size, cmsg2_size;
1544	u_int i;
1545	int fd2, rv, val;
1546
1547	fd2 = -1;
1548	rv = -2;
1549
1550	cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
1551	cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred));
1552	cmsg1_data = malloc(cmsg1_size);
1553	cmsg2_data = malloc(cmsg2_size);
1554	if (cmsg1_data == NULL || cmsg2_data == NULL) {
1555		logmsg("malloc");
1556		goto done;
1557	}
1558
1559	dbgmsg("setting LOCAL_CREDS");
1560	val = 1;
1561	if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
1562		logmsg("setsockopt(LOCAL_CREDS)");
1563		goto done;
1564	}
1565
1566	if (sync_send() < 0)
1567		goto done;
1568
1569	if (sock_type == SOCK_STREAM) {
1570		fd2 = socket_accept(fd1);
1571		if (fd2 < 0)
1572			goto done;
1573	} else
1574		fd2 = fd1;
1575
1576	cmsg_data = cmsg1_data;
1577	cmsg_size = cmsg1_size;
1578	rv = -1;
1579	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1580		dbgmsg("message #%u", i);
1581
1582		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1583		if (message_recv(fd2, &msghdr) < 0) {
1584			rv = -2;
1585			break;
1586		}
1587
1588		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1589			break;
1590
1591		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1592		if (i == 1 || sock_type == SOCK_DGRAM) {
1593			if (check_scm_creds_sockcred(cmsghdr) < 0)
1594				break;
1595		} else {
1596			if (check_scm_creds_cmsgcred(cmsghdr) < 0)
1597				break;
1598		}
1599
1600		cmsg_data = cmsg2_data;
1601		cmsg_size = cmsg2_size;
1602	}
1603	if (i > ipc_msg.msg_num)
1604		rv = 0;
1605done:
1606	free(cmsg1_data);
1607	free(cmsg2_data);
1608	if (sock_type == SOCK_STREAM && fd2 >= 0)
1609		if (socket_close(fd2) < 0)
1610			rv = -2;
1611	return (rv);
1612}
1613
1614static int
1615t_cmsgcred_sockcred(void)
1616{
1617	return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
1618}
1619
1620static int
1621t_timeval_client(int fd)
1622{
1623	struct msghdr msghdr;
1624	struct iovec iov[1];
1625	void *cmsg_data;
1626	size_t cmsg_size;
1627	int rv;
1628
1629	if (sync_recv() < 0)
1630		return (-2);
1631
1632	rv = -2;
1633
1634	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
1635	cmsg_data = malloc(cmsg_size);
1636	if (cmsg_data == NULL) {
1637		logmsg("malloc");
1638		goto done;
1639	}
1640	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1641	    SCM_TIMESTAMP, sizeof(struct timeval));
1642
1643	if (socket_connect(fd) < 0)
1644		goto done;
1645
1646	if (message_sendn(fd, &msghdr) < 0)
1647		goto done;
1648
1649	rv = 0;
1650done:
1651	free(cmsg_data);
1652	return (rv);
1653}
1654
1655static int
1656t_timeval_server(int fd1)
1657{
1658	struct msghdr msghdr;
1659	struct iovec iov[1];
1660	struct cmsghdr *cmsghdr;
1661	void *cmsg_data;
1662	size_t cmsg_size;
1663	u_int i;
1664	int fd2, rv;
1665
1666	if (sync_send() < 0)
1667		return (-2);
1668
1669	fd2 = -1;
1670	rv = -2;
1671
1672	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
1673	cmsg_data = malloc(cmsg_size);
1674	if (cmsg_data == NULL) {
1675		logmsg("malloc");
1676		goto done;
1677	}
1678
1679	if (sock_type == SOCK_STREAM) {
1680		fd2 = socket_accept(fd1);
1681		if (fd2 < 0)
1682			goto done;
1683	} else
1684		fd2 = fd1;
1685
1686	rv = -1;
1687	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1688		dbgmsg("message #%u", i);
1689
1690		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1691		if (message_recv(fd2, &msghdr) < 0) {
1692			rv = -2;
1693			break;
1694		}
1695
1696		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1697			break;
1698
1699		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1700		if (check_scm_timestamp(cmsghdr) < 0)
1701			break;
1702	}
1703	if (i > ipc_msg.msg_num)
1704		rv = 0;
1705done:
1706	free(cmsg_data);
1707	if (sock_type == SOCK_STREAM && fd2 >= 0)
1708		if (socket_close(fd2) < 0)
1709			rv = -2;
1710	return (rv);
1711}
1712
1713static int
1714t_timeval(void)
1715{
1716	return (t_generic(t_timeval_client, t_timeval_server));
1717}
1718
1719static int
1720t_bintime_client(int fd)
1721{
1722	struct msghdr msghdr;
1723	struct iovec iov[1];
1724	void *cmsg_data;
1725	size_t cmsg_size;
1726	int rv;
1727
1728	if (sync_recv() < 0)
1729		return (-2);
1730
1731	rv = -2;
1732
1733	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
1734	cmsg_data = malloc(cmsg_size);
1735	if (cmsg_data == NULL) {
1736		logmsg("malloc");
1737		goto done;
1738	}
1739	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1740	    SCM_BINTIME, sizeof(struct bintime));
1741
1742	if (socket_connect(fd) < 0)
1743		goto done;
1744
1745	if (message_sendn(fd, &msghdr) < 0)
1746		goto done;
1747
1748	rv = 0;
1749done:
1750	free(cmsg_data);
1751	return (rv);
1752}
1753
1754static int
1755t_bintime_server(int fd1)
1756{
1757	struct msghdr msghdr;
1758	struct iovec iov[1];
1759	struct cmsghdr *cmsghdr;
1760	void *cmsg_data;
1761	size_t cmsg_size;
1762	u_int i;
1763	int fd2, rv;
1764
1765	if (sync_send() < 0)
1766		return (-2);
1767
1768	fd2 = -1;
1769	rv = -2;
1770
1771	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
1772	cmsg_data = malloc(cmsg_size);
1773	if (cmsg_data == NULL) {
1774		logmsg("malloc");
1775		goto done;
1776	}
1777
1778	if (sock_type == SOCK_STREAM) {
1779		fd2 = socket_accept(fd1);
1780		if (fd2 < 0)
1781			goto done;
1782	} else
1783		fd2 = fd1;
1784
1785	rv = -1;
1786	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1787		dbgmsg("message #%u", i);
1788
1789		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1790		if (message_recv(fd2, &msghdr) < 0) {
1791			rv = -2;
1792			break;
1793		}
1794
1795		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1796			break;
1797
1798		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1799		if (check_scm_bintime(cmsghdr) < 0)
1800			break;
1801	}
1802	if (i > ipc_msg.msg_num)
1803		rv = 0;
1804done:
1805	free(cmsg_data);
1806	if (sock_type == SOCK_STREAM && fd2 >= 0)
1807		if (socket_close(fd2) < 0)
1808			rv = -2;
1809	return (rv);
1810}
1811
1812static int
1813t_bintime(void)
1814{
1815	return (t_generic(t_bintime_client, t_bintime_server));
1816}
1817
1818static int
1819t_cmsg_len_client(int fd)
1820{
1821	struct msghdr msghdr;
1822	struct iovec iov[1];
1823	struct cmsghdr *cmsghdr;
1824	void *cmsg_data;
1825	size_t size, cmsg_size;
1826	socklen_t socklen;
1827	int rv;
1828
1829	if (sync_recv() < 0)
1830		return (-2);
1831
1832	rv = -2;
1833
1834	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
1835	cmsg_data = malloc(cmsg_size);
1836	if (cmsg_data == NULL) {
1837		logmsg("malloc");
1838		goto done;
1839	}
1840	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1841	    SCM_CREDS, sizeof(struct cmsgcred));
1842	cmsghdr = CMSG_FIRSTHDR(&msghdr);
1843
1844	if (socket_connect(fd) < 0)
1845		goto done;
1846
1847	size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0;
1848	rv = -1;
1849	for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
1850		cmsghdr->cmsg_len = socklen;
1851		dbgmsg("send: data size %zu", size);
1852		dbgmsg("send: msghdr.msg_controllen %u",
1853		    (u_int)msghdr.msg_controllen);
1854		dbgmsg("send: cmsghdr.cmsg_len %u",
1855		    (u_int)cmsghdr->cmsg_len);
1856		if (sendmsg(fd, &msghdr, 0) < 0)
1857			continue;
1858		logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
1859		    (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
1860		break;
1861	}
1862	if (socklen == CMSG_LEN(0))
1863		rv = 0;
1864
1865	if (sync_send() < 0) {
1866		rv = -2;
1867		goto done;
1868	}
1869done:
1870	free(cmsg_data);
1871	return (rv);
1872}
1873
1874static int
1875t_cmsg_len_server(int fd1)
1876{
1877	int fd2, rv;
1878
1879	if (sync_send() < 0)
1880		return (-2);
1881
1882	rv = -2;
1883
1884	if (sock_type == SOCK_STREAM) {
1885		fd2 = socket_accept(fd1);
1886		if (fd2 < 0)
1887			goto done;
1888	} else
1889		fd2 = fd1;
1890
1891	if (sync_recv() < 0)
1892		goto done;
1893
1894	rv = 0;
1895done:
1896	if (sock_type == SOCK_STREAM && fd2 >= 0)
1897		if (socket_close(fd2) < 0)
1898			rv = -2;
1899	return (rv);
1900}
1901
1902static int
1903t_cmsg_len(void)
1904{
1905	return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
1906}
1907
1908static int
1909t_peercred_client(int fd)
1910{
1911	struct xucred xucred;
1912	socklen_t len;
1913
1914	if (sync_recv() < 0)
1915		return (-1);
1916
1917	if (socket_connect(fd) < 0)
1918		return (-1);
1919
1920	len = sizeof(xucred);
1921	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
1922		logmsg("getsockopt(LOCAL_PEERCRED)");
1923		return (-1);
1924	}
1925
1926	if (check_xucred(&xucred, len) < 0)
1927		return (-1);
1928
1929	return (0);
1930}
1931
1932static int
1933t_peercred_server(int fd1)
1934{
1935	struct xucred xucred;
1936	socklen_t len;
1937	int fd2, rv;
1938
1939	if (sync_send() < 0)
1940		return (-2);
1941
1942	fd2 = socket_accept(fd1);
1943	if (fd2 < 0)
1944		return (-2);
1945
1946	len = sizeof(xucred);
1947	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
1948		logmsg("getsockopt(LOCAL_PEERCRED)");
1949		rv = -2;
1950		goto done;
1951	}
1952
1953	if (check_xucred(&xucred, len) < 0) {
1954		rv = -1;
1955		goto done;
1956	}
1957
1958	rv = 0;
1959done:
1960	if (socket_close(fd2) < 0)
1961		rv = -2;
1962	return (rv);
1963}
1964
1965static int
1966t_peercred(void)
1967{
1968	return (t_generic(t_peercred_client, t_peercred_server));
1969}
1970