1143409Srwatson/*-
2143409Srwatson * Copyright (c) 2005 Robert N. M. Watson
3143409Srwatson * All rights reserved.
4143409Srwatson *
5143409Srwatson * Redistribution and use in source and binary forms, with or without
6143409Srwatson * modification, are permitted provided that the following conditions
7143409Srwatson * are met:
8143409Srwatson * 1. Redistributions of source code must retain the above copyright
9143409Srwatson *    notice, this list of conditions and the following disclaimer.
10143409Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11143409Srwatson *    notice, this list of conditions and the following disclaimer in the
12143409Srwatson *    documentation and/or other materials provided with the distribution.
13143409Srwatson *
14143409Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15143409Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16143409Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17143409Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18143409Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19143409Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20143409Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21143409Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22143409Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23143409Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24143409Srwatson * SUCH DAMAGE.
25143409Srwatson *
26143409Srwatson * $FreeBSD$
27143409Srwatson */
28143409Srwatson
29143409Srwatson#include <sys/types.h>
30143409Srwatson#include <sys/socket.h>
31143409Srwatson
32143409Srwatson#include <netinet/in.h>
33143409Srwatson
34143409Srwatson#include <err.h>
35143409Srwatson#include <errno.h>
36143409Srwatson#include <signal.h>
37143409Srwatson#include <stdio.h>
38143409Srwatson#include <stdlib.h>
39143409Srwatson#include <string.h>
40143409Srwatson#include <unistd.h>
41143409Srwatson
42143409Srwatson/*
43143409Srwatson * This regression test is intended to verify whether or not SIGPIPE is
44143409Srwatson * properly generated in several simple test cases, as well as testing
45143409Srwatson * whether SO_NOSIGPIPE disables SIGPIPE, if available on the system.
46143409Srwatson * SIGPIPE is generated if a write or send is attempted on a socket that has
47143409Srwatson * been shutdown for write.  This test runs several test cases with UNIX
48143409Srwatson * domain sockets and TCP sockets to confirm that either EPIPE or SIGPIPE is
49143409Srwatson * properly returned.
50143409Srwatson *
51143409Srwatson * For the purposes of testing TCP, an unused port number must be specified.
52143409Srwatson */
53143409Srwatsonstatic void
54143409Srwatsonusage(void)
55143409Srwatson{
56143409Srwatson
57143409Srwatson	errx(-1, "usage: sigpipe tcpport");
58143409Srwatson}
59143409Srwatson
60143409Srwatson/*
61143409Srwatson * Signal catcher.  Set a global flag that can be tested by the caller.
62143409Srwatson */
63143409Srwatsonstatic int signaled;
64143409Srwatsonstatic int
65143409Srwatsongot_signal(void)
66143409Srwatson{
67143409Srwatson
68143409Srwatson	return (signaled);
69143409Srwatson}
70143409Srwatson
71143409Srwatsonstatic void
72143409Srwatsonsignal_handler(int signum)
73143409Srwatson{
74143409Srwatson
75143409Srwatson	signaled = 1;
76143409Srwatson}
77143409Srwatson
78143409Srwatsonstatic void
79143409Srwatsonsignal_setup(const char *testname)
80143409Srwatson{
81143409Srwatson
82143409Srwatson	signaled = 0;
83143409Srwatson	if (signal(SIGPIPE, signal_handler) == SIG_ERR)
84143409Srwatson		err(-1, "%s: signal(SIGPIPE)", testname);
85143409Srwatson}
86143409Srwatson
87143409Srwatsonstatic void
88143409Srwatsontest_send(const char *testname, int sock)
89143409Srwatson{
90143409Srwatson	ssize_t len;
91143409Srwatson	char ch;
92143409Srwatson
93143409Srwatson	ch = 0;
94143409Srwatson	len = send(sock, &ch, sizeof(ch), 0);
95143409Srwatson	if (len < 0) {
96143409Srwatson		if (errno == EPIPE)
97143409Srwatson			return;
98143409Srwatson		err(-1, "%s: send", testname);
99143409Srwatson	}
100143409Srwatson	errx(-1, "%s: send: returned %d", testname, len);
101143409Srwatson}
102143409Srwatson
103143409Srwatsonstatic void
104143409Srwatsontest_write(const char *testname, int sock)
105143409Srwatson{
106143409Srwatson	ssize_t len;
107143409Srwatson	char ch;
108143409Srwatson
109143409Srwatson	ch = 0;
110143409Srwatson	len = write(sock, &ch, sizeof(ch));
111143409Srwatson	if (len < 0) {
112143409Srwatson		if (errno == EPIPE)
113143409Srwatson			return;
114143409Srwatson		err(-1, "%s: write", testname);
115143409Srwatson	}
116143409Srwatson	errx(-1, "%s: write: returned %d", testname, len);
117143409Srwatson}
118143409Srwatson
119143409Srwatsonstatic void
120143409Srwatsontest_send_wantsignal(const char *testname, int sock1, int sock2)
121143409Srwatson{
122143409Srwatson
123143413Srwatson	if (shutdown(sock2, SHUT_WR) < 0)
124143413Srwatson		err(-1, "%s: shutdown", testname);
125143409Srwatson	signal_setup(testname);
126143409Srwatson	test_send(testname, sock2);
127143409Srwatson	if (!got_signal())
128143409Srwatson		errx(-1, "%s: send: didn't receive SIGPIPE", testname);
129143409Srwatson	close(sock1);
130143409Srwatson	close(sock2);
131143409Srwatson}
132143409Srwatson
133143409Srwatson#ifdef SO_NOSIGPIPE
134143409Srwatsonstatic void
135143409Srwatsontest_send_dontsignal(const char *testname, int sock1, int sock2)
136143409Srwatson{
137143409Srwatson	int i;
138143409Srwatson
139143409Srwatson	i = 1;
140143409Srwatson	if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0)
141143409Srwatson		err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname);
142143413Srwatson	if (shutdown(sock2, SHUT_WR) < 0)
143143413Srwatson		err(-1, "%s: shutdown", testname);
144143409Srwatson	signal_setup(testname);
145143409Srwatson	test_send(testname, sock2);
146143409Srwatson	if (got_signal())
147143409Srwatson		errx(-1, "%s: send: got SIGPIPE", testname);
148143409Srwatson	close(sock1);
149143409Srwatson	close(sock2);
150143409Srwatson}
151143409Srwatson#endif
152143409Srwatson
153143409Srwatsonstatic void
154143409Srwatsontest_write_wantsignal(const char *testname, int sock1, int sock2)
155143409Srwatson{
156143409Srwatson
157143413Srwatson	if (shutdown(sock2, SHUT_WR) < 0)
158143413Srwatson		err(-1, "%s: shutdown", testname);
159143409Srwatson	signal_setup(testname);
160143409Srwatson	test_write(testname, sock2);
161143409Srwatson	if (!got_signal())
162143409Srwatson		errx(-1, "%s: write: didn't receive SIGPIPE", testname);
163143409Srwatson	close(sock1);
164143409Srwatson	close(sock2);
165143409Srwatson}
166143409Srwatson
167143409Srwatson#ifdef SO_NOSIGPIPE
168143409Srwatsonstatic void
169143409Srwatsontest_write_dontsignal(const char *testname, int sock1, int sock2)
170143409Srwatson{
171143409Srwatson	int i;
172143409Srwatson
173143409Srwatson	i = 1;
174143409Srwatson	if (setsockopt(sock2, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0)
175143409Srwatson		err(-1, "%s: setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", testname);
176143413Srwatson	if (shutdown(sock2, SHUT_WR) < 0)
177143413Srwatson		err(-1, "%s: shutdown", testname);
178143409Srwatson	signal_setup(testname);
179143409Srwatson	test_write(testname, sock2);
180143409Srwatson	if (got_signal())
181143409Srwatson		errx(-1, "%s: write: got SIGPIPE", testname);
182143409Srwatson	close(sock1);
183143409Srwatson	close(sock2);
184143409Srwatson}
185143409Srwatson#endif
186143409Srwatson
187143409Srwatsonstatic int listen_sock;
188143409Srwatsonstatic void
189143409Srwatsontcp_setup(u_short port)
190143409Srwatson{
191143409Srwatson	struct sockaddr_in sin;
192143409Srwatson
193143409Srwatson	listen_sock = socket(PF_INET, SOCK_STREAM, 0);
194143409Srwatson	if (listen_sock < 0)
195143409Srwatson		err(-1, "tcp_setup: listen");
196143409Srwatson
197143409Srwatson	bzero(&sin, sizeof(sin));
198143409Srwatson	sin.sin_len = sizeof(sin);
199143409Srwatson	sin.sin_family = AF_INET;
200143409Srwatson	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
201143409Srwatson	sin.sin_port = htons(port);
202143409Srwatson
203143409Srwatson	if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
204143409Srwatson		err(-1, "tcp_setup: bind");
205143409Srwatson
206143409Srwatson	if (listen(listen_sock, -1) < 0)
207143409Srwatson		err(-1, "tcp_setup: listen");
208143409Srwatson}
209143409Srwatson
210143409Srwatsonstatic void
211143409Srwatsontcp_teardown(void)
212143409Srwatson{
213143409Srwatson
214143409Srwatson	close(listen_sock);
215143409Srwatson}
216143409Srwatson
217143409Srwatsonstatic void
218143409Srwatsontcp_pair(u_short port, int sock[2])
219143409Srwatson{
220143409Srwatson	int accept_sock, connect_sock;
221143409Srwatson	struct sockaddr_in sin;
222143409Srwatson	socklen_t len;
223143409Srwatson
224143409Srwatson	connect_sock = socket(PF_INET, SOCK_STREAM, 0);
225143409Srwatson	if (connect_sock < 0)
226143409Srwatson		err(-1, "tcp_pair: socket");
227143409Srwatson
228143409Srwatson	bzero(&sin, sizeof(sin));
229143409Srwatson	sin.sin_len = sizeof(sin);
230143409Srwatson	sin.sin_family = AF_INET;
231143409Srwatson	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
232143409Srwatson	sin.sin_port = htons(port);
233143409Srwatson
234143409Srwatson	if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
235143409Srwatson		err(-1, "tcp_pair: connect");
236143409Srwatson
237143409Srwatson	sleep(1);				/* Time for TCP to settle. */
238143409Srwatson
239143409Srwatson	len = sizeof(sin);
240143409Srwatson	accept_sock = accept(listen_sock, (struct sockaddr *)&sin, &len);
241143409Srwatson	if (accept_sock < 0)
242143409Srwatson		err(-1, "tcp_pair: accept");
243143409Srwatson
244143409Srwatson	sleep(1);				/* Time for TCP to settle. */
245143409Srwatson
246143409Srwatson	sock[0] = accept_sock;
247143409Srwatson	sock[1] = connect_sock;
248143409Srwatson}
249143409Srwatson
250143409Srwatsonint
251143409Srwatsonmain(int argc, char *argv[])
252143409Srwatson{
253143409Srwatson	char *dummy;
254143409Srwatson	int sock[2];
255143409Srwatson	long port;
256143409Srwatson
257143409Srwatson	if (argc != 2)
258143409Srwatson		usage();
259143409Srwatson
260143409Srwatson	port = strtol(argv[1], &dummy, 10);
261143409Srwatson	if (port < 0 || port > 65535 || *dummy != '\0')
262143409Srwatson		usage();
263143409Srwatson
264143409Srwatson#ifndef SO_NOSIGPIPE
265143409Srwatson	warnx("sigpipe: SO_NOSIGPIPE not defined, skipping some tests");
266143409Srwatson#endif
267143409Srwatson
268143409Srwatson	/*
269143409Srwatson	 * UNIX domain socketpair().
270143409Srwatson	 */
271143409Srwatson	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
272143409Srwatson		err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
273143409Srwatson	test_send_wantsignal("test_send_wantsignal(PF_LOCAL)", sock[0],
274143409Srwatson	    sock[1]);
275143409Srwatson
276143409Srwatson#ifdef SO_NOSIGPIPE
277143409Srwatson	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
278143409Srwatson		err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
279143409Srwatson	test_send_dontsignal("test_send_dontsignal(PF_LOCAL)", sock[0],
280143409Srwatson	    sock[1]);
281143409Srwatson#endif
282143409Srwatson
283143409Srwatson	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
284143409Srwatson		err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
285143409Srwatson	test_write_wantsignal("test_write_wantsignal(PF_LOCAL)", sock[0],
286143409Srwatson	    sock[1]);
287143409Srwatson
288143409Srwatson#ifdef SO_NOSIGPIPE
289143409Srwatson	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sock) < 0)
290143409Srwatson		err(-1, "socketpair(PF_LOCAL, SOCK_STREAM)");
291143409Srwatson	test_write_dontsignal("test_write_dontsignal(PF_LOCAL)", sock[0],
292143409Srwatson	    sock[1]);
293143409Srwatson#endif
294143409Srwatson
295143409Srwatson	/*
296143409Srwatson	 * TCP.
297143409Srwatson	 */
298143409Srwatson	tcp_setup(port);
299143409Srwatson	tcp_pair(port, sock);
300143409Srwatson	test_send_wantsignal("test_send_wantsignal(PF_INET)", sock[0],
301143409Srwatson	    sock[1]);
302143409Srwatson
303143409Srwatson#ifdef SO_NOSIGPIPE
304143409Srwatson	tcp_pair(port, sock);
305143409Srwatson	test_send_dontsignal("test_send_dontsignal(PF_INET)", sock[0],
306143409Srwatson	    sock[1]);
307143409Srwatson#endif
308143409Srwatson
309143409Srwatson	tcp_pair(port, sock);
310143409Srwatson	test_write_wantsignal("test_write_wantsignal(PF_INET)", sock[0],
311143409Srwatson	    sock[1]);
312143409Srwatson
313143409Srwatson#ifdef SO_NOSIGPIPE
314143409Srwatson	tcp_pair(port, sock);
315143409Srwatson	test_write_dontsignal("test_write_dontsignal(PF_INET)", sock[0],
316143409Srwatson	    sock[1]);
317143409Srwatson#endif
318143409Srwatson	tcp_teardown();
319143409Srwatson
320143409Srwatson	fprintf(stderr, "PASS\n");
321143409Srwatson	return (0);
322143409Srwatson}
323