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