sigpipe.c revision 143409
1/*- 2 * Copyright (c) 2005 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/tools/regression/sockets/sigpipe/sigpipe.c 143409 2005-03-11 12:47:14Z rwatson $ 27 */ 28 29#include <sys/types.h> 30#include <sys/socket.h> 31 32#include <netinet/in.h> 33 34#include <err.h> 35#include <errno.h> 36#include <signal.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <unistd.h> 41 42/* 43 * This regression test is intended to verify whether or not SIGPIPE is 44 * properly generated in several simple test cases, as well as testing 45 * whether SO_NOSIGPIPE disables SIGPIPE, if available on the system. 46 * SIGPIPE is generated if a write or send is attempted on a socket that has 47 * been shutdown for write. This test runs several test cases with UNIX 48 * domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is 49 * properly returned. 50 * 51 * For the purposes of testing TCP, an unused port number must be specified. 52 */ 53static void 54usage(void) 55{ 56 57 errx(-1, "usage: sigpipe tcpport"); 58} 59 60/* 61 * Signal catcher. Set a global flag that can be tested by the caller. 62 */ 63static int signaled; 64static int 65got_signal(void) 66{ 67 68 return (signaled); 69} 70 71static void 72signal_handler(int signum) 73{ 74 75 signaled = 1; 76} 77 78static void 79signal_setup(const char *testname) 80{ 81 82 signaled = 0; 83 if (signal(SIGPIPE, signal_handler) == SIG_ERR) 84 err(-1, "%s: signal(SIGPIPE)", testname); 85} 86 87static void 88test_send(const char *testname, int sock) 89{ 90 ssize_t len; 91 char ch; 92 93 ch = 0; 94 len = send(sock, &ch, sizeof(ch), 0); 95 if (len < 0) { 96 if (errno == EPIPE) 97 return; 98 err(-1, "%s: send", testname); 99 } 100 errx(-1, "%s: send: returned %d", testname, len); 101} 102 103static void 104test_write(const char *testname, int sock) 105{ 106 ssize_t len; 107 char ch; 108 109 ch = 0; 110 len = write(sock, &ch, sizeof(ch)); 111 if (len < 0) { 112 if (errno == EPIPE) 113 return; 114 err(-1, "%s: write", testname); 115 } 116 errx(-1, "%s: write: returned %d", testname, len); 117} 118 119static void 120test_send_wantsignal(const char *testname, int sock1, int sock2) 121{ 122 123 shutdown(sock2, SHUT_WR); 124 signal_setup(testname); 125 test_send(testname, sock2); 126 if (!got_signal()) 127 errx(-1, "%s: send: didn't receive SIGPIPE", testname); 128 close(sock1); 129 close(sock2); 130} 131 132#ifdef SO_NOSIGPIPE 133static void 134test_send_dontsignal(const char *testname, int sock1, int sock2) 135{ 136 int i; 137 138 i = 1; 139 if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) 140 err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); 141 shutdown(sock2, SHUT_WR); 142 signal_setup(testname); 143 test_send(testname, sock2); 144 if (got_signal()) 145 errx(-1, "%s: send: got SIGPIPE", testname); 146 close(sock1); 147 close(sock2); 148} 149#endif 150 151static void 152test_write_wantsignal(const char *testname, int sock1, int sock2) 153{ 154 155 shutdown(sock2, SHUT_WR); 156 signal_setup(testname); 157 test_write(testname, sock2); 158 if (!got_signal()) 159 errx(-1, "%s: write: didn't receive SIGPIPE", testname); 160 close(sock1); 161 close(sock2); 162} 163 164#ifdef SO_NOSIGPIPE 165static void 166test_write_dontsignal(const char *testname, int sock1, int sock2) 167{ 168 int i; 169 170 i = 1; 171 if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) 172 err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); 173 shutdown(sock2, SHUT_WR); 174 signal_setup(testname); 175 test_write(testname, sock2); 176 if (got_signal()) 177 errx(-1, "%s: write: got SIGPIPE", testname); 178 close(sock1); 179 close(sock2); 180} 181#endif 182 183static int listen_sock; 184static void 185tcp_setup(u_short port) 186{ 187 struct sockaddr_in sin; 188 189 listen_sock = socket(PF_INET, SOCK_STREAM, 0); 190 if (listen_sock < 0) 191 err(-1, "tcp_setup: listen"); 192 193 bzero(&sin, sizeof(sin)); 194 sin.sin_len = sizeof(sin); 195 sin.sin_family = AF_INET; 196 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 197 sin.sin_port = htons(port); 198 199 if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) 200 err(-1, "tcp_setup: bind"); 201 202 if (listen(listen_sock, -1) < 0) 203 err(-1, "tcp_setup: listen"); 204} 205 206static void 207tcp_teardown(void) 208{ 209 210 close(listen_sock); 211} 212 213static void 214tcp_pair(u_short port, int sock[2]) 215{ 216 int accept_sock, connect_sock; 217 struct sockaddr_in sin; 218 socklen_t len; 219 220 connect_sock = socket(PF_INET, SOCK_STREAM, 0); 221 if (connect_sock < 0) 222 err(-1, "tcp_pair: socket"); 223 224 bzero(&sin, sizeof(sin)); 225 sin.sin_len = sizeof(sin); 226 sin.sin_family = AF_INET; 227 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 228 sin.sin_port = htons(port); 229 230 if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) 231 err(-1, "tcp_pair: connect"); 232 233 sleep(1); /* Time for TCP to settle. */ 234 235 len = sizeof(sin); 236 accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len); 237 if (accept_sock < 0) 238 err(-1, "tcp_pair: accept"); 239 240 sleep(1); /* Time for TCP to settle. */ 241 242 sock[0] = accept_sock; 243 sock[1] = connect_sock; 244} 245 246int 247main(int argc, char *argv[]) 248{ 249 char *dummy; 250 int sock[2]; 251 long port; 252 253 if (argc != 2) 254 usage(); 255 256 port = strtol(argv[1], &dummy, 10); 257 if (port < 0 || port > 65535 || *dummy != '\0') 258 usage(); 259 260#ifndef SO_NOSIGPIPE 261 warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests"); 262#endif 263 264 /* 265 * UNIX domain socketpair(). 266 */ 267 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 268 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 269 test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0], 270 sock[1]); 271 272#ifdef SO_NOSIGPIPE 273 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 274 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 275 test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0], 276 sock[1]); 277#endif 278 279 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 280 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 281 test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0], 282 sock[1]); 283 284#ifdef SO_NOSIGPIPE 285 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 286 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 287 test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0], 288 sock[1]); 289#endif 290 291 /* 292 * TCP. 293 */ 294 tcp_setup(port); 295 tcp_pair(port, sock); 296 test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0], 297 sock[1]); 298 299#ifdef SO_NOSIGPIPE 300 tcp_pair(port, sock); 301 test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0], 302 sock[1]); 303#endif 304 305 tcp_pair(port, sock); 306 test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0], 307 sock[1]); 308 309#ifdef SO_NOSIGPIPE 310 tcp_pair(port, sock); 311 test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0], 312 sock[1]); 313#endif 314 tcp_teardown(); 315 316 fprintf(stderr, "PASS\n"); 317 return (0); 318} 319