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/types.h>
31 #include <sys/limits.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 
35 #include <err.h>
36 #include <inttypes.h>
37 #include <paths.h>
38 #include <signal.h>
39 #include <stdarg.h>
40 #include <stdbool.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #include "uc_common.h"
47 #include "t_cmsgcred.h"
48 #include "t_bintime.h"
49 #include "t_generic.h"
50 #include "t_peercred.h"
51 #include "t_timeval.h"
52 #include "t_sockcred.h"
53 #include "t_cmsgcred_sockcred.h"
54 #include "t_cmsg_len.h"
55 #include "t_timespec_real.h"
56 #include "t_timespec_mono.h"
57 
58 /*
59  * There are tables with tests descriptions and pointers to test
60  * functions.  Each t_*() function returns 0 if its test passed,
61  * -1 if its test failed, -2 if some system error occurred.
62  * If a test function returns -2, then a program exits.
63  *
64  * If a test function forks a client process, then it waits for its
65  * termination.  If a return code of a client process is not equal
66  * to zero, or if a client process was terminated by a signal, then
67  * a test function returns -1 or -2 depending on exit status of
68  * a client process.
69  *
70  * Each function which can block, is run under TIMEOUT.  If timeout
71  * occurs, then a test function returns -2 or a client process exits
72  * with a non-zero return code.
73  */
74 
75 struct test_func {
76 	int		(*func)(void);
77 	const char	*desc;
78 };
79 
80 static const struct test_func test_stream_tbl[] = {
81 	{
82 	  .func = NULL,
83 	  .desc = "All tests"
84 	},
85 	{
86 	  .func = t_cmsgcred,
87 	  .desc = "Sending, receiving cmsgcred"
88 	},
89 	{
90 	  .func = t_sockcred_1,
91 	  .desc = "Receiving sockcred (listening socket)"
92 	},
93 	{
94 	  .func = t_sockcred_2,
95 	  .desc = "Receiving sockcred (accepted socket)"
96 	},
97 	{
98 	  .func = t_cmsgcred_sockcred,
99 	  .desc = "Sending cmsgcred, receiving sockcred"
100 	},
101 	{
102 	  .func = t_timeval,
103 	  .desc = "Sending, receiving timeval"
104 	},
105 	{
106 	  .func = t_bintime,
107 	  .desc = "Sending, receiving bintime"
108 	},
109 /*
110  * The testcase fails on 64-bit architectures (amd64), but passes on 32-bit
111  * architectures (i386); see bug 206543
112  */
113 #ifndef __LP64__
114 	{
115 	  .func = t_cmsg_len,
116 	  .desc = "Check cmsghdr.cmsg_len"
117 	},
118 #endif
119 	{
120 	  .func = t_peercred,
121 	  .desc = "Check LOCAL_PEERCRED socket option"
122 	},
123 #if defined(SCM_REALTIME)
124 	{
125 	  .func = t_timespec_real,
126 	  .desc = "Sending, receiving realtime"
127 	},
128 #endif
129 #if defined(SCM_MONOTONIC)
130 	{
131 	  .func = t_timespec_mono,
132 	  .desc = "Sending, receiving monotonic time (uptime)"
133 	}
134 #endif
135 };
136 
137 #define TEST_STREAM_TBL_SIZE \
138 	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
139 
140 static const struct test_func test_dgram_tbl[] = {
141 	{
142 	  .func = NULL,
143 	  .desc = "All tests"
144 	},
145 	{
146 	  .func = t_cmsgcred,
147 	  .desc = "Sending, receiving cmsgcred"
148 	},
149 	{
150 	  .func = t_sockcred_2,
151 	  .desc = "Receiving sockcred"
152 	},
153 	{
154 	  .func = t_cmsgcred_sockcred,
155 	  .desc = "Sending cmsgcred, receiving sockcred"
156 	},
157 	{
158 	  .func = t_timeval,
159 	  .desc = "Sending, receiving timeval"
160 	},
161 	{
162 	  .func = t_bintime,
163 	  .desc = "Sending, receiving bintime"
164 	},
165 #ifndef __LP64__
166 	{
167 	  .func = t_cmsg_len,
168 	  .desc = "Check cmsghdr.cmsg_len"
169 	},
170 #endif
171 #if defined(SCM_REALTIME)
172 	{
173 	  .func = t_timespec_real,
174 	  .desc = "Sending, receiving realtime"
175 	},
176 #endif
177 #if defined(SCM_MONOTONIC)
178 	{
179 	  .func = t_timespec_mono,
180 	  .desc = "Sending, receiving monotonic time (uptime)"
181 	}
182 #endif
183 };
184 
185 #define TEST_DGRAM_TBL_SIZE \
186 	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
187 
188 static bool	failed_flag = false;
189 
190 struct uc_cfg uc_cfg;
191 
192 static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
193 
194 #define IPC_MSG_NUM_DEF		5
195 #define IPC_MSG_NUM_MAX		10
196 #define IPC_MSG_SIZE_DEF	7
197 #define IPC_MSG_SIZE_MAX	128
198 
199 static void
200 usage(bool verbose)
201 {
202 	u_int i;
203 
204 	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
205 	    "[-z value] [testno]\n", getprogname());
206 	if (!verbose)
207 		return;
208 	printf("\n Options are:\n\
209   -d            Output debugging information\n\
210   -h            Output the help message and exit\n\
211   -n num        Number of messages to send\n\
212   -s size       Specify size of data for IPC\n\
213   -t type       Specify socket type (stream, dgram) for tests\n\
214   -z value      Do not send data in a message (bit 0x1), do not send\n\
215                 data array associated with a cmsghdr structure (bit 0x2)\n\
216   testno        Run one test by its number (require the -t option)\n\n");
217 	printf(" Available tests for stream sockets:\n");
218 	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
219 		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
220 	printf("\n Available tests for datagram sockets:\n");
221 	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
222 		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
223 }
224 
225 static int
226 run_tests(int type, u_int testno1)
227 {
228 	const struct test_func *tf;
229 	u_int i, testno2, failed_num;
230 
231 	uc_cfg.sock_type = type;
232 	if (type == SOCK_STREAM) {
233 		uc_cfg.sock_type_str = "SOCK_STREAM";
234 		tf = test_stream_tbl;
235 		i = TEST_STREAM_TBL_SIZE - 1;
236 	} else {
237 		uc_cfg.sock_type_str = "SOCK_DGRAM";
238 		tf = test_dgram_tbl;
239 		i = TEST_DGRAM_TBL_SIZE - 1;
240 	}
241 	if (testno1 == 0) {
242 		testno1 = 1;
243 		testno2 = i;
244 	} else
245 		testno2 = testno1;
246 
247 	uc_output("Running tests for %s sockets:\n", uc_cfg.sock_type_str);
248 	failed_num = 0;
249 	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
250 		uc_output("  %u: %s\n", i, tf->desc);
251 		switch (tf->func()) {
252 		case -1:
253 			++failed_num;
254 			break;
255 		case -2:
256 			uc_logmsgx("some system error or timeout occurred");
257 			return (-1);
258 		}
259 	}
260 
261 	if (failed_num != 0)
262 		failed_flag = true;
263 
264 	if (testno1 != testno2) {
265 		if (failed_num == 0)
266 			uc_output("-- all tests passed!\n");
267 		else
268 			uc_output("-- %u test%s failed!\n",
269 			    failed_num, failed_num == 1 ? "" : "s");
270 	} else {
271 		if (failed_num == 0)
272 			uc_output("-- test passed!\n");
273 		else
274 			uc_output("-- test failed!\n");
275 	}
276 
277 	return (0);
278 }
279 
280 static int
281 init(void)
282 {
283 	struct sigaction sigact;
284 	size_t idx;
285 	int rv;
286 
287 	uc_cfg.proc_name = "SERVER";
288 
289 	sigact.sa_handler = SIG_IGN;
290 	sigact.sa_flags = 0;
291 	sigemptyset(&sigact.sa_mask);
292 	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
293 		uc_logmsg("init: sigaction");
294 		return (-1);
295 	}
296 
297 	if (uc_cfg.ipc_msg.buf_size == 0)
298 		uc_cfg.ipc_msg.buf_send = uc_cfg.ipc_msg.buf_recv = NULL;
299 	else {
300 		uc_cfg.ipc_msg.buf_send = malloc(uc_cfg.ipc_msg.buf_size);
301 		uc_cfg.ipc_msg.buf_recv = malloc(uc_cfg.ipc_msg.buf_size);
302 		if (uc_cfg.ipc_msg.buf_send == NULL || uc_cfg.ipc_msg.buf_recv == NULL) {
303 			uc_logmsg("init: malloc");
304 			return (-1);
305 		}
306 		for (idx = 0; idx < uc_cfg.ipc_msg.buf_size; ++idx)
307 			uc_cfg.ipc_msg.buf_send[idx] = (char)idx;
308 	}
309 
310 	uc_cfg.proc_cred.uid = getuid();
311 	uc_cfg.proc_cred.euid = geteuid();
312 	uc_cfg.proc_cred.gid = getgid();
313 	uc_cfg.proc_cred.egid = getegid();
314 	uc_cfg.proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
315 	if (uc_cfg.proc_cred.gid_num < 0) {
316 		uc_logmsg("init: getgroups");
317 		return (-1);
318 	}
319 	uc_cfg.proc_cred.gid_arr = malloc(uc_cfg.proc_cred.gid_num *
320 	    sizeof(*uc_cfg.proc_cred.gid_arr));
321 	if (uc_cfg.proc_cred.gid_arr == NULL) {
322 		uc_logmsg("init: malloc");
323 		return (-1);
324 	}
325 	if (getgroups(uc_cfg.proc_cred.gid_num, uc_cfg.proc_cred.gid_arr) < 0) {
326 		uc_logmsg("init: getgroups");
327 		return (-1);
328 	}
329 
330 	memset(&uc_cfg.serv_addr_sun, 0, sizeof(uc_cfg.serv_addr_sun));
331 	rv = snprintf(uc_cfg.serv_addr_sun.sun_path, sizeof(uc_cfg.serv_addr_sun.sun_path),
332 	    "%s/%s", work_dir, uc_cfg.proc_name);
333 	if (rv < 0) {
334 		uc_logmsg("init: snprintf");
335 		return (-1);
336 	}
337 	if ((size_t)rv >= sizeof(uc_cfg.serv_addr_sun.sun_path)) {
338 		uc_logmsgx("init: not enough space for socket pathname");
339 		return (-1);
340 	}
341 	uc_cfg.serv_addr_sun.sun_family = PF_LOCAL;
342 	uc_cfg.serv_addr_sun.sun_len = SUN_LEN(&uc_cfg.serv_addr_sun);
343 
344 	return (0);
345 }
346 
347 int
348 main(int argc, char *argv[])
349 {
350 	const char *errstr;
351 	u_int testno, zvalue;
352 	int opt, rv;
353 	bool dgram_flag, stream_flag;
354 
355 	memset(&uc_cfg, '\0', sizeof(uc_cfg));
356 	uc_cfg.debug = false;
357 	uc_cfg.server_flag = true;
358 	uc_cfg.send_data_flag = true;
359 	uc_cfg.send_array_flag = true;
360 	uc_cfg.ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
361 	uc_cfg.ipc_msg.msg_num = IPC_MSG_NUM_DEF;
362 	dgram_flag = stream_flag = false;
363 	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
364 		switch (opt) {
365 		case 'd':
366 			uc_cfg.debug = true;
367 			break;
368 		case 'h':
369 			usage(true);
370 			return (EXIT_SUCCESS);
371 		case 'n':
372 			uc_cfg.ipc_msg.msg_num = strtonum(optarg, 1,
373 			    IPC_MSG_NUM_MAX, &errstr);
374 			if (errstr != NULL)
375 				errx(EXIT_FAILURE, "option -n: number is %s",
376 				    errstr);
377 			break;
378 		case 's':
379 			uc_cfg.ipc_msg.buf_size = strtonum(optarg, 0,
380 			    IPC_MSG_SIZE_MAX, &errstr);
381 			if (errstr != NULL)
382 				errx(EXIT_FAILURE, "option -s: number is %s",
383 				    errstr);
384 			break;
385 		case 't':
386 			if (strcmp(optarg, "stream") == 0)
387 				stream_flag = true;
388 			else if (strcmp(optarg, "dgram") == 0)
389 				dgram_flag = true;
390 			else
391 				errx(EXIT_FAILURE, "option -t: "
392 				    "wrong socket type");
393 			break;
394 		case 'z':
395 			zvalue = strtonum(optarg, 0, 3, &errstr);
396 			if (errstr != NULL)
397 				errx(EXIT_FAILURE, "option -z: number is %s",
398 				    errstr);
399 			if (zvalue & 0x1)
400 				uc_cfg.send_data_flag = false;
401 			if (zvalue & 0x2)
402 				uc_cfg.send_array_flag = false;
403 			break;
404 		default:
405 			usage(false);
406 			return (EXIT_FAILURE);
407 		}
408 
409 	if (optind < argc) {
410 		if (optind + 1 != argc)
411 			errx(EXIT_FAILURE, "too many arguments");
412 		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
413 		if (errstr != NULL)
414 			errx(EXIT_FAILURE, "test number is %s", errstr);
415 		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
416 			errx(EXIT_FAILURE, "given test %u for stream "
417 			    "sockets does not exist", testno);
418 		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
419 			errx(EXIT_FAILURE, "given test %u for datagram "
420 			    "sockets does not exist", testno);
421 	} else
422 		testno = 0;
423 
424 	if (!dgram_flag && !stream_flag) {
425 		if (testno != 0)
426 			errx(EXIT_FAILURE, "particular test number "
427 			    "can be used with the -t option only");
428 		dgram_flag = stream_flag = true;
429 	}
430 
431 	if (mkdtemp(work_dir) == NULL)
432 		err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
433 
434 	rv = EXIT_FAILURE;
435 	if (init() < 0)
436 		goto done;
437 
438 	if (stream_flag)
439 		if (run_tests(SOCK_STREAM, testno) < 0)
440 			goto done;
441 	if (dgram_flag)
442 		if (run_tests(SOCK_DGRAM, testno) < 0)
443 			goto done;
444 
445 	rv = EXIT_SUCCESS;
446 done:
447 	if (rmdir(work_dir) < 0) {
448 		uc_logmsg("rmdir(%s)", work_dir);
449 		rv = EXIT_FAILURE;
450 	}
451 	return (failed_flag ? EXIT_FAILURE : rv);
452 }
453