1 /*- 2 * Copyright (c) 2006 Robert N. M. Watson 3 * Copyright (c) 2011 Juniper Networks, Inc. 4 * All rights reserved. 5 * 6 * Portions of this software were developed by Robert N. M. Watson under 7 * contract to Juniper Networks, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * TCP regression test that opens a loopback TCP session, then closes one end 33 * while shutting down the other. This triggers an unusual TCP stack case in 34 * which an open file descriptor / socket is associated with a closed TCP 35 * connection. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/socket.h> 40 41 #include <netinet/in.h> 42 43 #include <err.h> 44 #include <errno.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 static void 52 tcp_server(pid_t partner, int listen_fd) 53 { 54 int error, accept_fd; 55 56 accept_fd = accept(listen_fd, NULL, NULL); 57 if (accept_fd < 0) { 58 error = errno; 59 (void)kill(partner, SIGTERM); 60 errno = error; 61 err(-1, "tcp_server: accept"); 62 } 63 close(accept_fd); 64 close(listen_fd); 65 } 66 67 static void 68 tcp_client(pid_t partner, u_short port, int secs) 69 { 70 struct sockaddr_in sin; 71 int error, sock; 72 73 sleep(1); 74 75 sock = socket(PF_INET, SOCK_STREAM, 0); 76 if (sock < 0) { 77 error = errno; 78 (void)kill(partner, SIGTERM); 79 errno = error; 80 err(-1, "socket"); 81 } 82 83 bzero(&sin, sizeof(sin)); 84 sin.sin_family = AF_INET; 85 sin.sin_len = sizeof(sin); 86 sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); 87 sin.sin_port = port; 88 89 if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 90 error = errno; 91 (void)kill(partner, SIGTERM); 92 errno = error; 93 err(-1, "connect"); 94 } 95 96 if (shutdown(sock, SHUT_RDWR) < 0) { 97 error = errno; 98 (void)kill(partner, SIGTERM); 99 errno = error; 100 err(-1, "shutdown"); 101 } 102 103 sleep(secs); 104 close(sock); 105 } 106 107 int 108 main(int argc, char *argv[]) 109 { 110 struct sockaddr_in sin; 111 pid_t child_pid, parent_pid; 112 int listen_fd; 113 socklen_t len; 114 u_short port; 115 116 if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) 117 err(-1, "signal"); 118 119 /* 120 * Run the whole thing twice: once, with a short sleep in the client, 121 * so that we close before time wait runs out, and once with a long 122 * sleep so that the time wait terminates while the socket is open. 123 * We don't reuse listen sockets between runs. 124 */ 125 listen_fd = socket(PF_INET, SOCK_STREAM, 0); 126 if (listen_fd < 0) 127 err(-1, "socket"); 128 129 /* 130 * We use the loopback, but let the kernel select a port for the 131 * server socket. 132 */ 133 bzero(&sin, sizeof(sin)); 134 sin.sin_family = AF_INET; 135 sin.sin_len = sizeof(sin); 136 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 137 138 if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 139 err(-1, "bind"); 140 141 if (listen(listen_fd, -1) < 0) 142 err(-1, "listen"); 143 144 /* 145 * Query the port so that the client can use it. 146 */ 147 bzero(&sin, sizeof(sin)); 148 sin.sin_family = AF_INET; 149 sin.sin_len = sizeof(sin); 150 len = sizeof(sin); 151 if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) 152 err(-1, "getsockname"); 153 port = sin.sin_port; 154 printf("Using port %d\n", ntohs(port)); 155 156 parent_pid = getpid(); 157 child_pid = fork(); 158 if (child_pid < 0) 159 err(-1, "fork"); 160 if (child_pid == 0) { 161 child_pid = getpid(); 162 tcp_server(child_pid, listen_fd); 163 exit(0); 164 } else 165 tcp_client(parent_pid, port, 1); 166 (void)kill(child_pid, SIGTERM); 167 close(listen_fd); 168 sleep(5); 169 170 /* 171 * Start again, this time long sleep. 172 */ 173 listen_fd = socket(PF_INET, SOCK_STREAM, 0); 174 if (listen_fd < 0) 175 err(-1, "socket"); 176 177 /* 178 * We use the loopback, but let the kernel select a port for the 179 * server socket. 180 */ 181 bzero(&sin, sizeof(sin)); 182 sin.sin_family = AF_INET; 183 sin.sin_len = sizeof(sin); 184 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 185 186 if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 187 err(-1, "bind"); 188 189 if (listen(listen_fd, -1) < 0) 190 err(-1, "listen"); 191 192 /* 193 * Query the port so that the client can use it. 194 */ 195 bzero(&sin, sizeof(sin)); 196 sin.sin_family = AF_INET; 197 sin.sin_len = sizeof(sin); 198 len = sizeof(sin); 199 if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) 200 err(-1, "getsockname"); 201 port = sin.sin_port; 202 printf("Using port %d\n", ntohs(port)); 203 204 parent_pid = getpid(); 205 child_pid = fork(); 206 if (child_pid < 0) 207 err(-1, "fork"); 208 if (child_pid == 0) { 209 child_pid = getpid(); 210 tcp_server(parent_pid, listen_fd); 211 } else 212 tcp_client(child_pid, port, 800); 213 214 return (0); 215 } 216