zerosend.c revision 294485
1/*-
2 * Copyright (c) 2007 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: stable/10/tools/regression/sockets/zerosend/zerosend.c 294485 2016-01-21 05:57:32Z ngie $
27 */
28
29#include <sys/select.h>
30#include <sys/socket.h>
31#include <sys/stat.h>
32
33#include <netinet/in.h>
34
35#include <arpa/inet.h>
36
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <limits.h>
41#include <stdio.h>
42#include <string.h>
43#include <unistd.h>
44
45#define	PORT1	10001
46#define	PORT2	10002
47
48static void
49try_0send(const char *test, int fd)
50{
51	ssize_t len;
52	char ch;
53
54	ch = 0;
55	len = send(fd, &ch, 0, 0);
56	if (len < 0)
57		err(1, "%s: try_0send", test);
58	if (len != 0)
59		errx(1, "%s: try_0send: returned %zd", test, len);
60}
61
62static void
63try_0write(const char *test, int fd)
64{
65	ssize_t len;
66	char ch;
67
68	ch = 0;
69	len = write(fd, &ch, 0);
70	if (len < 0)
71		err(1, "%s: try_0write", test);
72	if (len != 0)
73		errx(1, "%s: try_0write: returned %zd", test, len);
74}
75
76static void
77setup_udp(const char *test, int *fdp)
78{
79	struct sockaddr_in sin;
80	int sock1, sock2;
81
82	bzero(&sin, sizeof(sin));
83	sin.sin_len = sizeof(sin);
84	sin.sin_family = AF_INET;
85	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
86
87	sin.sin_port = htons(PORT1);
88	sock1 = socket(PF_INET, SOCK_DGRAM, 0);
89	if (sock1 < 0)
90		err(1, "%s: setup_udp: socket", test);
91	if (bind(sock1, (struct sockaddr *)&sin, sizeof(sin)) < 0)
92		err(1, "%s: setup_udp: bind(%s, %d)", test,
93		    inet_ntoa(sin.sin_addr), PORT1);
94	sin.sin_port = htons(PORT2);
95	if (connect(sock1, (struct sockaddr *)&sin, sizeof(sin)) < 0)
96		err(1, "%s: setup_udp: connect(%s, %d)", test,
97		    inet_ntoa(sin.sin_addr), PORT2);
98
99	sock2 = socket(PF_INET, SOCK_DGRAM, 0);
100	if (sock2 < 0)
101		err(1, "%s: setup_udp: socket", test);
102	if (bind(sock2, (struct sockaddr *)&sin, sizeof(sin)) < 0)
103		err(1, "%s: setup_udp: bind(%s, %d)", test,
104		    inet_ntoa(sin.sin_addr), PORT2);
105	sin.sin_port = htons(PORT1);
106	if (connect(sock2, (struct sockaddr *)&sin, sizeof(sin)) < 0)
107		err(1, "%s: setup_udp: connect(%s, %d)", test,
108		    inet_ntoa(sin.sin_addr), PORT1);
109
110	fdp[0] = sock1;
111	fdp[1] = sock2;
112}
113
114static void
115setup_tcp(const char *test, int *fdp)
116{
117	fd_set writefds, exceptfds;
118	struct sockaddr_in sin;
119	int ret, sock1, sock2, sock3;
120	struct timeval tv;
121
122	bzero(&sin, sizeof(sin));
123	sin.sin_len = sizeof(sin);
124	sin.sin_family = AF_INET;
125	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
126
127	/*
128	 * First set up the listen socket.
129	 */
130	sin.sin_port = htons(PORT1);
131	sock1 = socket(PF_INET, SOCK_STREAM, 0);
132	if (sock1 < 0)
133		err(1, "%s: setup_tcp: socket", test);
134	if (bind(sock1, (struct sockaddr *)&sin, sizeof(sin)) < 0)
135		err(1, "%s: bind(%s, %d)", test, inet_ntoa(sin.sin_addr),
136		    PORT1);
137	if (listen(sock1, -1) < 0)
138		err(1, "%s: listen", test);
139
140	/*
141	 * Now connect to it, non-blocking so that we don't deadlock against
142	 * ourselves.
143	 */
144	sock2 = socket(PF_INET, SOCK_STREAM, 0);
145	if (sock2 < 0)
146		err(1, "%s: setup_tcp: socket", test);
147	if (fcntl(sock2, F_SETFL, O_NONBLOCK) < 0)
148		err(1, "%s: setup_tcp: fcntl(O_NONBLOCK)", test);
149	if (connect(sock2, (struct sockaddr *)&sin, sizeof(sin)) < 0 &&
150	    errno != EINPROGRESS)
151		err(1, "%s: setup_tcp: connect(%s, %d)", test,
152		    inet_ntoa(sin.sin_addr), PORT1);
153
154	/*
155	 * Now pick up the connection after sleeping a moment to make sure
156	 * there's been time for some packets to go back and forth.
157	 */
158	if (sleep(1) != 0)
159		err(1, "%s: sleep(1)", test);
160	sock3 = accept(sock1, NULL, NULL);
161	if (sock3 < 0)
162		err(1, "%s: accept", test);
163	if (sleep(1) != 0)
164		err(1, "%s: sleep(1)", test);
165
166	FD_ZERO(&writefds);
167	FD_SET(sock2, &writefds);
168	FD_ZERO(&exceptfds);
169	FD_SET(sock2, &exceptfds);
170	tv.tv_sec = 1;
171	tv.tv_usec = 0;
172	ret = select(sock2 + 1, NULL, &writefds, &exceptfds, &tv);
173	if (ret < 0)
174		err(1, "%s: setup_tcp: select", test);
175	if (FD_ISSET(sock2, &exceptfds))
176		errx(1, "%s: setup_tcp: select: exception", test);
177	if (!FD_ISSET(sock2, &writefds))
178		errx(1, "%s: setup_tcp: select: not writable", test);
179
180	close(sock1);
181	fdp[0] = sock2;
182	fdp[1] = sock3;
183}
184
185static void
186setup_udsstream(const char *test, int *fdp)
187{
188
189	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fdp) < 0)
190		err(1, "%s: setup_udsstream: socketpair", test);
191}
192
193static void
194setup_udsdgram(const char *test, int *fdp)
195{
196
197	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fdp) < 0)
198		err(1, "%s: setup_udsdgram: socketpair", test);
199}
200
201static void
202setup_pipe(const char *test, int *fdp)
203{
204
205	if (pipe(fdp) < 0)
206		err(1, "%s: setup_pipe: pipe", test);
207}
208
209static void
210setup_fifo(const char *test, int *fdp)
211{
212	char path[] = "0send_fifo.XXXXXXX";
213	int fd1, fd2;
214
215	if (mkstemp(path) == -1)
216		err(1, "%s: setup_fifo: mktemp", test);
217	unlink(path);
218
219	if (mkfifo(path, 0600) < 0)
220		err(1, "%s: setup_fifo: mkfifo(%s)", test, path);
221
222	fd1 = open(path, O_RDONLY | O_NONBLOCK);
223	if (fd1 < 0)
224		err(1, "%s: setup_fifo: open(%s, O_RDONLY)", test, path);
225
226	fd2 = open(path, O_WRONLY | O_NONBLOCK);
227	if (fd2 < 0)
228		err(1, "%s: setup_fifo: open(%s, O_WRONLY)", test, path);
229
230	fdp[0] = fd2;
231	fdp[1] = fd1;
232}
233
234static void
235close_both(int *fdp)
236{
237
238	close(fdp[0]);
239	fdp[0] = -1;
240	close(fdp[1]);
241	fdp[1] = -1;
242}
243
244int
245main(void)
246{
247	int fd[2];
248
249	setup_udp("udp_0send", fd);
250	try_0send("udp_0send", fd[0]);
251	close_both(fd);
252
253	setup_udp("udp_0write", fd);
254	try_0write("udp_0write", fd[0]);
255	close_both(fd);
256
257	setup_tcp("tcp_0send", fd);
258	try_0send("tcp_0send", fd[0]);
259	close_both(fd);
260
261	setup_tcp("tcp_0write", fd);
262	try_0write("tcp_0write", fd[0]);
263	close_both(fd);
264
265	setup_udsstream("udsstream_0send", fd);
266	try_0send("udsstream_0send", fd[0]);
267	close_both(fd);
268
269	setup_udsstream("udsstream_0write", fd);
270	try_0write("udsstream_0write", fd[0]);
271	close_both(fd);
272
273	setup_udsdgram("udsdgram_0send", fd);
274	try_0send("udsdgram_0send", fd[0]);
275	close_both(fd);
276
277	setup_udsdgram("udsdgram_0write", fd);
278	try_0write("udsdgram_0write", fd[0]);
279	close_both(fd);
280
281	setup_pipe("pipe_0write", fd);
282	try_0write("pipd_0write", fd[0]);
283	close_both(fd);
284
285	setup_fifo("fifo_0write", fd);
286	try_0write("fifo_0write", fd[0]);
287	close_both(fd);
288
289	return (0);
290}
291