Path: blob/main/tools/regression/sockets/sigpipe/sigpipe.c
48254 views
/*-1* Copyright (c) 2005 Robert N. M. Watson2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/types.h>27#include <sys/socket.h>2829#include <netinet/in.h>3031#include <err.h>32#include <errno.h>33#include <signal.h>34#include <stdio.h>35#include <stdlib.h>36#include <string.h>37#include <unistd.h>3839/*40* This regression test is intended to verify whether or not SIGPIPE is41* properly generated in several simple test cases, as well as testing42* whether SO_NOSIGPIPE disables SIGPIPE, if available on the system.43* SIGPIPE is generated if a write or send is attempted on a socket that has44* been shutdown for write. This test runs several test cases with UNIX45* domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is46* properly returned.47*48* For the purposes of testing TCP, an unused port number must be specified.49*/50static void51usage(void)52{5354errx(-1, "usage: sigpipe tcpport");55}5657/*58* Signal catcher. Set a global flag that can be tested by the caller.59*/60static int signaled;61static int62got_signal(void)63{6465return (signaled);66}6768static void69signal_handler(int signum __unused)70{7172signaled = 1;73}7475static void76signal_setup(const char *testname)77{7879signaled = 0;80if (signal(SIGPIPE, signal_handler) == SIG_ERR)81err(-1, "%s: signal(SIGPIPE)", testname);82}8384static void85test_send(const char *testname, int sock)86{87ssize_t len;88char ch;8990ch = 0;91len = send(sock, &ch, sizeof(ch), 0);92if (len < 0) {93if (errno == EPIPE)94return;95err(-1, "%s: send", testname);96}97errx(-1, "%s: send: returned %zd", testname, len);98}99100static void101test_write(const char *testname, int sock)102{103ssize_t len;104char ch;105106ch = 0;107len = write(sock, &ch, sizeof(ch));108if (len < 0) {109if (errno == EPIPE)110return;111err(-1, "%s: write", testname);112}113errx(-1, "%s: write: returned %zd", testname, len);114}115116static void117test_send_wantsignal(const char *testname, int sock1, int sock2)118{119120if (shutdown(sock2, SHUT_WR) < 0)121err(-1, "%s: shutdown", testname);122signal_setup(testname);123test_send(testname, sock2);124if (!got_signal())125errx(-1, "%s: send: didn't receive SIGPIPE", testname);126close(sock1);127close(sock2);128}129130#ifdef SO_NOSIGPIPE131static void132test_send_dontsignal(const char *testname, int sock1, int sock2)133{134int i;135136i = 1;137if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0)138err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname);139if (shutdown(sock2, SHUT_WR) < 0)140err(-1, "%s: shutdown", testname);141signal_setup(testname);142test_send(testname, sock2);143if (got_signal())144errx(-1, "%s: send: got SIGPIPE", testname);145close(sock1);146close(sock2);147}148#endif149150static void151test_write_wantsignal(const char *testname, int sock1, int sock2)152{153154if (shutdown(sock2, SHUT_WR) < 0)155err(-1, "%s: shutdown", testname);156signal_setup(testname);157test_write(testname, sock2);158if (!got_signal())159errx(-1, "%s: write: didn't receive SIGPIPE", testname);160close(sock1);161close(sock2);162}163164#ifdef SO_NOSIGPIPE165static void166test_write_dontsignal(const char *testname, int sock1, int sock2)167{168int i;169170i = 1;171if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0)172err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname);173if (shutdown(sock2, SHUT_WR) < 0)174err(-1, "%s: shutdown", testname);175signal_setup(testname);176test_write(testname, sock2);177if (got_signal())178errx(-1, "%s: write: got SIGPIPE", testname);179close(sock1);180close(sock2);181}182#endif183184static int listen_sock;185static void186tcp_setup(u_short port)187{188struct sockaddr_in sin;189190listen_sock = socket(PF_INET, SOCK_STREAM, 0);191if (listen_sock < 0)192err(-1, "tcp_setup: listen");193194bzero(&sin, sizeof(sin));195sin.sin_len = sizeof(sin);196sin.sin_family = AF_INET;197sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);198sin.sin_port = htons(port);199200if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)201err(-1, "tcp_setup: bind");202203if (listen(listen_sock, -1) < 0)204err(-1, "tcp_setup: listen");205}206207static void208tcp_teardown(void)209{210211close(listen_sock);212}213214static void215tcp_pair(u_short port, int sock[2])216{217int accept_sock, connect_sock;218struct sockaddr_in sin;219socklen_t len;220221connect_sock = socket(PF_INET, SOCK_STREAM, 0);222if (connect_sock < 0)223err(-1, "tcp_pair: socket");224225bzero(&sin, sizeof(sin));226sin.sin_len = sizeof(sin);227sin.sin_family = AF_INET;228sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);229sin.sin_port = htons(port);230231if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)232err(-1, "tcp_pair: connect");233234sleep(1); /* Time for TCP to settle. */235236len = sizeof(sin);237accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len);238if (accept_sock < 0)239err(-1, "tcp_pair: accept");240241sleep(1); /* Time for TCP to settle. */242243sock[0] = accept_sock;244sock[1] = connect_sock;245}246247int248main(int argc, char *argv[])249{250char *dummy;251int sock[2];252long port;253254if (argc != 2)255usage();256257port = strtol(argv[1], &dummy, 10);258if (port < 0 || port > 65535 || *dummy != '\0')259usage();260261#ifndef SO_NOSIGPIPE262warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests");263#endif264265/*266* UNIX domain socketpair().267*/268if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)269err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");270test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0],271sock[1]);272273#ifdef SO_NOSIGPIPE274if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)275err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");276test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0],277sock[1]);278#endif279280if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)281err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");282test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0],283sock[1]);284285#ifdef SO_NOSIGPIPE286if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)287err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");288test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0],289sock[1]);290#endif291292/*293* TCP.294*/295tcp_setup(port);296tcp_pair(port, sock);297test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0],298sock[1]);299300#ifdef SO_NOSIGPIPE301tcp_pair(port, sock);302test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0],303sock[1]);304#endif305306tcp_pair(port, sock);307test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0],308sock[1]);309310#ifdef SO_NOSIGPIPE311tcp_pair(port, sock);312test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0],313sock[1]);314#endif315tcp_teardown();316317fprintf(stderr, "PASS\n");318return (0);319}320321322