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