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