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