Path: blob/main/tools/regression/sockets/unix_cmsg/unix_cmsg.c
96317 views
/*-1* Copyright (c) 2005 Andrey Simonenko2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/types.h>27#include <sys/limits.h>28#include <sys/socket.h>29#include <sys/un.h>3031#include <err.h>32#include <inttypes.h>33#include <paths.h>34#include <signal.h>35#include <stdarg.h>36#include <stdbool.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>4142#include "uc_common.h"43#include "t_cmsgcred.h"44#include "t_bintime.h"45#include "t_generic.h"46#include "t_peercred.h"47#include "t_timeval.h"48#include "t_sockcred.h"49#include "t_cmsgcred_sockcred.h"50#include "t_cmsg_len.h"51#include "t_timespec_real.h"52#include "t_timespec_mono.h"5354/*55* There are tables with tests descriptions and pointers to test56* functions. Each t_*() function returns 0 if its test passed,57* -1 if its test failed, -2 if some system error occurred.58* If a test function returns -2, then a program exits.59*60* If a test function forks a client process, then it waits for its61* termination. If a return code of a client process is not equal62* to zero, or if a client process was terminated by a signal, then63* a test function returns -1 or -2 depending on exit status of64* a client process.65*66* Each function which can block, is run under TIMEOUT. If timeout67* occurs, then a test function returns -2 or a client process exits68* with a non-zero return code.69*/7071struct test_func {72int (*func)(void);73const char *desc;74};7576static const struct test_func test_stream_tbl[] = {77{78.func = NULL,79.desc = "All tests"80},81{82.func = t_cmsgcred,83.desc = "Sending, receiving cmsgcred"84},85{86.func = t_sockcred_1,87.desc = "Receiving sockcred (listening socket)"88},89{90.func = t_sockcred_2,91.desc = "Receiving sockcred (accepted socket)"92},93{94.func = t_cmsgcred_sockcred,95.desc = "Sending cmsgcred, receiving sockcred"96},97{98.func = t_timeval,99.desc = "Sending, receiving timeval"100},101{102.func = t_bintime,103.desc = "Sending, receiving bintime"104},105/*106* The testcase fails on 64-bit architectures (amd64), but passes on 32-bit107* architectures (i386); see bug 206543108*/109#ifndef __LP64__110{111.func = t_cmsg_len,112.desc = "Check cmsghdr.cmsg_len"113},114#endif115{116.func = t_peercred,117.desc = "Check LOCAL_PEERCRED socket option"118},119#if defined(SCM_REALTIME)120{121.func = t_timespec_real,122.desc = "Sending, receiving realtime"123},124#endif125#if defined(SCM_MONOTONIC)126{127.func = t_timespec_mono,128.desc = "Sending, receiving monotonic time (uptime)"129}130#endif131};132133#define TEST_STREAM_TBL_SIZE \134(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))135136static const struct test_func test_dgram_tbl[] = {137{138.func = NULL,139.desc = "All tests"140},141{142.func = t_cmsgcred,143.desc = "Sending, receiving cmsgcred"144},145{146.func = t_sockcred_2,147.desc = "Receiving sockcred"148},149{150.func = t_cmsgcred_sockcred,151.desc = "Sending cmsgcred, receiving sockcred"152},153{154.func = t_timeval,155.desc = "Sending, receiving timeval"156},157{158.func = t_bintime,159.desc = "Sending, receiving bintime"160},161#ifndef __LP64__162{163.func = t_cmsg_len,164.desc = "Check cmsghdr.cmsg_len"165},166#endif167#if defined(SCM_REALTIME)168{169.func = t_timespec_real,170.desc = "Sending, receiving realtime"171},172#endif173#if defined(SCM_MONOTONIC)174{175.func = t_timespec_mono,176.desc = "Sending, receiving monotonic time (uptime)"177}178#endif179};180181#define TEST_DGRAM_TBL_SIZE \182(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))183184static bool failed_flag = false;185186struct uc_cfg uc_cfg;187188static char work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";189190#define IPC_MSG_NUM_DEF 5191#define IPC_MSG_NUM_MAX 10192#define IPC_MSG_SIZE_DEF 7193#define IPC_MSG_SIZE_MAX 128194195static void196usage(bool verbose)197{198u_int i;199200printf("usage: %s [-dh] [-n num] [-s size] [-t type] "201"[-z value] [testno]\n", getprogname());202if (!verbose)203return;204printf("\n Options are:\n\205-d Output debugging information\n\206-h Output the help message and exit\n\207-n num Number of messages to send\n\208-s size Specify size of data for IPC\n\209-t type Specify socket type (stream, dgram) for tests\n\210-z value Do not send data in a message (bit 0x1), do not send\n\211data array associated with a cmsghdr structure (bit 0x2)\n\212testno Run one test by its number (require the -t option)\n\n");213printf(" Available tests for stream sockets:\n");214for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)215printf(" %u: %s\n", i, test_stream_tbl[i].desc);216printf("\n Available tests for datagram sockets:\n");217for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)218printf(" %u: %s\n", i, test_dgram_tbl[i].desc);219}220221static int222run_tests(int type, u_int testno1)223{224const struct test_func *tf;225u_int i, testno2, failed_num;226227uc_cfg.sock_type = type;228if (type == SOCK_STREAM) {229uc_cfg.sock_type_str = "SOCK_STREAM";230tf = test_stream_tbl;231i = TEST_STREAM_TBL_SIZE - 1;232} else {233uc_cfg.sock_type_str = "SOCK_DGRAM";234tf = test_dgram_tbl;235i = TEST_DGRAM_TBL_SIZE - 1;236}237if (testno1 == 0) {238testno1 = 1;239testno2 = i;240} else241testno2 = testno1;242243uc_output("Running tests for %s sockets:\n", uc_cfg.sock_type_str);244failed_num = 0;245for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {246uc_output(" %u: %s\n", i, tf->desc);247switch (tf->func()) {248case -1:249++failed_num;250break;251case -2:252uc_logmsgx("some system error or timeout occurred");253return (-1);254}255}256257if (failed_num != 0)258failed_flag = true;259260if (testno1 != testno2) {261if (failed_num == 0)262uc_output("-- all tests passed!\n");263else264uc_output("-- %u test%s failed!\n",265failed_num, failed_num == 1 ? "" : "s");266} else {267if (failed_num == 0)268uc_output("-- test passed!\n");269else270uc_output("-- test failed!\n");271}272273return (0);274}275276static int277init(void)278{279struct sigaction sigact;280size_t idx;281int rv;282283uc_cfg.proc_name = "SERVER";284285sigact.sa_handler = SIG_IGN;286sigact.sa_flags = 0;287sigemptyset(&sigact.sa_mask);288if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {289uc_logmsg("init: sigaction");290return (-1);291}292293if (uc_cfg.ipc_msg.buf_size == 0)294uc_cfg.ipc_msg.buf_send = uc_cfg.ipc_msg.buf_recv = NULL;295else {296uc_cfg.ipc_msg.buf_send = malloc(uc_cfg.ipc_msg.buf_size);297uc_cfg.ipc_msg.buf_recv = malloc(uc_cfg.ipc_msg.buf_size);298if (uc_cfg.ipc_msg.buf_send == NULL || uc_cfg.ipc_msg.buf_recv == NULL) {299uc_logmsg("init: malloc");300return (-1);301}302for (idx = 0; idx < uc_cfg.ipc_msg.buf_size; ++idx)303uc_cfg.ipc_msg.buf_send[idx] = (char)idx;304}305306uc_cfg.proc_cred.uid = getuid();307uc_cfg.proc_cred.euid = geteuid();308uc_cfg.proc_cred.gid = getgid();309uc_cfg.proc_cred.egid = getegid();310uc_cfg.proc_cred.gid_num = getgroups(0, (gid_t *)NULL);311if (uc_cfg.proc_cred.gid_num < 0) {312uc_logmsg("init: getgroups");313return (-1);314}315uc_cfg.proc_cred.gid_arr = malloc(uc_cfg.proc_cred.gid_num *316sizeof(*uc_cfg.proc_cred.gid_arr));317if (uc_cfg.proc_cred.gid_arr == NULL) {318uc_logmsg("init: malloc");319return (-1);320}321if (getgroups(uc_cfg.proc_cred.gid_num, uc_cfg.proc_cred.gid_arr) < 0) {322uc_logmsg("init: getgroups");323return (-1);324}325326memset(&uc_cfg.serv_addr_sun, 0, sizeof(uc_cfg.serv_addr_sun));327rv = snprintf(uc_cfg.serv_addr_sun.sun_path, sizeof(uc_cfg.serv_addr_sun.sun_path),328"%s/%s", work_dir, uc_cfg.proc_name);329if (rv < 0) {330uc_logmsg("init: snprintf");331return (-1);332}333if ((size_t)rv >= sizeof(uc_cfg.serv_addr_sun.sun_path)) {334uc_logmsgx("init: not enough space for socket pathname");335return (-1);336}337uc_cfg.serv_addr_sun.sun_family = PF_LOCAL;338uc_cfg.serv_addr_sun.sun_len = SUN_LEN(&uc_cfg.serv_addr_sun);339340return (0);341}342343int344main(int argc, char *argv[])345{346const char *errstr;347u_int testno, zvalue;348int opt, rv;349bool dgram_flag, stream_flag;350351memset(&uc_cfg, '\0', sizeof(uc_cfg));352uc_cfg.debug = false;353uc_cfg.server_flag = true;354uc_cfg.send_data_flag = true;355uc_cfg.send_array_flag = true;356uc_cfg.ipc_msg.buf_size = IPC_MSG_SIZE_DEF;357uc_cfg.ipc_msg.msg_num = IPC_MSG_NUM_DEF;358dgram_flag = stream_flag = false;359while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)360switch (opt) {361case 'd':362uc_cfg.debug = true;363break;364case 'h':365usage(true);366return (EXIT_SUCCESS);367case 'n':368uc_cfg.ipc_msg.msg_num = strtonum(optarg, 1,369IPC_MSG_NUM_MAX, &errstr);370if (errstr != NULL)371errx(EXIT_FAILURE, "option -n: number is %s",372errstr);373break;374case 's':375uc_cfg.ipc_msg.buf_size = strtonum(optarg, 0,376IPC_MSG_SIZE_MAX, &errstr);377if (errstr != NULL)378errx(EXIT_FAILURE, "option -s: number is %s",379errstr);380break;381case 't':382if (strcmp(optarg, "stream") == 0)383stream_flag = true;384else if (strcmp(optarg, "dgram") == 0)385dgram_flag = true;386else387errx(EXIT_FAILURE, "option -t: "388"wrong socket type");389break;390case 'z':391zvalue = strtonum(optarg, 0, 3, &errstr);392if (errstr != NULL)393errx(EXIT_FAILURE, "option -z: number is %s",394errstr);395if (zvalue & 0x1)396uc_cfg.send_data_flag = false;397if (zvalue & 0x2)398uc_cfg.send_array_flag = false;399break;400default:401usage(false);402return (EXIT_FAILURE);403}404405if (optind < argc) {406if (optind + 1 != argc)407errx(EXIT_FAILURE, "too many arguments");408testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);409if (errstr != NULL)410errx(EXIT_FAILURE, "test number is %s", errstr);411if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)412errx(EXIT_FAILURE, "given test %u for stream "413"sockets does not exist", testno);414if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)415errx(EXIT_FAILURE, "given test %u for datagram "416"sockets does not exist", testno);417} else418testno = 0;419420if (!dgram_flag && !stream_flag) {421if (testno != 0)422errx(EXIT_FAILURE, "particular test number "423"can be used with the -t option only");424dgram_flag = stream_flag = true;425}426427if (mkdtemp(work_dir) == NULL)428err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);429430rv = EXIT_FAILURE;431if (init() < 0)432goto done;433434if (stream_flag)435if (run_tests(SOCK_STREAM, testno) < 0)436goto done;437if (dgram_flag)438if (run_tests(SOCK_DGRAM, testno) < 0)439goto done;440441rv = EXIT_SUCCESS;442done:443if (rmdir(work_dir) < 0) {444uc_logmsg("rmdir(%s)", work_dir);445rv = EXIT_FAILURE;446}447return (failed_flag ? EXIT_FAILURE : rv);448}449450451