1 /* $OpenBSD: unfdpass.c,v 1.20 2018/11/28 08:06:22 claudio Exp $ */ 2 /* $NetBSD: unfdpass.c,v 1.3 1998/06/24 23:51:30 thorpej Exp $ */ 3 4 /*- 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 10 * NASA Ames Research Center. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Test passing of file descriptors over Unix domain sockets and socketpairs. 36 */ 37 38 #include <sys/param.h> 39 #include <sys/socket.h> 40 #include <sys/time.h> 41 #include <sys/wait.h> 42 #include <sys/un.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <signal.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #define SOCK_NAME "test-sock" 53 54 int main(int, char *[]); 55 void child(int, int, int); 56 void catch_sigchld(int); 57 58 /* ARGSUSED */ 59 int 60 main(int argc, char *argv[]) 61 { 62 struct msghdr msg; 63 int sock, pfd[2], fd, i; 64 int listensock = -1; 65 char fname[16], buf[64]; 66 struct cmsghdr *cmp; 67 int *files = NULL; 68 struct sockaddr_un sun, csun; 69 int csunlen; 70 pid_t pid; 71 union { 72 struct cmsghdr hdr; 73 char buf[CMSG_SPACE(sizeof(int) * 3)]; 74 } cmsgbuf; 75 int pflag, oflag, rflag; 76 int type = SOCK_STREAM; 77 extern char *__progname; 78 79 pflag = 0; 80 oflag = 0; 81 rflag = 0; 82 while ((i = getopt(argc, argv, "opqr")) != -1) { 83 switch (i) { 84 case 'o': 85 oflag = 1; 86 break; 87 case 'p': 88 pflag = 1; 89 break; 90 case 'q': 91 type = SOCK_SEQPACKET; 92 break; 93 case 'r': 94 rflag = 1; 95 break; 96 default: 97 fprintf(stderr, "usage: %s [-opqr]\n", __progname); 98 exit(1); 99 } 100 } 101 102 /* 103 * Create the test files. 104 */ 105 for (i = 0; i < 5; i++) { 106 (void) snprintf(fname, sizeof fname, "file%d", i + 1); 107 if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) 108 err(1, "open %s", fname); 109 (void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1); 110 if (write(fd, buf, strlen(buf)) != (ssize_t) strlen(buf)) 111 err(1, "write %s", fname); 112 (void) close(fd); 113 } 114 115 if (pflag) { 116 /* 117 * Create the socketpair 118 */ 119 if (socketpair(PF_LOCAL, type, 0, pfd) == -1) 120 err(1, "socketpair"); 121 } else { 122 /* 123 * Create the listen socket. 124 */ 125 if ((listensock = socket(PF_LOCAL, type, 0)) == -1) 126 err(1, "socket"); 127 128 (void) unlink(SOCK_NAME); 129 (void) memset(&sun, 0, sizeof(sun)); 130 sun.sun_family = AF_LOCAL; 131 (void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path); 132 133 if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 134 err(1, "bind"); 135 136 if (listen(listensock, 1) == -1) 137 err(1, "listen"); 138 pfd[0] = pfd[1] = -1; 139 } 140 141 /* 142 * Create the sender. 143 */ 144 (void) signal(SIGCHLD, catch_sigchld); 145 pid = fork(); 146 switch (pid) { 147 case -1: 148 err(1, "fork"); 149 /* NOTREACHED */ 150 151 case 0: 152 if (pfd[0] != -1) 153 close(pfd[0]); 154 child(pfd[1], type, oflag); 155 /* NOTREACHED */ 156 } 157 158 if (pfd[0] != -1) { 159 close(pfd[1]); 160 sock = pfd[0]; 161 } else { 162 /* 163 * Wait for the sender to connect. 164 */ 165 if ((sock = accept(listensock, (struct sockaddr *)&csun, 166 &csunlen)) == -1) 167 err(1, "accept"); 168 } 169 170 /* 171 * Give sender a chance to run. We will get going again 172 * once the SIGCHLD arrives. 173 */ 174 (void) sleep(10); 175 176 if (rflag) { 177 if (read(sock, buf, sizeof(buf)) < 0) 178 err(1, "read"); 179 printf("read successfully returned\n"); 180 exit(0); 181 } 182 183 /* 184 * Grab the descriptors passed to us. 185 */ 186 memset(&msg, 0, sizeof(msg)); 187 msg.msg_control = &cmsgbuf.buf; 188 msg.msg_controllen = sizeof(cmsgbuf.buf); 189 190 if (recvmsg(sock, &msg, 0) < 0) { 191 if (errno == EMSGSIZE) { 192 printf("recvmsg returned EMSGSIZE\n"); 193 exit(0); 194 } else 195 err(1, "recvmsg"); 196 } 197 198 (void) close(sock); 199 200 if (msg.msg_controllen == 0) 201 errx(1, "no control messages received"); 202 203 if (msg.msg_flags & MSG_CTRUNC) 204 errx(1, "lost control message data"); 205 206 for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL; 207 cmp = CMSG_NXTHDR(&msg, cmp)) { 208 if (cmp->cmsg_level != SOL_SOCKET) 209 errx(1, "bad control message level %d", 210 cmp->cmsg_level); 211 212 switch (cmp->cmsg_type) { 213 case SCM_RIGHTS: 214 if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 3)) 215 errx(1, "bad fd control message length %d", 216 cmp->cmsg_len); 217 218 files = (int *)CMSG_DATA(cmp); 219 break; 220 221 default: 222 errx(1, "unexpected control message"); 223 /* NOTREACHED */ 224 } 225 } 226 227 /* 228 * Read the files and print their contents. 229 */ 230 if (files == NULL) 231 warnx("didn't get fd control message"); 232 else { 233 for (i = 0; i < 3; i++) { 234 (void) memset(buf, 0, sizeof(buf)); 235 if (read(files[i], buf, sizeof(buf)) <= 0) 236 err(1, "read file %d (%d)", i + 1, files[i]); 237 printf("%s", buf); 238 } 239 } 240 241 /* 242 * All done! 243 */ 244 exit(0); 245 } 246 247 void 248 catch_sigchld(sig) 249 int sig; 250 { 251 int save_errno = errno; 252 int status; 253 254 (void) wait(&status); 255 errno = save_errno; 256 } 257 258 void 259 child(int sock, int type, int oflag) 260 { 261 struct msghdr msg; 262 char fname[16]; 263 struct cmsghdr *cmp; 264 int i, fd, nfds = 3; 265 struct sockaddr_un sun; 266 size_t len; 267 char *cmsgbuf; 268 int *files; 269 270 /* 271 * Create socket if needed and connect to the receiver. 272 */ 273 if (sock == -1) { 274 if ((sock = socket(PF_LOCAL, type, 0)) == -1) 275 err(1, "child socket"); 276 277 (void) memset(&sun, 0, sizeof(sun)); 278 sun.sun_family = AF_LOCAL; 279 (void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path); 280 281 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 282 err(1, "child connect"); 283 } 284 285 if (oflag) 286 nfds = 5; 287 len = CMSG_SPACE(sizeof(int) * nfds); 288 if ((cmsgbuf = malloc(len)) == NULL) 289 err(1, "child"); 290 291 (void) memset(&msg, 0, sizeof(msg)); 292 msg.msg_control = cmsgbuf; 293 msg.msg_controllen = len; 294 295 cmp = CMSG_FIRSTHDR(&msg); 296 cmp->cmsg_len = CMSG_LEN((sizeof(int) * nfds)); 297 cmp->cmsg_level = SOL_SOCKET; 298 cmp->cmsg_type = SCM_RIGHTS; 299 300 /* 301 * Open the files again, and pass them to the parent over the socket. 302 */ 303 files = (int *)CMSG_DATA(cmp); 304 for (i = 0; i < nfds; i++) { 305 (void) snprintf(fname, sizeof fname, "file%d", i + 1); 306 if ((fd = open(fname, O_RDONLY, 0666)) == -1) 307 err(1, "child open %s", fname); 308 files[i] = fd; 309 } 310 311 if (sendmsg(sock, &msg, 0)) 312 err(1, "child sendmsg"); 313 314 /* 315 * All done! 316 */ 317 exit(0); 318 } 319