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