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