diff options
author | rwatson <rwatson@FreeBSD.org> | 2005-03-11 12:47:14 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2005-03-11 12:47:14 +0000 |
commit | 832b048264b11fed77402209394434bfab16f7e7 (patch) | |
tree | 835697f76bf91bad65e3b9050071c4304292bd0f /tools | |
parent | 7014c90c7dca3573722b559c70e9431a98a7cd0e (diff) | |
download | FreeBSD-src-832b048264b11fed77402209394434bfab16f7e7.zip FreeBSD-src-832b048264b11fed77402209394434bfab16f7e7.tar.gz |
Add sigpipe, a simple UNIX domain socket and TCP regression test that is
intended to verify that SIGPIPE is delivered to a process writing or
sending on a socket that has been shut down for write. If available,
SO_NOSIGPIPE is also tested.
This regression test is currently passed by RELENG_4, but not by HEAD or
RELENG_5, due to a bug in the write() code for sockets. SO_NOSIGPIPE is not
present in RELENG_4, however, so is not tested there.
Reported by: Mikko Tyolajarvi <mbsd at pacbell dot net>
PR: 78478
Diffstat (limited to 'tools')
-rw-r--r-- | tools/regression/sockets/sigpipe/Makefile | 9 | ||||
-rw-r--r-- | tools/regression/sockets/sigpipe/sigpipe.c | 318 |
2 files changed, 327 insertions, 0 deletions
diff --git a/tools/regression/sockets/sigpipe/Makefile b/tools/regression/sockets/sigpipe/Makefile new file mode 100644 index 0000000..aa301b6 --- /dev/null +++ b/tools/regression/sockets/sigpipe/Makefile @@ -0,0 +1,9 @@ +# +# $FreeBSD$ +# + +PROG= sigpipe +NO_MAN= +WARNS?= 2 + +.include <bsd.prog.mk> diff --git a/tools/regression/sockets/sigpipe/sigpipe.c b/tools/regression/sockets/sigpipe/sigpipe.c new file mode 100644 index 0000000..5b46d4f --- /dev/null +++ b/tools/regression/sockets/sigpipe/sigpipe.c @@ -0,0 +1,318 @@ +/*- + * Copyright (c) 2005 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * This regression test is intended to verify whether or not SIGPIPE is + * properly generated in several simple test cases, as well as testing + * whether SO_NOSIGPIPE disables SIGPIPE, if available on the system. + * SIGPIPE is generated if a write or send is attempted on a socket that has + * been shutdown for write. This test runs several test cases with UNIX + * domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is + * properly returned. + * + * For the purposes of testing TCP, an unused port number must be specified. + */ +static void +usage(void) +{ + + errx(-1, "usage: sigpipe tcpport"); +} + +/* + * Signal catcher. Set a global flag that can be tested by the caller. + */ +static int signaled; +static int +got_signal(void) +{ + + return (signaled); +} + +static void +signal_handler(int signum) +{ + + signaled = 1; +} + +static void +signal_setup(const char *testname) +{ + + signaled = 0; + if (signal(SIGPIPE, signal_handler) == SIG_ERR) + err(-1, "%s: signal(SIGPIPE)", testname); +} + +static void +test_send(const char *testname, int sock) +{ + ssize_t len; + char ch; + + ch = 0; + len = send(sock, &ch, sizeof(ch), 0); + if (len < 0) { + if (errno == EPIPE) + return; + err(-1, "%s: send", testname); + } + errx(-1, "%s: send: returned %d", testname, len); +} + +static void +test_write(const char *testname, int sock) +{ + ssize_t len; + char ch; + + ch = 0; + len = write(sock, &ch, sizeof(ch)); + if (len < 0) { + if (errno == EPIPE) + return; + err(-1, "%s: write", testname); + } + errx(-1, "%s: write: returned %d", testname, len); +} + +static void +test_send_wantsignal(const char *testname, int sock1, int sock2) +{ + + shutdown(sock2, SHUT_WR); + signal_setup(testname); + test_send(testname, sock2); + if (!got_signal()) + errx(-1, "%s: send: didn't receive SIGPIPE", testname); + close(sock1); + close(sock2); +} + +#ifdef SO_NOSIGPIPE +static void +test_send_dontsignal(const char *testname, int sock1, int sock2) +{ + int i; + + i = 1; + if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) + err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); + shutdown(sock2, SHUT_WR); + signal_setup(testname); + test_send(testname, sock2); + if (got_signal()) + errx(-1, "%s: send: got SIGPIPE", testname); + close(sock1); + close(sock2); +} +#endif + +static void +test_write_wantsignal(const char *testname, int sock1, int sock2) +{ + + shutdown(sock2, SHUT_WR); + signal_setup(testname); + test_write(testname, sock2); + if (!got_signal()) + errx(-1, "%s: write: didn't receive SIGPIPE", testname); + close(sock1); + close(sock2); +} + +#ifdef SO_NOSIGPIPE +static void +test_write_dontsignal(const char *testname, int sock1, int sock2) +{ + int i; + + i = 1; + if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) + err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); + shutdown(sock2, SHUT_WR); + signal_setup(testname); + test_write(testname, sock2); + if (got_signal()) + errx(-1, "%s: write: got SIGPIPE", testname); + close(sock1); + close(sock2); +} +#endif + +static int listen_sock; +static void +tcp_setup(u_short port) +{ + struct sockaddr_in sin; + + listen_sock = socket(PF_INET, SOCK_STREAM, 0); + if (listen_sock < 0) + err(-1, "tcp_setup: listen"); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = htons(port); + + if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) + err(-1, "tcp_setup: bind"); + + if (listen(listen_sock, -1) < 0) + err(-1, "tcp_setup: listen"); +} + +static void +tcp_teardown(void) +{ + + close(listen_sock); +} + +static void +tcp_pair(u_short port, int sock[2]) +{ + int accept_sock, connect_sock; + struct sockaddr_in sin; + socklen_t len; + + connect_sock = socket(PF_INET, SOCK_STREAM, 0); + if (connect_sock < 0) + err(-1, "tcp_pair: socket"); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = htons(port); + + if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) + err(-1, "tcp_pair: connect"); + + sleep(1); /* Time for TCP to settle. */ + + len = sizeof(sin); + accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len); + if (accept_sock < 0) + err(-1, "tcp_pair: accept"); + + sleep(1); /* Time for TCP to settle. */ + + sock[0] = accept_sock; + sock[1] = connect_sock; +} + +int +main(int argc, char *argv[]) +{ + char *dummy; + int sock[2]; + long port; + + if (argc != 2) + usage(); + + port = strtol(argv[1], &dummy, 10); + if (port < 0 || port > 65535 || *dummy != '\0') + usage(); + +#ifndef SO_NOSIGPIPE + warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests"); +#endif + + /* + * UNIX domain socketpair(). + */ + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) + err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); + test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0], + sock[1]); + +#ifdef SO_NOSIGPIPE + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) + err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); + test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0], + sock[1]); +#endif + + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) + err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); + test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0], + sock[1]); + +#ifdef SO_NOSIGPIPE + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) + err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); + test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0], + sock[1]); +#endif + + /* + * TCP. + */ + tcp_setup(port); + tcp_pair(port, sock); + test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0], + sock[1]); + +#ifdef SO_NOSIGPIPE + tcp_pair(port, sock); + test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0], + sock[1]); +#endif + + tcp_pair(port, sock); + test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0], + sock[1]); + +#ifdef SO_NOSIGPIPE + tcp_pair(port, sock); + test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0], + sock[1]); +#endif + tcp_teardown(); + + fprintf(stderr, "PASS\n"); + return (0); +} |