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$ 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 48 static void 49 try_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 62 static void 63 try_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 76 static void 77 setup_udp(const char *test, int *fdp, int port1, int port2) 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 114 static void 115 setup_tcp(const char *test, int *fdp, int port) 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(port); 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 185 static void 186 setup_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 193 static void 194 setup_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 201 static void 202 setup_pipe(const char *test, int *fdp) 203 { 204 205 if (pipe(fdp) < 0) 206 err(1, "%s: setup_pipe: pipe", test); 207 } 208 209 static void 210 setup_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 234 static void 235 close_both(int *fdp) 236 { 237 238 close(fdp[0]); 239 fdp[0] = -1; 240 close(fdp[1]); 241 fdp[1] = -1; 242 } 243 244 int 245 main(void) 246 { 247 int fd[2]; 248 249 setup_udp("udp_0send", fd, PORT1, PORT2); 250 try_0send("udp_0send", fd[0]); 251 close_both(fd); 252 253 setup_udp("udp_0write", fd, PORT1 + 10, PORT2 + 10); 254 try_0write("udp_0write", fd[0]); 255 close_both(fd); 256 257 setup_tcp("tcp_0send", fd, PORT1); 258 try_0send("tcp_0send", fd[0]); 259 close_both(fd); 260 261 setup_tcp("tcp_0write", fd, PORT1 + 10); 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