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 */ +}