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
push_path(const char * path)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
unwind(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
bind_test(const char * directory_path)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
connect_test(const char * directory_path)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
main(void)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