Postfix3.3.1
unix_recv_fd.c
[詳解]
1 /*++
2 /* NAME
3 /* unix_recv_fd 3
4 /* SUMMARY
5 /* receive file descriptor
6 /* SYNOPSIS
7 /* #include <iostuff.h>
8 /*
9 /* int unix_recv_fd(fd)
10 /* int fd;
11 /* DESCRIPTION
12 /* unix_recv_fd() receives a file descriptor via the specified
13 /* UNIX-domain socket. The result value is the received descriptor.
14 /*
15 /* Arguments:
16 /* .IP fd
17 /* File descriptor that connects the sending and receiving processes.
18 /* DIAGNOSTICS
19 /* unix_recv_fd() returns -1 upon failure.
20 /* LICENSE
21 /* .ad
22 /* .fi
23 /* The Secure Mailer license must be distributed with this software.
24 /* AUTHOR(S)
25 /* Wietse Venema
26 /* IBM T.J. Watson Research
27 /* P.O. Box 704
28 /* Yorktown Heights, NY 10598, USA
29 /*--*/
30 
31 /* System library. */
32 
33 #include <sys_defs.h> /* includes <sys/types.h> */
34 #include <sys/socket.h>
35 #include <sys/uio.h>
36 #include <string.h>
37 
38 /* Utility library. */
39 
40 #include <msg.h>
41 #include <iostuff.h>
42 
43 /* unix_recv_fd - receive file descriptor */
44 
45 int unix_recv_fd(int fd)
46 {
47  const char *myname = "unix_recv_fd";
48 
49  /*
50  * This code does not work with version <2.2 Linux kernels, and it does
51  * not compile with version <2 Linux libraries.
52  */
53 #ifdef CANT_USE_SEND_RECV_MSG
54  msg_warn("%s: your system has no support for file descriptor passing",
55  myname);
56  return (-1);
57 #else
58  struct msghdr msg;
59  int newfd;
60  struct iovec iov[1];
61  char buf[1];
62 
63  /*
64  * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1,
65  * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for
66  * portability to some LP64 environments. See also unix_send_fd.c.
67  */
68 #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
69  union {
70  struct cmsghdr just_for_alignment;
71  char control[CMSG_SPACE(sizeof(newfd))];
72  } control_un;
73  struct cmsghdr *cmptr;
74 
75  memset((void *) &msg, 0, sizeof(msg)); /* Fix 200512 */
76  msg.msg_control = control_un.control;
78  msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */
79  } else {
80  msg.msg_controllen = sizeof(control_un.control); /* normal */
81  }
82 #else
83  msg.msg_accrights = (char *) &newfd;
84  msg.msg_accrightslen = sizeof(newfd);
85 #endif
86 
87  msg.msg_name = 0;
88  msg.msg_namelen = 0;
89 
90  /*
91  * XXX We don't want to pass any data, just a file descriptor. However,
92  * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need
93  * to read_wait() before we can receive the descriptor, and the code
94  * fails after the first descriptor when we attempt to receive a sequence
95  * of descriptors.
96  */
97  iov->iov_base = buf;
98  iov->iov_len = sizeof(buf);
99  msg.msg_iov = iov;
100  msg.msg_iovlen = 1;
101 
102  if (recvmsg(fd, &msg, 0) < 0)
103  return (-1);
104 
105 #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL)
106  if ((cmptr = CMSG_FIRSTHDR(&msg)) != 0
107  && cmptr->cmsg_len == CMSG_LEN(sizeof(newfd))) {
108  if (cmptr->cmsg_level != SOL_SOCKET)
109  msg_fatal("%s: control level %d != SOL_SOCKET",
110  myname, cmptr->cmsg_level);
111  if (cmptr->cmsg_type != SCM_RIGHTS)
112  msg_fatal("%s: control type %d != SCM_RIGHTS",
113  myname, cmptr->cmsg_type);
114  return (*(int *) CMSG_DATA(cmptr));
115  } else
116  return (-1);
117 #else
118  if (msg.msg_accrightslen == sizeof(newfd))
119  return (newfd);
120  else
121  return (-1);
122 #endif
123 #endif
124 }
125 
126 #ifdef TEST
127 
128  /*
129  * Proof-of-concept program. Receive a descriptor (presumably from the
130  * unix_send_fd test program) and copy its content until EOF.
131  */
132 #include <unistd.h>
133 #include <string.h>
134 #include <stdlib.h>
135 #include <split_at.h>
136 #include <listen.h>
137 
138 int main(int argc, char **argv)
139 {
140  char *transport;
141  char *endpoint;
142  int listen_sock;
143  int client_sock;
144  int client_fd;
145  ssize_t read_count;
146  char buf[1024];
147 
148  if (argc < 2 || argc > 3
149  || (endpoint = split_at(transport = argv[1], ':')) == 0
150  || *endpoint == 0 || *transport == 0)
151  msg_fatal("usage: %s transport:endpoint [workaround]", argv[0]);
152 
153  if (strcmp(transport, "unix") == 0) {
154  listen_sock = unix_listen(endpoint, 10, BLOCKING);
155  } else {
156  msg_fatal("invalid transport name: %s", transport);
157  }
158  if (listen_sock < 0)
159  msg_fatal("listen %s:%s: %m", transport, endpoint);
160 
161  client_sock = accept(listen_sock, (struct sockaddr *) 0, (SOCKADDR_SIZE) 0);
162  if (client_sock < 0)
163  msg_fatal("accept: %m");
164 
165  set_unix_pass_fd_fix(argv[2] ? argv[2] : "");
166 
167  while ((client_fd = unix_recv_fd(client_sock)) >= 0) {
168  msg_info("client_fd = %d, fix=%d", client_fd, unix_pass_fd_fix);
169  while ((read_count = read(client_fd, buf, sizeof(buf))) > 0)
170  write(1, buf, read_count);
171  if (read_count < 0)
172  msg_fatal("read: %m");
173  close(client_fd);
174  }
175  exit(0);
176 }
177 
178 #endif
void set_unix_pass_fd_fix(const char *)
int main(int argc, char **argv)
Definition: anvil.c:1010
int unix_pass_fd_fix
#define SOCKADDR_SIZE
Definition: sys_defs.h:1411
void msg_warn(const char *fmt,...)
Definition: msg.c:215
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int unix_listen(const char *, int, int)
Definition: unix_listen.c:66
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
int unix_recv_fd(int fd)
Definition: unix_recv_fd.c:45
#define UNIX_PASS_FD_FIX_CMSG_LEN
Definition: iostuff.h:58
#define BLOCKING
Definition: iostuff.h:48
void msg_info(const char *fmt,...)
Definition: msg.c:199