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 
27 /*
28  * TCP regression test which opens a loopback TCP session, and closes it
29  * before the remote endpoint (server) can accept it.  Run the test twice,
30  * once using an explicit close() from the client, a second using a tcp drop.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/sysctl.h>
36 
37 #include <netinet/in.h>
38 
39 #include <err.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #define	TCP_PORT	9005
48 
49 static int
50 tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote)
51 {
52 	struct sockaddr_storage addrs[2];
53 
54 	/*
55 	 * Sysctl accepts an array of two sockaddr's, the first being the
56 	 * 'foreign' sockaddr, the second being the 'local' sockaddr.
57 	 */
58 
59 	bcopy(sin_remote, &addrs[0], sizeof(*sin_remote));
60 	bcopy(sin_local, &addrs[1], sizeof(*sin_local));
61 
62 	return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs,
63 	    sizeof(addrs)));
64 }
65 
66 
67 static void
68 tcp_server(pid_t partner)
69 {
70 	int error, listen_fd, accept_fd;
71 	struct sockaddr_in sin;
72 
73 	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
74 	if (listen_fd < 0) {
75 		error = errno;
76 		(void)kill(partner, SIGKILL);
77 		errno = error;
78 		err(-1, "tcp_server: socket");
79 	}
80 
81 	bzero(&sin, sizeof(sin));
82 	sin.sin_family = AF_INET;
83 	sin.sin_len = sizeof(sin);
84 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
85 	sin.sin_port = htons(TCP_PORT);
86 
87 	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
88 		error = errno;
89 		(void)kill(partner, SIGKILL);
90 		errno = error;
91 		err(-1, "tcp_server: bind");
92 	}
93 
94 	if (listen(listen_fd, -1) < 0) {
95 		error = errno;
96 		(void)kill(partner, SIGKILL);
97 		errno = error;
98 		err(-1, "tcp_server: listen");
99 	}
100 
101 	sleep(10);
102 
103 	accept_fd = accept(listen_fd, NULL, NULL);
104 	if (accept_fd < 0) {
105 		error = errno;
106 		(void)kill(partner, SIGKILL);
107 		errno = error;
108 		err(-1, "tcp_server: accept");
109 	}
110 	close(accept_fd);
111 	close(listen_fd);
112 }
113 
114 static void
115 tcp_client(pid_t partner, int dropflag)
116 {
117 	struct sockaddr_in sin, sin_local;
118 	int error, sock;
119 	socklen_t slen;
120 
121 	sleep(1);
122 
123 	sock = socket(PF_INET, SOCK_STREAM, 0);
124 	if (sock < 0) {
125 		error = errno;
126 		(void)kill(partner, SIGKILL);
127 		errno = error;
128 		err(-1, "socket");
129 	}
130 
131 	bzero(&sin, sizeof(sin));
132 	sin.sin_family = AF_INET;
133 	sin.sin_len = sizeof(sin);
134 	sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
135 	sin.sin_port = htons(TCP_PORT);
136 
137 	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
138 		error = errno;
139 		(void)kill(partner, SIGKILL);
140 		errno = error;
141 		err(-1, "connect");
142 	}
143 
144 	slen = sizeof(sin_local);
145 	if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) {
146 		error = errno;
147 		(void)kill(partner, SIGKILL);
148 		errno = error;
149 		err(-1, "getsockname");
150 	}
151 
152 	if (dropflag) {
153 		if (tcp_drop(&sin_local, &sin) < 0) {
154 			error = errno;
155 			(void)kill(partner, SIGKILL);
156 			errno = error;
157 			err(-1, "tcp_drop");
158 		}
159 		sleep(2);
160 	}
161 	close(sock);
162 }
163 
164 int
165 main(int argc, char *argv[])
166 {
167 	pid_t child_pid, parent_pid;
168 
169 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
170 		err(-1, "signal");
171 
172 	parent_pid = getpid();
173 	child_pid = fork();
174 	if (child_pid < 0)
175 		err(-1, "fork");
176 	if (child_pid == 0) {
177 		child_pid = getpid();
178 		tcp_server(parent_pid);
179 		return (0);
180 	} else
181 		tcp_client(child_pid, 0);
182 	(void)kill(child_pid, SIGTERM);
183 
184 	sleep(5);
185 
186 	parent_pid = getpid();
187 	child_pid = fork();
188 	if (child_pid < 0)
189 		err(-1, "fork");
190 	if (child_pid == 0) {
191 		child_pid = getpid();
192 		tcp_server(parent_pid);
193 		return (0);
194 	} else
195 		tcp_client(child_pid, 1);
196 	(void)kill(child_pid, SIGTERM);
197 
198 	return (0);
199 }
200