xref: /freebsd/tools/regression/netinet/tcpsocktimewait/tcpsocktimewait.c (revision 52267f7411adcc76ede961420e08c0e42f42d415)
1 /*-
2  * Copyright (c) 2006 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 /*
30  * TCP regression test that opens a loopback TCP session, then closes one end
31  * while shutting down the other.  This triggers an unusual TCP stack case in
32  * which an open file descriptor / socket is associated with a closed TCP
33  * connection.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 
39 #include <netinet/in.h>
40 
41 #include <err.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #define	TCP_PORT	9001
49 
50 static void
51 tcp_server(pid_t partner)
52 {
53 	int error, listen_fd, accept_fd;
54 	struct sockaddr_in sin;
55 
56 	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
57 	if (listen_fd < 0) {
58 		error = errno;
59 		(void)kill(partner, SIGTERM);
60 		errno = error;
61 		err(-1, "tcp_server: socket");
62 	}
63 
64 	bzero(&sin, sizeof(sin));
65 	sin.sin_family = AF_INET;
66 	sin.sin_len = sizeof(sin);
67 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
68 	sin.sin_port = htons(TCP_PORT);
69 
70 	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
71 		error = errno;
72 		(void)kill(partner, SIGTERM);
73 		errno = error;
74 		err(-1, "tcp_server: bind");
75 	}
76 
77 	if (listen(listen_fd, -1) < 0) {
78 		error = errno;
79 		(void)kill(partner, SIGTERM);
80 		errno = error;
81 		err(-1, "tcp_server: listen");
82 	}
83 
84 	accept_fd = accept(listen_fd, NULL, NULL);
85 	if (accept_fd < 0) {
86 		error = errno;
87 		(void)kill(partner, SIGTERM);
88 		errno = error;
89 		err(-1, "tcp_server: accept");
90 	}
91 	close(accept_fd);
92 	close(listen_fd);
93 }
94 
95 static void
96 tcp_client(pid_t partner, int secs)
97 {
98 	struct sockaddr_in sin;
99 	int error, sock;
100 
101 	sleep(1);
102 
103 	sock = socket(PF_INET, SOCK_STREAM, 0);
104 	if (sock < 0) {
105 		error = errno;
106 		(void)kill(partner, SIGTERM);
107 		errno = error;
108 		err(-1, "socket");
109 	}
110 
111 	bzero(&sin, sizeof(sin));
112 	sin.sin_family = AF_INET;
113 	sin.sin_len = sizeof(sin);
114 	sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
115 	sin.sin_port = htons(TCP_PORT);
116 
117 	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
118 		error = errno;
119 		(void)kill(partner, SIGTERM);
120 		errno = error;
121 		err(-1, "connect");
122 	}
123 
124 	if (shutdown(sock, SHUT_RDWR) < 0) {
125 		error = errno;
126 		(void)kill(partner, SIGTERM);
127 		errno = error;
128 		err(-1, "shutdown");
129 	}
130 
131 	sleep(secs);
132 	close(sock);
133 }
134 
135 int
136 main(int argc, char *argv[])
137 {
138 	pid_t child_pid, parent_pid;
139 
140 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
141 		err(-1, "signal");
142 
143 	/*
144 	 * Run the whole thing twice: once, with a short sleep in the client,
145 	 * so that we close before time wait runs out, and once with a long
146 	 * sleep so that the time wait terminates while the socket is open.
147 	 */
148 	parent_pid = getpid();
149 	child_pid = fork();
150 	if (child_pid < 0)
151 		err(-1, "fork");
152 	if (child_pid == 0) {
153 		child_pid = getpid();
154 		tcp_server(child_pid);
155 		exit(0);
156 	} else
157 		tcp_client(parent_pid, 1);
158 	(void)kill(child_pid, SIGTERM);
159 	sleep(5);
160 
161 	parent_pid = getpid();
162 	child_pid = fork();
163 	if (child_pid < 0)
164 		err(-1, "fork");
165 	if (child_pid == 0) {
166 		child_pid = getpid();
167 		tcp_server(parent_pid);
168 	} else
169 		tcp_client(child_pid, 800);
170 
171 	return (0);
172 }
173