/* * Test Program for Unix Domain Sockets * * Overview: This program tests Unix Domain Sockets. It attempts * to exercise the functions associated with Unix Domain Sockets. * It also attempts to make sure all of the functions which handle * file/socket descriptors work correctly when given a socket * descriptor for a Unix domain socket. It also implicitly checks * for the existance of constants like AF_UNIX and structures like * sockaddr_un (it won't compile if they aren't defined). Besides * checking that the sockets work properly, this test program also * checks that the errors returned conform to the POSIX 2008 * standards. Some tests are omitted as they could adversely affect * the operation of the host system. For example, implementing a test * for socket() failing with errno = ENFILE would require using up all * of the file descriptors supported by the OS (defined in * /proc/sys/fs/file-max on Linux); this could cause problems for * daemons and other processes running on the system. Some tests are * omitted because they would require changes to libc or the kernel. * For example, getting EINTR would require delaying the system call * execution time long enough to raise a signal to interupt it. Some * tests were omitted because the particular errors cannot occur when * using Unix domain sockets. For example, write() will never fail with * ENETDOWN because Unix domain sockets don't use network interfaces. * * Structure: Some functions can be tested or partially tested without * making a connection, socket() for example. These have test * functions like test_NAME(). The functionality that needs two way * communication is contained within test_xfer(). * * Functions Tested: accept(), bind(), close(), connect(), dup(), * dup2(), fstat(), getpeername(), getsockname(), getsockopt(), * listen(), read(), readv(), recv(), recvfrom(), recvmsg(), select(), * send(), sendmsg(), sendto(), setsockopt(), shutdown(), socket(), * socketpair(), write(), writev() */ #define DEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ansi.h" /* Maximum number of errors that we'll allow to occur before this test * program gives us and quits. */ #define MAX_ERROR 4 /* Use the common testing code instead of reinventing the wheel. */ #include "common.c" /* path of the unix domain socket */ #define TEST_SUN_PATH "test.sock" #define TEST_SUN_PATHB "testb.sock" /* filenames for symlinks -- we link these to each other to test ELOOP .*/ #define TEST_SYM_A "test.a" #define TEST_SYM_B "test.b" /* buffer for send/recv */ #define BUFSIZE (128) #define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S" /* socket types supported */ int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}; /* timestamps for debug and error logs */ char *get_timestamp(void) { struct tm *tm; time_t t; size_t len; char *s; len = sizeof(char) * 32; t = time(NULL); if (t == -1) { return NULL; } tm = gmtime(&t); if (tm == NULL) { return NULL; } s = (char *) malloc(len); if (!s) { perror("malloc"); return NULL; } memset(s, '\0', len); strftime(s, len - 1, ISO8601_FORMAT, tm); return s; } /* macro to display information about a failed test and increment the errct */ #define test_fail(msg) \ do { \ char *timestamp; \ timestamp = get_timestamp(); \ if (errct == 0) fprintf(stderr, "\n"); \ fprintf(stderr, "[ERROR][%s] (%s Line %d) %s [pid=%d:errno=%d:%s]\n", \ timestamp, __FILE__, __LINE__, msg, getpid(), \ errno, strerror(errno)); \ fflush(stderr); \ if (timestamp != NULL) { \ free(timestamp); \ timestamp = NULL; \ } \ errct++; \ if (errct++ > MAX_ERROR) { \ printf("Too many errors; test aborted\n"); \ cleanup(); \ exit(1); \ } \ } while (0) #if DEBUG == 1 /* macros to display debugging information */ #define debug(msg) \ do { \ char *timestamp; \ timestamp = get_timestamp(); \ fprintf(stdout,"[DEBUG][%s] (%s:%d) %s [pid=%d]\n", \ timestamp, __FILE__, __LINE__, msg, getpid()); \ fflush(stdout); \ if (timestamp != NULL) { \ free(timestamp); \ timestamp = NULL; \ } \ } while (0) #else #define debug(msg) \ do { /* nothing */; } while (0) #endif #define SOCKET(sd,domain,type,protocol) \ do { \ errno = 0; \ sd = socket(domain, type, protocol); \ if (sd == -1) { \ test_fail("sd = socket(domain, type, protocol) failed");\ } \ } while (0) #define UNLINK(path) \ do { \ int rc; \ errno = 0; \ rc = unlink(path); \ if (rc == -1 && errno != ENOENT) { \ test_fail("unlink(path) failed"); \ } \ } while(0) #define SYMLINK(oldpath,newpath) \ do { \ int rc; \ errno = 0; \ rc = symlink(oldpath,newpath); \ if (rc == -1) { \ test_fail("symlink(oldpath,newpath) failed"); \ } \ } while(0) #define CLOSE(sd) \ do { \ int rc; \ errno = 0; \ rc = close(sd); \ if (rc == -1) { \ test_fail("close(sd) failed"); \ } \ } while (0) void test_socket(void) { struct stat statbuf, statbuf2; int sd, sd2; int rc; int i; debug("entering test_socket()"); debug("Test socket() with an unsupported address family"); errno = 0; sd = socket(-1, SOCK_STREAM, 0); if (!(sd == -1 && errno == EAFNOSUPPORT)) { test_fail("socket"); if (sd != -1) { CLOSE(sd); } } debug("Test socket() with all available FDs open by this process"); for (i = 3; i < getdtablesize(); i++) { rc = open("/dev/null", O_RDONLY); if (rc == -1) { test_fail("we couldn't open /dev/null for read"); } } errno = 0; sd = socket(PF_UNIX, SOCK_STREAM, 0); if (!(sd == -1 && errno == EMFILE)) { test_fail("socket() call with all fds open should fail"); if (sd != -1) { CLOSE(sd); } } for (i = 3; i < getdtablesize(); i++) { CLOSE(i); } debug("Test socket() with an mismatched protocol"); errno = 0; sd = socket(PF_UNIX, SOCK_STREAM, 4); if (!(sd == -1 && errno == EPROTONOSUPPORT)) { test_fail("socket() should fail with errno = EPROTONOSUPPORT"); if (sd != -1) { CLOSE(sd); } } debug("Test socket() success"); /* * open 2 sockets at once and *then* close them. * This will test that /dev/uds is cloning properly. */ SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0); rc = fstat(sd, &statbuf); if (rc == -1) { test_fail("fstat failed on sd"); } rc = fstat(sd2, &statbuf2); if (rc == -1) { test_fail("fstat failed on sd2"); } if (statbuf.st_dev == statbuf2.st_dev) { test_fail("/dev/uds isn't being cloned"); } CLOSE(sd2); CLOSE(sd); debug("leaving test_socket()"); } void test_header(void) { struct sockaddr_un sun; debug("entering test_header()"); sun.sun_family = AF_UNIX; sun.sun_path[0] = 'x'; sun.sun_path[1] = 'x'; sun.sun_path[2] = 'x'; sun.sun_path[3] = '\0'; if (SUN_LEN(&sun) != 4) { test_fail("SUN_LEN(&sun) should be 4"); } if (PF_UNIX != PF_LOCAL || PF_UNIX != PF_FILE || PF_UNIX != AF_UNIX) { test_fail("PF_UNIX, PF_LOCAL, PF_FILE, and AF_UNIX"); } } void test_socketpair(void) { char buf[128]; struct sockaddr_un addr; int socket_vector[2]; int rc; int i; debug("entering test_socketpair()"); UNLINK(TEST_SUN_PATH); memset(&addr, '\0', sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); debug("Testing socketpair() success"); rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector); if (rc == -1) { test_fail("socketpair() should have worked"); } debug("Testing a simple read/write using sockets from socketpair()"); memset(buf, '\0', sizeof(buf)); strncpy(buf, "Howdy Partner", sizeof(buf) - 1); rc = write(socket_vector[0], buf, sizeof(buf)); if (rc == -1) { test_fail("write(sd, buf, sizeof(buf)) failed unexpectedly"); } memset(buf, '\0', sizeof(buf)); rc = read(socket_vector[1], buf, sizeof(buf)); if (rc == -1) { test_fail("read() failed unexpectedly"); } if (strncmp(buf, "Howdy Partner", strlen("Howdy Partner")) != 0) { test_fail("We did not read what we wrote"); } CLOSE(socket_vector[0]); CLOSE(socket_vector[1]); debug("Test socketpair() with all FDs open by this process"); for (i = 3; i < getdtablesize(); i++) { rc = open("/dev/null", O_RDONLY); if (rc == -1) { test_fail("we couldn't open /dev/null for read"); } } rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector); if (!(rc == -1 && errno == EMFILE)) { test_fail("socketpair() should have failed with EMFILE"); } for (i = 3; i < getdtablesize(); i++) { CLOSE(i); } rc = socketpair(PF_UNIX, SOCK_STREAM, 4, socket_vector); if (!(rc == -1 && errno == EPROTONOSUPPORT)) { test_fail("socketpair() should have failed"); } debug("leaving test_socketpair()"); } void test_ucred(void) { struct ucred credentials; socklen_t ucred_length; uid_t euid = geteuid(); gid_t egid = getegid(); int sv[2]; int rc; debug("Test credentials passing"); ucred_length = sizeof(struct ucred); rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); if (rc == -1) { test_fail("socketpair(PF_UNIX, SOCK_STREAM, 0, sv) failed"); } memset(&credentials, '\0', ucred_length); rc = getsockopt(sv[0], SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length); if (rc == -1) { test_fail("getsockopt(SO_PEERCRED) failed"); } else if (credentials.pid != getpid() || credentials.uid != geteuid() || credentials.gid != getegid()) { /* printf("%d=%d %d=%d %d=%d",credentials.pid, getpid(), credentials.uid, geteuid(), credentials.gid, getegid()); */ test_fail("Credential passing gave us the wrong cred"); } rc = getpeereid(sv[0], &euid, &egid); if (rc == -1) { test_fail("getpeereid(sv[0], &euid, &egid) failed"); } else if (credentials.uid != euid || credentials.gid != egid) { test_fail("getpeereid() didn't give the correct euid/egid"); } CLOSE(sv[0]); CLOSE(sv[1]); } void test_getsockname(void) { int sd; int rc; struct sockaddr_un addr, sock_addr; socklen_t sock_addr_len; memset(&addr, '\0', sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); debug("Test bind() success"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("bind() should have worked"); } debug("Test getsockname() success"); memset(&sock_addr, '\0', sizeof(struct sockaddr_un)); sock_addr_len = sizeof(struct sockaddr_un); rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len); if (rc == -1) { test_fail("getsockname() should have worked"); } if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path, addr.sun_path, sizeof(sock_addr.sun_path) - 1) == 0)) { test_fail("getsockname() did return the right address"); } CLOSE(sd); } void test_bind(void) { struct sockaddr_un addr; struct sockaddr_un sock_addr; socklen_t sock_addr_len; int sd; int sd2; int rc; int on; debug("entering test_bind()"); on = 1; UNLINK(TEST_SUN_PATH); memset(&addr, '\0', sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); debug("Test bind() success"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("bind() should have worked"); } debug("Test getsockname() success"); memset(&sock_addr, '\0', sizeof(struct sockaddr_un)); sock_addr_len = sizeof(struct sockaddr_un); rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len); if (rc == -1) { test_fail("getsockname() should have worked"); } if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path, addr.sun_path, sizeof(sock_addr.sun_path) - 1) == 0)) { test_fail("getsockname() didn't return the right addr"); } debug("Test bind() with a address that has already been bind()'d"); SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0); errno = 0; rc = bind(sd2, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (!((rc == -1) && (errno == EADDRINUSE))) { test_fail("bind() should have failed with EADDRINUSE"); } CLOSE(sd2); CLOSE(sd); UNLINK(TEST_SUN_PATH); debug("Test bind() with an empty sun_path"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); memset(addr.sun_path, '\0', sizeof(addr.sun_path)); errno = 0; rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (!(rc == -1 && errno == ENOENT)) { test_fail("bind() should have failed with ENOENT"); } CLOSE(sd); debug("Test bind() with a NULL address"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); errno = 0; rc = bind(sd, (struct sockaddr *) NULL, sizeof(struct sockaddr_un)); if (!((rc == -1) && (errno == EFAULT))) { test_fail("bind() should have failed with EFAULT"); } CLOSE(sd); debug("Test bind() using a symlink loop"); UNLINK(TEST_SUN_PATH); UNLINK(TEST_SYM_A); UNLINK(TEST_SYM_B); SYMLINK(TEST_SYM_A, TEST_SYM_B); SYMLINK(TEST_SYM_B, TEST_SYM_A); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1); errno = 0; rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (!((rc == -1) && (errno == ELOOP))) { test_fail("bind() should have failed with ELOOP"); } CLOSE(sd); UNLINK(TEST_SUN_PATH); UNLINK(TEST_SYM_A); UNLINK(TEST_SYM_B); debug("leaving test_bind()"); } void test_listen(void) { int sd; int rc; debug("entering test_listen()"); debug("Test listen() with a bad file descriptor"); errno = 0; rc = listen(-1, 0); if (!(rc == -1 && errno == EBADF)) { test_fail("listen(-1, 0) should have failed"); } debug("Test listen() with a non-socket file descriptor"); errno = 0; rc = listen(0, 0); if (!(rc == -1 && errno == ENOTTY)) { test_fail("listen(0, 0) should have failed"); } debug("leaving test_listen()"); } void test_shutdown(void) { int how[3] = { SHUT_RD, SHUT_WR, SHUT_RDWR }; int sd; int rc; int i; debug("entering test_shutdown()"); /* test for each direction (read, write, read-write) */ for (i = 0; i < 3; i++) { debug("test shutdown() with an invalid descriptor"); errno = 0; rc = shutdown(-1, how[i]); if (!(rc == -1 && errno == EBADF)) { test_fail("shutdown(-1, how[i]) should have failed"); } debug("test shutdown() with a non-socket descriptor"); errno = 0; rc = shutdown(0, how[i]); if (!(rc == -1 && errno == ENOSYS)) { test_fail("shutdown() should have failed with ENOSYS"); } debug("test shutdown() with a socket that is not connected"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); errno = 0; rc = shutdown(sd, how[i]); if (!(rc == -1 && errno == ENOTCONN)) { test_fail("shutdown() should have failed"); } CLOSE(sd); } SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); errno = 0; rc = shutdown(sd, -1); if (!(rc == -1 && errno == ENOTCONN)) { test_fail("shutdown(sd, -1) should have failed with ENOTCONN"); } CLOSE(sd); debug("leaving test_shutdown()"); } void test_close(void) { struct sockaddr_un addr; int sd, sd2; int rc; debug("entering test_close()"); UNLINK(TEST_SUN_PATH); memset(&addr, '\0', sizeof(struct sockaddr_un)); strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; debug("Test close() success"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc != 0) { test_fail("bind() should have worked"); } CLOSE(sd); debug("Close an already closed file descriptor"); errno = 0; rc = close(sd); if (!(rc == -1 && errno == EBADF)) { test_fail("close(sd) should have failed with EBADF"); } UNLINK(TEST_SUN_PATH); debug("dup()'ing a file descriptor and closing both should work"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc != 0) { test_fail("bind() should have worked"); } errno = 0; sd2 = dup(sd); if (sd2 == -1) { test_fail("dup(sd) should have worked"); } else { CLOSE(sd2); CLOSE(sd); } UNLINK(TEST_SUN_PATH); debug("leaving test_close()"); } void test_sockopts(void) { int i; int rc; int sd; int option_value; int option_value_orig; socklen_t option_len; debug("entering test_sockopts()"); for (i = 0; i < 3; i++) { SOCKET(sd, PF_UNIX, types[i], 0); debug("Test setsockopt() works"); option_value = 0; option_len = sizeof(option_value); errno = 0; rc = getsockopt(sd, SOL_SOCKET, SO_TYPE, &option_value, &option_len); if (rc != 0) { test_fail("setsockopt() should have worked"); } if (option_value != types[i]) { test_fail("SO_TYPE didn't seem to work."); } CLOSE(sd); } SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); debug("Test setsockopt() works"); option_value = 0; option_len = sizeof(option_value); errno = 0; rc = getsockopt(sd, SOL_SOCKET, SO_SNDBUF, &option_value, &option_len); if (rc != 0) { test_fail("getsockopt() should have worked"); } if (option_value != PIPE_BUF) { test_fail("SO_SNDBUF didn't seem to work."); } CLOSE(sd); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); debug("Test setsockopt() works"); option_value = 0; option_len = sizeof(option_value); errno = 0; rc = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &option_value, &option_len); if (rc != 0) { test_fail("getsockopt() should have worked"); } if (option_value != PIPE_BUF) { test_fail("SO_RCVBUF didn't seem to work."); } CLOSE(sd); debug("leaving test_sockopts()"); } void test_read(void) { int rc; int fd; char buf[BUFSIZE]; debug("entering test_read()"); errno = 0; rc = read(-1, buf, sizeof(buf)); if (!(rc == -1 && errno == EBADF)) { test_fail("read() should have failed with EBADF"); } fd = open("/tmp", O_RDONLY); if (fd == -1) { test_fail("open(\"/tmp\", O_RDONLY) should have worked"); } CLOSE(fd); debug("leaving test_read()"); } void test_write(void) { int rc; char buf[BUFSIZE]; debug("entering test_write()"); errno = 0; rc = write(-1, buf, sizeof(buf)); if (!(rc == -1 && errno == EBADF)) { test_fail("write() should have failed with EBADF"); } debug("leaving test_write()"); } void test_dup(void) { struct stat info1; struct stat info2; struct sockaddr_un addr; int sd, sd2; int rc; int i; debug("entering test_dup()"); UNLINK(TEST_SUN_PATH); memset(&addr, '\0', sizeof(struct sockaddr_un)); strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; debug("Test dup()"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc != 0) { test_fail("bind() should have worked"); } errno = 0; sd2 = dup(sd); if (sd2 == -1) { test_fail("dup(sd) should have worked"); } rc = fstat(sd, &info1); if (rc == -1) { test_fail("fstat(fd, &info1) failed"); } rc = fstat(sd2, &info2); if (rc == -1) { test_fail("fstat(sd, &info2) failed"); } if (info1.st_ino != info2.st_ino) { test_fail("dup() failed info1.st_ino != info2.st_ino"); } CLOSE(sd); CLOSE(sd2); debug("Test dup() with a closed socket"); errno = 0; rc = dup(sd); if (!(rc == -1 && errno == EBADF)) { test_fail("dup(sd) on a closed socket shouldn't have worked"); } debug("Test dup() with socket descriptor of -1"); errno = 0; rc = dup(-1); if (!(rc == -1 && errno == EBADF)) { test_fail("dup(-1) shouldn't have worked"); } debug("Test dup() when all of the file descriptors are taken"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); for (i = 4; i < getdtablesize(); i++) { rc = open("/dev/null", O_RDONLY); if (rc == -1) { test_fail("we couldn't open /dev/null for read"); } } errno = 0; sd2 = dup(sd); if (!(sd2 == -1 && errno == EMFILE)) { test_fail("dup(sd) should have failed with errno = EMFILE"); } for (i = 3; i < getdtablesize(); i++) { CLOSE(i); } UNLINK(TEST_SUN_PATH); debug("leaving test_dup()"); } void test_dup2(void) { struct stat info1; struct stat info2; struct sockaddr_un addr; int sd; int fd; int rc; debug("entering test_dup2()"); UNLINK(TEST_SUN_PATH); memset(&addr, '\0', sizeof(struct sockaddr_un)); strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc != 0) { test_fail("bind() should have worked"); } fd = open("/dev/null", O_RDONLY); if (fd == -1) { test_fail("open(\"/dev/null\", O_RDONLY) failed"); } fd = dup2(sd, fd); if (fd == -1) { test_fail("dup2(sd, fd) failed."); } memset(&info1, '\0', sizeof(struct stat)); memset(&info2, '\0', sizeof(struct stat)); rc = fstat(fd, &info1); if (rc == -1) { test_fail("fstat(fd, &info1) failed"); } rc = fstat(sd, &info2); if (rc == -1) { test_fail("fstat(sd, &info2) failed"); } if (!(info1.st_ino == info2.st_ino && major(info1.st_dev) == major(info2.st_dev) && minor(info1.st_dev) == minor(info2.st_dev))) { test_fail("dup2() failed"); } CLOSE(fd); CLOSE(sd); UNLINK(TEST_SUN_PATH); debug("leaving test_dup2()"); } /* * A toupper() server. This toy server converts a string to upper case. */ void test_xfer_server(pid_t pid) { struct ucred credentials; socklen_t ucred_length; int i; int on; struct timeval tv; fd_set readfds; int status; int rc; int sd; char buf[BUFSIZE]; socklen_t client_addr_size; int client_sd; struct sockaddr_un addr; struct sockaddr_un client_addr; uid_t euid; gid_t egid; on = 1; status = 0; rc = 0; sd = 0; ucred_length = sizeof(struct ucred); client_sd = 0; client_addr_size = sizeof(struct sockaddr_un); memset(&buf, '\0', sizeof(buf)); memset(&addr, '\0', sizeof(struct sockaddr_un)); memset(&client_addr, '\0', sizeof(struct sockaddr_un)); strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("bind() should have worked"); } rc = listen(sd, 8); if (rc == -1) { test_fail("listen(sd, 8) should have worked"); } /* we're ready for connections, time to tell the client to start * the test */ kill(pid, SIGUSR1); tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(sd, &readfds); /* use select() in case the client is really broken and never * attempts to connect (we don't want to block on accept() * forever). */ rc = select(sd + 1, &readfds, NULL, NULL, &tv); if (rc == -1) { test_fail("[server] select() should not have failed"); } if (rc != 1) { test_fail("[server] select() should have returned 1"); printf("[server] select returned %d\n", rc); } if (!(FD_ISSET(sd, &readfds))) { test_fail("[server] client didn't connect within 10 seconds"); kill(pid, SIGKILL); return; } client_sd = accept(sd, (struct sockaddr *) &client_addr, &client_addr_size); if (client_sd == -1) { test_fail("accept() should have worked"); kill(pid, SIGKILL); return; } else { debug("[server] client accept()'d"); } debug("[server] Reading message"); rc = read(client_sd, buf, sizeof(buf)); if (rc == -1) { test_fail("read() failed unexpectedly"); kill(pid, SIGKILL); return; } debug("[server] we got the following message:"); debug(buf); for (i = 0; i < rc && i < 127; i++) { buf[i] = toupper(buf[i]); } debug("[server] Writing message..."); rc = write(client_sd, buf, sizeof(buf)); if (rc == -1) { test_fail("write(client_sd, buf, sizeof(buf)) failed"); kill(pid, SIGKILL); return; } if (rc < strlen(buf)) { test_fail("[server] write didn't write all the bytes"); } memset(&buf, '\0', sizeof(buf)); debug("[server] Recv message"); rc = recv(client_sd, buf, sizeof(buf), 0); if (rc == -1) { test_fail("recv() failed unexpectedly"); kill(pid, SIGKILL); return; } debug("[server] we got the following message:"); debug(buf); for (i = 0; i < rc && i < 127; i++) { buf[i] = toupper(buf[i]); } debug("[server] Sending message..."); rc = send(client_sd, buf, sizeof(buf), 0); if (rc == -1) { test_fail("send(client_sd, buf, sizeof(buf), 0) failed"); kill(pid, SIGKILL); return; } if (rc < strlen(buf)) { test_fail("[server] write didn't write all the bytes"); } memset(&buf, '\0', sizeof(buf)); debug("[server] Recvfrom message"); rc = recvfrom(client_sd, buf, sizeof(buf), 0, NULL, 0); if (rc == -1) { test_fail("recvfrom() failed unexpectedly"); kill(pid, SIGKILL); return; } debug("[server] we got the following message:"); debug(buf); for (i = 0; i < rc && i < 127; i++) { buf[i] = toupper(buf[i]); } debug("[server] Sendto message..."); rc = sendto(client_sd, buf, sizeof(buf), 0, NULL, 0); if (rc == -1) { test_fail("sendto() failed"); kill(pid, SIGKILL); return; } if (rc < strlen(buf)) { test_fail("[server] write didn't write all the bytes"); } shutdown(client_sd, SHUT_RDWR); CLOSE(client_sd); shutdown(sd, SHUT_RDWR); CLOSE(sd); /* wait for client to exit */ do { errno = 0; rc = waitpid(pid, &status, 0); } while (rc == -1 && errno == EINTR); /* we use the exit status to get it's error count */ errct += WEXITSTATUS(status); } int server_ready = 0; /* signal handler for the client */ void test_xfer_sighdlr(int sig) { debug("entering signal handler"); switch (sig) { /* the server will send SIGUSR1 when it is time for us * to start the tests */ case SIGUSR1: server_ready = 1; debug("got SIGUSR1, the server is ready for the client"); break; default: debug("didn't get SIGUSR1"); } debug("leaving signal handler"); } /* * A toupper() client. */ void test_xfer_client(void) { struct ucred credentials; socklen_t ucred_length; struct timeval tv; fd_set readfds; struct sockaddr_un addr; struct sockaddr_un peer_addr; socklen_t peer_addr_len; int sd; int rc; char buf[BUFSIZE]; uid_t uid; gid_t gid; debug("[client] entering test_xfer_client()"); errct = 0; /* reset error count */ ucred_length = sizeof(struct ucred); memset(&buf, '\0', sizeof(buf)); while (server_ready == 0) { debug("[client] waiting for the server to signal"); sleep(1); } peer_addr_len = sizeof(struct sockaddr_un); debug("Creating symlink to TEST_SUN_PATH"); SYMLINK(TEST_SUN_PATH, TEST_SYM_A); memset(&addr, '\0', sizeof(struct sockaddr_un)); strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; debug("[client] creating client socket"); SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); debug("[client] connecting to server through the symlink"); rc = connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("[client] connect() should have worked"); } else { debug("[client] connected"); } debug("[client] testing getpeername()"); memset(&peer_addr, '\0', sizeof(struct sockaddr_un)); rc = getpeername(sd, (struct sockaddr *) &peer_addr, &peer_addr_len); if (rc == -1) { test_fail("[client] getpeername() should have worked"); } /* we need to use the full path "/usr/src/test/DIR_56/test.sock" * because that is what is returned by getpeername(). */ if (!(peer_addr.sun_family == AF_UNIX && strncmp(peer_addr.sun_path, "/usr/src/test/DIR_56/test.sock", sizeof(peer_addr.sun_path) - 1) == 0)) { test_fail("getpeername() didn't return the right address"); } strncpy(buf, "Hello, World!", sizeof(buf) - 1); debug("[client] send to server"); rc = write(sd, buf, sizeof(buf)); if (rc == -1) { test_fail("[client] write() failed unexpectedly"); } memset(buf, '\0', sizeof(buf)); debug("[client] read from server"); rc = read(sd, buf, sizeof(buf)); if (rc == -1) { test_fail("[client] read() failed unexpectedly"); } else { debug("[client] we got the following message:"); debug(buf); } if (strncmp(buf, "HELLO, WORLD!", sizeof(buf)) != 0) { test_fail("[client] We didn't get the correct response"); } memset(&buf, '\0', sizeof(buf)); strncpy(buf, "Bonjour!", sizeof(buf) - 1); debug("[client] send to server"); rc = send(sd, buf, sizeof(buf), 0); if (rc == -1) { test_fail("[client] send() failed unexpectedly"); } debug("Test passing the client credentials to the server"); memset(&credentials, '\0', ucred_length); rc = getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length); if (rc == -1) { test_fail("[client] getsockopt() failed"); } else if (credentials.uid != geteuid() || credentials.gid != getegid()) { printf("%d=%d=%d %d=%d=%d\n", credentials.uid, getuid(), geteuid(), credentials.gid, getgid(), getegid()); test_fail("[client] Credential passing gave us a bad UID/GID"); } debug("Testing select()"); tv.tv_sec = 2; tv.tv_usec = 500000; FD_ZERO(&readfds); FD_SET(sd, &readfds); rc = select(sd + 1, &readfds, NULL, NULL, &tv); if (rc == -1) { test_fail("[client] select() should not have failed"); } if (rc != 1) { test_fail("[client] select() should have returned 1"); } if (!(FD_ISSET(sd, &readfds))) { test_fail("The server didn't respond within 2.5 seconds"); } memset(buf, '\0', sizeof(buf)); debug("[client] recv from server"); rc = recv(sd, buf, sizeof(buf), 0); if (rc == -1) { test_fail("[client] recv() failed unexpectedly"); } else { debug("[client] we got the following message:"); debug(buf); } if (strncmp(buf, "BONJOUR!", sizeof(buf)) != 0) { test_fail("[client] We didn't get the right response."); } memset(&buf, '\0', sizeof(buf)); strncpy(buf, "Hola!", sizeof(buf) - 1); debug("[client] sendto to server"); rc = sendto(sd, buf, sizeof(buf), 0, NULL, 0); if (rc == -1) { test_fail("[client] sendto() failed"); } debug("Testing select()"); tv.tv_sec = 2; tv.tv_usec = 500000; FD_ZERO(&readfds); FD_SET(sd, &readfds); rc = select(sd + 1, &readfds, NULL, NULL, &tv); if (rc == -1) { test_fail("[client] select() should not have failed"); } if (rc != 1) { test_fail("[client] select() should have returned 1"); } if (!(FD_ISSET(sd, &readfds))) { test_fail("[client] The server didn't respond in 2.5 seconds"); } memset(buf, '\0', sizeof(buf)); debug("[client] recvfrom from server"); rc = recvfrom(sd, buf, sizeof(buf), 0, NULL, 0); if (rc == -1) { test_fail("[cleint] recvfrom() failed unexpectedly"); } else { debug("[client] we got the following message:"); debug(buf); } if (strncmp(buf, "HOLA!", sizeof(buf)) != 0) { test_fail("[client] We didn't get the right response."); } debug("[client] closing socket"); CLOSE(sd); UNLINK(TEST_SYM_A); debug("[client] leaving test_xfer_client()"); exit(errct); } void test_xfer(void) { pid_t pid; UNLINK(TEST_SUN_PATH); /* the signal handler is only used by the client, but we have to * install it now. if we don't the server may signal the client * before the handler is installed. */ debug("installing signal handler"); if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) { test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed"); } debug("signal handler installed"); server_ready = 0; pid = fork(); if (pid == -1) { test_fail("fork() failed"); return; } if (pid == 0) { debug("child"); test_xfer_client(); test_fail("we should never get here"); } else { debug("parent"); test_xfer_server(pid); debug("parent done"); } UNLINK(TEST_SUN_PATH); } void test_simple_client(int type) { char buf[BUFSIZE]; int sd, rc; struct sockaddr_un addr; sd = socket(PF_UNIX, type, 0); if (sd == -1) { test_fail("socket"); return; } while (server_ready == 0) { debug("[client] waiting for the server"); sleep(1); } strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; bzero(buf, BUFSIZE); snprintf(buf, BUFSIZE-1, "Hello, My Name is Client."); if (type == SOCK_DGRAM) { rc = sendto(sd, buf, strlen(buf) + 1, 0, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("sendto"); return; } } else { rc = connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("connect"); return; } rc = write(sd, buf, strlen(buf) + 1); if (rc == -1) { test_fail("write"); } rc = read(sd, buf, BUFSIZE); if (rc == -1) { test_fail("read"); } } rc = close(sd); if (rc == -1) { test_fail("close"); } exit(errct); } void test_simple_server(int type, pid_t pid) { char buf[BUFSIZE]; int sd, rc, client_sd, status; struct sockaddr_un addr; socklen_t addr_len; sd = socket(PF_UNIX, type, 0); if (sd == -1) { test_fail("socket"); } strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); addr.sun_family = AF_UNIX; rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (rc == -1) { test_fail("bind"); } if (type == SOCK_DGRAM) { /* ready for client */ kill(pid, SIGUSR1); rc = recvfrom(sd, buf, BUFSIZE, 0, (struct sockaddr *) &addr, &addr_len); if (rc == -1) { test_fail("recvfrom"); } } else { rc = listen(sd, 5); if (rc == -1) { test_fail("listen"); } /* we're ready for connections, time to tell the client * to start the test */ kill(pid, SIGUSR1); client_sd = accept(sd, (struct sockaddr *) &addr, &addr_len); if (client_sd == -1) { test_fail("accept"); } rc = read(client_sd, buf, BUFSIZE); if (rc == -1) { test_fail("read"); } /* added for extra fun to make the client block on read() */ sleep(1); bzero(buf, BUFSIZE); snprintf(buf, BUFSIZE-1, "Hello, My Name is Server."); rc = write(client_sd, buf, strlen(buf) + 1); if (rc == -1) { test_fail("write"); } rc = close(client_sd); if (rc == -1) { test_fail("close"); } } rc = close(sd); if (rc == -1) { test_fail("close"); } /* wait for client to exit */ do { errno = 0; rc = waitpid(pid, &status, 0); } while (rc == -1 && errno == EINTR); /* we use the exit status to get it's error count */ errct += WEXITSTATUS(status); } void test_simple_client_server(int type) { pid_t pid; UNLINK(TEST_SUN_PATH); /* the signal handler is only used by the client, but we have to * install it now. if we don't the server may signal the client * before the handler is installed. */ debug("installing signal handler"); if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) { test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed"); } debug("signal handler installed"); server_ready = 0; pid = fork(); if (pid == -1) { test_fail("fork() failed"); return; } if (pid == 0) { debug("child"); test_simple_client(type); test_fail("we should never get here"); } else { debug("parent"); test_simple_server(type, pid); debug("parent done"); } UNLINK(TEST_SUN_PATH); } void test_vectorio(int type) { int sv[2]; int rc; struct iovec iov[3]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE*3]; const struct iovec *iovp = iov; debug("begin vectorio tests"); memset(buf1, '\0', BUFSIZE); strncpy(buf1, "HELLO ", BUFSIZE - 1); memset(buf2, '\0', BUFSIZE); strncpy(buf2, "WORLD", BUFSIZE - 1); memset(buf3, '\0', BUFSIZE); rc = socketpair(PF_UNIX, type, 0, sv); if (rc == -1) { test_fail("socketpair"); } iov[0].iov_base = buf1; iov[0].iov_len = strlen(buf1); iov[1].iov_base = buf2; iov[1].iov_len = strlen(buf2); iov[2].iov_base = buf3; iov[2].iov_len = 1; rc = writev(sv[0], iovp, 3); if (rc == -1) { test_fail("writev"); } memset(buf4, '\0', BUFSIZE*3); rc = read(sv[1], buf4, BUFSIZE*3); if (rc == -1) { test_fail("read"); } if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) { test_fail("the string we read was not 'HELLO WORLD'"); } memset(buf1, '\0', BUFSIZE); strncpy(buf1, "Unit Test Time", BUFSIZE - 1); rc = write(sv[1], buf1, strlen(buf1) + 1); if (rc == -1) { test_fail("write"); } memset(buf2, '\0', BUFSIZE); memset(buf3, '\0', BUFSIZE); memset(buf4, '\0', BUFSIZE*3); iov[0].iov_base = buf2; iov[0].iov_len = 5; iov[1].iov_base = buf3; iov[1].iov_len = 5; iov[2].iov_base = buf4; iov[2].iov_len = 32; rc = readv(sv[0], iovp, 3); if (rc == -1) { test_fail("readv"); } if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) || strncmp(buf4, "Time", 4)) { test_fail("readv"); } rc = close(sv[0]); if (rc == -1) { test_fail("close"); } rc = close(sv[1]); if (rc == -1) { test_fail("close"); } } void test_msg(int type) { int sv[2]; int rc; struct msghdr msg1; struct msghdr msg2; struct iovec iov[3]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE*3]; debug("begin sendmsg/recvmsg tests"); memset(buf1, '\0', BUFSIZE); strncpy(buf1, "HELLO ", BUFSIZE - 1); memset(buf2, '\0', BUFSIZE); strncpy(buf2, "WORLD", BUFSIZE - 1); memset(buf3, '\0', BUFSIZE); rc = socketpair(PF_UNIX, type, 0, sv); if (rc == -1) { test_fail("socketpair"); } iov[0].iov_base = buf1; iov[0].iov_len = strlen(buf1); iov[1].iov_base = buf2; iov[1].iov_len = strlen(buf2); iov[2].iov_base = buf3; iov[2].iov_len = 1; memset(&msg1, '\0', sizeof(struct msghdr)); msg1.msg_name = NULL; msg1.msg_namelen = 0; msg1.msg_iov = iov; msg1.msg_iovlen = 3; msg1.msg_control = NULL; msg1.msg_controllen = 0; msg1.msg_flags = 0; rc = sendmsg(sv[0], &msg1, 0); if (rc == -1) { test_fail("writev"); } memset(buf4, '\0', BUFSIZE*3); rc = read(sv[1], buf4, BUFSIZE*3); if (rc == -1) { test_fail("read"); } if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) { test_fail("the string we read was not 'HELLO WORLD'"); } memset(buf1, '\0', BUFSIZE); strncpy(buf1, "Unit Test Time", BUFSIZE - 1); rc = write(sv[1], buf1, strlen(buf1) + 1); if (rc == -1) { test_fail("write"); } memset(buf2, '\0', BUFSIZE); memset(buf3, '\0', BUFSIZE); memset(buf4, '\0', BUFSIZE*3); iov[0].iov_base = buf2; iov[0].iov_len = 5; iov[1].iov_base = buf3; iov[1].iov_len = 5; iov[2].iov_base = buf4; iov[2].iov_len = 32; memset(&msg2, '\0', sizeof(struct msghdr)); msg2.msg_name = NULL; msg2.msg_namelen = 0; msg2.msg_iov = iov; msg2.msg_iovlen = 3; msg2.msg_control = NULL; msg2.msg_controllen = 0; msg2.msg_flags = 0; rc = recvmsg(sv[0], &msg2, 0); if (rc == -1) { test_fail("readv"); } if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) || strncmp(buf4, "Time", 4)) { test_fail("readv"); } rc = close(sv[0]); if (rc == -1) { test_fail("close"); } rc = close(sv[1]); if (rc == -1) { test_fail("close"); } } void test_msg_dgram(void) { int rc; int src; int dst; struct sockaddr_un addr; struct iovec iov[3]; struct msghdr msg1; struct msghdr msg2; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; socklen_t addrlen = sizeof(struct sockaddr_un); UNLINK(TEST_SUN_PATH); UNLINK(TEST_SUN_PATHB); src = socket(PF_UNIX, SOCK_DGRAM, 0); if (src == -1) { test_fail("socket"); } dst = socket(PF_UNIX, SOCK_DGRAM, 0); if (dst == -1) { test_fail("socket"); } memset(&addr, '\0', sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, TEST_SUN_PATHB, sizeof(addr.sun_path) - 1); rc = bind(src, (struct sockaddr *) &addr, addrlen); if (rc == -1) { test_fail("bind"); } memset(&addr, '\0', sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); rc = bind(dst, (struct sockaddr *) &addr, addrlen); if (rc == -1) { test_fail("bind"); } memset(&buf1, '\0', BUFSIZE); memset(&buf2, '\0', BUFSIZE); memset(&buf3, '\0', BUFSIZE); strncpy(buf1, "Minix ", BUFSIZE-1); strncpy(buf2, "is ", BUFSIZE-1); strncpy(buf3, "great!", BUFSIZE-1); iov[0].iov_base = buf1; iov[0].iov_len = 6; iov[1].iov_base = buf2; iov[1].iov_len = 3; iov[2].iov_base = buf3; iov[2].iov_len = 32; memset(&msg1, '\0', sizeof(struct msghdr)); msg1.msg_name = &addr; msg1.msg_namelen = addrlen; msg1.msg_iov = iov; msg1.msg_iovlen = 3; msg1.msg_control = NULL; msg1.msg_controllen = 0; msg1.msg_flags = 0; rc = sendmsg(src, &msg1, 0); if (rc == -1) { test_fail("sendmsg"); } memset(&buf1, '\0', BUFSIZE); memset(&buf2, '\0', BUFSIZE); iov[0].iov_base = buf1; iov[0].iov_len = 9; iov[1].iov_base = buf2; iov[1].iov_len = 32; memset(&addr, '\0', sizeof(struct sockaddr_un)); memset(&msg2, '\0', sizeof(struct msghdr)); msg2.msg_name = &addr; msg2.msg_namelen = sizeof(struct sockaddr_un); msg2.msg_iov = iov; msg2.msg_iovlen = 2; msg2.msg_control = NULL; msg2.msg_controllen = 0; msg2.msg_flags = 0; rc = recvmsg(dst, &msg2, 0); if (rc == -1) { test_fail("recvmsg"); } if (strncmp(buf1, "Minix is ", 9) || strncmp(buf2, "great!", 6)) { test_fail("recvmsg"); } /* we need to use the full path "/usr/src/test/DIR_56/testb.sock" * because that is what is returned by recvmsg(). */ if (addr.sun_family != AF_UNIX || strcmp(addr.sun_path, "/usr/src/test/DIR_56/testb.sock")) { test_fail("recvmsg"); } rc = close(dst); if (rc == -1) { test_fail("close"); } rc = close(src); if (rc == -1) { test_fail("close"); } UNLINK(TEST_SUN_PATH); UNLINK(TEST_SUN_PATHB); } int main(int argc, char *argv[]) { int i; debug("entering main()"); start(56); test_socket(); test_listen(); test_getsockname(); test_header(); test_shutdown(); test_close(); test_dup(); test_dup2(); test_socketpair(); test_shutdown(); test_read(); test_write(); test_sockopts(); test_ucred(); test_bind(); test_xfer(); for (i = 0; i < 3; i++) { test_simple_client_server(types[i]); if (types[i] != SOCK_DGRAM) test_vectorio(types[i]); if (types[i] != SOCK_DGRAM) test_msg(types[i]); } test_msg_dgram(); cleanup(); quit(); return -1; /* we should never get here */ }