1/************************************************
2
3  unixsocket.c -
4
5  created at: Thu Mar 31 12:21:29 JST 1994
6
7  Copyright (C) 1993-2007 Yukihiro Matsumoto
8
9************************************************/
10
11#include "rubysocket.h"
12
13#ifdef HAVE_SYS_UN_H
14struct unixsock_arg {
15    struct sockaddr_un *sockaddr;
16    socklen_t sockaddrlen;
17    int fd;
18};
19
20static VALUE
21unixsock_connect_internal(VALUE a)
22{
23    struct unixsock_arg *arg = (struct unixsock_arg *)a;
24    return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr,
25			        arg->sockaddrlen, 0);
26}
27
28VALUE
29rsock_init_unixsock(VALUE sock, VALUE path, int server)
30{
31    struct sockaddr_un sockaddr;
32    socklen_t sockaddrlen;
33    int fd, status;
34    rb_io_t *fptr;
35
36    SafeStringValue(path);
37    fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0);
38    if (fd < 0) {
39	rb_sys_fail("socket(2)");
40    }
41
42    MEMZERO(&sockaddr, struct sockaddr_un, 1);
43    sockaddr.sun_family = AF_UNIX;
44    if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
45        rb_raise(rb_eArgError, "too long unix socket path (%ldbytes given but %dbytes max)",
46            RSTRING_LEN(path), (int)sizeof(sockaddr.sun_path));
47    }
48    memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
49    sockaddrlen = rsock_unix_sockaddr_len(path);
50
51    if (server) {
52        status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen);
53    }
54    else {
55	int prot;
56	struct unixsock_arg arg;
57	arg.sockaddr = &sockaddr;
58	arg.sockaddrlen = sockaddrlen;
59	arg.fd = fd;
60        status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot);
61	if (prot) {
62	    close(fd);
63	    rb_jump_tag(prot);
64	}
65    }
66
67    if (status < 0) {
68	close(fd);
69	rb_sys_fail_str(rb_inspect(path));
70    }
71
72    if (server) {
73	if (listen(fd, SOMAXCONN) < 0) {
74	    close(fd);
75	    rb_sys_fail("listen(2)");
76	}
77    }
78
79    rsock_init_sock(sock, fd);
80    if (server) {
81	GetOpenFile(sock, fptr);
82        fptr->pathv = rb_str_new_frozen(path);
83    }
84
85    return sock;
86}
87
88/*
89 * call-seq:
90 *   UNIXSocket.new(path) => unixsocket
91 *
92 * Creates a new UNIX client socket connected to _path_.
93 *
94 *   s = UNIXSocket.new("/tmp/sock")
95 *   s.send "hello", 0
96 *
97 */
98static VALUE
99unix_init(VALUE sock, VALUE path)
100{
101    return rsock_init_unixsock(sock, path, 0);
102}
103
104/*
105 * call-seq:
106 *   unixsocket.path => path
107 *
108 * Returns the path of the local address of unixsocket.
109 *
110 *   s = UNIXServer.new("/tmp/sock")
111 *   p s.path #=> "/tmp/sock"
112 *
113 */
114static VALUE
115unix_path(VALUE sock)
116{
117    rb_io_t *fptr;
118
119    GetOpenFile(sock, fptr);
120    if (NIL_P(fptr->pathv)) {
121	struct sockaddr_un addr;
122	socklen_t len = (socklen_t)sizeof(addr);
123	socklen_t len0 = len;
124	if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
125	    rb_sys_fail(0);
126        if (len0 < len) len = len0;
127	fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len));
128    }
129    return rb_str_dup(fptr->pathv);
130}
131
132/*
133 * call-seq:
134 *   unixsocket.recvfrom(maxlen [, flags]) => [mesg, unixaddress]
135 *
136 * Receives a message via _unixsocket_.
137 *
138 * _maxlen_ is the maximum number of bytes to receive.
139 *
140 * _flags_ should be a bitwise OR of Socket::MSG_* constants.
141 *
142 *   s1 = Socket.new(:UNIX, :DGRAM, 0)
143 *   s1_ai = Addrinfo.unix("/tmp/sock1")
144 *   s1.bind(s1_ai)
145 *
146 *   s2 = Socket.new(:UNIX, :DGRAM, 0)
147 *   s2_ai = Addrinfo.unix("/tmp/sock2")
148 *   s2.bind(s2_ai)
149 *   s3 = UNIXSocket.for_fd(s2.fileno)
150 *
151 *   s1.send "a", 0, s2_ai
152 *   p s3.recvfrom(10) #=> ["a", ["AF_UNIX", "/tmp/sock1"]]
153 *
154 */
155static VALUE
156unix_recvfrom(int argc, VALUE *argv, VALUE sock)
157{
158    return rsock_s_recvfrom(sock, argc, argv, RECV_UNIX);
159}
160
161#if defined(HAVE_ST_MSG_CONTROL) && defined(SCM_RIGHTS)
162#define FD_PASSING_BY_MSG_CONTROL 1
163#else
164#define FD_PASSING_BY_MSG_CONTROL 0
165#endif
166
167#if defined(HAVE_ST_MSG_ACCRIGHTS)
168#define FD_PASSING_BY_MSG_ACCRIGHTS 1
169#else
170#define FD_PASSING_BY_MSG_ACCRIGHTS 0
171#endif
172
173struct iomsg_arg {
174    int fd;
175    struct msghdr msg;
176};
177
178#if defined(HAVE_SENDMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS)
179static VALUE
180sendmsg_blocking(void *data)
181{
182    struct iomsg_arg *arg = data;
183    return sendmsg(arg->fd, &arg->msg, 0);
184}
185
186/*
187 * call-seq:
188 *   unixsocket.send_io(io) => nil
189 *
190 * Sends _io_ as file descriptor passing.
191 *
192 *   s1, s2 = UNIXSocket.pair
193 *
194 *   s1.send_io STDOUT
195 *   stdout = s2.recv_io
196 *
197 *   p STDOUT.fileno #=> 1
198 *   p stdout.fileno #=> 6
199 *
200 *   stdout.puts "hello" # outputs "hello\n" to standard output.
201 */
202static VALUE
203unix_send_io(VALUE sock, VALUE val)
204{
205    int fd;
206    rb_io_t *fptr;
207    struct iomsg_arg arg;
208    struct iovec vec[1];
209    char buf[1];
210
211#if FD_PASSING_BY_MSG_CONTROL
212    struct {
213	struct cmsghdr hdr;
214	char pad[8+sizeof(int)+8];
215    } cmsg;
216#endif
217
218    if (rb_obj_is_kind_of(val, rb_cIO)) {
219        rb_io_t *valfptr;
220	GetOpenFile(val, valfptr);
221	fd = valfptr->fd;
222    }
223    else if (FIXNUM_P(val)) {
224        fd = FIX2INT(val);
225    }
226    else {
227	rb_raise(rb_eTypeError, "neither IO nor file descriptor");
228    }
229
230    GetOpenFile(sock, fptr);
231
232    arg.msg.msg_name = NULL;
233    arg.msg.msg_namelen = 0;
234
235    /* Linux and Solaris doesn't work if msg_iov is NULL. */
236    buf[0] = '\0';
237    vec[0].iov_base = buf;
238    vec[0].iov_len = 1;
239    arg.msg.msg_iov = vec;
240    arg.msg.msg_iovlen = 1;
241
242#if FD_PASSING_BY_MSG_CONTROL
243    arg.msg.msg_control = (caddr_t)&cmsg;
244    arg.msg.msg_controllen = (socklen_t)CMSG_LEN(sizeof(int));
245    arg.msg.msg_flags = 0;
246    MEMZERO((char*)&cmsg, char, sizeof(cmsg));
247    cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int));
248    cmsg.hdr.cmsg_level = SOL_SOCKET;
249    cmsg.hdr.cmsg_type = SCM_RIGHTS;
250    memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int));
251#else
252    arg.msg.msg_accrights = (caddr_t)&fd;
253    arg.msg.msg_accrightslen = sizeof(fd);
254#endif
255
256    arg.fd = fptr->fd;
257    while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) {
258	if (!rb_io_wait_writable(arg.fd))
259	    rb_sys_fail("sendmsg(2)");
260    }
261
262    return Qnil;
263}
264#else
265#define unix_send_io rb_f_notimplement
266#endif
267
268#if defined(HAVE_RECVMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS)
269static VALUE
270recvmsg_blocking(void *data)
271{
272    struct iomsg_arg *arg = data;
273    int flags = 0;
274    return rsock_recvmsg(arg->fd, &arg->msg, flags);
275}
276
277/*
278 * call-seq:
279 *   unixsocket.recv_io([klass [, mode]]) => io
280 *
281 *   UNIXServer.open("/tmp/sock") {|serv|
282 *     UNIXSocket.open("/tmp/sock") {|c|
283 *       s = serv.accept
284 *
285 *       c.send_io STDOUT
286 *       stdout = s.recv_io
287 *
288 *       p STDOUT.fileno #=> 1
289 *       p stdout.fileno #=> 7
290 *
291 *       stdout.puts "hello" # outputs "hello\n" to standard output.
292 *     }
293 *   }
294 *
295 */
296static VALUE
297unix_recv_io(int argc, VALUE *argv, VALUE sock)
298{
299    VALUE klass, mode;
300    rb_io_t *fptr;
301    struct iomsg_arg arg;
302    struct iovec vec[2];
303    char buf[1];
304
305    int fd;
306#if FD_PASSING_BY_MSG_CONTROL
307    struct {
308	struct cmsghdr hdr;
309	char pad[8+sizeof(int)+8];
310    } cmsg;
311#endif
312
313    rb_scan_args(argc, argv, "02", &klass, &mode);
314    if (argc == 0)
315	klass = rb_cIO;
316    if (argc <= 1)
317	mode = Qnil;
318
319    GetOpenFile(sock, fptr);
320
321    arg.msg.msg_name = NULL;
322    arg.msg.msg_namelen = 0;
323
324    vec[0].iov_base = buf;
325    vec[0].iov_len = sizeof(buf);
326    arg.msg.msg_iov = vec;
327    arg.msg.msg_iovlen = 1;
328
329#if FD_PASSING_BY_MSG_CONTROL
330    arg.msg.msg_control = (caddr_t)&cmsg;
331    arg.msg.msg_controllen = (socklen_t)CMSG_SPACE(sizeof(int));
332    arg.msg.msg_flags = 0;
333    cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int));
334    cmsg.hdr.cmsg_level = SOL_SOCKET;
335    cmsg.hdr.cmsg_type = SCM_RIGHTS;
336    fd = -1;
337    memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int));
338#else
339    arg.msg.msg_accrights = (caddr_t)&fd;
340    arg.msg.msg_accrightslen = sizeof(fd);
341    fd = -1;
342#endif
343
344    arg.fd = fptr->fd;
345    while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) {
346	if (!rb_io_wait_readable(arg.fd))
347	    rb_sys_fail("recvmsg(2)");
348    }
349
350#if FD_PASSING_BY_MSG_CONTROL
351    if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) {
352	rb_raise(rb_eSocket,
353		 "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)",
354		 (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr));
355    }
356    if (cmsg.hdr.cmsg_level != SOL_SOCKET) {
357	rb_raise(rb_eSocket,
358		 "file descriptor was not passed (cmsg_level=%d, %d expected)",
359		 cmsg.hdr.cmsg_level, SOL_SOCKET);
360    }
361    if (cmsg.hdr.cmsg_type != SCM_RIGHTS) {
362	rb_raise(rb_eSocket,
363		 "file descriptor was not passed (cmsg_type=%d, %d expected)",
364		 cmsg.hdr.cmsg_type, SCM_RIGHTS);
365    }
366    if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) {
367	rb_raise(rb_eSocket,
368		 "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)",
369		 (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int)));
370    }
371    if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) {
372	rb_raise(rb_eSocket,
373		 "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)",
374		 (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int)));
375    }
376    if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) {
377	rsock_discard_cmsg_resource(&arg.msg, 0);
378	rb_raise(rb_eSocket,
379		 "file descriptor was not passed (cmsg_len=%d, %d expected)",
380		 (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int)));
381    }
382#else
383    if (arg.msg.msg_accrightslen != sizeof(fd)) {
384	rb_raise(rb_eSocket,
385		 "file descriptor was not passed (accrightslen) : %d != %d",
386		 arg.msg.msg_accrightslen, (int)sizeof(fd));
387    }
388#endif
389
390#if FD_PASSING_BY_MSG_CONTROL
391    memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int));
392#endif
393    rb_fd_fix_cloexec(fd);
394
395    if (klass == Qnil)
396	return INT2FIX(fd);
397    else {
398	ID for_fd;
399	int ff_argc;
400	VALUE ff_argv[2];
401	CONST_ID(for_fd, "for_fd");
402	ff_argc = mode == Qnil ? 1 : 2;
403	ff_argv[0] = INT2FIX(fd);
404	ff_argv[1] = mode;
405        return rb_funcall2(klass, for_fd, ff_argc, ff_argv);
406    }
407}
408#else
409#define unix_recv_io rb_f_notimplement
410#endif
411
412/*
413 * call-seq:
414 *   unixsocket.addr => [address_family, unix_path]
415 *
416 * Returns the local address as an array which contains
417 * address_family and unix_path.
418 *
419 * Example
420 *   serv = UNIXServer.new("/tmp/sock")
421 *   p serv.addr #=> ["AF_UNIX", "/tmp/sock"]
422 */
423static VALUE
424unix_addr(VALUE sock)
425{
426    rb_io_t *fptr;
427    struct sockaddr_un addr;
428    socklen_t len = (socklen_t)sizeof addr;
429    socklen_t len0 = len;
430
431    GetOpenFile(sock, fptr);
432
433    if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
434	rb_sys_fail("getsockname(2)");
435    if (len0 < len) len = len0;
436    return rsock_unixaddr(&addr, len);
437}
438
439/*
440 * call-seq:
441 *   unixsocket.peeraddr => [address_family, unix_path]
442 *
443 * Returns the remote address as an array which contains
444 * address_family and unix_path.
445 *
446 * Example
447 *   serv = UNIXServer.new("/tmp/sock")
448 *   c = UNIXSocket.new("/tmp/sock")
449 *   p c.peeraddr #=> ["AF_UNIX", "/tmp/sock"]
450 */
451static VALUE
452unix_peeraddr(VALUE sock)
453{
454    rb_io_t *fptr;
455    struct sockaddr_un addr;
456    socklen_t len = (socklen_t)sizeof addr;
457    socklen_t len0 = len;
458
459    GetOpenFile(sock, fptr);
460
461    if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
462	rb_sys_fail("getpeername(2)");
463    if (len0 < len) len = len0;
464    return rsock_unixaddr(&addr, len);
465}
466
467/*
468 * call-seq:
469 *   UNIXSocket.pair([type [, protocol]])       => [unixsocket1, unixsocket2]
470 *   UNIXSocket.socketpair([type [, protocol]]) => [unixsocket1, unixsocket2]
471 *
472 * Creates a pair of sockets connected each other.
473 *
474 * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc.
475 *
476 * _protocol_ should be a protocol defined in the domain.
477 * 0 is default protocol for the domain.
478 *
479 *   s1, s2 = UNIXSocket.pair
480 *   s1.send "a", 0
481 *   s1.send "b", 0
482 *   p s2.recv(10) #=> "ab"
483 *
484 */
485static VALUE
486unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
487{
488    VALUE domain, type, protocol;
489    VALUE args[3];
490
491    domain = INT2FIX(PF_UNIX);
492    rb_scan_args(argc, argv, "02", &type, &protocol);
493    if (argc == 0)
494	type = INT2FIX(SOCK_STREAM);
495    if (argc <= 1)
496	protocol = INT2FIX(0);
497
498    args[0] = domain;
499    args[1] = type;
500    args[2] = protocol;
501
502    return rsock_sock_s_socketpair(3, args, klass);
503}
504#endif
505
506void
507rsock_init_unixsocket(void)
508{
509#ifdef HAVE_SYS_UN_H
510    /*
511     * Document-class: UNIXSocket < BasicSocket
512     *
513     * UNIXSocket represents a UNIX domain stream client socket.
514     */
515    rb_cUNIXSocket = rb_define_class("UNIXSocket", rb_cBasicSocket);
516    rb_define_method(rb_cUNIXSocket, "initialize", unix_init, 1);
517    rb_define_method(rb_cUNIXSocket, "path", unix_path, 0);
518    rb_define_method(rb_cUNIXSocket, "addr", unix_addr, 0);
519    rb_define_method(rb_cUNIXSocket, "peeraddr", unix_peeraddr, 0);
520    rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1);
521    rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1);
522    rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1);
523    rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1);
524    rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1);
525#endif
526}
527