sigpipe.c revision 281974
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: stable/10/tools/regression/sockets/sigpipe/sigpipe.c 281974 2015-04-25 05:31:52Z ngie $ 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 __unused) 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 %zd", 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 %zd", testname, len); 117} 118 119static void 120test_send_wantsignal(const char *testname, int sock1, int sock2) 121{ 122 123 if (shutdown(sock2, SHUT_WR) < 0) 124 err(-1, "%s: shutdown", testname); 125 signal_setup(testname); 126 test_send(testname, sock2); 127 if (!got_signal()) 128 errx(-1, "%s: send: didn't receive SIGPIPE", testname); 129 close(sock1); 130 close(sock2); 131} 132 133#ifdef SO_NOSIGPIPE 134static void 135test_send_dontsignal(const char *testname, int sock1, int sock2) 136{ 137 int i; 138 139 i = 1; 140 if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) 141 err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); 142 if (shutdown(sock2, SHUT_WR) < 0) 143 err(-1, "%s: shutdown", testname); 144 signal_setup(testname); 145 test_send(testname, sock2); 146 if (got_signal()) 147 errx(-1, "%s: send: got SIGPIPE", testname); 148 close(sock1); 149 close(sock2); 150} 151#endif 152 153static void 154test_write_wantsignal(const char *testname, int sock1, int sock2) 155{ 156 157 if (shutdown(sock2, SHUT_WR) < 0) 158 err(-1, "%s: shutdown", testname); 159 signal_setup(testname); 160 test_write(testname, sock2); 161 if (!got_signal()) 162 errx(-1, "%s: write: didn't receive SIGPIPE", testname); 163 close(sock1); 164 close(sock2); 165} 166 167#ifdef SO_NOSIGPIPE 168static void 169test_write_dontsignal(const char *testname, int sock1, int sock2) 170{ 171 int i; 172 173 i = 1; 174 if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) 175 err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); 176 if (shutdown(sock2, SHUT_WR) < 0) 177 err(-1, "%s: shutdown", testname); 178 signal_setup(testname); 179 test_write(testname, sock2); 180 if (got_signal()) 181 errx(-1, "%s: write: got SIGPIPE", testname); 182 close(sock1); 183 close(sock2); 184} 185#endif 186 187static int listen_sock; 188static void 189tcp_setup(u_short port) 190{ 191 struct sockaddr_in sin; 192 193 listen_sock = socket(PF_INET, SOCK_STREAM, 0); 194 if (listen_sock < 0) 195 err(-1, "tcp_setup: listen"); 196 197 bzero(&sin, sizeof(sin)); 198 sin.sin_len = sizeof(sin); 199 sin.sin_family = AF_INET; 200 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 201 sin.sin_port = htons(port); 202 203 if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) 204 err(-1, "tcp_setup: bind"); 205 206 if (listen(listen_sock, -1) < 0) 207 err(-1, "tcp_setup: listen"); 208} 209 210static void 211tcp_teardown(void) 212{ 213 214 close(listen_sock); 215} 216 217static void 218tcp_pair(u_short port, int sock[2]) 219{ 220 int accept_sock, connect_sock; 221 struct sockaddr_in sin; 222 socklen_t len; 223 224 connect_sock = socket(PF_INET, SOCK_STREAM, 0); 225 if (connect_sock < 0) 226 err(-1, "tcp_pair: socket"); 227 228 bzero(&sin, sizeof(sin)); 229 sin.sin_len = sizeof(sin); 230 sin.sin_family = AF_INET; 231 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 232 sin.sin_port = htons(port); 233 234 if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) 235 err(-1, "tcp_pair: connect"); 236 237 sleep(1); /* Time for TCP to settle. */ 238 239 len = sizeof(sin); 240 accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len); 241 if (accept_sock < 0) 242 err(-1, "tcp_pair: accept"); 243 244 sleep(1); /* Time for TCP to settle. */ 245 246 sock[0] = accept_sock; 247 sock[1] = connect_sock; 248} 249 250int 251main(int argc, char *argv[]) 252{ 253 char *dummy; 254 int sock[2]; 255 long port; 256 257 if (argc != 2) 258 usage(); 259 260 port = strtol(argv[1], &dummy, 10); 261 if (port < 0 || port > 65535 || *dummy != '\0') 262 usage(); 263 264#ifndef SO_NOSIGPIPE 265 warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests"); 266#endif 267 268 /* 269 * UNIX domain socketpair(). 270 */ 271 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 272 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 273 test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0], 274 sock[1]); 275 276#ifdef SO_NOSIGPIPE 277 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 278 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 279 test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0], 280 sock[1]); 281#endif 282 283 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 284 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 285 test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0], 286 sock[1]); 287 288#ifdef SO_NOSIGPIPE 289 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 290 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 291 test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0], 292 sock[1]); 293#endif 294 295 /* 296 * TCP. 297 */ 298 tcp_setup(port); 299 tcp_pair(port, sock); 300 test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0], 301 sock[1]); 302 303#ifdef SO_NOSIGPIPE 304 tcp_pair(port, sock); 305 test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0], 306 sock[1]); 307#endif 308 309 tcp_pair(port, sock); 310 test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0], 311 sock[1]); 312 313#ifdef SO_NOSIGPIPE 314 tcp_pair(port, sock); 315 test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0], 316 sock[1]); 317#endif 318 tcp_teardown(); 319 320 fprintf(stderr, "PASS\n"); 321 return (0); 322} 323