1/*	$NetBSD: t_tcp.c,v 1.4 2016/03/04 18:52:01 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *        This product includes software developed by the NetBSD
18 *        Foundation, Inc. and its contributors.
19 * 4. Neither the name of The NetBSD Foundation nor the names of its
20 *    contributors may be used to endorse or promote products derived
21 *    from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37#ifdef __RCSID
38__RCSID("$Id: t_tcp.c,v 1.4 2016/03/04 18:52:01 christos Exp $");
39#endif
40
41/* Example code. Should block; does with accept not paccept. */
42/* Original by: Justin Cormack <justin@specialbusrvrervice.com> */
43
44#include <sys/param.h>
45#include <sys/types.h>
46#include <sys/socket.h>
47#include <netinet/in.h>
48#include <stdio.h>
49#include <stdbool.h>
50#include <unistd.h>
51#include <fcntl.h>
52#include <errno.h>
53#include <err.h>
54#include <stdlib.h>
55#include <signal.h>
56
57#ifdef TEST
58#define FAIL(msg, ...)  err(EXIT_FAILURE, msg, ## __VA_ARGS__)
59#else
60#include <atf-c.h>
61#define FAIL(msg, ...)  ATF_CHECK_MSG(0, msg, ## __VA_ARGS__); goto fail
62#endif
63
64#ifdef __linux__
65#define paccept(a, b, c, d, e) accept4((a), (b), (c), (e))
66#endif
67
68static void
69ding(int al)
70{
71}
72
73static void
74paccept_block(bool pacceptblock, bool fcntlblock)
75{
76	int srvr = -1, clnt = -1, as = -1;
77	int ok, fl;
78	ssize_t n;
79	char buf[10];
80	struct sockaddr_in sin, ba;
81	struct sigaction sa;
82
83	srvr = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
84	if (srvr == -1)
85		FAIL("socket");
86
87	memset(&sin, 0, sizeof(sin));
88	sin.sin_family = AF_INET;
89#ifdef BSD4_4
90	sin.sin_len = sizeof(sin);
91#endif
92	sin.sin_port = htons(0);
93	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
94	ok = bind(srvr, (const struct sockaddr *)&sin, (socklen_t)sizeof(sin));
95	if (ok == -1)
96		FAIL("bind");
97
98	socklen_t addrlen = sizeof(struct sockaddr_in);
99	ok = getsockname(srvr, (struct sockaddr *)&ba, &addrlen);
100	if (ok == -1)
101		FAIL("getsockname");
102
103	ok = listen(srvr, SOMAXCONN);
104	if (ok == -1)
105		FAIL("listen");
106
107	clnt = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
108	if (clnt == -1)
109		FAIL("socket");
110
111	/* may not connect first time */
112	ok = connect(clnt, (struct sockaddr *) &ba, addrlen);
113	if (ok != -1 || errno != EINPROGRESS)
114		FAIL("expected connect to fail");
115	as = paccept(srvr, NULL, NULL, NULL, pacceptblock ? 0 : SOCK_NONBLOCK);
116	ok = connect(clnt, (struct sockaddr *) &ba, addrlen);
117	if (ok == -1 && errno != EISCONN)
118		FAIL("connect failed");
119
120#if 0
121	fl = fcntl(srvr, F_GETFL, 0);
122	if (fl == -1)
123		FAIL("fnctl getfl");
124
125	ok = fcntl(srvr, F_SETFL, fl & ~O_NONBLOCK);
126	if (ok == -1)
127		FAIL("fnctl setfl");
128#endif
129
130	if (as == -1) {		/* not true under NetBSD */
131		as = paccept(srvr, NULL, NULL, NULL, pacceptblock ? 0 : SOCK_NONBLOCK);
132		if (as == -1)
133			FAIL("paccept");
134	}
135	if (fcntlblock) {
136		fl = fcntl(as, F_GETFL, 0);
137		if (fl == -1)
138			FAIL("fnctl");
139		if (fl != (O_RDWR|O_NONBLOCK))
140			FAIL("fl 0x%x != 0x%x\n", fl, O_RDWR|O_NONBLOCK);
141		ok = fcntl(as, F_SETFL, fl & ~O_NONBLOCK);
142		if (ok == -1)
143			FAIL("fnctl setfl");
144
145		fl = fcntl(as, F_GETFL, 0);
146		if (fl & O_NONBLOCK)
147			FAIL("fl non blocking after reset");
148	}
149	sa.sa_handler = ding;
150	sa.sa_flags = 0;
151	sigemptyset(&sa.sa_mask);
152	sigaction(SIGALRM, &sa, NULL);
153	alarm(1);
154	n = read(as, buf, 10);
155
156	if (pacceptblock || fcntlblock) {
157		if (n == -1 && errno != EINTR)
158			FAIL("read");
159	} else {
160		if (n != -1 || errno != EWOULDBLOCK)
161			FAIL("read");
162	}
163	return;
164fail:
165	close(srvr);
166	close(clnt);
167	close(as);
168}
169
170#ifndef TEST
171
172ATF_TC(paccept_reset_nonblock);
173ATF_TC_HEAD(paccept_reset_nonblock, tc)
174{
175
176	atf_tc_set_md_var(tc, "descr", "Check that paccept(2) resets "
177	    "the non-blocking flag on non-blocking sockets");
178}
179
180ATF_TC_BODY(paccept_reset_nonblock, tc)
181{
182	paccept_block(true, false);
183}
184
185ATF_TC(fcntl_reset_nonblock);
186ATF_TC_HEAD(fcntl_reset_nonblock, tc)
187{
188
189	atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets "
190	    "the non-blocking flag on non-blocking sockets");
191}
192
193ATF_TC_BODY(fcntl_reset_nonblock, tc)
194{
195	paccept_block(false, true);
196}
197
198ATF_TC(paccept_nonblock);
199ATF_TC_HEAD(paccept_nonblock, tc)
200{
201
202	atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets "
203	    "the non-blocking flag on non-blocking sockets");
204}
205
206ATF_TC_BODY(paccept_nonblock, tc)
207{
208	paccept_block(false, false);
209}
210
211ATF_TP_ADD_TCS(tp)
212{
213
214	ATF_TP_ADD_TC(tp, paccept_reset_nonblock);
215	ATF_TP_ADD_TC(tp, fcntl_reset_nonblock);
216	ATF_TP_ADD_TC(tp, paccept_nonblock);
217	return atf_no_error();
218}
219#else
220int
221main(int argc, char *argv[])
222{
223	paccept_block(false);
224	paccept_block(true);
225	return 0;
226}
227#endif
228