From 294d15901798159321c130a37eb712a6b9107ab2 Mon Sep 17 00:00:00 2001 From: Erik van der Kouwe Date: Wed, 8 Jul 2015 09:46:56 +0200 Subject: [PATCH] Add new tests 80 (TCP) and 81 (UDP) These new tests are largely based on the code from test 56 (UDS). Common code is moved into a separate file common-socket.c. In some instances the tests are too strict for TCP/UDP sockets, which may not always react instantly to whatever happens on the other side (even locally). For these cases, the ignore_* fields in struct socket_test_info indicate that there needs to be an exception. There are also tests where it seems the functionality of inet is either incorrect or incomplete with regard to the POSIX standard. In these cases, the bug_* fields are used to document the issues while avoiding failure of the test. Change-Id: Ia860deb4559d42608790451936b1aade866faebc --- distrib/sets/lists/minix/mi | 2 + minix/tests/Makefile | 6 +- minix/tests/common-socket.c | 2121 ++++++++++++++++++++++++++++++++ minix/tests/common-socket.h | 124 ++ minix/tests/run | 4 +- minix/tests/test56.c | 2280 ++--------------------------------- minix/tests/test80.c | 141 +++ minix/tests/test81.c | 134 ++ 8 files changed, 2632 insertions(+), 2180 deletions(-) create mode 100644 minix/tests/common-socket.c create mode 100644 minix/tests/common-socket.h create mode 100644 minix/tests/test80.c create mode 100644 minix/tests/test81.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index ba41a70f7..bd4f92f2a 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -6329,6 +6329,8 @@ ./usr/tests/minix-posix/test78 minix-sys ./usr/tests/minix-posix/test79 minix-sys ./usr/tests/minix-posix/test8 minix-sys +./usr/tests/minix-posix/test80 minix-sys +./usr/tests/minix-posix/test81 minix-sys ./usr/tests/minix-posix/test9 minix-sys ./usr/tests/minix-posix/testinterp minix-sys ./usr/tests/minix-posix/testisofs minix-sys diff --git a/minix/tests/Makefile b/minix/tests/Makefile index ae7a9e38a..27bb860b3 100644 --- a/minix/tests/Makefile +++ b/minix/tests/Makefile @@ -36,6 +36,9 @@ LDADD.test77= -lutil # Some have an extra file OBJS.test57= test57loop.o +OBJS.test56+= common-socket.o +OBJS.test80+= common-socket.o +OBJS.test81+= common-socket.o # Cache testing programs OBJS.test71+= testcache.o @@ -55,7 +58,8 @@ MINIX_TESTS= \ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 46 48 49 50 52 53 54 55 56 58 59 60 \ -61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 +61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \ +81 .if ${MACHINE_ARCH} == "i386" MINIX_TESTS+= \ diff --git a/minix/tests/common-socket.c b/minix/tests/common-socket.c new file mode 100644 index 000000000..e33b64305 --- /dev/null +++ b/minix/tests/common-socket.c @@ -0,0 +1,2121 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "common-socket.h" + +#define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S" + +/* timestamps for debug and error logs */ +static 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; +} + +void test_fail_fl(char *msg, char *file, int line) +{ + 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; + } + e(7); +} + +#if DEBUG == 1 +void debug_fl(char *msg, char *file, int line) +{ + 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; + } +} +#endif + +void test_socket(const struct socket_test_info *info) +{ + 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, info->type, 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(info->domain, info->type, 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(info->domain, info->type, 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, info->domain, info->type, 0); + SOCKET(sd2, info->domain, info->type, 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_getsockname(const struct socket_test_info *info) +{ + int sd; + int rc; + struct sockaddr_storage sock_addr; + socklen_t sock_addr_len; + + SOCKET(sd, info->domain, info->type, 0); + rc = bind(sd, info->serveraddr, info->serveraddrlen); + if (rc == -1) { + test_fail("bind() should have worked"); + } + + debug("Test getsockname() success"); + + memset(&sock_addr, '\0', sizeof(sock_addr)); + sock_addr_len = sizeof(sock_addr); + + rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len); + if (rc == -1) { + test_fail("getsockname() should have worked"); + } + + info->callback_check_sockaddr((struct sockaddr *) &sock_addr, + sock_addr_len, "getsockname", 1); + + CLOSE(sd); +} + +void test_bind(const struct socket_test_info *info) +{ + struct sockaddr_storage sock_addr; + socklen_t sock_addr_len; + int sd; + int sd2; + int rc; + + debug("entering test_bind()"); + info->callback_cleanup(); + + debug("Test bind() success"); + + SOCKET(sd, info->domain, info->type, 0); + rc = bind(sd, info->serveraddr, info->serveraddrlen); + if (rc == -1) { + test_fail("bind() should have worked"); + } + + debug("Test getsockname() success"); + + memset(&sock_addr, '\0', sizeof(sock_addr)); + sock_addr_len = sizeof(sock_addr); + + rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len); + if (rc == -1) { + test_fail("getsockname() should have worked"); + } + + info->callback_check_sockaddr((struct sockaddr *) &sock_addr, + sock_addr_len, "getsockname", 1); + + debug("Test bind() with a address that has already been bind()'d"); + + SOCKET(sd2, info->domain, info->type, 0); + errno = 0; + rc = bind(sd2, info->serveraddr, info->serveraddrlen); + if (!((rc == -1) && (errno == EADDRINUSE)) && + !info->bug_bind_in_use) { + test_fail("bind() should have failed with EADDRINUSE"); + } + CLOSE(sd2); + CLOSE(sd); + info->callback_cleanup(); + + if (!info->bug_bind_null) { + debug("Test bind() with a NULL address"); + + SOCKET(sd, info->domain, info->type, 0); + errno = 0; + rc = bind(sd, (struct sockaddr *) NULL, + sizeof(struct sockaddr_storage)); + if (!((rc == -1) && (errno == EFAULT))) { + test_fail("bind() should have failed with EFAULT"); + } + CLOSE(sd); + } + + debug("leaving test_bind()"); +} + +void test_listen(const struct socket_test_info *info) +{ + 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); + /* Test on errno disabled here: there's currently no telling what this + * will return. POSIX says it should be ENOTSOCK, MINIX3 libc returns + * ENOSYS, and we used to test for ENOTTY here.. + */ + if (!(rc == -1)) { + test_fail("listen(0, 0) should have failed"); + } + + debug("leaving test_listen()"); +} + +void test_shutdown(const struct socket_test_info *info) +{ + 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++) { + + if (info->bug_shutdown_read && how[i] == SHUT_RD) continue; + + debug("test shutdown() with an invalid descriptor"); + + errno = 0; + rc = shutdown(-1, how[i]); + if (!(rc == -1 && errno == EBADF) && !info->bug_shutdown) { + 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) && !info->bug_shutdown) { + test_fail("shutdown() should have failed with ENOSYS"); + } + + debug("test shutdown() with a socket that is not connected"); + + SOCKET(sd, info->domain, info->type, 0); + errno = 0; + rc = shutdown(sd, how[i]); + if (!(rc == -1 && errno == ENOTCONN) && + !info->bug_shutdown_not_conn && + !info->bug_shutdown) { + test_fail("shutdown() should have failed"); + } + CLOSE(sd); + } + + SOCKET(sd, info->domain, info->type, 0); + errno = 0; + rc = shutdown(sd, -1); + if (!(rc == -1 && errno == ENOTCONN) && + !info->bug_shutdown_not_conn && + !info->bug_shutdown) { + test_fail("shutdown(sd, -1) should have failed with ENOTCONN"); + } + CLOSE(sd); + + debug("leaving test_shutdown()"); +} + +void test_close(const struct socket_test_info *info) +{ + int sd, sd2; + int rc, i; + + debug("entering test_close()"); + + info->callback_cleanup(); + + debug("Test close() success"); + + SOCKET(sd, info->domain, info->type, 0); + rc = bind(sd, info->serveraddr, info->serveraddrlen); + 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"); + } + + info->callback_cleanup(); + + debug("dup()'ing a file descriptor and closing both should work"); + + SOCKET(sd, info->domain, info->type, 0); + rc = bind(sd, info->serveraddr, info->serveraddrlen); + 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); + } + + info->callback_cleanup(); + + /* Create and close a socket a bunch of times. + * If the implementation doesn't properly free the + * socket during close(), eventually socket() will + * fail when the internal descriptor table is full. + */ + for (i = 0; i < 1024; i++) { + SOCKET(sd, info->domain, info->type, 0); + CLOSE(sd); + } + + debug("leaving test_close()"); +} + +void test_sockopts(const struct socket_test_info *info) +{ + int i; + int rc; + int sd; + int option_value; + socklen_t option_len; + + debug("entering test_sockopts()"); + + for (i = 0; i < info->typecount; i++) { + + SOCKET(sd, info->domain, info->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 != info->types[i]) { + test_fail("SO_TYPE didn't seem to work."); + } + + CLOSE(sd); + } + + + + SOCKET(sd, info->domain, info->type, 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 && !info->bug_sockopt_sndbuf) { + test_fail("getsockopt() should have worked"); + } + + if (info->expected_sndbuf >= 0 && + option_value != info->expected_sndbuf && + !info->bug_sockopt_sndbuf) { + test_fail("SO_SNDBUF didn't seem to work."); + } + + CLOSE(sd); + + + SOCKET(sd, info->domain, info->type, 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 && !info->bug_sockopt_rcvbuf) { + test_fail("getsockopt() should have worked"); + } + + if (info->expected_rcvbuf >= 0 && + option_value != info->expected_rcvbuf && + !info->bug_sockopt_rcvbuf) { + test_fail("SO_RCVBUF didn't seem to work."); + } + + CLOSE(sd); + + + debug("leaving test_sockopts()"); +} + +void test_read(const struct socket_test_info *info) +{ + 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(const struct socket_test_info *info) +{ + 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(const struct socket_test_info *info) +{ + struct stat info1; + struct stat info2; + int sd, sd2; + int rc; + int i; + + debug("entering test_dup()"); + + info->callback_cleanup(); + + debug("Test dup()"); + + SOCKET(sd, info->domain, info->type, 0); + rc = bind(sd, info->serveraddr, info->serveraddrlen); + 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, info->domain, info->type, 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); + } + + info->callback_cleanup(); + + debug("leaving test_dup()"); +} + +void test_dup2(const struct socket_test_info *info) +{ + struct stat info1; + struct stat info2; + int sd; + int fd; + int rc; + + debug("entering test_dup2()"); + info->callback_cleanup(); + + SOCKET(sd, info->domain, info->type, 0); + + rc = bind(sd, info->serveraddr, info->serveraddrlen); + 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); + + info->callback_cleanup(); + debug("leaving test_dup2()"); + +} + +/* + * A toupper() server. This toy server converts a string to upper case. + */ +static void test_xfer_server(const struct socket_test_info *info, pid_t pid) +{ + int i; + struct timeval tv; + fd_set readfds; + int status; + int rc; + int sd; + unsigned char buf[BUFSIZE]; + socklen_t client_addr_size; + int client_sd; + struct sockaddr_storage client_addr; + + status = 0; + rc = 0; + sd = 0; + client_sd = 0; + client_addr_size = sizeof(struct sockaddr_storage); + + memset(&buf, '\0', sizeof(buf)); + memset(&client_addr, '\0', sizeof(client_addr)); + + SOCKET(sd, info->domain, info->type, 0); + + rc = bind(sd, info->serveraddr, info->serveraddrlen); + 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 its 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. + */ +static void test_xfer_client(const struct socket_test_info *info) +{ + struct timeval tv; + fd_set readfds; + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + int sd; + int rc; + char buf[BUFSIZE]; + + debug("[client] entering test_xfer_client()"); + errct = 0; /* reset error count */ + memset(&buf, '\0', sizeof(buf)); + + while (server_ready == 0) { + debug("[client] waiting for the server to signal"); + sleep(1); + } + + peer_addr_len = sizeof(peer_addr); + + + if (info->callback_xfer_prepclient) info->callback_xfer_prepclient(); + + debug("[client] creating client socket"); + SOCKET(sd, info->domain, info->type, 0); + + debug("[client] connecting to server through the symlink"); + rc = connect(sd, info->clientaddrsym, info->clientaddrsymlen); + if (rc == -1) { + test_fail("[client] connect() should have worked"); + } else { + debug("[client] connected"); + } + + debug("[client] testing getpeername()"); + memset(&peer_addr, '\0', sizeof(peer_addr)); + 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(). + */ + + info->callback_check_sockaddr((struct sockaddr *) &peer_addr, + peer_addr_len, "getpeername", 1); + + 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"); + } + + if (info->callback_xfer_peercred) info->callback_xfer_peercred(sd); + + 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); + + debug("[client] leaving test_xfer_client()"); + exit(errct); +} + +void test_xfer(const struct socket_test_info *info) +{ + pid_t pid; + + debug("entering test_xfer()"); + info->callback_cleanup(); + + /* 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; + } else if (pid == 0) { + debug("child"); + errct = 0; + test_xfer_client(info); + test_fail("we should never get here"); + exit(1); + } else { + debug("parent"); + test_xfer_server(info, pid); + debug("parent done"); + } + + info->callback_cleanup(); + debug("leaving test_xfer()"); +} + +static void test_simple_client(const struct socket_test_info *info, int type) +{ + char buf[BUFSIZE]; + int sd, rc; + + sd = socket(info->domain, type, 0); + if (sd == -1) { + test_fail("socket"); + exit(errct); + } + + while (server_ready == 0) { + debug("[client] waiting for the server"); + sleep(1); + } + + bzero(buf, BUFSIZE); + snprintf(buf, BUFSIZE-1, "Hello, My Name is Client."); + + if (type == SOCK_DGRAM) { + + rc = sendto(sd, buf, strlen(buf) + 1, 0, + info->clientaddr, info->clientaddrlen); + if (rc == -1) { + test_fail("sendto"); + exit(errct); + } + + } else { + + rc = connect(sd, info->clientaddr, info->clientaddrlen); + if (rc == -1) { + test_fail("connect"); + exit(errct); + } + + rc = write(sd, buf, strlen(buf) + 1); + + if (rc == -1) { + test_fail("write"); + } + + memset(buf, '\0', BUFSIZE); + rc = read(sd, buf, BUFSIZE); + if (rc == -1) { + test_fail("read"); + } + + if (strcmp("Hello, My Name is Server.", buf) != 0) { + test_fail("didn't read the correct string"); + } + } + + rc = close(sd); + if (rc == -1) { + test_fail("close"); + } + + exit(errct); +} + +static void test_simple_server(const struct socket_test_info *info, int type, + pid_t pid) +{ + char buf[BUFSIZE]; + int sd, rc, client_sd, status; + struct sockaddr_storage addr; + socklen_t addr_len; + + addr_len = info->clientaddrlen; + + sd = socket(info->domain, type, 0); + if (sd == -1) { + test_fail("socket"); + } + + assert(info->clientaddrlen <= sizeof(addr)); + memcpy(&addr, info->clientaddr, info->clientaddrlen); + + rc = bind(sd, info->serveraddr, info->serveraddrlen); + 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"); + } + + memset(buf, '\0', BUFSIZE); + rc = read(client_sd, buf, BUFSIZE); + if (rc == -1) { + test_fail("read"); + } + + if (strcmp("Hello, My Name is Client.", buf) != 0) { + test_fail("didn't read the correct string"); + } + + /* 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 its error count */ + errct += WEXITSTATUS(status); +} + +static void test_abort_client(const struct socket_test_info *info, + int abort_type); +static void test_abort_server(const struct socket_test_info *info, + pid_t pid, int abort_type); + +void test_abort_client_server(const struct socket_test_info *info, + int abort_type) +{ + pid_t pid; + + debug("test_simple_client_server()"); + + info->callback_cleanup(); + + /* 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; + } else if (pid == 0) { + debug("child"); + errct = 0; + test_abort_client(info, abort_type); + test_fail("we should never get here"); + exit(1); + } else { + debug("parent"); + test_abort_server(info, pid, abort_type); + debug("parent done"); + } + + info->callback_cleanup(); +} + +static void test_abort_client(const struct socket_test_info *info, + int abort_type) +{ + char buf[BUFSIZE]; + int sd, rc; + + sd = socket(info->domain, info->type, 0); + if (sd == -1) { + test_fail("socket"); + exit(errct); + } + + while (server_ready == 0) { + debug("[client] waiting for the server"); + sleep(1); + } + + bzero(buf, BUFSIZE); + snprintf(buf, BUFSIZE-1, "Hello, My Name is Client."); + + rc = connect(sd, info->clientaddr, info->clientaddrlen); + if (rc == -1) { + test_fail("connect"); + exit(errct); + } + + if (abort_type == 2) { + /* Give server a chance to close connection */ + sleep(2); + rc = write(sd, buf, strlen(buf) + 1); + if (rc != -1) { + if (!info->ignore_write_conn_reset) { + test_fail("write should have failed\n"); + } + } else if (errno != ECONNRESET) { + test_fail("errno should've been ECONNRESET\n"); + } + } + + rc = close(sd); + if (rc == -1) { + test_fail("close"); + } + + exit(errct); +} + +static void test_abort_server(const struct socket_test_info *info, + pid_t pid, int abort_type) +{ + char buf[BUFSIZE]; + int sd, rc, client_sd, status; + struct sockaddr_storage addr; + socklen_t addr_len; + + addr_len = info->clientaddrlen; + + sd = socket(info->domain, info->type, 0); + if (sd == -1) { + test_fail("socket"); + } + + assert(sizeof(addr) >= info->clientaddrlen); + memcpy(&addr, info->clientaddr, info->clientaddrlen); + + rc = bind(sd, info->serveraddr, info->serveraddrlen); + if (rc == -1) { + test_fail("bind"); + } + + 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"); + } + + if (abort_type == 1) { + memset(buf, '\0', BUFSIZE); + rc = read(client_sd, buf, BUFSIZE); + if (rc != -1 && (rc != 0 || !info->ignore_read_conn_reset)) { + test_fail("read should've failed or returned zero\n"); + } + if (rc != 0 && errno != ECONNRESET) { + test_fail("errno should've been ECONNRESET\n"); + } + } /* else if (abort_type == 2) { */ + 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 its error count */ + errct += WEXITSTATUS(status); +} + +void test_simple_client_server(const struct socket_test_info *info, int type) +{ + pid_t pid; + + debug("entering test_simple_client_server()"); + + info->callback_cleanup(); + + /* 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; + } else if (pid == 0) { + debug("child"); + errct = 0; + test_simple_client(info, type); + test_fail("we should never get here"); + exit(1); + } else { + debug("parent"); + test_simple_server(info, type, pid); + debug("parent done"); + } + + info->callback_cleanup(); + debug("leaving test_simple_client_server()"); +} + +void test_msg_dgram(const struct socket_test_info *info) +{ + int rc; + int src; + int dst; + struct sockaddr_storage addr; + struct iovec iov[3]; + struct msghdr msg1; + struct msghdr msg2; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + + debug("entering test_msg_dgram"); + + info->callback_cleanup(); + + src = socket(info->domain, SOCK_DGRAM, 0); + if (src == -1) { + test_fail("socket"); + } + + dst = socket(info->domain, SOCK_DGRAM, 0); + if (dst == -1) { + test_fail("socket"); + } + + rc = bind(src, info->serveraddr2, info->serveraddr2len); + if (rc == -1) { + test_fail("bind"); + } + + assert(info->clientaddrlen <= sizeof(addr)); + memcpy(&addr, info->clientaddr, info->clientaddrlen); + + rc = bind(dst, info->serveraddr, info->serveraddrlen); + 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 = info->clientaddrlen; + 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(addr)); + memset(&msg2, '\0', sizeof(struct msghdr)); + msg2.msg_name = &addr; + msg2.msg_namelen = sizeof(addr); + 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(). + */ + info->callback_check_sockaddr((struct sockaddr *) &addr, + msg2.msg_namelen, "recvmsg", 2); + + rc = close(dst); + if (rc == -1) { + test_fail("close"); + } + + rc = close(src); + if (rc == -1) { + test_fail("close"); + } + + info->callback_cleanup(); + debug("leaving test_msg_dgram"); +} + +#define check_select(sd, rd, wr, block) \ + check_select_internal(sd, rd, wr, block, 1, __LINE__) +#define check_select_cond(sd, rd, wr, block, allchecks) \ + check_select_internal(sd, rd, wr, block, allchecks, __LINE__) + +static void +check_select_internal(int sd, int rd, int wr, int block, int allchecks, int line) +{ + fd_set read_set, write_set; + struct timeval tv; + + FD_ZERO(&read_set); + if (rd != -1) + FD_SET(sd, &read_set); + + FD_ZERO(&write_set); + if (wr != -1) + FD_SET(sd, &write_set); + + tv.tv_sec = block ? 2 : 0; + tv.tv_usec = 0; + + errno = 0; + if (select(sd + 1, &read_set, &write_set, NULL, &tv) < 0) + test_fail_fl("select() failed unexpectedly", __FILE__, line); + + if (rd != -1 && !!FD_ISSET(sd, &read_set) != rd && allchecks) + test_fail_fl("select() mismatch on read operation", + __FILE__, line); + + if (wr != -1 && !!FD_ISSET(sd, &write_set) != wr && allchecks) + test_fail_fl("select() mismatch on write operation", + __FILE__, line); +} + +/* + * Verify that: + * - a nonblocking connecting socket for which there is no accepter, will + * return EINPROGRESS and complete in the background later; + * - a nonblocking listening socket will return EAGAIN on accept; + * - connecting a connecting socket yields EALREADY; + * - connecting a connected socket yields EISCONN; + * - selecting for read and write on a connecting socket will only satisfy the + * write only once it is connected; + * - doing a nonblocking write on a connecting socket yields EAGAIN; + * - doing a nonblocking read on a connected socket with no pending data yields + * EAGAIN. + */ +void +test_nonblock(const struct socket_test_info *info) +{ + char buf[BUFSIZE]; + socklen_t len; + int server_sd, client_sd; + struct sockaddr_storage addr; + int status; + + debug("entering test_nonblock()"); + memset(buf, 0, sizeof(buf)); + + SOCKET(server_sd, info->domain, info->type, 0); + + if (bind(server_sd, info->serveraddr, info->serveraddrlen) == -1) + test_fail("bind() should have worked"); + + if (listen(server_sd, 8) == -1) + test_fail("listen() should have worked"); + + fcntl(server_sd, F_SETFL, fcntl(server_sd, F_GETFL) | O_NONBLOCK); + + check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + len = sizeof(addr); + if (accept(server_sd, (struct sockaddr *) &addr, &len) != -1 || + errno != EAGAIN) + test_fail("accept() should have yielded EAGAIN"); + + SOCKET(client_sd, info->domain, info->type, 0); + + fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1) { + test_fail("connect() should have failed"); + } else if (errno != EINPROGRESS) { + test_fail("connect() should have yielded EINPROGRESS"); + } + + check_select_cond(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/, + !info->ignore_select_delay); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1) { + test_fail("connect() should have failed"); + } else if (errno != EALREADY && errno != EISCONN) { + test_fail("connect() should have yielded EALREADY"); + } + + if (recv(client_sd, buf, sizeof(buf), 0) != -1 || errno != EAGAIN) + test_fail("recv() should have yielded EAGAIN"); + + /* This may be an implementation aspect, or even plain wrong (?). */ + if (send(client_sd, buf, sizeof(buf), 0) != -1) { + if (!info->ignore_send_waiting) { + test_fail("send() should have failed"); + } + } else if (errno != EAGAIN) { + test_fail("send() should have yielded EAGAIN"); + } + + switch (fork()) { + case 0: + errct = 0; + close(client_sd); + + check_select(server_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); + + len = sizeof(addr); + client_sd = accept(server_sd, (struct sockaddr *) &addr, &len); + if (client_sd == -1) + test_fail("accept() should have succeeded"); + + check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + close(server_sd); + + /* Let the socket become writable in the parent process. */ + sleep(1); + + if (write(client_sd, buf, 1) != 1) + test_fail("write() should have succeeded"); + + /* Wait for the client side to close. */ + check_select_cond(client_sd, 0 /*read*/, 1 /*write*/, + 0 /*block*/, !info->ignore_select_delay /*allchecks*/); + check_select(client_sd, 1 /*read*/, -1 /*write*/, 1 /*block*/); + check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); + + exit(errct); + case -1: + test_fail("can't fork"); + default: + break; + } + + close(server_sd); + + check_select(client_sd, 0 /*read*/, 1 /*write*/, 1 /*block*/); + check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1 || + errno != EISCONN) + test_fail("connect() should have yielded EISCONN"); + + check_select(client_sd, 1 /*read*/, -1 /*write*/, 1 /*block*/); + check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); + + if (read(client_sd, buf, 1) != 1) + test_fail("read() should have succeeded"); + + check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + if (read(client_sd, buf, 1) != -1 || errno != EAGAIN) + test_fail("read() should have yielded EAGAIN"); + + /* Let the child process block on the select waiting for the close. */ + sleep(1); + + close(client_sd); + + errno = 0; + if (wait(&status) <= 0) + test_fail("wait() should have succeeded"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + test_fail("child process failed the test"); + + info->callback_cleanup(); + debug("leaving test_nonblock()"); +} + +/* + * Verify that a nonblocking connect for which there is an accepter, succeeds + * immediately. A pretty lame test, only here for completeness. + */ +void +test_connect_nb(const struct socket_test_info *info) +{ + socklen_t len; + int server_sd, client_sd; + struct sockaddr_storage addr; + int status; + + debug("entering test_connect_nb()"); + SOCKET(server_sd, info->domain, info->type, 0); + + if (bind(server_sd, info->serveraddr, info->serveraddrlen) == -1) + test_fail("bind() should have worked"); + + if (listen(server_sd, 8) == -1) + test_fail("listen() should have worked"); + + switch (fork()) { + case 0: + errct = 0; + len = sizeof(addr); + if (accept(server_sd, (struct sockaddr *) &addr, &len) == -1) + test_fail("accept() should have succeeded"); + + exit(errct); + case -1: + test_fail("can't fork"); + default: + break; + } + + close(server_sd); + + sleep(1); + + SOCKET(client_sd, info->domain, info->type, 0); + + fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != 0) { + if (!info->ignore_connect_delay) { + test_fail("connect() should have succeeded"); + } else if (errno != EINPROGRESS) { + test_fail("connect() should have succeeded or " + "failed with EINPROGRESS"); + } + } + + close(client_sd); + + if (wait(&status) <= 0) + test_fail("wait() should have succeeded"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + test_fail("child process failed the test"); + + info->callback_cleanup(); + debug("leaving test_connect_nb()"); +} + +static void +dummy_handler(int sig) +{ + /* Nothing. */ +} + +/* + * Verify that: + * - interrupting a blocking connect will return EINTR but complete in the + * background later; + * - doing a blocking write on an asynchronously connecting socket succeeds + * once the socket is connected. + * - doing a nonblocking write on a connected socket with lots of pending data + * yields EAGAIN. + */ +void +test_intr(const struct socket_test_info *info) +{ + struct sigaction act, oact; + char buf[BUFSIZE]; + int isconn; + socklen_t len; + int server_sd, client_sd; + struct sockaddr_storage addr; + int r, status; + + debug("entering test_intr()"); + memset(buf, 0, sizeof(buf)); + + SOCKET(server_sd, info->domain, info->type, 0); + + if (bind(server_sd, info->serveraddr, info->serveraddrlen) == -1) + test_fail("bind() should have worked"); + + if (listen(server_sd, 8) == -1) + test_fail("listen() should have worked"); + + SOCKET(client_sd, info->domain, info->type, 0); + + memset(&act, 0, sizeof(act)); + act.sa_handler = dummy_handler; + if (sigaction(SIGALRM, &act, &oact) == -1) + test_fail("sigaction() should have succeeded"); + + if (info->domain != PF_INET) alarm(1); + + isconn = 0; + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1) { + if (!info->ignore_connect_unaccepted) { + test_fail("connect() should have failed"); + } + isconn = 1; + } else if (errno != EINTR) { + test_fail("connect() should have yielded EINTR"); + } + + alarm(0); + + check_select(client_sd, 0 /*read*/, isconn /*write*/, 0 /*block*/); + + switch (fork()) { + case 0: + errct = 0; + close(client_sd); + + check_select(server_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); + + len = sizeof(addr); + client_sd = accept(server_sd, (struct sockaddr *) &addr, &len); + if (client_sd == -1) + test_fail("accept() should have succeeded"); + + check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + close(server_sd); + + check_select(client_sd, 1 /*read*/, -1 /*write*/, 1 /*block*/); + check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); + + if (recv(client_sd, buf, sizeof(buf), 0) != sizeof(buf)) + test_fail("recv() should have yielded bytes"); + + /* No partial transfers should be happening. */ + check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + sleep(1); + + fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | + O_NONBLOCK); + + /* We can only test nonblocking writes by filling the pipe. */ + while ((r = write(client_sd, buf, sizeof(buf))) > 0); + + if (r != -1) { + test_fail("write() should have failed"); + } else if (errno != EAGAIN) { + test_fail("write() should have yielded EAGAIN"); + } + + check_select(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/); + + if (write(client_sd, buf, 1) != -1) { + test_fail("write() should have failed"); + } else if (errno != EAGAIN) { + test_fail("write() should have yielded EAGAIN"); + } + + exit(errct); + case -1: + test_fail("can't fork"); + default: + break; + } + + close(server_sd); + + if (send(client_sd, buf, sizeof(buf), 0) != sizeof(buf)) + test_fail("send() should have succeded"); + + check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + if (wait(&status) <= 0) + test_fail("wait() should have succeeded"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + test_fail("child process failed the test"); + + check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); + + close(client_sd); + + sigaction(SIGALRM, &oact, NULL); + + info->callback_cleanup(); + debug("leaving test_intr()"); +} + +/* + * Verify that closing a connecting socket before it is accepted will result in + * no activity on the accepting side later. + */ +void +test_connect_close(const struct socket_test_info *info) +{ + int server_sd, client_sd; + struct sockaddr_storage addr; + socklen_t len; + + debug("entering test_connect_close()"); + SOCKET(server_sd, info->domain, info->type, 0); + + if (bind(server_sd, info->serveraddr, info->serveraddrlen) == -1) + test_fail("bind() should have worked"); + + if (listen(server_sd, 8) == -1) + test_fail("listen() should have worked"); + + fcntl(server_sd, F_SETFL, fcntl(server_sd, F_GETFL) | O_NONBLOCK); + + check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); + + SOCKET(client_sd, info->domain, info->type, 0); + + fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1 || + errno != EINPROGRESS) + test_fail("connect() should have yielded EINPROGRESS"); + + check_select_cond(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/, + !info->ignore_select_delay); + check_select_cond(server_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/, + !info->ignore_select_delay); + + close(client_sd); + + check_select_cond(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/, + !info->ignore_select_delay); + + len = sizeof(addr); + errno = 0; + if (accept(server_sd, (struct sockaddr *) &addr, &len) != -1) { + if (!info->ignore_accept_delay) { + test_fail("accept() should have failed"); + } + } else if (errno != EAGAIN) { + test_fail("accept() should have yielded EAGAIN"); + } + close(server_sd); + + info->callback_cleanup(); + debug("leaving test_connect_close()"); +} + +/* + * Verify that closing a listening socket will cause a blocking connect to fail + * with ECONNRESET, and that a subsequent write will yield EPIPE. + */ +void +test_listen_close(const struct socket_test_info *info) +{ + int server_sd, client_sd; + int status; + char byte; + + debug("entering test_listen_close()"); + SOCKET(server_sd, info->domain, info->type, 0); + + if (bind(server_sd, info->serveraddr, info->serveraddrlen) == -1) + test_fail("bind() should have worked"); + + if (listen(server_sd, 8) == -1) + test_fail("listen() should have worked"); + + switch (fork()) { + case 0: + sleep(1); + + exit(0); + case -1: + test_fail("can't fork"); + default: + break; + } + + close(server_sd); + + SOCKET(client_sd, info->domain, info->type, 0); + + byte = 0; + if (write(client_sd, &byte, 1) != -1 || errno != ENOTCONN) + /* Yes, you fucked up the fix for the FIXME below. */ + test_fail("write() should have yielded ENOTCONN"); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1) { + if (!info->bug_connect_after_close) { + test_fail("connect() should have failed"); + } + } else if (errno != ECONNRESET) { + test_fail("connect() should have yielded ECONNRESET"); + } + + /* + * FIXME: currently UDS cannot distinguish between sockets that have + * not yet been connected, and sockets that have been disconnected. + * Thus, we get the same error for both: ENOTCONN instead of EPIPE. + */ +#if 0 + if (write(client_sd, &byte, 1) != -1 || errno != EPIPE) + test_fail("write() should have yielded EPIPE"); +#endif + + close(client_sd); + + if (wait(&status) <= 0) + test_fail("wait() should have succeeded"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + test_fail("child process failed the test"); + + info->callback_cleanup(); + debug("leaving test_listen_close()"); +} + +/* + * Verify that closing a listening socket will cause a nonblocking connect to + * result in the socket becoming readable and writable, and yielding ECONNRESET + * and EPIPE on the next two writes, respectively. + */ +void +test_listen_close_nb(const struct socket_test_info *info) +{ + int server_sd, client_sd; + int status; + char byte; + + debug("entering test_listen_close_nb()"); + SOCKET(server_sd, info->domain, info->type, 0); + + if (bind(server_sd, info->serveraddr, info->serveraddrlen) == -1) + test_fail("bind() should have worked"); + + if (listen(server_sd, 8) == -1) + test_fail("listen() should have worked"); + + switch (fork()) { + case 0: + sleep(1); + + exit(0); + case -1: + test_fail("can't fork"); + default: + break; + } + + close(server_sd); + + SOCKET(client_sd, info->domain, info->type, 0); + + fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); + + if (connect(client_sd, info->clientaddr, info->clientaddrlen) != -1 || + errno != EINPROGRESS) + test_fail("connect() should have yielded EINPROGRESS"); + + check_select_cond(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/, + !info->ignore_select_delay); + check_select_cond(client_sd, 1 /*read*/, 1 /*write*/, 1 /*block*/, + !info->ignore_select_delay); + + byte = 0; + if (write(client_sd, &byte, 1) != -1) { + if (!info->ignore_write_conn_reset) { + test_fail("write() should have failed"); + } + } else if (errno != ECONNRESET) { + test_fail("write() should have yielded ECONNRESET"); + } + + /* + * FIXME: currently UDS cannot distinguish between sockets that have + * not yet been connected, and sockets that have been disconnected. + * Thus, we get the same error for both: ENOTCONN instead of EPIPE. + */ +#if 0 + if (write(client_sd, &byte, 1) != -1 || errno != EPIPE) + test_fail("write() should have yielded EPIPE"); +#endif + + check_select_cond(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/, + !info->ignore_select_delay); + + close(client_sd); + + if (wait(&status) <= 0) + test_fail("wait() should have succeeded"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + test_fail("child process failed the test"); + + info->callback_cleanup(); + debug("leaving test_listen_close_nb()"); +} diff --git a/minix/tests/common-socket.h b/minix/tests/common-socket.h new file mode 100644 index 000000000..f03015be5 --- /dev/null +++ b/minix/tests/common-socket.h @@ -0,0 +1,124 @@ +#define DEBUG 0 + +/* buffer for send/recv */ +#define BUFSIZE (128) + +/* macro to display information about a failed test and increment the errct */ +void test_fail_fl(char *msg, char *file, int line); +#define test_fail(msg) test_fail_fl(msg, __FILE__, __LINE__) + +#if DEBUG == 1 +/* macros to display debugging information */ +void debug_fl(char *msg, char *file, int line); +#define debug(msg) debug_fl(msg, __FILE__, __LINE__) +#else +#define debug(msg) +#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) + +extern int server_ready; +void test_xfer_sighdlr(int sig); + +struct socket_test_info { + const struct sockaddr *clientaddr; + socklen_t clientaddrlen; + const struct sockaddr *clientaddr2; + socklen_t clientaddr2len; + const struct sockaddr *clientaddrsym; + socklen_t clientaddrsymlen; + int domain; + int expected_rcvbuf; + int expected_sndbuf; + const struct sockaddr *serveraddr; + socklen_t serveraddrlen; + const struct sockaddr *serveraddr2; + socklen_t serveraddr2len; + int type; + const int *types; + size_t typecount; + + int buf_accept_intr; /* accept can return success when interrupted */ + int bug_bind_in_use; /* bind does not return EADDRINUSE */ + int bug_bind_null; /* bind segfaults with NULL pointer */ + int bug_connect_after_close; /* connect succeeds after server closed */ + int bug_select_nonblock; /* select unexpected results for nb sockets */ + int bug_shutdown; /* shutdown not supported */ + int bug_shutdown_not_conn; /* shutdown does not return ENOTCONN */ + int bug_shutdown_read; /* shutdown does not support SHUT_RD */ + int bug_sockopt_rcvbuf; /* get/setsockopt does not support SO_RCVBUF */ + int bug_sockopt_sndbuf; /* get/setsockopt does not support SO_SNDBUF */ + int ignore_accept_delay; /* success from accept after aborted connect */ + int ignore_connect_delay; /* nb connect not instant */ + int ignore_connect_unaccepted; /* connect succeeds without accept */ + int ignore_read_conn_reset; /* read does not guarantee ECONNRESET */ + int ignore_select_delay; /* select delay reflecting other side nb op */ + int ignore_send_waiting; /* can send while waiting for nb recv */ + int ignore_write_conn_reset; /* write does not guarantee ECONNRESET */ + + void (* callback_check_sockaddr)(const struct sockaddr *sockaddr, + socklen_t sockaddrlen, const char *callname, int addridx); + void (* callback_cleanup)(void); + void (* callback_xfer_peercred)(int sd); /* can be NULL */ + void (* callback_xfer_prepclient)(void); /* can be NULL */ +}; + +void test_abort_client_server(const struct socket_test_info *info, + int abort_type); +void test_bind(const struct socket_test_info *info); +void test_close(const struct socket_test_info *info); +void test_connect_close(const struct socket_test_info *info); +void test_connect_nb(const struct socket_test_info *info); +void test_dup(const struct socket_test_info *info); +void test_dup2(const struct socket_test_info *info); +void test_getsockname(const struct socket_test_info *info); +void test_intr(const struct socket_test_info *info); +void test_listen(const struct socket_test_info *info); +void test_listen_close(const struct socket_test_info *info); +void test_listen_close_nb(const struct socket_test_info *info); +void test_msg_dgram(const struct socket_test_info *info); +void test_nonblock(const struct socket_test_info *info); +void test_read(const struct socket_test_info *info); +void test_shutdown(const struct socket_test_info *info); +void test_simple_client_server(const struct socket_test_info *info, int type); +void test_sockopts(const struct socket_test_info *info); +void test_socket(const struct socket_test_info *info); +void test_write(const struct socket_test_info *info); +void test_xfer(const struct socket_test_info *info); diff --git a/minix/tests/run b/minix/tests/run index 7bc938aa2..08c7c3f22 100755 --- a/minix/tests/run +++ b/minix/tests/run @@ -29,8 +29,8 @@ rootscripts="testisofs testvnd testrelpol" alltests="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \ - 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 \ - sh1 sh2 interp mfs isofs vnd" + 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \ + 81 sh1 sh2 interp mfs isofs vnd" tests_no=`expr 0` # If root, make sure the setuid tests have the correct permissions diff --git a/minix/tests/test56.c b/minix/tests/test56.c index b3295ce60..bb1a2bb77 100644 --- a/minix/tests/test56.c +++ b/minix/tests/test56.c @@ -35,8 +35,6 @@ * socketpair(), write(), writev() */ -#define DEBUG 0 - #include #include #include @@ -60,6 +58,7 @@ */ int max_error = 4; #include "common.h" +#include "common-socket.h" /* Use the common testing code instead of reinventing the wheel. */ @@ -76,69 +75,12 @@ int max_error = 4; #define TEST_TXT_FILE "test.txt" #define MSG "This raccoon loves to eat bugs.\n" -/* buffer for send/recv */ -#define BUFSIZE (128) - -#define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S" - /* socket types supported */ static int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}; static char sock_fullpath[PATH_MAX + 1]; -static void test_abort_client_server(int abort_type); -static void test_abort_client(int abort_type); -static void test_abort_server(pid_t pid, int abort_type); - -/* timestamps for debug and error logs */ -static 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 */ -static void test_fail_fl(char *msg, char *file, int line) -{ - 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; - } - e(7); -} -#define test_fail(msg) test_fail_fl(msg, __FILE__, __LINE__) - /* Convert name to the full path of the socket. Assumes name is in cwd. */ -static char *fullpath(char *name) +static char *fullpath(const char *name) { char cwd[PATH_MAX + 1]; @@ -150,148 +92,6 @@ static char *fullpath(char *name) return(sock_fullpath); } -#if DEBUG == 1 -/* macros to display debugging information */ -static void debug_fl(char *msg, char *file, int line) -{ - 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; - } -} -#define debug(msg) debug_fl(msg, __FILE__, __LINE__) -#else -#define debug(msg) -#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) - -static 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()"); -} - static void test_header(void) { struct sockaddr_un sun; @@ -426,99 +226,54 @@ static void test_ucred(void) CLOSE(sv[1]); } -static void test_getsockname(void) -{ - int sd; - int rc; - struct sockaddr_un addr, sock_addr; - socklen_t sock_addr_len; +static void callback_check_sockaddr(const struct sockaddr *sockaddr, + socklen_t sockaddrlen, const char *callname, int addridx) { + char buf[256]; + const char *path; + const struct sockaddr_un *sockaddr_un = + (const struct sockaddr_un *) sockaddr; - memset(&addr, '\0', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); - - 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"); + switch (addridx) { + case 1: path = TEST_SUN_PATH; break; + case 2: path = TEST_SUN_PATHB; break; + default: + fprintf(stderr, "error: invalid addridx %d in " + "callback_check_sockaddr\n", addridx); + abort(); } - debug("Test getsockname() success"); + if (!(sockaddr_un->sun_family == AF_UNIX && + strncmp(sockaddr_un->sun_path, + fullpath(path), + sizeof(sockaddr_un->sun_path) - 1) == 0)) { - 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"); + snprintf(buf, sizeof(buf), "%s() didn't return the right addr", + callname); + test_fail(buf); + fprintf(stderr, "exp: '%s' | got: '%s'\n", path, + sockaddr_un->sun_path); } - - if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path, - fullpath(TEST_SUN_PATH), - sizeof(sock_addr.sun_path) - 1) == 0)) { - test_fail("getsockname() did return the right address"); - fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path, - sock_addr.sun_path); - } - - CLOSE(sd); } -static void test_bind(void) +static void callback_cleanup(void) { + UNLINK(TEST_SUN_PATH); + UNLINK(TEST_SUN_PATHB); + UNLINK(TEST_SYM_A); + UNLINK(TEST_SYM_B); +} + +static void test_bind_unix(void) { struct sockaddr_un addr; - struct sockaddr_un sock_addr; - socklen_t sock_addr_len; int sd; - int sd2; int rc; - debug("entering test_bind()"); + debug("entering test_bind_unix()"); 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, - fullpath(TEST_SUN_PATH), - sizeof(sock_addr.sun_path) - 1) == 0)) { - - test_fail("getsockname() didn't return the right addr"); - fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path, - sock_addr.sun_path); - } - - 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); @@ -531,16 +286,6 @@ static void test_bind(void) } 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); @@ -579,719 +324,21 @@ static void test_bind(void) CLOSE(sd); UNLINK("foo"); - debug("leaving test_bind()"); + debug("leaving test_bind_unix()"); } -static void test_listen(void) -{ - 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); - /* Test on errno disabled here: there's currently no telling what this - * will return. POSIX says it should be ENOTSOCK, MINIX3 libc returns - * ENOSYS, and we used to test for ENOTTY here.. - */ - if (!(rc == -1)) { - test_fail("listen(0, 0) should have failed"); - } - - debug("leaving test_listen()"); -} - -static 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()"); -} - -static void test_close(void) -{ - struct sockaddr_un addr; - int sd, sd2; - int rc, i; - - 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); - - /* Create and close a socket a bunch of times. - * If the implementation doesn't properly free the - * socket during close(), eventually socket() will - * fail when the internal descriptor table is full. - */ - for (i = 0; i < 1024; i++) { - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - CLOSE(sd); - } - - debug("leaving test_close()"); -} - -static void test_sockopts(void) -{ - int i; - int rc; - int sd; - int option_value; - 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()"); -} - -static 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()"); -} - -static 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()"); -} - -static 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()"); -} - -static 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. - */ -static void test_xfer_server(pid_t pid) -{ - int i; - struct timeval tv; - fd_set readfds; - int status; - int rc; - int sd; - unsigned char buf[BUFSIZE]; - socklen_t client_addr_size; - int client_sd; - struct sockaddr_un addr; - struct sockaddr_un client_addr; - - status = 0; - rc = 0; - sd = 0; - 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 its error count */ - errct += WEXITSTATUS(status); -} - -static int server_ready = 0; - -/* signal handler for the client */ -static 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. - */ -static void test_xfer_client(void) -{ - struct uucred 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]; - - debug("[client] entering test_xfer_client()"); - errct = 0; /* reset error count */ - ucred_length = sizeof(struct uucred); - 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); - - +static void callback_xfer_prepclient(void) { 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; +static void callback_xfer_peercred(int sd) { + struct uucred credentials; + int rc; + socklen_t ucred_length; - 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, - fullpath(TEST_SUN_PATH), - 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"); - } + ucred_length = sizeof(struct uucred); debug("Test passing the client credentials to the server"); @@ -1307,480 +354,6 @@ static void test_xfer_client(void) geteuid(), credentials.cr_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); - - debug("[client] leaving test_xfer_client()"); - exit(errct); -} - -static void test_xfer(void) -{ - pid_t pid; - - UNLINK(TEST_SYM_A); - 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; - } else if (pid == 0) { - debug("child"); - test_xfer_client(); - test_fail("we should never get here"); - exit(1); - } else { - debug("parent"); - test_xfer_server(pid); - debug("parent done"); - } - - UNLINK(TEST_SYM_A); - UNLINK(TEST_SUN_PATH); -} - -static 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"); - exit(errct); - } - - 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"); - exit(errct); - } - - } else { - - rc = connect(sd, (struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); - if (rc == -1) { - test_fail("connect"); - exit(errct); - } - - rc = write(sd, buf, strlen(buf) + 1); - - if (rc == -1) { - test_fail("write"); - } - - memset(buf, '\0', BUFSIZE); - rc = read(sd, buf, BUFSIZE); - if (rc == -1) { - test_fail("read"); - } - - if (strcmp("Hello, My Name is Server.", buf) != 0) { - test_fail("didn't read the correct string"); - } - } - - rc = close(sd); - if (rc == -1) { - test_fail("close"); - } - - exit(errct); -} - -static 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; - - addr_len = sizeof(struct sockaddr_un); - - 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"); - } - - memset(buf, '\0', BUFSIZE); - rc = read(client_sd, buf, BUFSIZE); - if (rc == -1) { - test_fail("read"); - } - - if (strcmp("Hello, My Name is Client.", buf) != 0) { - test_fail("didn't read the correct string"); - } - - /* 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 its error count */ - errct += WEXITSTATUS(status); -} - -static void test_abort_client_server(int abort_type) -{ - pid_t pid; - debug("test_simple_client_server()"); - - 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; - } else if (pid == 0) { - debug("child"); - test_abort_client(abort_type); - test_fail("we should never get here"); - exit(1); - } else { - debug("parent"); - test_abort_server(pid, abort_type); - debug("parent done"); - } - - UNLINK(TEST_SUN_PATH); -} - -static void test_abort_client(int abort_type) -{ - char buf[BUFSIZE]; - int sd, rc; - struct sockaddr_un addr; - - sd = socket(PF_UNIX, SOCK_STREAM, 0); - if (sd == -1) { - test_fail("socket"); - exit(errct); - } - - 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."); - - rc = connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); - if (rc == -1) { - test_fail("connect"); - exit(errct); - } - - if (abort_type == 2) { - /* Give server a chance to close connection */ - sleep(2); - rc = write(sd, buf, strlen(buf) + 1); - if (rc != -1) { - test_fail("write should have failed\n"); - } - if (errno != ECONNRESET) { - test_fail("errno should've been ECONNRESET\n"); - } - } - - rc = close(sd); - if (rc == -1) { - test_fail("close"); - } - - exit(errct); -} - -static void test_abort_server(pid_t pid, int abort_type) -{ - char buf[BUFSIZE]; - int sd, rc, client_sd, status; - struct sockaddr_un addr; - socklen_t addr_len; - - addr_len = sizeof(struct sockaddr_un); - - sd = socket(PF_UNIX, SOCK_STREAM, 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"); - } - - 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"); - } - - if (abort_type == 1) { - memset(buf, '\0', BUFSIZE); - rc = read(client_sd, buf, BUFSIZE); - if (rc != -1) { - test_fail("read should've failed\n"); - } - if (errno != ECONNRESET) { - test_fail("errno should've been ECONNRESET\n"); - } - } /* else if (abort_type == 2) { */ - 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 its error count */ - errct += WEXITSTATUS(status); -} - -static void test_simple_client_server(int type) -{ - pid_t pid; - debug("test_simple_client_server()"); - - 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; - } else if (pid == 0) { - debug("child"); - test_simple_client(type); - test_fail("we should never get here"); - exit(1); - } else { - debug("parent"); - test_simple_server(type, pid); - debug("parent done"); - } - - UNLINK(TEST_SUN_PATH); } static void test_vectorio(int type) @@ -1982,130 +555,6 @@ static void test_msg(int type) } } -static 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); - - debug("test msg_dgram"); - - 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, - fullpath(TEST_SUN_PATHB))) { - 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); -} - static void test_scm_credentials(void) { int rc; @@ -2270,7 +719,7 @@ static void test_scm_credentials(void) UNLINK(TEST_SUN_PATHB); } -static void test_connect(void) +static void test_connect(const struct socket_test_info *info) { int i, sd, sds[2], rc; @@ -2294,7 +743,7 @@ static void test_connect(void) } for (i = 0; i < 3; i++) { - test_simple_client_server(types[i]); + test_simple_client_server(info, types[i]); } rc = close(sds[1]); @@ -2928,600 +1377,77 @@ static void test_fchmod() close(socks[1]); } -static void -check_select(int sd, int rd, int wr, int block) -{ - fd_set read_set, write_set; - struct timeval tv; - - FD_ZERO(&read_set); - if (rd != -1) - FD_SET(sd, &read_set); - - FD_ZERO(&write_set); - if (wr != -1) - FD_SET(sd, &write_set); - - tv.tv_sec = block ? 2 : 0; - tv.tv_usec = 0; - - if (select(sd + 1, &read_set, &write_set, NULL, &tv) < 0) - test_fail("select() failed unexpectedly"); - - if (rd != -1 && !!FD_ISSET(sd, &read_set) != rd) - test_fail("select() mismatch on read operation"); - - if (wr != -1 && !!FD_ISSET(sd, &write_set) != wr) - test_fail("select() mismatch on write operation"); -} - -/* - * Verify that: - * - a nonblocking connecting socket for which there is no accepter, will - * return EINPROGRESS and complete in the background later; - * - a nonblocking listening socket will return EAGAIN on accept; - * - connecting a connecting socket yields EALREADY; - * - connecting a connected socket yields EISCONN; - * - selecting for read and write on a connecting socket will only satisfy the - * write only once it is connected; - * - doing a nonblocking write on a connecting socket yields EAGAIN; - * - doing a nonblocking read on a connected socket with no pending data yields - * EAGAIN. - */ -static void -test_nonblock(void) -{ - char buf[BUFSIZE]; - socklen_t len; - int server_sd, client_sd; - struct sockaddr_un server_addr, client_addr, addr; - int status; - - memset(buf, 0, sizeof(buf)); - - memset(&server_addr, 0, sizeof(server_addr)); - strlcpy(server_addr.sun_path, TEST_SUN_PATH, - sizeof(server_addr.sun_path)); - server_addr.sun_family = AF_UNIX; - - client_addr = server_addr; - - SOCKET(server_sd, PF_UNIX, SOCK_STREAM, 0); - - if (bind(server_sd, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr_un)) == -1) - test_fail("bind() should have worked"); - - if (listen(server_sd, 8) == -1) - test_fail("listen() should have worked"); - - fcntl(server_sd, F_SETFL, fcntl(server_sd, F_GETFL) | O_NONBLOCK); - - check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - len = sizeof(addr); - if (accept(server_sd, (struct sockaddr *) &addr, &len) != -1 || - errno != EAGAIN) - test_fail("accept() should have yielded EAGAIN"); - - SOCKET(client_sd, PF_UNIX, SOCK_STREAM, 0); - - fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != EINPROGRESS) - test_fail("connect() should have yielded EINPROGRESS"); - - check_select(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != EALREADY) - test_fail("connect() should have yielded EALREADY"); - - if (recv(client_sd, buf, sizeof(buf), 0) != -1 || errno != EAGAIN) - test_fail("recv() should have yielded EAGAIN"); - - /* This may be an implementation aspect, or even plain wrong (?). */ - if (send(client_sd, buf, sizeof(buf), 0) != -1 || errno != EAGAIN) - test_fail("send() should have yielded EAGAIN"); - - switch (fork()) { - case 0: - close(client_sd); - - check_select(server_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - len = sizeof(addr); - client_sd = accept(server_sd, (struct sockaddr *) &addr, &len); - if (client_sd == -1) - test_fail("accept() should have succeeded"); - - check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - close(server_sd); - - /* Let the socket become writable in the parent process. */ - sleep(1); - - if (write(client_sd, buf, 1) != 1) - test_fail("write() should have succeeded"); - - /* Wait for the client side to close. */ - check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - check_select(client_sd, 1 /*read*/, -1 /*write*/, 1 /*block*/); - check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - exit(errct); - case -1: - test_fail("can't fork"); - default: - break; - } - - close(server_sd); - - check_select(client_sd, 0 /*read*/, 1 /*write*/, 1 /*block*/); - check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != EISCONN) - test_fail("connect() should have yielded EISCONN"); - - check_select(client_sd, 1 /*read*/, -1 /*write*/, 1 /*block*/); - check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - if (read(client_sd, buf, 1) != 1) - test_fail("read() should have succeeded"); - - check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - if (read(client_sd, buf, 1) != -1 || errno != EAGAIN) - test_fail("read() should have yielded EAGAIN"); - - /* Let the child process block on the select waiting for the close. */ - sleep(1); - - close(client_sd); - - if (wait(&status) <= 0) - test_fail("wait() should have succeeded"); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - test_fail("child process failed the test"); - - UNLINK(TEST_SUN_PATH); -} - -/* - * Verify that a nonblocking connect for which there is an accepter, succeeds - * immediately. A pretty lame test, only here for completeness. - */ -static void -test_connect_nb(void) -{ - socklen_t len; - int server_sd, client_sd; - struct sockaddr_un server_addr, client_addr, addr; - int status; - - memset(&server_addr, 0, sizeof(server_addr)); - strlcpy(server_addr.sun_path, TEST_SUN_PATH, - sizeof(server_addr.sun_path)); - server_addr.sun_family = AF_UNIX; - - client_addr = server_addr; - - SOCKET(server_sd, PF_UNIX, SOCK_STREAM, 0); - - if (bind(server_sd, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr_un)) == -1) - test_fail("bind() should have worked"); - - if (listen(server_sd, 8) == -1) - test_fail("listen() should have worked"); - - switch (fork()) { - case 0: - len = sizeof(addr); - if (accept(server_sd, (struct sockaddr *) &addr, &len) == -1) - test_fail("accept() should have succeeded"); - - exit(errct); - case -1: - test_fail("can't fork"); - default: - break; - } - - close(server_sd); - - sleep(1); - - SOCKET(client_sd, PF_UNIX, SOCK_STREAM, 0); - - fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != 0) - test_fail("connect() should have succeeded"); - - close(client_sd); - - if (wait(&status) <= 0) - test_fail("wait() should have succeeded"); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - test_fail("child process failed the test"); - - UNLINK(TEST_SUN_PATH); -} - -static void -dummy_handler(int sig) -{ - /* Nothing. */ -} - -/* - * Verify that: - * - interrupting a blocking connect will return EINTR but complete in the - * background later; - * - doing a blocking write on an asynchronously connecting socket succeeds - * once the socket is connected. - * - doing a nonblocking write on a connected socket with lots of pending data - * yields EAGAIN. - */ -static void -test_intr(void) -{ - struct sigaction act, oact; - char buf[BUFSIZE]; - socklen_t len; - int server_sd, client_sd; - struct sockaddr_un server_addr, client_addr, addr; - int r, status; - - memset(buf, 0, sizeof(buf)); - - memset(&server_addr, 0, sizeof(server_addr)); - strlcpy(server_addr.sun_path, TEST_SUN_PATH, - sizeof(server_addr.sun_path)); - server_addr.sun_family = AF_UNIX; - - client_addr = server_addr; - - SOCKET(server_sd, PF_UNIX, SOCK_STREAM, 0); - - if (bind(server_sd, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr_un)) == -1) - test_fail("bind() should have worked"); - - if (listen(server_sd, 8) == -1) - test_fail("listen() should have worked"); - - SOCKET(client_sd, PF_UNIX, SOCK_STREAM, 0); - - memset(&act, 0, sizeof(act)); - act.sa_handler = dummy_handler; - if (sigaction(SIGALRM, &act, &oact) == -1) - test_fail("sigaction() should have succeeded"); - - alarm(1); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != EINTR) - test_fail("connect() should have yielded EINTR"); - - check_select(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/); - - switch (fork()) { - case 0: - close(client_sd); - - check_select(server_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - len = sizeof(addr); - client_sd = accept(server_sd, (struct sockaddr *) &addr, &len); - if (client_sd == -1) - test_fail("accept() should have succeeded"); - - check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - close(server_sd); - - check_select(client_sd, 1 /*read*/, -1 /*write*/, 1 /*block*/); - check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - if (recv(client_sd, buf, sizeof(buf), 0) != sizeof(buf)) - test_fail("recv() should have yielded bytes"); - - /* No partial transfers should be happening. */ - check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - sleep(1); - - fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | - O_NONBLOCK); - - /* We can only test nonblocking writes by filling the pipe. */ - while ((r = write(client_sd, buf, sizeof(buf))) > 0); - - if (r != -1 || errno != EAGAIN) - test_fail("write() should have yielded EAGAIN"); - - check_select(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/); - - if (write(client_sd, buf, 1) != -1 || errno != EAGAIN) - test_fail("write() should have yielded EAGAIN"); - - exit(errct); - case -1: - test_fail("can't fork"); - default: - break; - } - - close(server_sd); - - if (send(client_sd, buf, sizeof(buf), 0) != sizeof(buf)) - test_fail("send() should have succeded"); - - check_select(client_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - if (wait(&status) <= 0) - test_fail("wait() should have succeeded"); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - test_fail("child process failed the test"); - - check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - close(client_sd); - - sigaction(SIGALRM, &oact, NULL); - - UNLINK(TEST_SUN_PATH); -} - -/* - * Verify that closing a connecting socket before it is accepted will result in - * no activity on the accepting side later. - */ -static void -test_connect_close(void) -{ - int server_sd, client_sd; - struct sockaddr_un server_addr, client_addr; - socklen_t len; - - memset(&server_addr, 0, sizeof(server_addr)); - strlcpy(server_addr.sun_path, TEST_SUN_PATH, - sizeof(server_addr.sun_path)); - server_addr.sun_family = AF_UNIX; - - client_addr = server_addr; - - SOCKET(server_sd, PF_UNIX, SOCK_STREAM, 0); - - if (bind(server_sd, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr_un)) == -1) - test_fail("bind() should have worked"); - - if (listen(server_sd, 8) == -1) - test_fail("listen() should have worked"); - - fcntl(server_sd, F_SETFL, fcntl(server_sd, F_GETFL) | O_NONBLOCK); - - check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - SOCKET(client_sd, PF_UNIX, SOCK_STREAM, 0); - - fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != EINPROGRESS) - test_fail("connect() should have yielded EINPROGRESS"); - - check_select(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/); - check_select(server_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - close(client_sd); - - check_select(server_sd, 0 /*read*/, 1 /*write*/, 0 /*block*/); - - len = sizeof(client_addr); - if (accept(server_sd, (struct sockaddr *) &client_addr, &len) != -1 || - errno != EAGAIN) - test_fail("accept() should have yielded EAGAIN"); - - close(server_sd); - - UNLINK(TEST_SUN_PATH); -} - -/* - * Verify that closing a listening socket will cause a blocking connect to fail - * with ECONNRESET, and that a subsequent write will yield EPIPE. - */ -static void -test_listen_close(void) -{ - int server_sd, client_sd; - struct sockaddr_un server_addr, client_addr; - int status; - char byte; - - memset(&server_addr, 0, sizeof(server_addr)); - strlcpy(server_addr.sun_path, TEST_SUN_PATH, - sizeof(server_addr.sun_path)); - server_addr.sun_family = AF_UNIX; - - client_addr = server_addr; - - SOCKET(server_sd, PF_UNIX, SOCK_STREAM, 0); - - if (bind(server_sd, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr_un)) == -1) - test_fail("bind() should have worked"); - - if (listen(server_sd, 8) == -1) - test_fail("listen() should have worked"); - - switch (fork()) { - case 0: - sleep(1); - - exit(0); - case -1: - test_fail("can't fork"); - default: - break; - } - - close(server_sd); - - SOCKET(client_sd, PF_UNIX, SOCK_STREAM, 0); - - byte = 0; - if (write(client_sd, &byte, 1) != -1 || errno != ENOTCONN) - /* Yes, you fucked up the fix for the FIXME below. */ - test_fail("write() should have yielded ENOTCONN"); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != ECONNRESET) - test_fail("connect() should have yielded ECONNRESET"); - - /* - * FIXME: currently UDS cannot distinguish between sockets that have - * not yet been connected, and sockets that have been disconnected. - * Thus, we get the same error for both: ENOTCONN instead of EPIPE. - */ -#if 0 - if (write(client_sd, &byte, 1) != -1 || errno != EPIPE) - test_fail("write() should have yielded EPIPE"); -#endif - - close(client_sd); - - if (wait(&status) <= 0) - test_fail("wait() should have succeeded"); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - test_fail("child process failed the test"); - - UNLINK(TEST_SUN_PATH); -} - -/* - * Verify that closing a listening socket will cause a nonblocking connect to - * result in the socket becoming readable and writable, and yielding ECONNRESET - * and EPIPE on the next two writes, respectively. - */ -static void -test_listen_close_nb(void) -{ - int server_sd, client_sd; - struct sockaddr_un server_addr, client_addr; - int status; - char byte; - - memset(&server_addr, 0, sizeof(server_addr)); - strlcpy(server_addr.sun_path, TEST_SUN_PATH, - sizeof(server_addr.sun_path)); - server_addr.sun_family = AF_UNIX; - - client_addr = server_addr; - - SOCKET(server_sd, PF_UNIX, SOCK_STREAM, 0); - - if (bind(server_sd, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr_un)) == -1) - test_fail("bind() should have worked"); - - if (listen(server_sd, 8) == -1) - test_fail("listen() should have worked"); - - switch (fork()) { - case 0: - sleep(1); - - exit(0); - case -1: - test_fail("can't fork"); - default: - break; - } - - close(server_sd); - - SOCKET(client_sd, PF_UNIX, SOCK_STREAM, 0); - - fcntl(client_sd, F_SETFL, fcntl(client_sd, F_GETFL) | O_NONBLOCK); - - if (connect(client_sd, (struct sockaddr *) &client_addr, - sizeof(struct sockaddr_un)) != -1 || errno != EINPROGRESS) - test_fail("connect() should have yielded EINPROGRESS"); - - check_select(client_sd, 0 /*read*/, 0 /*write*/, 0 /*block*/); - check_select(client_sd, 1 /*read*/, 1 /*write*/, 1 /*block*/); - - byte = 0; - if (write(client_sd, &byte, 1) != -1 || errno != ECONNRESET) - test_fail("write() should have yielded ECONNRESET"); - - /* - * FIXME: currently UDS cannot distinguish between sockets that have - * not yet been connected, and sockets that have been disconnected. - * Thus, we get the same error for both: ENOTCONN instead of EPIPE. - */ -#if 0 - if (write(client_sd, &byte, 1) != -1 || errno != EPIPE) - test_fail("write() should have yielded EPIPE"); -#endif - - check_select(client_sd, 1 /*read*/, 1 /*write*/, 0 /*block*/); - - close(client_sd); - - if (wait(&status) <= 0) - test_fail("wait() should have succeeded"); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - test_fail("child process failed the test"); - - UNLINK(TEST_SUN_PATH); -} - int main(int argc, char *argv[]) { int i; + struct sockaddr_un clientaddr = { + .sun_family = AF_UNIX, + .sun_path = TEST_SUN_PATH, + }; + struct sockaddr_un clientaddr2 = { + .sun_family = AF_UNIX, + .sun_path = TEST_SUN_PATHB, + }; + struct sockaddr_un clientaddrsym = { + .sun_family = AF_UNIX, + .sun_path = TEST_SYM_A, + }; + const struct socket_test_info info = { + .clientaddr = (struct sockaddr *) &clientaddr, + .clientaddrlen = sizeof(clientaddr), + .clientaddr2 = (struct sockaddr *) &clientaddr2, + .clientaddr2len = sizeof(clientaddr2), + .clientaddrsym = (struct sockaddr *) &clientaddrsym, + .clientaddrsymlen = sizeof(clientaddrsym), + .domain = PF_UNIX, + .expected_rcvbuf = PIPE_BUF, + .expected_sndbuf = PIPE_BUF, + .serveraddr = (struct sockaddr *) &clientaddr, + .serveraddrlen = sizeof(clientaddr), + .serveraddr2 = (struct sockaddr *) &clientaddr2, + .serveraddr2len = sizeof(clientaddr2), + .type = SOCK_STREAM, + .types = types, + .typecount = 3, + .callback_check_sockaddr = callback_check_sockaddr, + .callback_cleanup = callback_cleanup, + .callback_xfer_prepclient = callback_xfer_prepclient, + .callback_xfer_peercred = callback_xfer_peercred, + }; debug("entering main()"); start(56); - test_socket(); - test_bind(); - test_listen(); - test_getsockname(); + test_socket(&info); + test_bind(&info); + test_bind_unix(); + test_listen(&info); + test_getsockname(&info); test_header(); - test_shutdown(); - test_close(); + test_shutdown(&info); + test_close(&info); test_permissions(); - test_dup(); - test_dup2(); + test_dup(&info); + test_dup2(&info); test_socketpair(); - test_shutdown(); - test_read(); - test_write(); - test_sockopts(); + test_shutdown(&info); + test_read(&info); + test_write(&info); + test_sockopts(&info); test_ucred(); - test_xfer(); + test_xfer(&info); for (i = 0; i < 3; i++) { - test_simple_client_server(types[i]); + test_simple_client_server(&info, types[i]); if (types[i] != SOCK_DGRAM) test_vectorio(types[i]); if (types[i] != SOCK_DGRAM) test_msg(types[i]); } - test_abort_client_server(1); - test_abort_client_server(2); - test_msg_dgram(); - test_connect(); + + test_abort_client_server(&info, 1); + test_abort_client_server(&info, 2); + test_msg_dgram(&info); + test_connect(&info); test_multiproc_read(); test_multiproc_write(); test_scm_credentials(); @@ -3529,12 +1455,12 @@ int main(int argc, char *argv[]) test_select(); test_select_close(); test_fchmod(); - test_nonblock(); - test_connect_nb(); - test_intr(); - test_connect_close(); - test_listen_close(); - test_listen_close_nb(); + test_nonblock(&info); + test_connect_nb(&info); + test_intr(&info); + test_connect_close(&info); + test_listen_close(&info); + test_listen_close_nb(&info); quit(); diff --git a/minix/tests/test80.c b/minix/tests/test80.c new file mode 100644 index 000000000..3db8140b1 --- /dev/null +++ b/minix/tests/test80.c @@ -0,0 +1,141 @@ +/* + * test80: use the functions originally written for test56 to test TCP + */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "common-socket.h" + +#define PORT1 4321 +#define PORT2 4322 + +static void callback_check_sockaddr(const struct sockaddr *sockaddr, + socklen_t sockaddrlen, const char *callname, int addridx) { + char buf[256]; + int port; + const struct sockaddr_in *sockaddr_in = + (const struct sockaddr_in *) sockaddr; + + switch (addridx) { + case 1: port = PORT1; break; + case 2: port = PORT2; break; + default: + fprintf(stderr, "error: invalid addridx %d in " + "callback_check_sockaddr\n", addridx); + abort(); + } + + if (sockaddr_in->sin_family != AF_INET || + sockaddr_in->sin_port != htons(port)) { + snprintf(buf, sizeof(buf), "%s() didn't return the right addr", + callname); + test_fail(buf); + + memset(buf, 0, sizeof(buf)); + inet_ntop(sockaddr_in->sin_family, &sockaddr_in->sin_addr, + buf, sizeof(buf)); + fprintf(stderr, "exp: localhost:%d | got: %s:%d\n", port, buf, + ntohs(sockaddr_in->sin_port)); + } +} + +static void callback_cleanup(void) { + /* nothing to do */ +} + +int main(int argc, char *argv[]) +{ + int i; + struct sockaddr_in clientaddr = { + .sin_family = AF_INET, + .sin_port = htons(PORT1), + .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }, + }; + struct sockaddr_in clientaddr2 = { + .sin_family = AF_INET, + .sin_port = htons(PORT2), + .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }, + }; + struct sockaddr_in serveraddr = { + .sin_family = AF_INET, + .sin_port = htons(PORT1), + .sin_addr = { .s_addr = htonl(INADDR_ANY) }, + }; + struct sockaddr_in serveraddr2 = { + .sin_family = AF_INET, + .sin_port = htons(PORT2), + .sin_addr = { .s_addr = htonl(INADDR_ANY) }, + }; + const struct socket_test_info info = { + .clientaddr = (struct sockaddr *) &clientaddr, + .clientaddrlen = sizeof(clientaddr), + .clientaddr2 = (struct sockaddr *) &clientaddr2, + .clientaddr2len = sizeof(clientaddr2), + .clientaddrsym = (struct sockaddr *) &clientaddr, + .clientaddrsymlen = sizeof(clientaddr), + .domain = PF_INET, + .expected_rcvbuf = -1, + .expected_sndbuf = -1, + .serveraddr = (struct sockaddr *) &serveraddr, + .serveraddrlen = sizeof(serveraddr), + .serveraddr2 = (struct sockaddr *) &serveraddr2, + .serveraddr2len = sizeof(serveraddr2), + .type = SOCK_STREAM, + .types = &info.type, + .typecount = 1, + + .bug_bind_in_use = 1, + .bug_bind_null = 1, + .bug_connect_after_close = 1, + .bug_shutdown_not_conn = 1, + .bug_shutdown_read = 1, + .ignore_accept_delay = 1, + .ignore_connect_unaccepted = 1, + .ignore_connect_delay = 1, + .ignore_read_conn_reset = 1, + .ignore_select_delay = 1, + .ignore_send_waiting = 1, + .ignore_write_conn_reset = 1, + + .callback_check_sockaddr = callback_check_sockaddr, + .callback_cleanup = callback_cleanup, + .callback_xfer_prepclient = NULL, + .callback_xfer_peercred = NULL, + }; + + debug("entering main()"); + + start(80); + + test_socket(&info); + test_bind(&info); + test_listen(&info); + test_getsockname(&info); + test_shutdown(&info); + test_close(&info); + test_dup(&info); + test_dup2(&info); + test_shutdown(&info); + test_read(&info); + test_write(&info); + test_sockopts(&info); + test_xfer(&info); + test_simple_client_server(&info, info.type); + test_abort_client_server(&info, 1); + test_abort_client_server(&info, 2); + test_nonblock(&info); + test_connect_nb(&info); + test_intr(&info); + test_connect_close(&info); + test_listen_close(&info); + test_listen_close_nb(&info); + + quit(); + + return -1; /* we should never get here */ +} diff --git a/minix/tests/test81.c b/minix/tests/test81.c new file mode 100644 index 000000000..1043bcc2f --- /dev/null +++ b/minix/tests/test81.c @@ -0,0 +1,134 @@ +/* + * test81: use the functions originally written for test56 to test UDP + */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "common-socket.h" + +#define PORT1 4321 +#define PORT2 4322 + +static void callback_check_sockaddr(const struct sockaddr *sockaddr, + socklen_t sockaddrlen, const char *callname, int addridx) { + char buf[256]; + int port; + const struct sockaddr_in *sockaddr_in = + (const struct sockaddr_in *) sockaddr; + + switch (addridx) { + case 1: port = PORT1; break; + case 2: port = PORT2; break; + default: + fprintf(stderr, "error: invalid addridx %d in " + "callback_check_sockaddr\n", addridx); + abort(); + } + + if (sockaddr_in->sin_family != AF_INET || + sockaddr_in->sin_port != htons(port)) { + snprintf(buf, sizeof(buf), "%s() didn't return the right addr", + callname); + test_fail(buf); + + memset(buf, 0, sizeof(buf)); + inet_ntop(sockaddr_in->sin_family, &sockaddr_in->sin_addr, + buf, sizeof(buf)); + fprintf(stderr, "exp: localhost:%d | got: %s:%d\n", port, buf, + ntohs(sockaddr_in->sin_port)); + } +} + +static void callback_cleanup(void) { + /* nothing to do */ +} + +int main(int argc, char *argv[]) +{ + int i; + struct sockaddr_in clientaddr = { + .sin_family = AF_INET, + .sin_port = htons(PORT1), + .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }, + }; + struct sockaddr_in clientaddr2 = { + .sin_family = AF_INET, + .sin_port = htons(PORT2), + .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }, + }; + struct sockaddr_in serveraddr = { + .sin_family = AF_INET, + .sin_port = htons(PORT1), + .sin_addr = { .s_addr = htonl(INADDR_ANY) }, + }; + struct sockaddr_in serveraddr2 = { + .sin_family = AF_INET, + .sin_port = htons(PORT2), + .sin_addr = { .s_addr = htonl(INADDR_ANY) }, + }; + const struct socket_test_info info = { + .clientaddr = (struct sockaddr *) &clientaddr, + .clientaddrlen = sizeof(clientaddr), + .clientaddr2 = (struct sockaddr *) &clientaddr2, + .clientaddr2len = sizeof(clientaddr2), + .clientaddrsym = (struct sockaddr *) &clientaddr, + .clientaddrsymlen = sizeof(clientaddr), + .domain = PF_INET, + .expected_rcvbuf = -1, + .expected_sndbuf = -1, + .serveraddr = (struct sockaddr *) &serveraddr, + .serveraddrlen = sizeof(serveraddr), + .serveraddr2 = (struct sockaddr *) &serveraddr2, + .serveraddr2len = sizeof(serveraddr2), + .type = SOCK_DGRAM, + .types = &info.type, + .typecount = 1, + + .bug_bind_in_use = 1, + .bug_bind_null = 1, + .bug_connect_after_close = 1, + .bug_shutdown = 1, /* UDP only problem */ + .bug_shutdown_not_conn = 1, + .bug_shutdown_read = 1, + .bug_sockopt_rcvbuf = 1, /* UDP only problem */ + .bug_sockopt_sndbuf = 1, /* UDP only problem */ + .ignore_accept_delay = 1, + .ignore_connect_unaccepted = 1, + .ignore_connect_delay = 1, + .ignore_read_conn_reset = 1, + .ignore_select_delay = 1, + .ignore_send_waiting = 1, + .ignore_write_conn_reset = 1, + + .callback_check_sockaddr = callback_check_sockaddr, + .callback_cleanup = callback_cleanup, + .callback_xfer_prepclient = NULL, + .callback_xfer_peercred = NULL, + }; + + debug("entering main()"); + + start(81); + + test_socket(&info); + test_bind(&info); + test_getsockname(&info); + test_shutdown(&info); + test_close(&info); + test_dup(&info); + test_dup2(&info); + test_shutdown(&info); + test_read(&info); + test_write(&info); + test_sockopts(&info); + test_simple_client_server(&info, info.type); + + quit(); + + return -1; /* we should never get here */ +}