Merge branch 'pu'
[jungerl.git] / lib / fd_server / c_src / fdlib.c
blob4c67b84c798bc22a7ba72a87f42588b342ba1f04
1 /*
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.
9 * Modified:
13 #include <config.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <ctype.h>
18 #include <string.h>
19 #include <strings.h>
20 #include <unistd.h>
21 #include <netdb.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/uio.h>
26 #include <sys/un.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/param.h>
31 #include "erl_driver.h"
32 #ifndef ERL_DRV_NIL
33 #include "erl_driver_compat.h"
34 #endif
36 #include "fdlib.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)
45 int ret ,fd;
46 char buf[4];
48 if ((fd = fd_listen_path(port, path)) == -1)
49 return -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';
56 write(1, buf, 3);
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.
64 close(fd);
65 unlink(path);
66 return ret;
71 int fd_listen_path(ErlDrvPort port, char* path)
74 int s, len;
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);
84 return -1;
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);
91 return -1;
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)
102 perror("chown");
104 listen(s, 1);
107 return s;
110 int fd_accept_path(ErlDrvPort port, int s, char* path)
112 int c;
113 unsigned len = 0;
115 /* Need to make this non blocking .. */
117 if ((c = accept(s, (struct sockaddr *)NULL, &len)) < 0) {
118 reply_err_errno(port);
119 return -1;
121 return c;
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. */
134 #ifndef CMSG_LEN
135 #define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
136 #endif
137 #ifndef CMSG_SPACE
138 #define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
139 #endif
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)
146 #endif
147 #ifndef CMSG_DATA
148 #define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
149 #endif
150 #endif
152 int send_fd(fd, sock_fd)
153 int *fd;
154 int sock_fd;
156 struct msghdr msg;
157 struct iovec iov[1];
158 #ifdef HAVE_MSGHDR_MSG_CONTROL
159 union {
160 struct cmsghdr cm;
161 char control[CMSG_SPACE(sizeof(int))];
162 } control_un;
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;
173 #else
174 msg.msg_accrights = (caddr_t) fd;
175 msg.msg_accrightslen = sizeof(*fd);
176 #endif
178 iov[0].iov_base = ""; /* send one byte */
179 iov[0].iov_len = 1;
180 msg.msg_iov = iov;
181 msg.msg_iovlen = 1;
182 msg.msg_name = NULL;
183 msg.msg_namelen = 0;
185 if (sendmsg(sock_fd, &msg, 0) < 0) {
186 perror("sendmsg");
187 return -1;
189 return 1;
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)
218 int family;
220 family = AF_INET; /* default unless specified */
221 if (strncmp("inet6:", str, strlen("inet6:")) == 0) {
222 #ifdef HAVE_GETADDRINFO
223 family = AF_INET6;
224 str += strlen("inet6:");
225 #else
226 return -1;
227 #endif /* HAVE_GETADDRINFO */
228 } else if (strncmp("inet:", str, strlen("inet:")) == 0) {
229 family = AF_INET;
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)
240 *portstr++ = '\0';
241 else {
242 portstr = str;
243 str = "";
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) {
254 return -1;
255 } else {
256 memmove(addr, res->ai_addr, res->ai_addrlen);
257 freeaddrinfo(res);
258 return 0;
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)
268 int port = 0;
269 char *cp;
270 struct hostent *hp;
271 struct servent *se;
273 if ((cp = strrchr(str, (int)':')) != NULL)
274 *cp++ = '\0';
275 if (cp) {
276 if (!isdigit((int)cp[0])) {
277 if ((se = getservbyname(cp, "tcp")) != NULL) {
278 port = ntohs(se->s_port);
279 } else {
280 /* fprintf(stderr, "unknown port %s\n", cp); */
281 return -1;
283 } else {
284 port = atoi(cp);
287 if (port < 0 || port > 0xffff) {
288 /* fprintf(stderr, "bad port number %d\n", port); */
289 return -1;
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;
297 } else {
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);*/
301 return -1;
302 } else {
303 bcopy(hp->h_addr_list[0], &addr->sin_addr.s_addr,hp->h_length);
307 return 0;
311 #ifdef DYNAMIC_DRIVER
312 void reply_int(ErlDrvPort port, int i)
314 char buf[5];
315 buf[0] = 1;
316 put_int32(i, buf+1);
317 driver_output(port, buf, 5);
320 void reply_ok(ErlDrvPort port)
322 char c = 1;
323 driver_output(port, &c, 1);
326 void reply_err(ErlDrvPort port)
328 char c = 0;
329 driver_output(port, &c, 1);
332 void reply_err_string(ErlDrvPort port, char* s)
334 char buf[BUFSIZ];
335 buf[0] = 0;
336 strncpy(buf+1, s, BUFSIZ-1);
338 driver_output(port, buf, strlen(buf+1) + 1);
341 void reply_err_errno(ErlDrvPort port)
343 char buf[BUFSIZ];
344 buf[0] = 0;
345 strncpy(buf+1, strerror(errno), BUFSIZ-1);
346 driver_output(port, buf, strlen(buf+1) + 1);
348 #else
349 void reply_err_errno(ErlDrvPort port)
351 perror("");
354 #endif