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