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