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