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