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$ 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 */ 53 static void 54 usage(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 */ 63 static int signaled; 64 static int 65 got_signal(void) 66 { 67 68 return (signaled); 69 } 70 71 static void 72 signal_handler(int signum) 73 { 74 75 signaled = 1; 76 } 77 78 static void 79 signal_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 87 static void 88 test_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 103 static void 104 test_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 119 static void 120 test_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 134 static void 135 test_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 153 static void 154 test_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 168 static void 169 test_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 187 static int listen_sock; 188 static void 189 tcp_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 210 static void 211 tcp_teardown(void) 212 { 213 214 close(listen_sock); 215 } 216 217 static void 218 tcp_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 250 int 251 main(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