1159045Smaxim/*-
2159045Smaxim * Copyright (c) 2005 Andrey Simonenko
3159045Smaxim * All rights reserved.
4159045Smaxim *
5159045Smaxim * Redistribution and use in source and binary forms, with or without
6159045Smaxim * modification, are permitted provided that the following conditions
7159045Smaxim * are met:
8159045Smaxim * 1. Redistributions of source code must retain the above copyright
9159045Smaxim *    notice, this list of conditions and the following disclaimer.
10159045Smaxim * 2. Redistributions in binary form must reproduce the above copyright
11159045Smaxim *    notice, this list of conditions and the following disclaimer in the
12159045Smaxim *    documentation and/or other materials provided with the distribution.
13159045Smaxim *
14159045Smaxim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15159045Smaxim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16159045Smaxim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17159045Smaxim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18159045Smaxim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19159045Smaxim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20159045Smaxim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21159045Smaxim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22159045Smaxim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23159045Smaxim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24159045Smaxim * SUCH DAMAGE.
25159045Smaxim */
26159045Smaxim
27159045Smaxim#include <sys/cdefs.h>
28159045Smaxim__FBSDID("$FreeBSD$");
29159045Smaxim
30246670Spluknet#include <sys/param.h>
31159045Smaxim#include <sys/resource.h>
32159045Smaxim#include <sys/time.h>
33246670Spluknet#include <sys/select.h>
34159045Smaxim#include <sys/socket.h>
35246670Spluknet#include <sys/ucred.h>
36159045Smaxim#include <sys/un.h>
37159045Smaxim#include <sys/wait.h>
38159045Smaxim
39159045Smaxim#include <ctype.h>
40159045Smaxim#include <err.h>
41159045Smaxim#include <errno.h>
42246670Spluknet#include <fcntl.h>
43159045Smaxim#include <inttypes.h>
44159045Smaxim#include <limits.h>
45246670Spluknet#include <paths.h>
46159045Smaxim#include <signal.h>
47159045Smaxim#include <stdarg.h>
48246670Spluknet#include <stdbool.h>
49159045Smaxim#include <stdint.h>
50159045Smaxim#include <stdio.h>
51159045Smaxim#include <stdlib.h>
52159045Smaxim#include <string.h>
53159045Smaxim#include <unistd.h>
54159045Smaxim
55159045Smaxim/*
56159045Smaxim * There are tables with tests descriptions and pointers to test
57159045Smaxim * functions.  Each t_*() function returns 0 if its test passed,
58246670Spluknet * -1 if its test failed, -2 if some system error occurred.
59246670Spluknet * If a test function returns -2, then a program exits.
60159045Smaxim *
61246670Spluknet * If a test function forks a client process, then it waits for its
62246670Spluknet * termination.  If a return code of a client process is not equal
63246670Spluknet * to zero, or if a client process was terminated by a signal, then
64246670Spluknet * a test function returns -1 or -2 depending on exit status of
65246670Spluknet * a client process.
66159045Smaxim *
67246670Spluknet * Each function which can block, is run under TIMEOUT.  If timeout
68246670Spluknet * occurs, then a test function returns -2 or a client process exits
69246670Spluknet * with a non-zero return code.
70159045Smaxim */
71159045Smaxim
72159045Smaxim#ifndef LISTENQ
73159045Smaxim# define LISTENQ	1
74159045Smaxim#endif
75159045Smaxim
76159045Smaxim#ifndef TIMEOUT
77246670Spluknet# define TIMEOUT	2
78159045Smaxim#endif
79159045Smaxim
80246670Spluknetstatic int	t_cmsgcred(void);
81246670Spluknetstatic int	t_sockcred_1(void);
82246670Spluknetstatic int	t_sockcred_2(void);
83246670Spluknetstatic int	t_cmsgcred_sockcred(void);
84246670Spluknetstatic int	t_timeval(void);
85246670Spluknetstatic int	t_bintime(void);
86246670Spluknetstatic int	t_cmsg_len(void);
87246670Spluknetstatic int	t_peercred(void);
88159045Smaxim
89159045Smaximstruct test_func {
90246670Spluknet	int		(*func)(void);
91246670Spluknet	const char	*desc;
92159045Smaxim};
93159045Smaxim
94246670Spluknetstatic const struct test_func test_stream_tbl[] = {
95246670Spluknet	{
96246670Spluknet	  .func = NULL,
97246670Spluknet	  .desc = "All tests"
98246670Spluknet	},
99246670Spluknet	{
100246670Spluknet	  .func = t_cmsgcred,
101246670Spluknet	  .desc = "Sending, receiving cmsgcred"
102246670Spluknet	},
103246670Spluknet	{
104246670Spluknet	  .func = t_sockcred_1,
105246670Spluknet	  .desc = "Receiving sockcred (listening socket)"
106246670Spluknet	},
107246670Spluknet	{
108246670Spluknet	  .func = t_sockcred_2,
109246670Spluknet	  .desc = "Receiving sockcred (accepted socket)"
110246670Spluknet	},
111246670Spluknet	{
112246670Spluknet	  .func = t_cmsgcred_sockcred,
113246670Spluknet	  .desc = "Sending cmsgcred, receiving sockcred"
114246670Spluknet	},
115246670Spluknet	{
116246670Spluknet	  .func = t_timeval,
117246670Spluknet	  .desc = "Sending, receiving timeval"
118246670Spluknet	},
119246670Spluknet	{
120246670Spluknet	  .func = t_bintime,
121246670Spluknet	  .desc = "Sending, receiving bintime"
122246670Spluknet	},
123246670Spluknet	{
124246670Spluknet	  .func = t_cmsg_len,
125246670Spluknet	  .desc = "Check cmsghdr.cmsg_len"
126246670Spluknet	},
127246670Spluknet	{
128246670Spluknet	  .func = t_peercred,
129246670Spluknet	  .desc = "Check LOCAL_PEERCRED socket option"
130246670Spluknet	}
131159045Smaxim};
132159045Smaxim
133246670Spluknet#define TEST_STREAM_TBL_SIZE \
134246670Spluknet	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
135246670Spluknet
136246670Spluknetstatic const struct test_func test_dgram_tbl[] = {
137246670Spluknet	{
138246670Spluknet	  .func = NULL,
139246670Spluknet	  .desc = "All tests"
140246670Spluknet	},
141246670Spluknet	{
142246670Spluknet	  .func = t_cmsgcred,
143246670Spluknet	  .desc = "Sending, receiving cmsgcred"
144246670Spluknet	},
145246670Spluknet	{
146246670Spluknet	  .func = t_sockcred_2,
147246670Spluknet	  .desc = "Receiving sockcred"
148246670Spluknet	},
149246670Spluknet	{
150246670Spluknet	  .func = t_cmsgcred_sockcred,
151246670Spluknet	  .desc = "Sending cmsgcred, receiving sockcred"
152246670Spluknet	},
153246670Spluknet	{
154246670Spluknet	  .func = t_timeval,
155246670Spluknet	  .desc = "Sending, receiving timeval"
156246670Spluknet	},
157246670Spluknet	{
158246670Spluknet	  .func = t_bintime,
159246670Spluknet	  .desc = "Sending, receiving bintime"
160246670Spluknet	},
161246670Spluknet	{
162246670Spluknet	  .func = t_cmsg_len,
163246670Spluknet	  .desc = "Check cmsghdr.cmsg_len"
164246670Spluknet	}
165159045Smaxim};
166159045Smaxim
167246670Spluknet#define TEST_DGRAM_TBL_SIZE \
168246670Spluknet	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
169159045Smaxim
170246670Spluknetstatic bool	debug = false;
171246670Spluknetstatic bool	server_flag = true;
172246670Spluknetstatic bool	send_data_flag = true;
173246670Spluknetstatic bool	send_array_flag = true;
174246670Spluknetstatic bool	failed_flag = false;
175159045Smaxim
176246670Spluknetstatic int	sock_type;
177246670Spluknetstatic const char *sock_type_str;
178159045Smaxim
179246670Spluknetstatic const char *proc_name;
180159045Smaxim
181246670Spluknetstatic char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
182246670Spluknetstatic int	serv_sock_fd;
183246670Spluknetstatic struct sockaddr_un serv_addr_sun;
184159045Smaxim
185246670Spluknetstatic struct {
186246670Spluknet	char		*buf_send;
187246670Spluknet	char		*buf_recv;
188246670Spluknet	size_t		buf_size;
189246670Spluknet	u_int		msg_num;
190246670Spluknet}		ipc_msg;
191159045Smaxim
192246670Spluknet#define IPC_MSG_NUM_DEF		5
193246670Spluknet#define IPC_MSG_NUM_MAX		10
194246670Spluknet#define IPC_MSG_SIZE_DEF	7
195246670Spluknet#define IPC_MSG_SIZE_MAX	128
196159045Smaxim
197246670Spluknetstatic struct {
198246670Spluknet	uid_t		uid;
199246670Spluknet	uid_t		euid;
200246670Spluknet	gid_t		gid;
201246670Spluknet	gid_t		egid;
202246670Spluknet	gid_t		*gid_arr;
203246670Spluknet	int		gid_num;
204246670Spluknet}		proc_cred;
205159045Smaxim
206246670Spluknetstatic pid_t	client_pid;
207159045Smaxim
208246670Spluknet#define SYNC_SERVER	0
209246670Spluknet#define SYNC_CLIENT	1
210246670Spluknet#define SYNC_RECV	0
211246670Spluknet#define SYNC_SEND	1
212159045Smaxim
213246670Spluknetstatic int	sync_fd[2][2];
214159045Smaxim
215246670Spluknet#define LOGMSG_SIZE	128
216159045Smaxim
217159045Smaximstatic void	logmsg(const char *, ...) __printflike(1, 2);
218159045Smaximstatic void	logmsgx(const char *, ...) __printflike(1, 2);
219246670Spluknetstatic void	dbgmsg(const char *, ...) __printflike(1, 2);
220159045Smaximstatic void	output(const char *, ...) __printflike(1, 2);
221159045Smaxim
222159045Smaximstatic void
223246670Spluknetusage(bool verbose)
224159045Smaxim{
225246670Spluknet	u_int i;
226159045Smaxim
227246670Spluknet	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
228246670Spluknet	    "[-z value] [testno]\n", getprogname());
229246670Spluknet	if (!verbose)
230159095Smaxim		return;
231246670Spluknet	printf("\n Options are:\n\
232246670Spluknet  -d            Output debugging information\n\
233246670Spluknet  -h            Output the help message and exit\n\
234246670Spluknet  -n num        Number of messages to send\n\
235246670Spluknet  -s size       Specify size of data for IPC\n\
236246670Spluknet  -t type       Specify socket type (stream, dgram) for tests\n\
237246670Spluknet  -z value      Do not send data in a message (bit 0x1), do not send\n\
238246670Spluknet                data array associated with a cmsghdr structure (bit 0x2)\n\
239246670Spluknet  testno        Run one test by its number (require the -t option)\n\n");
240246670Spluknet	printf(" Available tests for stream sockets:\n");
241246670Spluknet	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
242246670Spluknet		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
243246670Spluknet	printf("\n Available tests for datagram sockets:\n");
244246670Spluknet	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
245246670Spluknet		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
246159045Smaxim}
247159045Smaxim
248159045Smaximstatic void
249159045Smaximoutput(const char *format, ...)
250159045Smaxim{
251246670Spluknet	char buf[LOGMSG_SIZE];
252159045Smaxim	va_list ap;
253159045Smaxim
254159045Smaxim	va_start(ap, format);
255159045Smaxim	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
256246670Spluknet		err(EXIT_FAILURE, "output: vsnprintf failed");
257159045Smaxim	write(STDOUT_FILENO, buf, strlen(buf));
258159045Smaxim	va_end(ap);
259159045Smaxim}
260159045Smaxim
261159045Smaximstatic void
262159045Smaximlogmsg(const char *format, ...)
263159045Smaxim{
264246670Spluknet	char buf[LOGMSG_SIZE];
265159045Smaxim	va_list ap;
266159045Smaxim	int errno_save;
267159045Smaxim
268246670Spluknet	errno_save = errno;
269159045Smaxim	va_start(ap, format);
270159045Smaxim	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
271246670Spluknet		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
272159045Smaxim	if (errno_save == 0)
273246670Spluknet		output("%s: %s\n", proc_name, buf);
274159045Smaxim	else
275246670Spluknet		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
276159045Smaxim	va_end(ap);
277246670Spluknet	errno = errno_save;
278246670Spluknet}
279159045Smaxim
280246670Spluknetstatic void
281246670Spluknetvlogmsgx(const char *format, va_list ap)
282246670Spluknet{
283246670Spluknet	char buf[LOGMSG_SIZE];
284246670Spluknet
285246670Spluknet	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
286246670Spluknet		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
287246670Spluknet	output("%s: %s\n", proc_name, buf);
288246670Spluknet
289159045Smaxim}
290159045Smaxim
291159045Smaximstatic void
292159045Smaximlogmsgx(const char *format, ...)
293159045Smaxim{
294159045Smaxim	va_list ap;
295159045Smaxim
296159045Smaxim	va_start(ap, format);
297246670Spluknet	vlogmsgx(format, ap);
298159045Smaxim	va_end(ap);
299159045Smaxim}
300159045Smaxim
301246670Spluknetstatic void
302246670Spluknetdbgmsg(const char *format, ...)
303246670Spluknet{
304246670Spluknet	va_list ap;
305246670Spluknet
306246670Spluknet	if (debug) {
307246670Spluknet		va_start(ap, format);
308246670Spluknet		vlogmsgx(format, ap);
309246670Spluknet		va_end(ap);
310246670Spluknet	}
311246670Spluknet}
312246670Spluknet
313159045Smaximstatic int
314246670Spluknetrun_tests(int type, u_int testno1)
315159045Smaxim{
316246670Spluknet	const struct test_func *tf;
317246670Spluknet	u_int i, testno2, failed_num;
318159045Smaxim
319246670Spluknet	sock_type = type;
320246670Spluknet	if (type == SOCK_STREAM) {
321246670Spluknet		sock_type_str = "SOCK_STREAM";
322246670Spluknet		tf = test_stream_tbl;
323246670Spluknet		i = TEST_STREAM_TBL_SIZE - 1;
324246670Spluknet	} else {
325246670Spluknet		sock_type_str = "SOCK_DGRAM";
326246670Spluknet		tf = test_dgram_tbl;
327246670Spluknet		i = TEST_DGRAM_TBL_SIZE - 1;
328246670Spluknet	}
329246670Spluknet	if (testno1 == 0) {
330246670Spluknet		testno1 = 1;
331246670Spluknet		testno2 = i;
332246670Spluknet	} else
333246670Spluknet		testno2 = testno1;
334246670Spluknet
335159045Smaxim	output("Running tests for %s sockets:\n", sock_type_str);
336246670Spluknet	failed_num = 0;
337246670Spluknet	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
338246670Spluknet		output("  %u: %s\n", i, tf->desc);
339246670Spluknet		switch (tf->func()) {
340159045Smaxim		case -1:
341246670Spluknet			++failed_num;
342159045Smaxim			break;
343159045Smaxim		case -2:
344246670Spluknet			logmsgx("some system error or timeout occurred");
345159045Smaxim			return (-1);
346159045Smaxim		}
347159045Smaxim	}
348159045Smaxim
349246670Spluknet	if (failed_num != 0)
350246670Spluknet		failed_flag = true;
351159045Smaxim
352159045Smaxim	if (testno1 != testno2) {
353246670Spluknet		if (failed_num == 0)
354246670Spluknet			output("-- all tests passed!\n");
355159045Smaxim		else
356246670Spluknet			output("-- %u test%s failed!\n",
357246670Spluknet			    failed_num, failed_num == 1 ? "" : "s");
358159045Smaxim	} else {
359246670Spluknet		if (failed_num == 0)
360246670Spluknet			output("-- test passed!\n");
361159045Smaxim		else
362159045Smaxim			output("-- test failed!\n");
363159045Smaxim	}
364159045Smaxim
365159045Smaxim	return (0);
366159045Smaxim}
367159045Smaxim
368246670Spluknetstatic int
369246670Spluknetinit(void)
370159045Smaxim{
371246670Spluknet	struct sigaction sigact;
372246670Spluknet	size_t idx;
373246670Spluknet	int rv;
374246670Spluknet
375246670Spluknet	proc_name = "SERVER";
376246670Spluknet
377246670Spluknet	sigact.sa_handler = SIG_IGN;
378246670Spluknet	sigact.sa_flags = 0;
379246670Spluknet	sigemptyset(&sigact.sa_mask);
380246670Spluknet	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
381246670Spluknet		logmsg("init: sigaction");
382246670Spluknet		return (-1);
383246670Spluknet	}
384246670Spluknet
385246670Spluknet	if (ipc_msg.buf_size == 0)
386246670Spluknet		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
387246670Spluknet	else {
388246670Spluknet		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
389246670Spluknet		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
390246670Spluknet		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
391246670Spluknet			logmsg("init: malloc");
392246670Spluknet			return (-1);
393246670Spluknet		}
394246670Spluknet		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
395246670Spluknet			ipc_msg.buf_send[idx] = (char)idx;
396246670Spluknet	}
397246670Spluknet
398246670Spluknet	proc_cred.uid = getuid();
399246670Spluknet	proc_cred.euid = geteuid();
400246670Spluknet	proc_cred.gid = getgid();
401246670Spluknet	proc_cred.egid = getegid();
402246670Spluknet	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
403246670Spluknet	if (proc_cred.gid_num < 0) {
404246670Spluknet		logmsg("init: getgroups");
405246670Spluknet		return (-1);
406246670Spluknet	}
407246670Spluknet	proc_cred.gid_arr = malloc(proc_cred.gid_num *
408246670Spluknet	    sizeof(*proc_cred.gid_arr));
409246670Spluknet	if (proc_cred.gid_arr == NULL) {
410246670Spluknet		logmsg("init: malloc");
411246670Spluknet		return (-1);
412246670Spluknet	}
413246670Spluknet	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
414246670Spluknet		logmsg("init: getgroups");
415246670Spluknet		return (-1);
416246670Spluknet	}
417246670Spluknet
418246670Spluknet	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
419246670Spluknet	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
420246670Spluknet	    "%s/%s", work_dir, proc_name);
421246670Spluknet	if (rv < 0) {
422246670Spluknet		logmsg("init: snprintf");
423246670Spluknet		return (-1);
424246670Spluknet	}
425246670Spluknet	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
426246670Spluknet		logmsgx("init: not enough space for socket pathname");
427246670Spluknet		return (-1);
428246670Spluknet	}
429246670Spluknet	serv_addr_sun.sun_family = PF_LOCAL;
430246670Spluknet	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
431246670Spluknet
432246670Spluknet	return (0);
433159045Smaxim}
434159045Smaxim
435246670Spluknetstatic int
436246670Spluknetclient_fork(void)
437246670Spluknet{
438246670Spluknet	int fd1, fd2;
439246670Spluknet
440246670Spluknet	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
441246670Spluknet	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
442246670Spluknet		logmsg("client_fork: pipe");
443246670Spluknet		return (-1);
444246670Spluknet	}
445246670Spluknet	client_pid = fork();
446246670Spluknet	if (client_pid == (pid_t)-1) {
447246670Spluknet		logmsg("client_fork: fork");
448246670Spluknet		return (-1);
449246670Spluknet	}
450246670Spluknet	if (client_pid == 0) {
451246670Spluknet		proc_name = "CLIENT";
452246670Spluknet		server_flag = false;
453246670Spluknet		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
454246670Spluknet		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
455246670Spluknet	} else {
456246670Spluknet		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
457246670Spluknet		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
458246670Spluknet	}
459246670Spluknet	if (close(fd1) < 0 || close(fd2) < 0) {
460246670Spluknet		logmsg("client_fork: close");
461246670Spluknet		return (-1);
462246670Spluknet	}
463246670Spluknet	return (client_pid != 0);
464246670Spluknet}
465246670Spluknet
466159045Smaximstatic void
467246670Spluknetclient_exit(int rv)
468159045Smaxim{
469246670Spluknet	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
470246670Spluknet	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
471246670Spluknet		logmsg("client_exit: close");
472246670Spluknet		rv = -1;
473246670Spluknet	}
474246670Spluknet	rv = rv == 0 ? EXIT_SUCCESS : -rv;
475246670Spluknet	dbgmsg("exit: code %d", rv);
476246670Spluknet	_exit(rv);
477246670Spluknet}
478159045Smaxim
479246670Spluknetstatic int
480246670Spluknetclient_wait(void)
481246670Spluknet{
482246670Spluknet	int status;
483246670Spluknet	pid_t pid;
484159045Smaxim
485246670Spluknet	dbgmsg("waiting for client");
486246670Spluknet
487246670Spluknet	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
488246670Spluknet	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
489246670Spluknet		logmsg("client_wait: close");
490246670Spluknet		return (-1);
491246670Spluknet	}
492246670Spluknet
493246670Spluknet	pid = waitpid(client_pid, &status, 0);
494246670Spluknet	if (pid == (pid_t)-1) {
495246670Spluknet		logmsg("client_wait: waitpid");
496246670Spluknet		return (-1);
497246670Spluknet	}
498246670Spluknet
499246670Spluknet	if (WIFEXITED(status)) {
500246670Spluknet		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
501246670Spluknet			logmsgx("client exit status is %d",
502246670Spluknet			    WEXITSTATUS(status));
503246670Spluknet			return (-WEXITSTATUS(status));
504246670Spluknet		}
505246670Spluknet	} else {
506246670Spluknet		if (WIFSIGNALED(status))
507246670Spluknet			logmsgx("abnormal termination of client, signal %d%s",
508246670Spluknet			    WTERMSIG(status), WCOREDUMP(status) ?
509246670Spluknet			    " (core file generated)" : "");
510246670Spluknet		else
511246670Spluknet			logmsgx("termination of client, unknown status");
512246670Spluknet		return (-1);
513246670Spluknet	}
514246670Spluknet
515246670Spluknet	return (0);
516159045Smaxim}
517159045Smaxim
518159045Smaximint
519159045Smaximmain(int argc, char *argv[])
520159045Smaxim{
521159045Smaxim	const char *errstr;
522246670Spluknet	u_int testno, zvalue;
523246670Spluknet	int opt, rv;
524246670Spluknet	bool dgram_flag, stream_flag;
525159045Smaxim
526246670Spluknet	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
527246670Spluknet	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
528246670Spluknet	dgram_flag = stream_flag = false;
529246670Spluknet	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
530159045Smaxim		switch (opt) {
531159045Smaxim		case 'd':
532246670Spluknet			debug = true;
533159045Smaxim			break;
534159045Smaxim		case 'h':
535246670Spluknet			usage(true);
536246670Spluknet			return (EXIT_SUCCESS);
537246670Spluknet		case 'n':
538246670Spluknet			ipc_msg.msg_num = strtonum(optarg, 1,
539246670Spluknet			    IPC_MSG_NUM_MAX, &errstr);
540246670Spluknet			if (errstr != NULL)
541246670Spluknet				errx(EXIT_FAILURE, "option -n: number is %s",
542246670Spluknet				    errstr);
543246670Spluknet			break;
544246670Spluknet		case 's':
545246670Spluknet			ipc_msg.buf_size = strtonum(optarg, 0,
546246670Spluknet			    IPC_MSG_SIZE_MAX, &errstr);
547246670Spluknet			if (errstr != NULL)
548246670Spluknet				errx(EXIT_FAILURE, "option -s: number is %s",
549246670Spluknet				    errstr);
550246670Spluknet			break;
551159045Smaxim		case 't':
552159045Smaxim			if (strcmp(optarg, "stream") == 0)
553246670Spluknet				stream_flag = true;
554159045Smaxim			else if (strcmp(optarg, "dgram") == 0)
555246670Spluknet				dgram_flag = true;
556159045Smaxim			else
557246670Spluknet				errx(EXIT_FAILURE, "option -t: "
558246670Spluknet				    "wrong socket type");
559159045Smaxim			break;
560159045Smaxim		case 'z':
561246670Spluknet			zvalue = strtonum(optarg, 0, 3, &errstr);
562246670Spluknet			if (errstr != NULL)
563246670Spluknet				errx(EXIT_FAILURE, "option -z: number is %s",
564246670Spluknet				    errstr);
565246670Spluknet			if (zvalue & 0x1)
566246670Spluknet				send_data_flag = false;
567246670Spluknet			if (zvalue & 0x2)
568246670Spluknet				send_array_flag = false;
569159045Smaxim			break;
570159045Smaxim		default:
571246670Spluknet			usage(false);
572246670Spluknet			return (EXIT_FAILURE);
573159045Smaxim		}
574159045Smaxim
575159045Smaxim	if (optind < argc) {
576159045Smaxim		if (optind + 1 != argc)
577246670Spluknet			errx(EXIT_FAILURE, "too many arguments");
578246670Spluknet		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
579159045Smaxim		if (errstr != NULL)
580246670Spluknet			errx(EXIT_FAILURE, "test number is %s", errstr);
581246670Spluknet		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
582246670Spluknet			errx(EXIT_FAILURE, "given test %u for stream "
583246670Spluknet			    "sockets does not exist", testno);
584246670Spluknet		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
585246670Spluknet			errx(EXIT_FAILURE, "given test %u for datagram "
586246670Spluknet			    "sockets does not exist", testno);
587159045Smaxim	} else
588246670Spluknet		testno = 0;
589159045Smaxim
590246670Spluknet	if (!dgram_flag && !stream_flag) {
591246670Spluknet		if (testno != 0)
592246670Spluknet			errx(EXIT_FAILURE, "particular test number "
593246670Spluknet			    "can be used with the -t option only");
594246670Spluknet		dgram_flag = stream_flag = true;
595159045Smaxim	}
596159045Smaxim
597246670Spluknet	if (mkdtemp(work_dir) == NULL)
598246670Spluknet		err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
599159045Smaxim
600246670Spluknet	rv = EXIT_FAILURE;
601246670Spluknet	if (init() < 0)
602246670Spluknet		goto done;
603159045Smaxim
604246670Spluknet	if (stream_flag)
605246670Spluknet		if (run_tests(SOCK_STREAM, testno) < 0)
606246670Spluknet			goto done;
607246670Spluknet	if (dgram_flag)
608246670Spluknet		if (run_tests(SOCK_DGRAM, testno) < 0)
609246670Spluknet			goto done;
610159045Smaxim
611246670Spluknet	rv = EXIT_SUCCESS;
612246670Spluknetdone:
613246670Spluknet	if (rmdir(work_dir) < 0) {
614246670Spluknet		logmsg("rmdir(%s)", work_dir);
615246670Spluknet		rv = EXIT_FAILURE;
616159045Smaxim	}
617246670Spluknet	return (failed_flag ? EXIT_FAILURE : rv);
618246670Spluknet}
619159045Smaxim
620246670Spluknetstatic int
621246670Spluknetsocket_close(int fd)
622246670Spluknet{
623246670Spluknet	int rv;
624159045Smaxim
625246670Spluknet	rv = 0;
626246670Spluknet	if (close(fd) < 0) {
627246670Spluknet		logmsg("socket_close: close");
628246670Spluknet		rv = -1;
629159045Smaxim	}
630246670Spluknet	if (server_flag && fd == serv_sock_fd)
631246670Spluknet		if (unlink(serv_addr_sun.sun_path) < 0) {
632246670Spluknet			logmsg("socket_close: unlink(%s)",
633246670Spluknet			    serv_addr_sun.sun_path);
634246670Spluknet			rv = -1;
635246670Spluknet		}
636246670Spluknet	return (rv);
637159045Smaxim}
638159045Smaxim
639159045Smaximstatic int
640246670Spluknetsocket_create(void)
641159045Smaxim{
642246670Spluknet	struct timeval tv;
643246670Spluknet	int fd;
644159045Smaxim
645246670Spluknet	fd = socket(PF_LOCAL, sock_type, 0);
646246670Spluknet	if (fd < 0) {
647246670Spluknet		logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str);
648159045Smaxim		return (-1);
649159045Smaxim	}
650246670Spluknet	if (server_flag)
651246670Spluknet		serv_sock_fd = fd;
652159045Smaxim
653246670Spluknet	tv.tv_sec = TIMEOUT;
654246670Spluknet	tv.tv_usec = 0;
655246670Spluknet	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
656246670Spluknet	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
657246670Spluknet		logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
658246670Spluknet		goto failed;
659246670Spluknet	}
660159045Smaxim
661246670Spluknet	if (server_flag) {
662246670Spluknet		if (bind(fd, (struct sockaddr *)&serv_addr_sun,
663246670Spluknet		    serv_addr_sun.sun_len) < 0) {
664246670Spluknet			logmsg("socket_create: bind(%s)",
665246670Spluknet			    serv_addr_sun.sun_path);
666159045Smaxim			goto failed;
667159045Smaxim		}
668246670Spluknet		if (sock_type == SOCK_STREAM) {
669246670Spluknet			int val;
670159045Smaxim
671246670Spluknet			if (listen(fd, LISTENQ) < 0) {
672246670Spluknet				logmsg("socket_create: listen");
673246670Spluknet				goto failed;
674246670Spluknet			}
675246670Spluknet			val = fcntl(fd, F_GETFL, 0);
676246670Spluknet			if (val < 0) {
677246670Spluknet				logmsg("socket_create: fcntl(F_GETFL)");
678246670Spluknet				goto failed;
679246670Spluknet			}
680246670Spluknet			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
681246670Spluknet				logmsg("socket_create: fcntl(F_SETFL)");
682246670Spluknet				goto failed;
683246670Spluknet			}
684159045Smaxim		}
685159045Smaxim	}
686159045Smaxim
687159045Smaxim	return (fd);
688159045Smaxim
689159045Smaximfailed:
690159045Smaxim	if (close(fd) < 0)
691246670Spluknet		logmsg("socket_create: close");
692246670Spluknet	if (server_flag)
693246670Spluknet		if (unlink(serv_addr_sun.sun_path) < 0)
694246670Spluknet			logmsg("socket_close: unlink(%s)",
695246670Spluknet			    serv_addr_sun.sun_path);
696159045Smaxim	return (-1);
697159045Smaxim}
698159045Smaxim
699159045Smaximstatic int
700246670Spluknetsocket_connect(int fd)
701159045Smaxim{
702246670Spluknet	dbgmsg("connect");
703159045Smaxim
704246670Spluknet	if (connect(fd, (struct sockaddr *)&serv_addr_sun,
705246670Spluknet	    serv_addr_sun.sun_len) < 0) {
706246670Spluknet		logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path);
707246670Spluknet		return (-1);
708246670Spluknet	}
709246670Spluknet	return (0);
710159045Smaxim}
711159045Smaxim
712159045Smaximstatic int
713246670Spluknetsync_recv(void)
714159045Smaxim{
715246670Spluknet	ssize_t ssize;
716246670Spluknet	int fd;
717246670Spluknet	char buf;
718159045Smaxim
719246670Spluknet	dbgmsg("sync: wait");
720159045Smaxim
721246670Spluknet	fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
722159045Smaxim
723246670Spluknet	ssize = read(fd, &buf, 1);
724246670Spluknet	if (ssize < 0) {
725246670Spluknet		logmsg("sync_recv: read");
726159045Smaxim		return (-1);
727159045Smaxim	}
728246670Spluknet	if (ssize < 1) {
729246670Spluknet		logmsgx("sync_recv: read %zd of 1 byte", ssize);
730246670Spluknet		return (-1);
731246670Spluknet	}
732159045Smaxim
733246670Spluknet	dbgmsg("sync: received");
734246670Spluknet
735159045Smaxim	return (0);
736159045Smaxim}
737159045Smaxim
738159045Smaximstatic int
739246670Spluknetsync_send(void)
740159045Smaxim{
741246670Spluknet	ssize_t ssize;
742246670Spluknet	int fd;
743159045Smaxim
744246670Spluknet	dbgmsg("sync: send");
745159045Smaxim
746246670Spluknet	fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
747159045Smaxim
748246670Spluknet	ssize = write(fd, "", 1);
749246670Spluknet	if (ssize < 0) {
750246670Spluknet		logmsg("sync_send: write");
751159045Smaxim		return (-1);
752159045Smaxim	}
753246670Spluknet	if (ssize < 1) {
754246670Spluknet		logmsgx("sync_send: sent %zd of 1 byte", ssize);
755159045Smaxim		return (-1);
756159045Smaxim	}
757159045Smaxim
758159045Smaxim	return (0);
759159045Smaxim}
760159045Smaxim
761159045Smaximstatic int
762246670Spluknetmessage_send(int fd, const struct msghdr *msghdr)
763159045Smaxim{
764246670Spluknet	const struct cmsghdr *cmsghdr;
765246670Spluknet	size_t size;
766246670Spluknet	ssize_t ssize;
767159045Smaxim
768246670Spluknet	size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
769246670Spluknet	dbgmsg("send: data size %zu", size);
770246670Spluknet	dbgmsg("send: msghdr.msg_controllen %u",
771246670Spluknet	    (u_int)msghdr->msg_controllen);
772246670Spluknet	cmsghdr = CMSG_FIRSTHDR(msghdr);
773246670Spluknet	if (cmsghdr != NULL)
774246670Spluknet		dbgmsg("send: cmsghdr.cmsg_len %u",
775246670Spluknet		    (u_int)cmsghdr->cmsg_len);
776159045Smaxim
777246670Spluknet	ssize = sendmsg(fd, msghdr, 0);
778246670Spluknet	if (ssize < 0) {
779246670Spluknet		logmsg("message_send: sendmsg");
780159045Smaxim		return (-1);
781159045Smaxim	}
782246670Spluknet	if ((size_t)ssize != size) {
783246670Spluknet		logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
784246670Spluknet		    ssize, size);
785246670Spluknet		return (-1);
786246670Spluknet	}
787159045Smaxim
788246670Spluknet	if (!send_data_flag)
789246670Spluknet		if (sync_send() < 0)
790246670Spluknet			return (-1);
791159045Smaxim
792246670Spluknet	return (0);
793246670Spluknet}
794159045Smaxim
795246670Spluknetstatic int
796246670Spluknetmessage_sendn(int fd, struct msghdr *msghdr)
797246670Spluknet{
798246670Spluknet	u_int i;
799159045Smaxim
800246670Spluknet	for (i = 1; i <= ipc_msg.msg_num; ++i) {
801246670Spluknet		dbgmsg("message #%u", i);
802246670Spluknet		if (message_send(fd, msghdr) < 0)
803246670Spluknet			return (-1);
804159045Smaxim	}
805246670Spluknet	return (0);
806159045Smaxim}
807159045Smaxim
808159045Smaximstatic int
809246670Spluknetmessage_recv(int fd, struct msghdr *msghdr)
810159045Smaxim{
811246670Spluknet	const struct cmsghdr *cmsghdr;
812246670Spluknet	size_t size;
813246670Spluknet	ssize_t ssize;
814159045Smaxim
815246670Spluknet	if (!send_data_flag)
816246670Spluknet		if (sync_recv() < 0)
817246670Spluknet			return (-1);
818159045Smaxim
819246670Spluknet	size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
820246670Spluknet	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
821246670Spluknet	if (ssize < 0) {
822246670Spluknet		logmsg("message_recv: recvmsg");
823159045Smaxim		return (-1);
824159045Smaxim	}
825246670Spluknet	if ((size_t)ssize != size) {
826246670Spluknet		logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
827246670Spluknet		    ssize, size);
828159045Smaxim		return (-1);
829159045Smaxim	}
830159045Smaxim
831246670Spluknet	dbgmsg("recv: data size %zd", ssize);
832246670Spluknet	dbgmsg("recv: msghdr.msg_controllen %u",
833246670Spluknet	    (u_int)msghdr->msg_controllen);
834246670Spluknet	cmsghdr = CMSG_FIRSTHDR(msghdr);
835246670Spluknet	if (cmsghdr != NULL)
836246670Spluknet		dbgmsg("recv: cmsghdr.cmsg_len %u",
837246670Spluknet		    (u_int)cmsghdr->cmsg_len);
838246670Spluknet
839246670Spluknet	if (memcmp(ipc_msg.buf_recv, ipc_msg.buf_send, size) != 0) {
840246670Spluknet		logmsgx("message_recv: received message has wrong content");
841159045Smaxim		return (-1);
842159045Smaxim	}
843159045Smaxim
844159045Smaxim	return (0);
845159045Smaxim}
846159045Smaxim
847159045Smaximstatic int
848246670Spluknetsocket_accept(int listenfd)
849159045Smaxim{
850246670Spluknet	fd_set rset;
851246670Spluknet	struct timeval tv;
852246670Spluknet	int fd, rv, val;
853159045Smaxim
854246670Spluknet	dbgmsg("accept");
855159045Smaxim
856246670Spluknet	FD_ZERO(&rset);
857246670Spluknet	FD_SET(listenfd, &rset);
858246670Spluknet	tv.tv_sec = TIMEOUT;
859246670Spluknet	tv.tv_usec = 0;
860246670Spluknet	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
861246670Spluknet	if (rv < 0) {
862246670Spluknet		logmsg("socket_accept: select");
863159045Smaxim		return (-1);
864159045Smaxim	}
865246670Spluknet	if (rv == 0) {
866246670Spluknet		logmsgx("socket_accept: select timeout");
867159045Smaxim		return (-1);
868159045Smaxim	}
869159045Smaxim
870246670Spluknet	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
871246670Spluknet	if (fd < 0) {
872246670Spluknet		logmsg("socket_accept: accept");
873159045Smaxim		return (-1);
874159045Smaxim	}
875159045Smaxim
876246670Spluknet	val = fcntl(fd, F_GETFL, 0);
877246670Spluknet	if (val < 0) {
878246670Spluknet		logmsg("socket_accept: fcntl(F_GETFL)");
879246670Spluknet		goto failed;
880246670Spluknet	}
881246670Spluknet	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
882246670Spluknet		logmsg("socket_accept: fcntl(F_SETFL)");
883246670Spluknet		goto failed;
884246670Spluknet	}
885246670Spluknet
886246670Spluknet	return (fd);
887246670Spluknet
888246670Spluknetfailed:
889246670Spluknet	if (close(fd) < 0)
890246670Spluknet		logmsg("socket_accept: close");
891246670Spluknet	return (-1);
892159045Smaxim}
893159045Smaxim
894159045Smaximstatic int
895246670Spluknetcheck_msghdr(const struct msghdr *msghdr, size_t size)
896159045Smaxim{
897246670Spluknet	if (msghdr->msg_flags & MSG_TRUNC) {
898246670Spluknet		logmsgx("msghdr.msg_flags has MSG_TRUNC");
899159045Smaxim		return (-1);
900159045Smaxim	}
901246670Spluknet	if (msghdr->msg_flags & MSG_CTRUNC) {
902246670Spluknet		logmsgx("msghdr.msg_flags has MSG_CTRUNC");
903159045Smaxim		return (-1);
904159045Smaxim	}
905246670Spluknet	if (msghdr->msg_controllen < size) {
906246670Spluknet		logmsgx("msghdr.msg_controllen %u < %zu",
907246670Spluknet		    (u_int)msghdr->msg_controllen, size);
908159045Smaxim		return (-1);
909159045Smaxim	}
910246670Spluknet	if (msghdr->msg_controllen > 0 && size == 0) {
911246670Spluknet		logmsgx("msghdr.msg_controllen %u > 0",
912246670Spluknet		    (u_int)msghdr->msg_controllen);
913246670Spluknet		return (-1);
914246670Spluknet	}
915159045Smaxim	return (0);
916159045Smaxim}
917159045Smaxim
918159045Smaximstatic int
919246670Spluknetcheck_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
920159045Smaxim{
921246670Spluknet	if (cmsghdr == NULL) {
922246670Spluknet		logmsgx("cmsghdr is NULL");
923159045Smaxim		return (-1);
924159045Smaxim	}
925246670Spluknet	if (cmsghdr->cmsg_level != SOL_SOCKET) {
926246670Spluknet		logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
927246670Spluknet		    cmsghdr->cmsg_level);
928159045Smaxim		return (-1);
929159045Smaxim	}
930246670Spluknet	if (cmsghdr->cmsg_type != type) {
931246670Spluknet		logmsgx("cmsghdr.cmsg_type %d != %d",
932246670Spluknet		    cmsghdr->cmsg_type, type);
933159045Smaxim		return (-1);
934159045Smaxim	}
935246670Spluknet	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
936246670Spluknet		logmsgx("cmsghdr.cmsg_len %u != %zu",
937246670Spluknet		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
938246670Spluknet		return (-1);
939246670Spluknet	}
940159045Smaxim	return (0);
941159045Smaxim}
942159045Smaxim
943159045Smaximstatic int
944246670Spluknetcheck_groups(const char *gid_arr_str, const gid_t *gid_arr,
945246670Spluknet    const char *gid_num_str, int gid_num, bool all_gids)
946159045Smaxim{
947246670Spluknet	int i;
948159045Smaxim
949246670Spluknet	for (i = 0; i < gid_num; ++i)
950246670Spluknet		dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
951246670Spluknet
952246670Spluknet	if (all_gids) {
953246670Spluknet		if (gid_num != proc_cred.gid_num) {
954246670Spluknet			logmsgx("%s %d != %d", gid_num_str, gid_num,
955246670Spluknet			    proc_cred.gid_num);
956246670Spluknet			return (-1);
957159045Smaxim		}
958246670Spluknet	} else {
959246670Spluknet		if (gid_num > proc_cred.gid_num) {
960246670Spluknet			logmsgx("%s %d > %d", gid_num_str, gid_num,
961246670Spluknet			    proc_cred.gid_num);
962246670Spluknet			return (-1);
963159045Smaxim		}
964159045Smaxim	}
965246670Spluknet	if (memcmp(gid_arr, proc_cred.gid_arr,
966246670Spluknet	    gid_num * sizeof(*gid_arr)) != 0) {
967246670Spluknet		logmsgx("%s content is wrong", gid_arr_str);
968246670Spluknet		for (i = 0; i < gid_num; ++i)
969246670Spluknet			if (gid_arr[i] != proc_cred.gid_arr[i]) {
970246670Spluknet				logmsgx("%s[%d] %lu != %lu",
971246670Spluknet				    gid_arr_str, i, (u_long)gid_arr[i],
972246670Spluknet				    (u_long)proc_cred.gid_arr[i]);
973246670Spluknet				break;
974246670Spluknet			}
975246670Spluknet		return (-1);
976246670Spluknet	}
977246670Spluknet	return (0);
978159045Smaxim}
979159045Smaxim
980246670Spluknetstatic int
981246670Spluknetcheck_xucred(const struct xucred *xucred, socklen_t len)
982159045Smaxim{
983246670Spluknet	if (len != sizeof(*xucred)) {
984246670Spluknet		logmsgx("option value size %zu != %zu",
985246670Spluknet		    (size_t)len, sizeof(*xucred));
986246670Spluknet		return (-1);
987246670Spluknet	}
988159045Smaxim
989246670Spluknet	dbgmsg("xucred.cr_version %u", xucred->cr_version);
990246670Spluknet	dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
991246670Spluknet	dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
992159045Smaxim
993246670Spluknet	if (xucred->cr_version != XUCRED_VERSION) {
994246670Spluknet		logmsgx("xucred.cr_version %u != %d",
995246670Spluknet		    xucred->cr_version, XUCRED_VERSION);
996246670Spluknet		return (-1);
997246670Spluknet	}
998246670Spluknet	if (xucred->cr_uid != proc_cred.euid) {
999246670Spluknet		logmsgx("xucred.cr_uid %lu != %lu (EUID)",
1000246670Spluknet		   (u_long)xucred->cr_uid, (u_long)proc_cred.euid);
1001246670Spluknet		return (-1);
1002246670Spluknet	}
1003246670Spluknet	if (xucred->cr_ngroups == 0) {
1004246670Spluknet		logmsgx("xucred.cr_ngroups == 0");
1005246670Spluknet		return (-1);
1006246670Spluknet	}
1007246670Spluknet	if (xucred->cr_ngroups < 0) {
1008246670Spluknet		logmsgx("xucred.cr_ngroups < 0");
1009246670Spluknet		return (-1);
1010246670Spluknet	}
1011246670Spluknet	if (xucred->cr_ngroups > XU_NGROUPS) {
1012246670Spluknet		logmsgx("xucred.cr_ngroups %hu > %u (max)",
1013246670Spluknet		    xucred->cr_ngroups, XU_NGROUPS);
1014246670Spluknet		return (-1);
1015246670Spluknet	}
1016246670Spluknet	if (xucred->cr_groups[0] != proc_cred.egid) {
1017246670Spluknet		logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
1018246670Spluknet		    (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid);
1019246670Spluknet		return (-1);
1020246670Spluknet	}
1021246670Spluknet	if (check_groups("xucred.cr_groups", xucred->cr_groups,
1022246670Spluknet	    "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
1023246670Spluknet		return (-1);
1024246670Spluknet	return (0);
1025246670Spluknet}
1026159045Smaxim
1027246670Spluknetstatic int
1028246670Spluknetcheck_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
1029246670Spluknet{
1030246670Spluknet	const struct cmsgcred *cmsgcred;
1031159045Smaxim
1032246670Spluknet	if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0)
1033246670Spluknet		return (-1);
1034159045Smaxim
1035246670Spluknet	cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
1036159045Smaxim
1037246670Spluknet	dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid);
1038246670Spluknet	dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid);
1039246670Spluknet	dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid);
1040246670Spluknet	dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid);
1041246670Spluknet	dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups);
1042159045Smaxim
1043246670Spluknet	if (cmsgcred->cmcred_pid != client_pid) {
1044246670Spluknet		logmsgx("cmsgcred.cmcred_pid %ld != %ld",
1045246670Spluknet		    (long)cmsgcred->cmcred_pid, (long)client_pid);
1046246670Spluknet		return (-1);
1047159045Smaxim	}
1048246670Spluknet	if (cmsgcred->cmcred_uid != proc_cred.uid) {
1049246670Spluknet		logmsgx("cmsgcred.cmcred_uid %lu != %lu",
1050246670Spluknet		    (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid);
1051246670Spluknet		return (-1);
1052246670Spluknet	}
1053246670Spluknet	if (cmsgcred->cmcred_euid != proc_cred.euid) {
1054246670Spluknet		logmsgx("cmsgcred.cmcred_euid %lu != %lu",
1055246670Spluknet		    (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid);
1056246670Spluknet		return (-1);
1057246670Spluknet	}
1058246670Spluknet	if (cmsgcred->cmcred_gid != proc_cred.gid) {
1059246670Spluknet		logmsgx("cmsgcred.cmcred_gid %lu != %lu",
1060246670Spluknet		    (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid);
1061246670Spluknet		return (-1);
1062246670Spluknet	}
1063246670Spluknet	if (cmsgcred->cmcred_ngroups == 0) {
1064246670Spluknet		logmsgx("cmsgcred.cmcred_ngroups == 0");
1065246670Spluknet		return (-1);
1066246670Spluknet	}
1067246670Spluknet	if (cmsgcred->cmcred_ngroups < 0) {
1068246670Spluknet		logmsgx("cmsgcred.cmcred_ngroups %d < 0",
1069246670Spluknet		    cmsgcred->cmcred_ngroups);
1070246670Spluknet		return (-1);
1071246670Spluknet	}
1072246670Spluknet	if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) {
1073246670Spluknet		logmsgx("cmsgcred.cmcred_ngroups %d > %d",
1074246670Spluknet		    cmsgcred->cmcred_ngroups, CMGROUP_MAX);
1075246670Spluknet		return (-1);
1076246670Spluknet	}
1077246670Spluknet	if (cmsgcred->cmcred_groups[0] != proc_cred.egid) {
1078246670Spluknet		logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
1079246670Spluknet		    (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid);
1080246670Spluknet		return (-1);
1081246670Spluknet	}
1082246670Spluknet	if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups,
1083246670Spluknet	    "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0)
1084246670Spluknet		return (-1);
1085246670Spluknet	return (0);
1086246670Spluknet}
1087159045Smaxim
1088246670Spluknetstatic int
1089246670Spluknetcheck_scm_creds_sockcred(struct cmsghdr *cmsghdr)
1090246670Spluknet{
1091246670Spluknet	const struct sockcred *sockcred;
1092159045Smaxim
1093246670Spluknet	if (check_cmsghdr(cmsghdr, SCM_CREDS,
1094246670Spluknet	    SOCKCREDSIZE(proc_cred.gid_num)) < 0)
1095246670Spluknet		return (-1);
1096159045Smaxim
1097246670Spluknet	sockcred = (struct sockcred *)CMSG_DATA(cmsghdr);
1098159045Smaxim
1099246670Spluknet	dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid);
1100246670Spluknet	dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid);
1101246670Spluknet	dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid);
1102246670Spluknet	dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid);
1103246670Spluknet	dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups);
1104246670Spluknet
1105246670Spluknet	if (sockcred->sc_uid != proc_cred.uid) {
1106246670Spluknet		logmsgx("sockcred.sc_uid %lu != %lu",
1107246670Spluknet		    (u_long)sockcred->sc_uid, (u_long)proc_cred.uid);
1108246670Spluknet		return (-1);
1109246670Spluknet	}
1110246670Spluknet	if (sockcred->sc_euid != proc_cred.euid) {
1111246670Spluknet		logmsgx("sockcred.sc_euid %lu != %lu",
1112246670Spluknet		    (u_long)sockcred->sc_euid, (u_long)proc_cred.euid);
1113246670Spluknet		return (-1);
1114246670Spluknet	}
1115246670Spluknet	if (sockcred->sc_gid != proc_cred.gid) {
1116246670Spluknet		logmsgx("sockcred.sc_gid %lu != %lu",
1117246670Spluknet		    (u_long)sockcred->sc_gid, (u_long)proc_cred.gid);
1118246670Spluknet		return (-1);
1119246670Spluknet	}
1120246670Spluknet	if (sockcred->sc_egid != proc_cred.egid) {
1121246670Spluknet		logmsgx("sockcred.sc_egid %lu != %lu",
1122246670Spluknet		    (u_long)sockcred->sc_egid, (u_long)proc_cred.egid);
1123246670Spluknet		return (-1);
1124246670Spluknet	}
1125246670Spluknet	if (sockcred->sc_ngroups == 0) {
1126246670Spluknet		logmsgx("sockcred.sc_ngroups == 0");
1127246670Spluknet		return (-1);
1128246670Spluknet	}
1129246670Spluknet	if (sockcred->sc_ngroups < 0) {
1130246670Spluknet		logmsgx("sockcred.sc_ngroups %d < 0",
1131246670Spluknet		    sockcred->sc_ngroups);
1132246670Spluknet		return (-1);
1133246670Spluknet	}
1134246670Spluknet	if (sockcred->sc_ngroups != proc_cred.gid_num) {
1135246670Spluknet		logmsgx("sockcred.sc_ngroups %d != %u",
1136246670Spluknet		    sockcred->sc_ngroups, proc_cred.gid_num);
1137246670Spluknet		return (-1);
1138246670Spluknet	}
1139246670Spluknet	if (check_groups("sockcred.sc_groups", sockcred->sc_groups,
1140246670Spluknet	    "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0)
1141246670Spluknet		return (-1);
1142246670Spluknet	return (0);
1143159045Smaxim}
1144159045Smaxim
1145159045Smaximstatic int
1146246670Spluknetcheck_scm_timestamp(struct cmsghdr *cmsghdr)
1147159045Smaxim{
1148246670Spluknet	const struct timeval *timeval;
1149159045Smaxim
1150246670Spluknet	if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0)
1151246670Spluknet		return (-1);
1152159045Smaxim
1153246670Spluknet	timeval = (struct timeval *)CMSG_DATA(cmsghdr);
1154159045Smaxim
1155246670Spluknet	dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX,
1156246670Spluknet	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec);
1157159045Smaxim
1158246670Spluknet	return (0);
1159246670Spluknet}
1160159045Smaxim
1161246670Spluknetstatic int
1162246670Spluknetcheck_scm_bintime(struct cmsghdr *cmsghdr)
1163246670Spluknet{
1164246670Spluknet	const struct bintime *bintime;
1165159045Smaxim
1166246670Spluknet	if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0)
1167246670Spluknet		return (-1);
1168159045Smaxim
1169246670Spluknet	bintime = (struct bintime *)CMSG_DATA(cmsghdr);
1170159045Smaxim
1171246670Spluknet	dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64,
1172246670Spluknet	    (intmax_t)bintime->sec, bintime->frac);
1173159045Smaxim
1174246670Spluknet	return (0);
1175246670Spluknet}
1176159045Smaxim
1177246670Spluknetstatic void
1178246670Spluknetmsghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
1179246670Spluknet{
1180246670Spluknet	msghdr->msg_name = NULL;
1181246670Spluknet	msghdr->msg_namelen = 0;
1182246670Spluknet	if (send_data_flag) {
1183246670Spluknet		iov->iov_base = server_flag ?
1184246670Spluknet		    ipc_msg.buf_recv : ipc_msg.buf_send;
1185246670Spluknet		iov->iov_len = ipc_msg.buf_size;
1186246670Spluknet		msghdr->msg_iov = iov;
1187246670Spluknet		msghdr->msg_iovlen = 1;
1188246670Spluknet	} else {
1189246670Spluknet		msghdr->msg_iov = NULL;
1190246670Spluknet		msghdr->msg_iovlen = 0;
1191246670Spluknet	}
1192246670Spluknet	msghdr->msg_control = cmsg_data;
1193246670Spluknet	msghdr->msg_flags = 0;
1194246670Spluknet}
1195159045Smaxim
1196246670Spluknetstatic void
1197246670Spluknetmsghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
1198246670Spluknet    void *cmsg_data, size_t cmsg_size)
1199246670Spluknet{
1200246670Spluknet	msghdr_init_generic(msghdr, iov, cmsg_data);
1201246670Spluknet	msghdr->msg_controllen = cmsg_size;
1202246670Spluknet	dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
1203246670Spluknet	    msghdr->msg_iov->iov_len : (size_t)0);
1204246670Spluknet	dbgmsg("init: msghdr.msg_controllen %u",
1205246670Spluknet	    (u_int)msghdr->msg_controllen);
1206246670Spluknet}
1207159045Smaxim
1208246670Spluknetstatic void
1209246670Spluknetmsghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
1210246670Spluknet    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
1211246670Spluknet{
1212246670Spluknet	struct cmsghdr *cmsghdr;
1213159045Smaxim
1214246670Spluknet	msghdr_init_generic(msghdr, iov, cmsg_data);
1215246670Spluknet	if (cmsg_data != NULL) {
1216246670Spluknet		msghdr->msg_controllen = send_array_flag ?
1217246670Spluknet		    cmsg_size : CMSG_SPACE(0);
1218246670Spluknet		cmsghdr = CMSG_FIRSTHDR(msghdr);
1219246670Spluknet		cmsghdr->cmsg_level = SOL_SOCKET;
1220246670Spluknet		cmsghdr->cmsg_type = type;
1221246670Spluknet		cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0);
1222246670Spluknet	} else
1223246670Spluknet		msghdr->msg_controllen = 0;
1224246670Spluknet}
1225159045Smaxim
1226246670Spluknetstatic int
1227246670Spluknett_generic(int (*client_func)(int), int (*server_func)(int))
1228246670Spluknet{
1229246670Spluknet	int fd, rv, rv_client;
1230159045Smaxim
1231246670Spluknet	switch (client_fork()) {
1232246670Spluknet	case 0:
1233246670Spluknet		fd = socket_create();
1234246670Spluknet		if (fd < 0)
1235246670Spluknet			rv = -2;
1236246670Spluknet		else {
1237246670Spluknet			rv = client_func(fd);
1238246670Spluknet			if (socket_close(fd) < 0)
1239246670Spluknet				rv = -2;
1240159045Smaxim		}
1241246670Spluknet		client_exit(rv);
1242246670Spluknet		break;
1243246670Spluknet	case 1:
1244246670Spluknet		fd = socket_create();
1245246670Spluknet		if (fd < 0)
1246246670Spluknet			rv = -2;
1247246670Spluknet		else {
1248246670Spluknet			rv = server_func(fd);
1249246670Spluknet			rv_client = client_wait();
1250246670Spluknet			if (rv == 0 || (rv == -2 && rv_client != 0))
1251246670Spluknet				rv = rv_client;
1252246670Spluknet			if (socket_close(fd) < 0)
1253246670Spluknet				rv = -2;
1254159045Smaxim		}
1255246670Spluknet		break;
1256246670Spluknet	default:
1257246670Spluknet		rv = -2;
1258246670Spluknet	}
1259246670Spluknet	return (rv);
1260246670Spluknet}
1261159045Smaxim
1262246670Spluknetstatic int
1263246670Spluknett_cmsgcred_client(int fd)
1264246670Spluknet{
1265246670Spluknet	struct msghdr msghdr;
1266246670Spluknet	struct iovec iov[1];
1267246670Spluknet	void *cmsg_data;
1268246670Spluknet	size_t cmsg_size;
1269246670Spluknet	int rv;
1270159045Smaxim
1271246670Spluknet	if (sync_recv() < 0)
1272246670Spluknet		return (-2);
1273159045Smaxim
1274246670Spluknet	rv = -2;
1275246670Spluknet
1276246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
1277246670Spluknet	cmsg_data = malloc(cmsg_size);
1278246670Spluknet	if (cmsg_data == NULL) {
1279246670Spluknet		logmsg("malloc");
1280246670Spluknet		goto done;
1281159045Smaxim	}
1282246670Spluknet	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1283246670Spluknet	    SCM_CREDS, sizeof(struct cmsgcred));
1284159045Smaxim
1285246670Spluknet	if (socket_connect(fd) < 0)
1286246670Spluknet		goto done;
1287159045Smaxim
1288246670Spluknet	if (message_sendn(fd, &msghdr) < 0)
1289246670Spluknet		goto done;
1290246670Spluknet
1291246670Spluknet	rv = 0;
1292246670Spluknetdone:
1293246670Spluknet	free(cmsg_data);
1294246670Spluknet	return (rv);
1295159045Smaxim}
1296159045Smaxim
1297159045Smaximstatic int
1298246670Spluknett_cmsgcred_server(int fd1)
1299159045Smaxim{
1300246670Spluknet	struct msghdr msghdr;
1301246670Spluknet	struct iovec iov[1];
1302246670Spluknet	struct cmsghdr *cmsghdr;
1303246670Spluknet	void *cmsg_data;
1304246670Spluknet	size_t cmsg_size;
1305246670Spluknet	u_int i;
1306246670Spluknet	int fd2, rv;
1307159045Smaxim
1308246670Spluknet	if (sync_send() < 0)
1309159045Smaxim		return (-2);
1310159045Smaxim
1311246670Spluknet	fd2 = -1;
1312246670Spluknet	rv = -2;
1313159045Smaxim
1314246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
1315246670Spluknet	cmsg_data = malloc(cmsg_size);
1316246670Spluknet	if (cmsg_data == NULL) {
1317246670Spluknet		logmsg("malloc");
1318246670Spluknet		goto done;
1319159045Smaxim	}
1320159045Smaxim
1321246670Spluknet	if (sock_type == SOCK_STREAM) {
1322246670Spluknet		fd2 = socket_accept(fd1);
1323246670Spluknet		if (fd2 < 0)
1324246670Spluknet			goto done;
1325246670Spluknet	} else
1326246670Spluknet		fd2 = fd1;
1327159045Smaxim
1328246670Spluknet	rv = -1;
1329246670Spluknet	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1330246670Spluknet		dbgmsg("message #%u", i);
1331159045Smaxim
1332246670Spluknet		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1333246670Spluknet		if (message_recv(fd2, &msghdr) < 0) {
1334246670Spluknet			rv = -2;
1335246670Spluknet			break;
1336246670Spluknet		}
1337159045Smaxim
1338246670Spluknet		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1339246670Spluknet			break;
1340246670Spluknet
1341246670Spluknet		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1342246670Spluknet		if (check_scm_creds_cmsgcred(cmsghdr) < 0)
1343246670Spluknet			break;
1344159045Smaxim	}
1345246670Spluknet	if (i > ipc_msg.msg_num)
1346246670Spluknet		rv = 0;
1347246670Spluknetdone:
1348246670Spluknet	free(cmsg_data);
1349246670Spluknet	if (sock_type == SOCK_STREAM && fd2 >= 0)
1350246670Spluknet		if (socket_close(fd2) < 0)
1351246670Spluknet			rv = -2;
1352246670Spluknet	return (rv);
1353246670Spluknet}
1354159045Smaxim
1355246670Spluknetstatic int
1356246670Spluknett_cmsgcred(void)
1357246670Spluknet{
1358246670Spluknet	return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
1359159045Smaxim}
1360159045Smaxim
1361246670Spluknetstatic int
1362246670Spluknett_sockcred_client(int type, int fd)
1363159045Smaxim{
1364246670Spluknet	struct msghdr msghdr;
1365159045Smaxim	struct iovec iov[1];
1366246670Spluknet	int rv;
1367159045Smaxim
1368246670Spluknet	if (sync_recv() < 0)
1369246670Spluknet		return (-2);
1370159045Smaxim
1371246670Spluknet	rv = -2;
1372159045Smaxim
1373246670Spluknet	msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
1374159045Smaxim
1375246670Spluknet	if (socket_connect(fd) < 0)
1376246670Spluknet		goto done;
1377159045Smaxim
1378246670Spluknet	if (type == 2)
1379246670Spluknet		if (sync_recv() < 0)
1380246670Spluknet			goto done;
1381159045Smaxim
1382246670Spluknet	if (message_sendn(fd, &msghdr) < 0)
1383246670Spluknet		goto done;
1384159045Smaxim
1385246670Spluknet	rv = 0;
1386246670Spluknetdone:
1387246670Spluknet	return (rv);
1388159045Smaxim}
1389159045Smaxim
1390159045Smaximstatic int
1391246670Spluknett_sockcred_server(int type, int fd1)
1392159045Smaxim{
1393246670Spluknet	struct msghdr msghdr;
1394159045Smaxim	struct iovec iov[1];
1395246670Spluknet	struct cmsghdr *cmsghdr;
1396246670Spluknet	void *cmsg_data;
1397246670Spluknet	size_t cmsg_size;
1398159045Smaxim	u_int i;
1399246670Spluknet	int fd2, rv, val;
1400159045Smaxim
1401246670Spluknet	fd2 = -1;
1402246670Spluknet	rv = -2;
1403159045Smaxim
1404246670Spluknet	cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
1405246670Spluknet	cmsg_data = malloc(cmsg_size);
1406246670Spluknet	if (cmsg_data == NULL) {
1407246670Spluknet		logmsg("malloc");
1408246670Spluknet		goto done;
1409246670Spluknet	}
1410159045Smaxim
1411246670Spluknet	if (type == 1) {
1412246670Spluknet		dbgmsg("setting LOCAL_CREDS");
1413246670Spluknet		val = 1;
1414246670Spluknet		if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
1415246670Spluknet			logmsg("setsockopt(LOCAL_CREDS)");
1416246670Spluknet			goto done;
1417159045Smaxim		}
1418246670Spluknet	}
1419159045Smaxim
1420246670Spluknet	if (sync_send() < 0)
1421246670Spluknet		goto done;
1422159045Smaxim
1423246670Spluknet	if (sock_type == SOCK_STREAM) {
1424246670Spluknet		fd2 = socket_accept(fd1);
1425246670Spluknet		if (fd2 < 0)
1426246670Spluknet			goto done;
1427246670Spluknet	} else
1428246670Spluknet		fd2 = fd1;
1429159045Smaxim
1430246670Spluknet	if (type == 2) {
1431246670Spluknet		dbgmsg("setting LOCAL_CREDS");
1432246670Spluknet		val = 1;
1433246670Spluknet		if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
1434246670Spluknet			logmsg("setsockopt(LOCAL_CREDS)");
1435246670Spluknet			goto done;
1436159045Smaxim		}
1437246670Spluknet		if (sync_send() < 0)
1438246670Spluknet			goto done;
1439246670Spluknet	}
1440159045Smaxim
1441246670Spluknet	rv = -1;
1442246670Spluknet	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1443246670Spluknet		dbgmsg("message #%u", i);
1444159045Smaxim
1445246670Spluknet		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1446246670Spluknet		if (message_recv(fd2, &msghdr) < 0) {
1447246670Spluknet			rv = -2;
1448246670Spluknet			break;
1449159045Smaxim		}
1450159045Smaxim
1451246670Spluknet		if (i > 1 && sock_type == SOCK_STREAM) {
1452246670Spluknet			if (check_msghdr(&msghdr, 0) < 0)
1453246670Spluknet				break;
1454246670Spluknet		} else {
1455246670Spluknet			if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1456246670Spluknet				break;
1457159045Smaxim
1458246670Spluknet			cmsghdr = CMSG_FIRSTHDR(&msghdr);
1459246670Spluknet			if (check_scm_creds_sockcred(cmsghdr) < 0)
1460246670Spluknet				break;
1461159045Smaxim		}
1462246670Spluknet	}
1463246670Spluknet	if (i > ipc_msg.msg_num)
1464246670Spluknet		rv = 0;
1465246670Spluknetdone:
1466246670Spluknet	free(cmsg_data);
1467246670Spluknet	if (sock_type == SOCK_STREAM && fd2 >= 0)
1468246670Spluknet		if (socket_close(fd2) < 0)
1469246670Spluknet			rv = -2;
1470246670Spluknet	return (rv);
1471246670Spluknet}
1472159045Smaxim
1473246670Spluknetstatic int
1474246670Spluknett_sockcred_1(void)
1475246670Spluknet{
1476246670Spluknet	u_int i;
1477246670Spluknet	int fd, rv, rv_client;
1478159045Smaxim
1479246670Spluknet	switch (client_fork()) {
1480246670Spluknet	case 0:
1481246670Spluknet		for (i = 1; i <= 2; ++i) {
1482246670Spluknet			dbgmsg("client #%u", i);
1483246670Spluknet			fd = socket_create();
1484246670Spluknet			if (fd < 0)
1485246670Spluknet				rv = -2;
1486246670Spluknet			else {
1487246670Spluknet				rv = t_sockcred_client(1, fd);
1488246670Spluknet				if (socket_close(fd) < 0)
1489246670Spluknet					rv = -2;
1490159045Smaxim			}
1491246670Spluknet			if (rv != 0)
1492246670Spluknet				break;
1493159045Smaxim		}
1494246670Spluknet		client_exit(rv);
1495246670Spluknet		break;
1496246670Spluknet	case 1:
1497246670Spluknet		fd = socket_create();
1498246670Spluknet		if (fd < 0)
1499246670Spluknet			rv = -2;
1500246670Spluknet		else {
1501246670Spluknet			rv = t_sockcred_server(1, fd);
1502246670Spluknet			if (rv == 0)
1503246670Spluknet				rv = t_sockcred_server(3, fd);
1504246670Spluknet			rv_client = client_wait();
1505246670Spluknet			if (rv == 0 || (rv == -2 && rv_client != 0))
1506246670Spluknet				rv = rv_client;
1507246670Spluknet			if (socket_close(fd) < 0)
1508246670Spluknet				rv = -2;
1509159045Smaxim		}
1510246670Spluknet		break;
1511246670Spluknet	default:
1512246670Spluknet		rv = -2;
1513159045Smaxim	}
1514159045Smaxim
1515246670Spluknet	return (rv);
1516246670Spluknet}
1517159045Smaxim
1518246670Spluknetstatic int
1519246670Spluknett_sockcred_2_client(int fd)
1520246670Spluknet{
1521246670Spluknet	return (t_sockcred_client(2, fd));
1522159045Smaxim}
1523159045Smaxim
1524159045Smaximstatic int
1525246670Spluknett_sockcred_2_server(int fd)
1526159045Smaxim{
1527246670Spluknet	return (t_sockcred_server(2, fd));
1528246670Spluknet}
1529159045Smaxim
1530246670Spluknetstatic int
1531246670Spluknett_sockcred_2(void)
1532246670Spluknet{
1533246670Spluknet	return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
1534246670Spluknet}
1535159045Smaxim
1536246670Spluknetstatic int
1537246670Spluknett_cmsgcred_sockcred_server(int fd1)
1538246670Spluknet{
1539246670Spluknet	struct msghdr msghdr;
1540246670Spluknet	struct iovec iov[1];
1541246670Spluknet	struct cmsghdr *cmsghdr;
1542246670Spluknet	void *cmsg_data, *cmsg1_data, *cmsg2_data;
1543246670Spluknet	size_t cmsg_size, cmsg1_size, cmsg2_size;
1544246670Spluknet	u_int i;
1545246670Spluknet	int fd2, rv, val;
1546159045Smaxim
1547246670Spluknet	fd2 = -1;
1548246670Spluknet	rv = -2;
1549159045Smaxim
1550246670Spluknet	cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
1551246670Spluknet	cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred));
1552246670Spluknet	cmsg1_data = malloc(cmsg1_size);
1553246670Spluknet	cmsg2_data = malloc(cmsg2_size);
1554246670Spluknet	if (cmsg1_data == NULL || cmsg2_data == NULL) {
1555246670Spluknet		logmsg("malloc");
1556246670Spluknet		goto done;
1557159045Smaxim	}
1558159045Smaxim
1559246670Spluknet	dbgmsg("setting LOCAL_CREDS");
1560246670Spluknet	val = 1;
1561246670Spluknet	if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
1562246670Spluknet		logmsg("setsockopt(LOCAL_CREDS)");
1563246670Spluknet		goto done;
1564159045Smaxim	}
1565159045Smaxim
1566246670Spluknet	if (sync_send() < 0)
1567246670Spluknet		goto done;
1568159045Smaxim
1569246670Spluknet	if (sock_type == SOCK_STREAM) {
1570246670Spluknet		fd2 = socket_accept(fd1);
1571246670Spluknet		if (fd2 < 0)
1572246670Spluknet			goto done;
1573246670Spluknet	} else
1574246670Spluknet		fd2 = fd1;
1575159045Smaxim
1576246670Spluknet	cmsg_data = cmsg1_data;
1577246670Spluknet	cmsg_size = cmsg1_size;
1578246670Spluknet	rv = -1;
1579246670Spluknet	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1580246670Spluknet		dbgmsg("message #%u", i);
1581159045Smaxim
1582246670Spluknet		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1583246670Spluknet		if (message_recv(fd2, &msghdr) < 0) {
1584246670Spluknet			rv = -2;
1585246670Spluknet			break;
1586246670Spluknet		}
1587246670Spluknet
1588246670Spluknet		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1589246670Spluknet			break;
1590246670Spluknet
1591246670Spluknet		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1592246670Spluknet		if (i == 1 || sock_type == SOCK_DGRAM) {
1593246670Spluknet			if (check_scm_creds_sockcred(cmsghdr) < 0)
1594246670Spluknet				break;
1595246670Spluknet		} else {
1596246670Spluknet			if (check_scm_creds_cmsgcred(cmsghdr) < 0)
1597246670Spluknet				break;
1598246670Spluknet		}
1599246670Spluknet
1600246670Spluknet		cmsg_data = cmsg2_data;
1601246670Spluknet		cmsg_size = cmsg2_size;
1602159045Smaxim	}
1603246670Spluknet	if (i > ipc_msg.msg_num)
1604246670Spluknet		rv = 0;
1605246670Spluknetdone:
1606246670Spluknet	free(cmsg1_data);
1607246670Spluknet	free(cmsg2_data);
1608246670Spluknet	if (sock_type == SOCK_STREAM && fd2 >= 0)
1609246670Spluknet		if (socket_close(fd2) < 0)
1610246670Spluknet			rv = -2;
1611246670Spluknet	return (rv);
1612159045Smaxim}
1613159045Smaxim
1614159045Smaximstatic int
1615246670Spluknett_cmsgcred_sockcred(void)
1616159045Smaxim{
1617246670Spluknet	return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
1618159045Smaxim}
1619159045Smaxim
1620159045Smaximstatic int
1621246670Spluknett_timeval_client(int fd)
1622159045Smaxim{
1623246670Spluknet	struct msghdr msghdr;
1624246670Spluknet	struct iovec iov[1];
1625246670Spluknet	void *cmsg_data;
1626246670Spluknet	size_t cmsg_size;
1627246670Spluknet	int rv;
1628159045Smaxim
1629246670Spluknet	if (sync_recv() < 0)
1630246670Spluknet		return (-2);
1631246670Spluknet
1632246670Spluknet	rv = -2;
1633246670Spluknet
1634246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
1635246670Spluknet	cmsg_data = malloc(cmsg_size);
1636246670Spluknet	if (cmsg_data == NULL) {
1637246670Spluknet		logmsg("malloc");
1638246670Spluknet		goto done;
1639246670Spluknet	}
1640246670Spluknet	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1641246670Spluknet	    SCM_TIMESTAMP, sizeof(struct timeval));
1642246670Spluknet
1643246670Spluknet	if (socket_connect(fd) < 0)
1644246670Spluknet		goto done;
1645246670Spluknet
1646246670Spluknet	if (message_sendn(fd, &msghdr) < 0)
1647246670Spluknet		goto done;
1648246670Spluknet
1649246670Spluknet	rv = 0;
1650246670Spluknetdone:
1651246670Spluknet	free(cmsg_data);
1652246670Spluknet	return (rv);
1653159045Smaxim}
1654159045Smaxim
1655159045Smaximstatic int
1656246670Spluknett_timeval_server(int fd1)
1657159045Smaxim{
1658246670Spluknet	struct msghdr msghdr;
1659246670Spluknet	struct iovec iov[1];
1660246670Spluknet	struct cmsghdr *cmsghdr;
1661246670Spluknet	void *cmsg_data;
1662246670Spluknet	size_t cmsg_size;
1663246670Spluknet	u_int i;
1664246670Spluknet	int fd2, rv;
1665159045Smaxim
1666246670Spluknet	if (sync_send() < 0)
1667159045Smaxim		return (-2);
1668159045Smaxim
1669246670Spluknet	fd2 = -1;
1670246670Spluknet	rv = -2;
1671159045Smaxim
1672246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
1673246670Spluknet	cmsg_data = malloc(cmsg_size);
1674246670Spluknet	if (cmsg_data == NULL) {
1675246670Spluknet		logmsg("malloc");
1676246670Spluknet		goto done;
1677159045Smaxim	}
1678159045Smaxim
1679246670Spluknet	if (sock_type == SOCK_STREAM) {
1680246670Spluknet		fd2 = socket_accept(fd1);
1681246670Spluknet		if (fd2 < 0)
1682246670Spluknet			goto done;
1683246670Spluknet	} else
1684246670Spluknet		fd2 = fd1;
1685159045Smaxim
1686246670Spluknet	rv = -1;
1687246670Spluknet	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1688246670Spluknet		dbgmsg("message #%u", i);
1689159045Smaxim
1690246670Spluknet		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1691246670Spluknet		if (message_recv(fd2, &msghdr) < 0) {
1692246670Spluknet			rv = -2;
1693246670Spluknet			break;
1694246670Spluknet		}
1695159045Smaxim
1696246670Spluknet		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1697246670Spluknet			break;
1698159045Smaxim
1699246670Spluknet		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1700246670Spluknet		if (check_scm_timestamp(cmsghdr) < 0)
1701246670Spluknet			break;
1702159045Smaxim	}
1703246670Spluknet	if (i > ipc_msg.msg_num)
1704246670Spluknet		rv = 0;
1705246670Spluknetdone:
1706246670Spluknet	free(cmsg_data);
1707246670Spluknet	if (sock_type == SOCK_STREAM && fd2 >= 0)
1708246670Spluknet		if (socket_close(fd2) < 0)
1709246670Spluknet			rv = -2;
1710246670Spluknet	return (rv);
1711246670Spluknet}
1712159045Smaxim
1713246670Spluknetstatic int
1714246670Spluknett_timeval(void)
1715246670Spluknet{
1716246670Spluknet	return (t_generic(t_timeval_client, t_timeval_server));
1717159045Smaxim}
1718159045Smaxim
1719246670Spluknetstatic int
1720246670Spluknett_bintime_client(int fd)
1721159045Smaxim{
1722246670Spluknet	struct msghdr msghdr;
1723159045Smaxim	struct iovec iov[1];
1724246670Spluknet	void *cmsg_data;
1725246670Spluknet	size_t cmsg_size;
1726246670Spluknet	int rv;
1727159045Smaxim
1728246670Spluknet	if (sync_recv() < 0)
1729246670Spluknet		return (-2);
1730159045Smaxim
1731246670Spluknet	rv = -2;
1732159045Smaxim
1733246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
1734246670Spluknet	cmsg_data = malloc(cmsg_size);
1735246670Spluknet	if (cmsg_data == NULL) {
1736246670Spluknet		logmsg("malloc");
1737246670Spluknet		goto done;
1738246670Spluknet	}
1739246670Spluknet	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1740246670Spluknet	    SCM_BINTIME, sizeof(struct bintime));
1741159045Smaxim
1742246670Spluknet	if (socket_connect(fd) < 0)
1743246670Spluknet		goto done;
1744159045Smaxim
1745246670Spluknet	if (message_sendn(fd, &msghdr) < 0)
1746246670Spluknet		goto done;
1747159045Smaxim
1748246670Spluknet	rv = 0;
1749246670Spluknetdone:
1750246670Spluknet	free(cmsg_data);
1751246670Spluknet	return (rv);
1752159045Smaxim}
1753159045Smaxim
1754159045Smaximstatic int
1755246670Spluknett_bintime_server(int fd1)
1756159045Smaxim{
1757246670Spluknet	struct msghdr msghdr;
1758159045Smaxim	struct iovec iov[1];
1759246670Spluknet	struct cmsghdr *cmsghdr;
1760246670Spluknet	void *cmsg_data;
1761246670Spluknet	size_t cmsg_size;
1762246670Spluknet	u_int i;
1763246670Spluknet	int fd2, rv;
1764159045Smaxim
1765246670Spluknet	if (sync_send() < 0)
1766246670Spluknet		return (-2);
1767246670Spluknet
1768246670Spluknet	fd2 = -1;
1769246670Spluknet	rv = -2;
1770246670Spluknet
1771246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
1772246670Spluknet	cmsg_data = malloc(cmsg_size);
1773246670Spluknet	if (cmsg_data == NULL) {
1774246670Spluknet		logmsg("malloc");
1775246670Spluknet		goto done;
1776246670Spluknet	}
1777246670Spluknet
1778159045Smaxim	if (sock_type == SOCK_STREAM) {
1779246670Spluknet		fd2 = socket_accept(fd1);
1780246670Spluknet		if (fd2 < 0)
1781246670Spluknet			goto done;
1782159045Smaxim	} else
1783159045Smaxim		fd2 = fd1;
1784159045Smaxim
1785246670Spluknet	rv = -1;
1786246670Spluknet	for (i = 1; i <= ipc_msg.msg_num; ++i) {
1787246670Spluknet		dbgmsg("message #%u", i);
1788159045Smaxim
1789246670Spluknet		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
1790246670Spluknet		if (message_recv(fd2, &msghdr) < 0) {
1791246670Spluknet			rv = -2;
1792246670Spluknet			break;
1793246670Spluknet		}
1794159045Smaxim
1795246670Spluknet		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
1796246670Spluknet			break;
1797159045Smaxim
1798246670Spluknet		cmsghdr = CMSG_FIRSTHDR(&msghdr);
1799246670Spluknet		if (check_scm_bintime(cmsghdr) < 0)
1800246670Spluknet			break;
1801159045Smaxim	}
1802246670Spluknet	if (i > ipc_msg.msg_num)
1803246670Spluknet		rv = 0;
1804246670Spluknetdone:
1805246670Spluknet	free(cmsg_data);
1806246670Spluknet	if (sock_type == SOCK_STREAM && fd2 >= 0)
1807246670Spluknet		if (socket_close(fd2) < 0)
1808246670Spluknet			rv = -2;
1809246670Spluknet	return (rv);
1810246670Spluknet}
1811159045Smaxim
1812246670Spluknetstatic int
1813246670Spluknett_bintime(void)
1814246670Spluknet{
1815246670Spluknet	return (t_generic(t_bintime_client, t_bintime_server));
1816246670Spluknet}
1817159045Smaxim
1818246670Spluknetstatic int
1819246670Spluknett_cmsg_len_client(int fd)
1820246670Spluknet{
1821246670Spluknet	struct msghdr msghdr;
1822246670Spluknet	struct iovec iov[1];
1823246670Spluknet	struct cmsghdr *cmsghdr;
1824246670Spluknet	void *cmsg_data;
1825246670Spluknet	size_t size, cmsg_size;
1826246670Spluknet	socklen_t socklen;
1827246670Spluknet	int rv;
1828159045Smaxim
1829246670Spluknet	if (sync_recv() < 0)
1830246670Spluknet		return (-2);
1831159045Smaxim
1832246670Spluknet	rv = -2;
1833246670Spluknet
1834246670Spluknet	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
1835246670Spluknet	cmsg_data = malloc(cmsg_size);
1836246670Spluknet	if (cmsg_data == NULL) {
1837246670Spluknet		logmsg("malloc");
1838159045Smaxim		goto done;
1839159045Smaxim	}
1840246670Spluknet	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
1841246670Spluknet	    SCM_CREDS, sizeof(struct cmsgcred));
1842246670Spluknet	cmsghdr = CMSG_FIRSTHDR(&msghdr);
1843159045Smaxim
1844246670Spluknet	if (socket_connect(fd) < 0)
1845159045Smaxim		goto done;
1846246670Spluknet
1847246670Spluknet	size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0;
1848246670Spluknet	rv = -1;
1849246670Spluknet	for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
1850246670Spluknet		cmsghdr->cmsg_len = socklen;
1851246670Spluknet		dbgmsg("send: data size %zu", size);
1852246670Spluknet		dbgmsg("send: msghdr.msg_controllen %u",
1853246670Spluknet		    (u_int)msghdr.msg_controllen);
1854246670Spluknet		dbgmsg("send: cmsghdr.cmsg_len %u",
1855246670Spluknet		    (u_int)cmsghdr->cmsg_len);
1856246670Spluknet		if (sendmsg(fd, &msghdr, 0) < 0)
1857246670Spluknet			continue;
1858246670Spluknet		logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
1859246670Spluknet		    (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
1860246670Spluknet		break;
1861159045Smaxim	}
1862246670Spluknet	if (socklen == CMSG_LEN(0))
1863246670Spluknet		rv = 0;
1864159045Smaxim
1865246670Spluknet	if (sync_send() < 0) {
1866246670Spluknet		rv = -2;
1867159045Smaxim		goto done;
1868159045Smaxim	}
1869246670Spluknetdone:
1870246670Spluknet	free(cmsg_data);
1871246670Spluknet	return (rv);
1872246670Spluknet}
1873159045Smaxim
1874246670Spluknetstatic int
1875246670Spluknett_cmsg_len_server(int fd1)
1876246670Spluknet{
1877246670Spluknet	int fd2, rv;
1878159045Smaxim
1879246670Spluknet	if (sync_send() < 0)
1880246670Spluknet		return (-2);
1881159045Smaxim
1882246670Spluknet	rv = -2;
1883246670Spluknet
1884246670Spluknet	if (sock_type == SOCK_STREAM) {
1885246670Spluknet		fd2 = socket_accept(fd1);
1886246670Spluknet		if (fd2 < 0)
1887246670Spluknet			goto done;
1888246670Spluknet	} else
1889246670Spluknet		fd2 = fd1;
1890246670Spluknet
1891246670Spluknet	if (sync_recv() < 0)
1892159045Smaxim		goto done;
1893159045Smaxim
1894246670Spluknet	rv = 0;
1895159045Smaximdone:
1896246670Spluknet	if (sock_type == SOCK_STREAM && fd2 >= 0)
1897246670Spluknet		if (socket_close(fd2) < 0)
1898246670Spluknet			rv = -2;
1899246670Spluknet	return (rv);
1900246670Spluknet}
1901159045Smaxim
1902246670Spluknetstatic int
1903246670Spluknett_cmsg_len(void)
1904246670Spluknet{
1905246670Spluknet	return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
1906159045Smaxim}
1907159045Smaxim
1908159045Smaximstatic int
1909246670Spluknett_peercred_client(int fd)
1910159045Smaxim{
1911246670Spluknet	struct xucred xucred;
1912246670Spluknet	socklen_t len;
1913159045Smaxim
1914246670Spluknet	if (sync_recv() < 0)
1915246670Spluknet		return (-1);
1916159045Smaxim
1917246670Spluknet	if (socket_connect(fd) < 0)
1918246670Spluknet		return (-1);
1919159045Smaxim
1920246670Spluknet	len = sizeof(xucred);
1921246670Spluknet	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
1922246670Spluknet		logmsg("getsockopt(LOCAL_PEERCRED)");
1923246670Spluknet		return (-1);
1924159045Smaxim	}
1925159045Smaxim
1926246670Spluknet	if (check_xucred(&xucred, len) < 0)
1927246670Spluknet		return (-1);
1928159045Smaxim
1929246670Spluknet	return (0);
1930246670Spluknet}
1931159045Smaxim
1932246670Spluknetstatic int
1933246670Spluknett_peercred_server(int fd1)
1934246670Spluknet{
1935246670Spluknet	struct xucred xucred;
1936246670Spluknet	socklen_t len;
1937246670Spluknet	int fd2, rv;
1938159045Smaxim
1939246670Spluknet	if (sync_send() < 0)
1940159045Smaxim		return (-2);
1941246670Spluknet
1942246670Spluknet	fd2 = socket_accept(fd1);
1943246670Spluknet	if (fd2 < 0)
1944246670Spluknet		return (-2);
1945246670Spluknet
1946246670Spluknet	len = sizeof(xucred);
1947246670Spluknet	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
1948246670Spluknet		logmsg("getsockopt(LOCAL_PEERCRED)");
1949246670Spluknet		rv = -2;
1950246670Spluknet		goto done;
1951159045Smaxim	}
1952159045Smaxim
1953246670Spluknet	if (check_xucred(&xucred, len) < 0) {
1954246670Spluknet		rv = -1;
1955246670Spluknet		goto done;
1956246670Spluknet	}
1957246670Spluknet
1958246670Spluknet	rv = 0;
1959246670Spluknetdone:
1960246670Spluknet	if (socket_close(fd2) < 0)
1961246670Spluknet		rv = -2;
1962246670Spluknet	return (rv);
1963159045Smaxim}
1964246670Spluknet
1965246670Spluknetstatic int
1966246670Spluknett_peercred(void)
1967246670Spluknet{
1968246670Spluknet	return (t_generic(t_peercred_client, t_peercred_server));
1969246670Spluknet}
1970