From d26290a0178fd26e889805e81e685413e34b819f Mon Sep 17 00:00:00 2001 From: Thomas Veerman Date: Thu, 15 Jul 2010 14:52:29 +0000 Subject: [PATCH] Add test56 to test our UDS implementation. Contributed by Thomas Cort --- test/Makefile | 3 +- test/test56.c | 1939 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1941 insertions(+), 1 deletion(-) create mode 100644 test/test56.c diff --git a/test/Makefile b/test/Makefile index 2b725b63b..83c9b2b40 100644 --- a/test/Makefile +++ b/test/Makefile @@ -12,7 +12,7 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \ test30 test31 test32 test34 test35 test36 test37 test38 \ test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \ test42 test45 test47 test48 test49 test50 test51 test52 test53 \ - test54 test55 + test54 test55 test56 BIGOBJ= test20 test24 ROOTOBJ= test11 test33 test43 test44 test46 @@ -111,3 +111,4 @@ test52: test52.c test52-gcc: test52.c test54: test54.c test55: test55.c +test56: test56.c diff --git a/test/test56.c b/test/test56.c new file mode 100644 index 000000000..f60bea7a4 --- /dev/null +++ b/test/test56.c @@ -0,0 +1,1939 @@ +/* + * 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 */ +} -- 2.44.0