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