2 * Created: Sebastian Strollo <seb@erix.ericsson.se>, 1998-04-03
3 * Purpose: Program that passes open file descriptors between
4 * processes using AF_UNIX stream sockets, as described in
5 * Stevens UNIX Network programming. This is a server program
6 * which can be run as setuid root to give it access to
7 * priviliged ports. The program is meant to be run from
8 * erlang with 1 byte length packets.
22 #include <sys/types.h>
23 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/param.h>
31 #include "erl_driver.h"
33 #include "erl_driver_compat.h"
40 * Create a named pipe with name "path". Accept connection on it and then
41 * close it and unlink "path".
43 int fd_establish_communication(ErlDrvPort port
, char* path
)
48 if ((fd
= fd_listen_path(port
, path
)) == -1)
51 /* We need to synchronize the creation of the named pipe - so that the
52 * client does not try to connect to it until it is created.
53 * This is used as synchronization.
55 buf
[0] = 2; buf
[1] = 'o'; buf
[2] = 'k';
58 ret
= fd_accept_path(port
, fd
, path
);
61 /* Communication established, no need to listen to the pipe anymore,
62 * or to have it lying around in the filesystem.
71 int fd_listen_path(ErlDrvPort port
, char* path
)
75 struct sockaddr_un addr
;
77 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
78 bzero((char *)&addr
, sizeof(addr
));
79 addr
.sun_family
= AF_UNIX
;
81 /* make sure we don't overrun */
82 if (strlen(path
) > (sizeof(addr
.sun_path
) + 1)) {
83 fprintf(stderr
, "path too long: %s\n", path
);
86 strcpy(addr
.sun_path
, path
);
87 len
= strlen(addr
.sun_path
) + sizeof(addr
.sun_family
) + 1;
89 if (bind(s
, (struct sockaddr
*)&addr
, len
) < 0) {
90 reply_err_errno(port
);
94 /* Limit access to the pipe */
95 chmod(path
, S_IRUSR
|S_IWUSR
);
98 /* An OS who shall remain unnamed creates files with owner set to euid
99 * instead of uid. Undo the damage.
101 if (chown(path
, getuid(), getgid()) < 0)
110 int fd_accept_path(ErlDrvPort port
, int s
, char* path
)
115 /* Need to make this non blocking .. */
117 if ((c
= accept(s
, (struct sockaddr
*)NULL
, &len
)) < 0) {
118 reply_err_errno(port
);
126 * Function to send a file descriptor, fd, over a stream socket, sock_fd.
127 * This function is more or less directly from Stevens.
130 #ifdef HAVE_MSGHDR_MSG_CONTROL
131 /* We need the newer CMSG_LEN() and CMSG_SPACE() macros, but few
132 implementations support them today. These two macros really need
133 an ALIGN() macro, but each implementation does this differently. */
135 #define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
138 #define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
141 /* These two macros are really needed as well */
142 #ifndef CMSG_FIRSTHDR
143 #define CMSG_FIRSTHDR(mhdr) \
144 ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr) \
145 ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) NULL)
148 #define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
152 int send_fd(fd
, sock_fd
)
158 #ifdef HAVE_MSGHDR_MSG_CONTROL
161 char control
[CMSG_SPACE(sizeof(int))];
163 struct cmsghdr
*cmptr
;
165 msg
.msg_control
= control_un
.control
;
166 msg
.msg_controllen
= sizeof(control_un
.control
);
168 cmptr
= CMSG_FIRSTHDR(&msg
);
169 cmptr
->cmsg_len
= CMSG_LEN(sizeof(int));
170 cmptr
->cmsg_level
= SOL_SOCKET
;
171 cmptr
->cmsg_type
= SCM_RIGHTS
;
172 *((int *) CMSG_DATA(cmptr
)) = *fd
;
174 msg
.msg_accrights
= (caddr_t
) fd
;
175 msg
.msg_accrightslen
= sizeof(*fd
);
178 iov
[0].iov_base
= ""; /* send one byte */
185 if (sendmsg(sock_fd
, &msg
, 0) < 0) {
193 * Parse an address specification and fill in a sockaddr_in accordingly.
194 * The address should be on the form:
195 * [{inet|inet6}:][a.d.re.ss|hostname:]{portnumber|servicename}
197 * The following table summarizes the interpretation:
199 * str family addr port comment
200 * -----------------------------------------------------------
201 * "123" AF_INET 0.0.0.0 123
202 * ":123" AF_INET 0.0.0.0 123
203 * "inet:123" AF_INET 0.0.0.0 123
204 * "inet::123" AF_INET 0.0.0.0 123
205 * "inet6:123" AF_INET6 0.0.0.0 123
206 * "inet6::123" AF_INET6 0.0.0.0 123
207 * "0.0.0.0:123" AF_INET 0.0.0.0 123
208 * "localhost:123" AF_INET 127.0.0.1 123
209 * "inet6::::123" AF_INET6 :: 123 ipv6 any
210 * "inet6:::1:123" AF_INET6 ::1 123 ipv6 loopback
211 * "inet:0.0.0.0:123" AF_INET 0.0.0.0 123
212 * "inet:localhost:123" AF_INET 127.0.0.1 123
214 * A service name can be specified instead of a port number.
216 int fd_parse_addr(struct sockaddr_storage
*addr
, char *str
)
220 family
= AF_INET
; /* default unless specified */
221 if (strncmp("inet6:", str
, strlen("inet6:")) == 0) {
222 #ifdef HAVE_GETADDRINFO
224 str
+= strlen("inet6:");
227 #endif /* HAVE_GETADDRINFO */
228 } else if (strncmp("inet:", str
, strlen("inet:")) == 0) {
230 str
+= strlen("inet:");
233 #ifdef HAVE_GETADDRINFO
235 struct addrinfo hints
;
236 struct addrinfo
*res
;
237 char *bindipstr
, *portstr
;
239 if ((portstr
= strrchr(str
, (int)':')) != NULL
)
246 memset(&hints
, 0, sizeof(struct addrinfo
));
247 hints
.ai_flags
= AI_ADDRCONFIG
| AI_PASSIVE
;
248 hints
.ai_family
= family
;
249 hints
.ai_socktype
= 0;
250 hints
.ai_protocol
= 0;
252 bindipstr
= *str
== '\0'? NULL
: str
;
253 if (getaddrinfo(bindipstr
, portstr
, &hints
, &res
) != 0) {
256 memmove(addr
, res
->ai_addr
, res
->ai_addrlen
);
261 #else /* HAVE_GETADDRINFO */
262 return fd_parse_addr_ipv4(addr
, str
);
263 #endif /* HAVE_GETADDRINFO */
266 int fd_parse_addr_ipv4(struct sockaddr_in
*addr
, char *str
)
273 if ((cp
= strrchr(str
, (int)':')) != NULL
)
276 if (!isdigit((int)cp
[0])) {
277 if ((se
= getservbyname(cp
, "tcp")) != NULL
) {
278 port
= ntohs(se
->s_port
);
280 /* fprintf(stderr, "unknown port %s\n", cp); */
287 if (port
< 0 || port
> 0xffff) {
288 /* fprintf(stderr, "bad port number %d\n", port); */
292 bzero(addr
, sizeof(*addr
));
293 addr
->sin_family
= AF_INET
;
294 addr
->sin_port
= htons(port
);
295 if (*str
== '\000') {
296 addr
->sin_addr
.s_addr
= INADDR_ANY
;
298 if ((addr
->sin_addr
.s_addr
= inet_addr(str
)) == INADDR_NONE
) {
299 if ((hp
= gethostbyname(str
)) == NULL
) {
300 /*fprintf(stderr, "\"%s\" unknown host or address!\n", str);*/
303 bcopy(hp
->h_addr_list
[0], &addr
->sin_addr
.s_addr
,hp
->h_length
);
311 #ifdef DYNAMIC_DRIVER
312 void reply_int(ErlDrvPort port
, int i
)
317 driver_output(port
, buf
, 5);
320 void reply_ok(ErlDrvPort port
)
323 driver_output(port
, &c
, 1);
326 void reply_err(ErlDrvPort port
)
329 driver_output(port
, &c
, 1);
332 void reply_err_string(ErlDrvPort port
, char* s
)
336 strncpy(buf
+1, s
, BUFSIZ
-1);
338 driver_output(port
, buf
, strlen(buf
+1) + 1);
341 void reply_err_errno(ErlDrvPort port
)
345 strncpy(buf
+1, strerror(errno
), BUFSIZ
-1);
346 driver_output(port
, buf
, strlen(buf
+1) + 1);
349 void reply_err_errno(ErlDrvPort port
)