sigpipe.c revision 303975
1262639Sdelphij/*- 2262639Sdelphij * Copyright (c) 2005 Robert N. M. Watson 3262639Sdelphij * All rights reserved. 4262639Sdelphij * 5262639Sdelphij * Redistribution and use in source and binary forms, with or without 6262639Sdelphij * modification, are permitted provided that the following conditions 7262639Sdelphij * are met: 8262639Sdelphij * 1. Redistributions of source code must retain the above copyright 9262639Sdelphij * notice, this list of conditions and the following disclaimer. 10262639Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 11262639Sdelphij * notice, this list of conditions and the following disclaimer in the 12262639Sdelphij * documentation and/or other materials provided with the distribution. 13262639Sdelphij * 14262639Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15262639Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16262639Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17262639Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18262639Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19262639Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20262639Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21262639Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22262639Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23262639Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24262639Sdelphij * SUCH DAMAGE. 25262639Sdelphij * 26262639Sdelphij * $FreeBSD: releng/11.0/tools/regression/sockets/sigpipe/sigpipe.c 281397 2015-04-11 03:24:49Z ngie $ 27262639Sdelphij */ 28262639Sdelphij 29262639Sdelphij#include <sys/types.h> 30262639Sdelphij#include <sys/socket.h> 31262639Sdelphij 32262639Sdelphij#include <netinet/in.h> 33262639Sdelphij 34262639Sdelphij#include <err.h> 35262639Sdelphij#include <errno.h> 36262639Sdelphij#include <signal.h> 37262639Sdelphij#include <stdio.h> 38262639Sdelphij#include <stdlib.h> 39262639Sdelphij#include <string.h> 40262639Sdelphij#include <unistd.h> 41262639Sdelphij 42262639Sdelphij/* 43262639Sdelphij * This regression test is intended to verify whether or not SIGPIPE is 44262639Sdelphij * properly generated in several simple test cases, as well as testing 45262639Sdelphij * whether SO_NOSIGPIPE disables SIGPIPE, if available on the system. 46262639Sdelphij * SIGPIPE is generated if a write or send is attempted on a socket that has 47262639Sdelphij * been shutdown for write. This test runs several test cases with UNIX 48262639Sdelphij * domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is 49262639Sdelphij * properly returned. 50262639Sdelphij * 51262639Sdelphij * For the purposes of testing TCP, an unused port number must be specified. 52262639Sdelphij */ 53262639Sdelphijstatic void 54262639Sdelphijusage(void) 55262639Sdelphij{ 56262639Sdelphij 57262639Sdelphij errx(-1, "usage: sigpipe tcpport"); 58262639Sdelphij} 59262639Sdelphij 60262639Sdelphij/* 61262639Sdelphij * Signal catcher. Set a global flag that can be tested by the caller. 62262639Sdelphij */ 63262639Sdelphijstatic int signaled; 64262639Sdelphijstatic int 65262639Sdelphijgot_signal(void) 66262639Sdelphij{ 67262639Sdelphij 68262639Sdelphij return (signaled); 69262639Sdelphij} 70262639Sdelphij 71262639Sdelphijstatic void 72262639Sdelphijsignal_handler(int signum __unused) 73262639Sdelphij{ 74262639Sdelphij 75262639Sdelphij signaled = 1; 76262639Sdelphij} 77262639Sdelphij 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