xref: /freebsd/tools/regression/sockets/unix_bindconnect/unix_bindconnect.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*-
2  * Copyright (c) 2005 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/types.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 
31 #include <err.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 /*
39  * Simple regression test to exercise some error cases relating to the use of
40  * bind() and connect() on UNIX domain sockets.  In particular, make sure
41  * that when two sockets rendezvous using the file system name space, they
42  * get the expected success/failure cases.
43  *
44  * TODO:
45  * - Check that the resulting file mode/owner are right.
46  * - Do the same tests with UNIX domain sockets.
47  * - Check the results of getsockaddr() and getpeeraddr().
48  */
49 
50 #define	SOCK_NAME_ONE	"socket.1"
51 #define	SOCK_NAME_TWO	"socket.2"
52 
53 #define	UNWIND_MAX	1024
54 
55 static int unwind_len;
56 static struct unwind {
57 	char	u_path[PATH_MAX];
58 } unwind_list[UNWIND_MAX];
59 
60 static void
61 push_path(const char *path)
62 {
63 
64 	if (unwind_len >= UNWIND_MAX)
65 		err(-1, "push_path: one path too many (%s)", path);
66 
67 	strlcpy(unwind_list[unwind_len].u_path, path, PATH_MAX);
68 	unwind_len++;
69 }
70 
71 static void
72 unwind(void)
73 {
74 	int i;
75 
76 	for (i = unwind_len - 1; i >= 0; i--) {
77 		unlink(unwind_list[i].u_path);
78 		rmdir(unwind_list[i].u_path);
79 	}
80 }
81 
82 static int
83 bind_test(const char *directory_path)
84 {
85 	char socket_path[PATH_MAX];
86 	struct sockaddr_un sun;
87 	int sock1, sock2;
88 
89 	sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
90 	if (sock1 < 0) {
91 		warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
92 		return (-1);
93 	}
94 
95 	if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
96 	    directory_path, SOCK_NAME_ONE) >= PATH_MAX) {
97 		warn("bind_test: snprintf(socket_path)");
98 		close(sock1);
99 		return (-1);
100 	}
101 
102 	bzero(&sun, sizeof(sun));
103 	sun.sun_len = sizeof(sun);
104 	sun.sun_family = AF_UNIX;
105 	if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
106 	    >= (int)sizeof(sun.sun_path)) {
107 		warn("bind_test: snprintf(sun.sun_path)");
108 		close(sock1);
109 		return (-1);
110 	}
111 
112 	if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
113 		warn("bind_test: bind(sun) #1");
114 		close(sock1);
115 		return (-1);
116 	}
117 
118 	push_path(socket_path);
119 
120 	/*
121 	 * Once a STREAM UNIX domain socket has been bound, it can't be
122 	 * rebound.  Expected error is EINVAL.
123 	 */
124 	if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
125 		warnx("bind_test: bind(sun) #2 succeeded");
126 		close(sock1);
127 		return (-1);
128 	}
129 	if (errno != EINVAL) {
130 		warn("bind_test: bind(sun) #2");
131 		close(sock1);
132 		return (-1);
133 	}
134 
135 	sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
136 	if (sock2 < 0) {
137 		warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");
138 		close(sock1);
139 		return (-1);
140 	}
141 
142 	/*
143 	 * Since a socket is already bound to the pathname, it can't be bound
144 	 * to a second socket.  Expected error is EADDRINUSE.
145 	 */
146 	if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
147 		warnx("bind_test: bind(sun) #3 succeeded");
148 		close(sock1);
149 		close(sock2);
150 		return (-1);
151 	}
152 	if (errno != EADDRINUSE) {
153 		warn("bind_test: bind(sun) #2");
154 		close(sock1);
155 		close(sock2);
156 		return (-1);
157 	}
158 
159 	close(sock1);
160 
161 	/*
162 	 * The socket bound to the pathname has been closed, but the pathname
163 	 * can't be reused without first being unlinked.  Expected error is
164 	 * EADDRINUSE.
165 	 */
166 	if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
167 		warnx("bind_test: bind(sun) #4 succeeded");
168 		close(sock2);
169 		return (-1);
170 	}
171 	if (errno != EADDRINUSE) {
172 		warn("bind_test: bind(sun) #4");
173 		close(sock2);
174 		return (-1);
175 	}
176 
177 	unlink(socket_path);
178 
179 	/*
180 	 * The pathname is now free, so the socket should be able to bind to
181 	 * it.
182 	 */
183 	if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
184 		warn("bind_test: bind(sun) #5");
185 		close(sock2);
186 		return (-1);
187 	}
188 
189 	close(sock2);
190 	return (0);
191 }
192 
193 static int
194 connect_test(const char *directory_path)
195 {
196 	char socket_path[PATH_MAX];
197 	struct sockaddr_un sun;
198 	int sock1, sock2;
199 
200 	sock1 = socket(PF_UNIX, SOCK_STREAM, 0);
201 	if (sock1 < 0) {
202 		warn("connect_test: socket(PF_UNIX, SOCK_STREAM, 0)");
203 		return (-1);
204 	}
205 
206 	if (snprintf(socket_path, sizeof(socket_path), "%s/%s",
207 	    directory_path, SOCK_NAME_TWO) >= PATH_MAX) {
208 		warn("connect_test: snprintf(socket_path)");
209 		close(sock1);
210 		return (-1);
211 	}
212 
213 	bzero(&sun, sizeof(sun));
214 	sun.sun_len = sizeof(sun);
215 	sun.sun_family = AF_UNIX;
216 	if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)
217 	    >= (int)sizeof(sun.sun_path)) {
218 		warn("connect_test: snprintf(sun.sun_path)");
219 		close(sock1);
220 		return (-1);
221 	}
222 
223 	/*
224 	 * Try connecting to a path that doesn't yet exist.  Should fail with
225 	 * ENOENT.
226 	 */
227 	if (connect(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
228 		warnx("connect_test: connect(sun) #1 succeeded");
229 		close(sock1);
230 		return (-1);
231 	}
232 	if (errno != ENOENT) {
233 		warn("connect_test: connect(sun) #1");
234 		close(sock1);
235 		return (-1);
236 	}
237 
238 	if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
239 		warn("connect_test: bind(sun) #1");
240 		close(sock1);
241 		return (-1);
242 	}
243 
244 	if (listen(sock1, 3) < 0) {
245 		warn("connect_test: listen(sock1)");
246 		close(sock1);
247 		return (-1);
248 	}
249 
250 	push_path(socket_path);
251 
252 	sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
253 	if (sock2 < 0) {
254 		warn("socket(PF_UNIX, SOCK_STREAM, 0)");
255 		close(sock1);
256 		return (-1);
257 	}
258 
259 	/*
260 	 * Do a simple connect and make sure that works.
261 	 */
262 	if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
263 		warn("connect(sun) #2");
264 		close(sock1);
265 		return (-1);
266 	}
267 
268 	close(sock2);
269 
270 	close(sock1);
271 
272 	sock2 = socket(PF_UNIX, SOCK_STREAM, 0);
273 	if (sock2 < 0) {
274 		warn("socket(PF_UNIX, SOCK_STREAM, 0)");
275 		return (-1);
276 	}
277 
278 	/*
279 	 * Confirm that once the listen socket is closed, we get a
280 	 * connection refused (ECONNREFUSED) when attempting to connect to
281 	 * the pathname.
282 	 */
283 	if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
284 		warnx("connect(sun) #3 succeeded");
285 		close(sock2);
286 		return (-1);
287 	}
288 	if (errno != ECONNREFUSED) {
289 		warn("connect(sun) #3");
290 		close(sock2);
291 		return (-1);
292 	}
293 
294 	close(sock2);
295 	unlink(socket_path);
296 	return (0);
297 }
298 int
299 main(void)
300 {
301 	char directory_path[PATH_MAX];
302 	int error;
303 
304 	strlcpy(directory_path, "/tmp/unix_bind.XXXXXXX", PATH_MAX);
305 	if (mkdtemp(directory_path) == NULL)
306 		err(-1, "mkdtemp");
307 	push_path(directory_path);
308 
309 	error = bind_test(directory_path);
310 
311 	if (error == 0)
312 		error = connect_test(directory_path);
313 
314 	unwind();
315 	return (error);
316 }
317