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  * $FreeBSD$
31  */
32 
33 /*
34  * TCP regression test that opens a loopback TCP session, then closes one end
35  * while shutting down the other.  This triggers an unusual TCP stack case in
36  * which an open file descriptor / socket is associated with a closed TCP
37  * connection.
38  */
39 
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 
43 #include <netinet/in.h>
44 
45 #include <err.h>
46 #include <errno.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 static void
54 tcp_server(pid_t partner, int listen_fd)
55 {
56 	int error, accept_fd;
57 
58 	accept_fd = accept(listen_fd, NULL, NULL);
59 	if (accept_fd < 0) {
60 		error = errno;
61 		(void)kill(partner, SIGTERM);
62 		errno = error;
63 		err(-1, "tcp_server: accept");
64 	}
65 	close(accept_fd);
66 	close(listen_fd);
67 }
68 
69 static void
70 tcp_client(pid_t partner, u_short port, int secs)
71 {
72 	struct sockaddr_in sin;
73 	int error, sock;
74 
75 	sleep(1);
76 
77 	sock = socket(PF_INET, SOCK_STREAM, 0);
78 	if (sock < 0) {
79 		error = errno;
80 		(void)kill(partner, SIGTERM);
81 		errno = error;
82 		err(-1, "socket");
83 	}
84 
85 	bzero(&sin, sizeof(sin));
86 	sin.sin_family = AF_INET;
87 	sin.sin_len = sizeof(sin);
88 	sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
89 	sin.sin_port = port;
90 
91 	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
92 		error = errno;
93 		(void)kill(partner, SIGTERM);
94 		errno = error;
95 		err(-1, "connect");
96 	}
97 
98 	if (shutdown(sock, SHUT_RDWR) < 0) {
99 		error = errno;
100 		(void)kill(partner, SIGTERM);
101 		errno = error;
102 		err(-1, "shutdown");
103 	}
104 
105 	sleep(secs);
106 	close(sock);
107 }
108 
109 int
110 main(int argc, char *argv[])
111 {
112 	struct sockaddr_in sin;
113 	pid_t child_pid, parent_pid;
114 	int listen_fd;
115 	socklen_t len;
116 	u_short port;
117 
118 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
119 		err(-1, "signal");
120 
121 	/*
122 	 * Run the whole thing twice: once, with a short sleep in the client,
123 	 * so that we close before time wait runs out, and once with a long
124 	 * sleep so that the time wait terminates while the socket is open.
125 	 * We don't reuse listen sockets between runs.
126 	 */
127 	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
128 	if (listen_fd < 0)
129 		err(-1, "socket");
130 
131 	/*
132 	 * We use the loopback, but let the kernel select a port for the
133 	 * server socket.
134 	 */
135 	bzero(&sin, sizeof(sin));
136 	sin.sin_family = AF_INET;
137 	sin.sin_len = sizeof(sin);
138 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
139 
140 	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
141 		err(-1, "bind");
142 
143 	if (listen(listen_fd, -1) < 0)
144 		err(-1, "listen");
145 
146 	/*
147 	 * Query the port so that the client can use it.
148 	 */
149 	bzero(&sin, sizeof(sin));
150 	sin.sin_family = AF_INET;
151 	sin.sin_len = sizeof(sin);
152 	len = sizeof(sin);
153 	if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0)
154 		err(-1, "getsockname");
155 	port = sin.sin_port;
156 	printf("Using port %d\n", ntohs(port));
157 
158 	parent_pid = getpid();
159 	child_pid = fork();
160 	if (child_pid < 0)
161 		err(-1, "fork");
162 	if (child_pid == 0) {
163 		child_pid = getpid();
164 		tcp_server(child_pid, listen_fd);
165 		exit(0);
166 	} else
167 		tcp_client(parent_pid, port, 1);
168 	(void)kill(child_pid, SIGTERM);
169 	close(listen_fd);
170 	sleep(5);
171 
172 	/*
173 	 * Start again, this time long sleep.
174 	 */
175 	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
176 	if (listen_fd < 0)
177 		err(-1, "socket");
178 
179 	/*
180 	 * We use the loopback, but let the kernel select a port for the
181 	 * server socket.
182 	 */
183 	bzero(&sin, sizeof(sin));
184 	sin.sin_family = AF_INET;
185 	sin.sin_len = sizeof(sin);
186 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
187 
188 	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
189 		err(-1, "bind");
190 
191 	if (listen(listen_fd, -1) < 0)
192 		err(-1, "listen");
193 
194 	/*
195 	 * Query the port so that the client can use it.
196 	 */
197 	bzero(&sin, sizeof(sin));
198 	sin.sin_family = AF_INET;
199 	sin.sin_len = sizeof(sin);
200 	len = sizeof(sin);
201 	if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0)
202 		err(-1, "getsockname");
203 	port = sin.sin_port;
204 	printf("Using port %d\n", ntohs(port));
205 
206 	parent_pid = getpid();
207 	child_pid = fork();
208 	if (child_pid < 0)
209 		err(-1, "fork");
210 	if (child_pid == 0) {
211 		child_pid = getpid();
212 		tcp_server(parent_pid, listen_fd);
213 	} else
214 		tcp_client(child_pid, port, 800);
215 
216 	return (0);
217 }
218