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 27 #include <sys/types.h> 28 #include <sys/socket.h> 29 30 #include <netinet/in.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 /* 41 * This regression test is intended to verify whether or not SIGPIPE is 42 * properly generated in several simple test cases, as well as testing 43 * whether SO_NOSIGPIPE disables SIGPIPE, if available on the system. 44 * SIGPIPE is generated if a write or send is attempted on a socket that has 45 * been shutdown for write. This test runs several test cases with UNIX 46 * domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is 47 * properly returned. 48 * 49 * For the purposes of testing TCP, an unused port number must be specified. 50 */ 51 static void 52 usage(void) 53 { 54 55 errx(-1, "usage: sigpipe tcpport"); 56 } 57 58 /* 59 * Signal catcher. Set a global flag that can be tested by the caller. 60 */ 61 static int signaled; 62 static int 63 got_signal(void) 64 { 65 66 return (signaled); 67 } 68 69 static void 70 signal_handler(int signum __unused) 71 { 72 73 signaled = 1; 74 } 75 76 static void 77 signal_setup(const char *testname) 78 { 79 80 signaled = 0; 81 if (signal(SIGPIPE, signal_handler) == SIG_ERR) 82 err(-1, "%s: signal(SIGPIPE)", testname); 83 } 84 85 static void 86 test_send(const char *testname, int sock) 87 { 88 ssize_t len; 89 char ch; 90 91 ch = 0; 92 len = send(sock, &ch, sizeof(ch), 0); 93 if (len < 0) { 94 if (errno == EPIPE) 95 return; 96 err(-1, "%s: send", testname); 97 } 98 errx(-1, "%s: send: returned %zd", testname, len); 99 } 100 101 static void 102 test_write(const char *testname, int sock) 103 { 104 ssize_t len; 105 char ch; 106 107 ch = 0; 108 len = write(sock, &ch, sizeof(ch)); 109 if (len < 0) { 110 if (errno == EPIPE) 111 return; 112 err(-1, "%s: write", testname); 113 } 114 errx(-1, "%s: write: returned %zd", testname, len); 115 } 116 117 static void 118 test_send_wantsignal(const char *testname, int sock1, int sock2) 119 { 120 121 if (shutdown(sock2, SHUT_WR) < 0) 122 err(-1, "%s: shutdown", testname); 123 signal_setup(testname); 124 test_send(testname, sock2); 125 if (!got_signal()) 126 errx(-1, "%s: send: didn't receive SIGPIPE", testname); 127 close(sock1); 128 close(sock2); 129 } 130 131 #ifdef SO_NOSIGPIPE 132 static void 133 test_send_dontsignal(const char *testname, int sock1, int sock2) 134 { 135 int i; 136 137 i = 1; 138 if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) 139 err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); 140 if (shutdown(sock2, SHUT_WR) < 0) 141 err(-1, "%s: shutdown", testname); 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 151 static void 152 test_write_wantsignal(const char *testname, int sock1, int sock2) 153 { 154 155 if (shutdown(sock2, SHUT_WR) < 0) 156 err(-1, "%s: shutdown", testname); 157 signal_setup(testname); 158 test_write(testname, sock2); 159 if (!got_signal()) 160 errx(-1, "%s: write: didn't receive SIGPIPE", testname); 161 close(sock1); 162 close(sock2); 163 } 164 165 #ifdef SO_NOSIGPIPE 166 static void 167 test_write_dontsignal(const char *testname, int sock1, int sock2) 168 { 169 int i; 170 171 i = 1; 172 if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) 173 err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname); 174 if (shutdown(sock2, SHUT_WR) < 0) 175 err(-1, "%s: shutdown", testname); 176 signal_setup(testname); 177 test_write(testname, sock2); 178 if (got_signal()) 179 errx(-1, "%s: write: got SIGPIPE", testname); 180 close(sock1); 181 close(sock2); 182 } 183 #endif 184 185 static int listen_sock; 186 static void 187 tcp_setup(u_short port) 188 { 189 struct sockaddr_in sin; 190 191 listen_sock = socket(PF_INET, SOCK_STREAM, 0); 192 if (listen_sock < 0) 193 err(-1, "tcp_setup: listen"); 194 195 bzero(&sin, sizeof(sin)); 196 sin.sin_len = sizeof(sin); 197 sin.sin_family = AF_INET; 198 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 199 sin.sin_port = htons(port); 200 201 if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) 202 err(-1, "tcp_setup: bind"); 203 204 if (listen(listen_sock, -1) < 0) 205 err(-1, "tcp_setup: listen"); 206 } 207 208 static void 209 tcp_teardown(void) 210 { 211 212 close(listen_sock); 213 } 214 215 static void 216 tcp_pair(u_short port, int sock[2]) 217 { 218 int accept_sock, connect_sock; 219 struct sockaddr_in sin; 220 socklen_t len; 221 222 connect_sock = socket(PF_INET, SOCK_STREAM, 0); 223 if (connect_sock < 0) 224 err(-1, "tcp_pair: socket"); 225 226 bzero(&sin, sizeof(sin)); 227 sin.sin_len = sizeof(sin); 228 sin.sin_family = AF_INET; 229 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 230 sin.sin_port = htons(port); 231 232 if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) 233 err(-1, "tcp_pair: connect"); 234 235 sleep(1); /* Time for TCP to settle. */ 236 237 len = sizeof(sin); 238 accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len); 239 if (accept_sock < 0) 240 err(-1, "tcp_pair: accept"); 241 242 sleep(1); /* Time for TCP to settle. */ 243 244 sock[0] = accept_sock; 245 sock[1] = connect_sock; 246 } 247 248 int 249 main(int argc, char *argv[]) 250 { 251 char *dummy; 252 int sock[2]; 253 long port; 254 255 if (argc != 2) 256 usage(); 257 258 port = strtol(argv[1], &dummy, 10); 259 if (port < 0 || port > 65535 || *dummy != '\0') 260 usage(); 261 262 #ifndef SO_NOSIGPIPE 263 warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests"); 264 #endif 265 266 /* 267 * UNIX domain socketpair(). 268 */ 269 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 270 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 271 test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0], 272 sock[1]); 273 274 #ifdef SO_NOSIGPIPE 275 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 276 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 277 test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0], 278 sock[1]); 279 #endif 280 281 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 282 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 283 test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0], 284 sock[1]); 285 286 #ifdef SO_NOSIGPIPE 287 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0) 288 err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)"); 289 test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0], 290 sock[1]); 291 #endif 292 293 /* 294 * TCP. 295 */ 296 tcp_setup(port); 297 tcp_pair(port, sock); 298 test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0], 299 sock[1]); 300 301 #ifdef SO_NOSIGPIPE 302 tcp_pair(port, sock); 303 test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0], 304 sock[1]); 305 #endif 306 307 tcp_pair(port, sock); 308 test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0], 309 sock[1]); 310 311 #ifdef SO_NOSIGPIPE 312 tcp_pair(port, sock); 313 test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0], 314 sock[1]); 315 #endif 316 tcp_teardown(); 317 318 fprintf(stderr, "PASS\n"); 319 return (0); 320 } 321