ext/socket/unixsocket.c

Go to the documentation of this file.
00001 /************************************************
00002 
00003   unixsocket.c -
00004 
00005   created at: Thu Mar 31 12:21:29 JST 1994
00006 
00007   Copyright (C) 1993-2007 Yukihiro Matsumoto
00008 
00009 ************************************************/
00010 
00011 #include "rubysocket.h"
00012 
00013 #ifdef HAVE_SYS_UN_H
00014 struct unixsock_arg {
00015     struct sockaddr_un *sockaddr;
00016     socklen_t sockaddrlen;
00017     int fd;
00018 };
00019 
00020 static VALUE
00021 unixsock_connect_internal(VALUE a)
00022 {
00023     struct unixsock_arg *arg = (struct unixsock_arg *)a;
00024     return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr,
00025                                 arg->sockaddrlen, 0);
00026 }
00027 
00028 VALUE
00029 rsock_init_unixsock(VALUE sock, VALUE path, int server)
00030 {
00031     struct sockaddr_un sockaddr;
00032     socklen_t sockaddrlen;
00033     int fd, status;
00034     rb_io_t *fptr;
00035 
00036     SafeStringValue(path);
00037     fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0);
00038     if (fd < 0) {
00039         rsock_sys_fail_path("socket(2)", path);
00040     }
00041 
00042     INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
00043     if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
00044         rb_raise(rb_eArgError, "too long unix socket path (%ldbytes given but %dbytes max)",
00045             RSTRING_LEN(path), (int)sizeof(sockaddr.sun_path));
00046     }
00047     memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
00048     sockaddrlen = rsock_unix_sockaddr_len(path);
00049 
00050     if (server) {
00051         status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen);
00052     }
00053     else {
00054         int prot;
00055         struct unixsock_arg arg;
00056         arg.sockaddr = &sockaddr;
00057         arg.sockaddrlen = sockaddrlen;
00058         arg.fd = fd;
00059         status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot);
00060         if (prot) {
00061             close(fd);
00062             rb_jump_tag(prot);
00063         }
00064     }
00065 
00066     if (status < 0) {
00067         close(fd);
00068         rsock_sys_fail_path("connect(2)", path);
00069     }
00070 
00071     if (server) {
00072         if (listen(fd, SOMAXCONN) < 0) {
00073             close(fd);
00074             rsock_sys_fail_path("listen(2)", path);
00075         }
00076     }
00077 
00078     rsock_init_sock(sock, fd);
00079     if (server) {
00080         GetOpenFile(sock, fptr);
00081         fptr->pathv = rb_str_new_frozen(path);
00082     }
00083 
00084     return sock;
00085 }
00086 
00087 /*
00088  * call-seq:
00089  *   UNIXSocket.new(path) => unixsocket
00090  *
00091  * Creates a new UNIX client socket connected to _path_.
00092  *
00093  *   s = UNIXSocket.new("/tmp/sock")
00094  *   s.send "hello", 0
00095  *
00096  */
00097 static VALUE
00098 unix_init(VALUE sock, VALUE path)
00099 {
00100     return rsock_init_unixsock(sock, path, 0);
00101 }
00102 
00103 /*
00104  * call-seq:
00105  *   unixsocket.path => path
00106  *
00107  * Returns the path of the local address of unixsocket.
00108  *
00109  *   s = UNIXServer.new("/tmp/sock")
00110  *   p s.path #=> "/tmp/sock"
00111  *
00112  */
00113 static VALUE
00114 unix_path(VALUE sock)
00115 {
00116     rb_io_t *fptr;
00117 
00118     GetOpenFile(sock, fptr);
00119     if (NIL_P(fptr->pathv)) {
00120         struct sockaddr_un addr;
00121         socklen_t len = (socklen_t)sizeof(addr);
00122         socklen_t len0 = len;
00123         if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
00124             rsock_sys_fail_path("getsockname(2)", fptr->pathv);
00125         if (len0 < len) len = len0;
00126         fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len));
00127     }
00128     return rb_str_dup(fptr->pathv);
00129 }
00130 
00131 /*
00132  * call-seq:
00133  *   unixsocket.recvfrom(maxlen [, flags]) => [mesg, unixaddress]
00134  *
00135  * Receives a message via _unixsocket_.
00136  *
00137  * _maxlen_ is the maximum number of bytes to receive.
00138  *
00139  * _flags_ should be a bitwise OR of Socket::MSG_* constants.
00140  *
00141  *   s1 = Socket.new(:UNIX, :DGRAM, 0)
00142  *   s1_ai = Addrinfo.unix("/tmp/sock1")
00143  *   s1.bind(s1_ai)
00144  *
00145  *   s2 = Socket.new(:UNIX, :DGRAM, 0)
00146  *   s2_ai = Addrinfo.unix("/tmp/sock2")
00147  *   s2.bind(s2_ai)
00148  *   s3 = UNIXSocket.for_fd(s2.fileno)
00149  *
00150  *   s1.send "a", 0, s2_ai
00151  *   p s3.recvfrom(10) #=> ["a", ["AF_UNIX", "/tmp/sock1"]]
00152  *
00153  */
00154 static VALUE
00155 unix_recvfrom(int argc, VALUE *argv, VALUE sock)
00156 {
00157     return rsock_s_recvfrom(sock, argc, argv, RECV_UNIX);
00158 }
00159 
00160 #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && defined(SCM_RIGHTS)
00161 #define FD_PASSING_BY_MSG_CONTROL 1
00162 #else
00163 #define FD_PASSING_BY_MSG_CONTROL 0
00164 #endif
00165 
00166 #if defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
00167 #define FD_PASSING_BY_MSG_ACCRIGHTS 1
00168 #else
00169 #define FD_PASSING_BY_MSG_ACCRIGHTS 0
00170 #endif
00171 
00172 struct iomsg_arg {
00173     int fd;
00174     struct msghdr msg;
00175 };
00176 
00177 #if defined(HAVE_SENDMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS)
00178 static VALUE
00179 sendmsg_blocking(void *data)
00180 {
00181     struct iomsg_arg *arg = data;
00182     return sendmsg(arg->fd, &arg->msg, 0);
00183 }
00184 
00185 /*
00186  * call-seq:
00187  *   unixsocket.send_io(io) => nil
00188  *
00189  * Sends _io_ as file descriptor passing.
00190  *
00191  *   s1, s2 = UNIXSocket.pair
00192  *
00193  *   s1.send_io STDOUT
00194  *   stdout = s2.recv_io
00195  *
00196  *   p STDOUT.fileno #=> 1
00197  *   p stdout.fileno #=> 6
00198  *
00199  *   stdout.puts "hello" # outputs "hello\n" to standard output.
00200  */
00201 static VALUE
00202 unix_send_io(VALUE sock, VALUE val)
00203 {
00204     int fd;
00205     rb_io_t *fptr;
00206     struct iomsg_arg arg;
00207     struct iovec vec[1];
00208     char buf[1];
00209 
00210 #if FD_PASSING_BY_MSG_CONTROL
00211     union {
00212         struct cmsghdr hdr;
00213         char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
00214     } cmsg;
00215 #endif
00216 
00217     if (rb_obj_is_kind_of(val, rb_cIO)) {
00218         rb_io_t *valfptr;
00219         GetOpenFile(val, valfptr);
00220         fd = valfptr->fd;
00221     }
00222     else if (FIXNUM_P(val)) {
00223         fd = FIX2INT(val);
00224     }
00225     else {
00226         rb_raise(rb_eTypeError, "neither IO nor file descriptor");
00227     }
00228 
00229     GetOpenFile(sock, fptr);
00230 
00231     arg.msg.msg_name = NULL;
00232     arg.msg.msg_namelen = 0;
00233 
00234     /* Linux and Solaris doesn't work if msg_iov is NULL. */
00235     buf[0] = '\0';
00236     vec[0].iov_base = buf;
00237     vec[0].iov_len = 1;
00238     arg.msg.msg_iov = vec;
00239     arg.msg.msg_iovlen = 1;
00240 
00241 #if FD_PASSING_BY_MSG_CONTROL
00242     arg.msg.msg_control = (caddr_t)&cmsg;
00243     arg.msg.msg_controllen = (socklen_t)CMSG_LEN(sizeof(int));
00244     arg.msg.msg_flags = 0;
00245     MEMZERO((char*)&cmsg, char, sizeof(cmsg));
00246     cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int));
00247     cmsg.hdr.cmsg_level = SOL_SOCKET;
00248     cmsg.hdr.cmsg_type = SCM_RIGHTS;
00249     memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int));
00250 #else
00251     arg.msg.msg_accrights = (caddr_t)&fd;
00252     arg.msg.msg_accrightslen = sizeof(fd);
00253 #endif
00254 
00255     arg.fd = fptr->fd;
00256     while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) {
00257         if (!rb_io_wait_writable(arg.fd))
00258             rsock_sys_fail_path("sendmsg(2)", fptr->pathv);
00259     }
00260 
00261     return Qnil;
00262 }
00263 #else
00264 #define unix_send_io rb_f_notimplement
00265 #endif
00266 
00267 #if defined(HAVE_RECVMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS)
00268 static VALUE
00269 recvmsg_blocking(void *data)
00270 {
00271     struct iomsg_arg *arg = data;
00272     int flags = 0;
00273     return rsock_recvmsg(arg->fd, &arg->msg, flags);
00274 }
00275 
00276 /*
00277  * call-seq:
00278  *   unixsocket.recv_io([klass [, mode]]) => io
00279  *
00280  *   UNIXServer.open("/tmp/sock") {|serv|
00281  *     UNIXSocket.open("/tmp/sock") {|c|
00282  *       s = serv.accept
00283  *
00284  *       c.send_io STDOUT
00285  *       stdout = s.recv_io
00286  *
00287  *       p STDOUT.fileno #=> 1
00288  *       p stdout.fileno #=> 7
00289  *
00290  *       stdout.puts "hello" # outputs "hello\n" to standard output.
00291  *     }
00292  *   }
00293  *
00294  */
00295 static VALUE
00296 unix_recv_io(int argc, VALUE *argv, VALUE sock)
00297 {
00298     VALUE klass, mode;
00299     rb_io_t *fptr;
00300     struct iomsg_arg arg;
00301     struct iovec vec[2];
00302     char buf[1];
00303 
00304     int fd;
00305 #if FD_PASSING_BY_MSG_CONTROL
00306     union {
00307         struct cmsghdr hdr;
00308         char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
00309     } cmsg;
00310 #endif
00311 
00312     rb_scan_args(argc, argv, "02", &klass, &mode);
00313     if (argc == 0)
00314         klass = rb_cIO;
00315     if (argc <= 1)
00316         mode = Qnil;
00317 
00318     GetOpenFile(sock, fptr);
00319 
00320     arg.msg.msg_name = NULL;
00321     arg.msg.msg_namelen = 0;
00322 
00323     vec[0].iov_base = buf;
00324     vec[0].iov_len = sizeof(buf);
00325     arg.msg.msg_iov = vec;
00326     arg.msg.msg_iovlen = 1;
00327 
00328 #if FD_PASSING_BY_MSG_CONTROL
00329     arg.msg.msg_control = (caddr_t)&cmsg;
00330     arg.msg.msg_controllen = (socklen_t)CMSG_SPACE(sizeof(int));
00331     arg.msg.msg_flags = 0;
00332     cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int));
00333     cmsg.hdr.cmsg_level = SOL_SOCKET;
00334     cmsg.hdr.cmsg_type = SCM_RIGHTS;
00335     fd = -1;
00336     memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int));
00337 #else
00338     arg.msg.msg_accrights = (caddr_t)&fd;
00339     arg.msg.msg_accrightslen = sizeof(fd);
00340     fd = -1;
00341 #endif
00342 
00343     arg.fd = fptr->fd;
00344     while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) {
00345         if (!rb_io_wait_readable(arg.fd))
00346             rsock_sys_fail_path("recvmsg(2)", fptr->pathv);
00347     }
00348 
00349 #if FD_PASSING_BY_MSG_CONTROL
00350     if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) {
00351         rb_raise(rb_eSocket,
00352                  "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)",
00353                  (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr));
00354     }
00355     if (cmsg.hdr.cmsg_level != SOL_SOCKET) {
00356         rb_raise(rb_eSocket,
00357                  "file descriptor was not passed (cmsg_level=%d, %d expected)",
00358                  cmsg.hdr.cmsg_level, SOL_SOCKET);
00359     }
00360     if (cmsg.hdr.cmsg_type != SCM_RIGHTS) {
00361         rb_raise(rb_eSocket,
00362                  "file descriptor was not passed (cmsg_type=%d, %d expected)",
00363                  cmsg.hdr.cmsg_type, SCM_RIGHTS);
00364     }
00365     if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) {
00366         rb_raise(rb_eSocket,
00367                  "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)",
00368                  (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int)));
00369     }
00370     if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) {
00371         rb_raise(rb_eSocket,
00372                  "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)",
00373                  (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int)));
00374     }
00375     if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) {
00376         rsock_discard_cmsg_resource(&arg.msg, 0);
00377         rb_raise(rb_eSocket,
00378                  "file descriptor was not passed (cmsg_len=%d, %d expected)",
00379                  (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int)));
00380     }
00381 #else
00382     if (arg.msg.msg_accrightslen != sizeof(fd)) {
00383         rb_raise(rb_eSocket,
00384                  "file descriptor was not passed (accrightslen=%d, %d expected)",
00385                  arg.msg.msg_accrightslen, (int)sizeof(fd));
00386     }
00387 #endif
00388 
00389 #if FD_PASSING_BY_MSG_CONTROL
00390     memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int));
00391 #endif
00392     rb_fd_fix_cloexec(fd);
00393 
00394     if (klass == Qnil)
00395         return INT2FIX(fd);
00396     else {
00397         ID for_fd;
00398         int ff_argc;
00399         VALUE ff_argv[2];
00400         CONST_ID(for_fd, "for_fd");
00401         ff_argc = mode == Qnil ? 1 : 2;
00402         ff_argv[0] = INT2FIX(fd);
00403         ff_argv[1] = mode;
00404         return rb_funcall2(klass, for_fd, ff_argc, ff_argv);
00405     }
00406 }
00407 #else
00408 #define unix_recv_io rb_f_notimplement
00409 #endif
00410 
00411 /*
00412  * call-seq:
00413  *   unixsocket.addr => [address_family, unix_path]
00414  *
00415  * Returns the local address as an array which contains
00416  * address_family and unix_path.
00417  *
00418  * Example
00419  *   serv = UNIXServer.new("/tmp/sock")
00420  *   p serv.addr #=> ["AF_UNIX", "/tmp/sock"]
00421  */
00422 static VALUE
00423 unix_addr(VALUE sock)
00424 {
00425     rb_io_t *fptr;
00426     struct sockaddr_un addr;
00427     socklen_t len = (socklen_t)sizeof addr;
00428     socklen_t len0 = len;
00429 
00430     GetOpenFile(sock, fptr);
00431 
00432     if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
00433         rsock_sys_fail_path("getsockname(2)", fptr->pathv);
00434     if (len0 < len) len = len0;
00435     return rsock_unixaddr(&addr, len);
00436 }
00437 
00438 /*
00439  * call-seq:
00440  *   unixsocket.peeraddr => [address_family, unix_path]
00441  *
00442  * Returns the remote address as an array which contains
00443  * address_family and unix_path.
00444  *
00445  * Example
00446  *   serv = UNIXServer.new("/tmp/sock")
00447  *   c = UNIXSocket.new("/tmp/sock")
00448  *   p c.peeraddr #=> ["AF_UNIX", "/tmp/sock"]
00449  */
00450 static VALUE
00451 unix_peeraddr(VALUE sock)
00452 {
00453     rb_io_t *fptr;
00454     struct sockaddr_un addr;
00455     socklen_t len = (socklen_t)sizeof addr;
00456     socklen_t len0 = len;
00457 
00458     GetOpenFile(sock, fptr);
00459 
00460     if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
00461         rsock_sys_fail_path("getpeername(2)", fptr->pathv);
00462     if (len0 < len) len = len0;
00463     return rsock_unixaddr(&addr, len);
00464 }
00465 
00466 /*
00467  * call-seq:
00468  *   UNIXSocket.pair([type [, protocol]])       => [unixsocket1, unixsocket2]
00469  *   UNIXSocket.socketpair([type [, protocol]]) => [unixsocket1, unixsocket2]
00470  *
00471  * Creates a pair of sockets connected each other.
00472  *
00473  * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc.
00474  *
00475  * _protocol_ should be a protocol defined in the domain.
00476  * 0 is default protocol for the domain.
00477  *
00478  *   s1, s2 = UNIXSocket.pair
00479  *   s1.send "a", 0
00480  *   s1.send "b", 0
00481  *   p s2.recv(10) #=> "ab"
00482  *
00483  */
00484 static VALUE
00485 unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
00486 {
00487     VALUE domain, type, protocol;
00488     VALUE args[3];
00489 
00490     domain = INT2FIX(PF_UNIX);
00491     rb_scan_args(argc, argv, "02", &type, &protocol);
00492     if (argc == 0)
00493         type = INT2FIX(SOCK_STREAM);
00494     if (argc <= 1)
00495         protocol = INT2FIX(0);
00496 
00497     args[0] = domain;
00498     args[1] = type;
00499     args[2] = protocol;
00500 
00501     return rsock_sock_s_socketpair(3, args, klass);
00502 }
00503 #endif
00504 
00505 void
00506 rsock_init_unixsocket(void)
00507 {
00508 #ifdef HAVE_SYS_UN_H
00509     /*
00510      * Document-class: UNIXSocket < BasicSocket
00511      *
00512      * UNIXSocket represents a UNIX domain stream client socket.
00513      */
00514     rb_cUNIXSocket = rb_define_class("UNIXSocket", rb_cBasicSocket);
00515     rb_define_method(rb_cUNIXSocket, "initialize", unix_init, 1);
00516     rb_define_method(rb_cUNIXSocket, "path", unix_path, 0);
00517     rb_define_method(rb_cUNIXSocket, "addr", unix_addr, 0);
00518     rb_define_method(rb_cUNIXSocket, "peeraddr", unix_peeraddr, 0);
00519     rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1);
00520     rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1);
00521     rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1);
00522     rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1);
00523     rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1);
00524 #endif
00525 }
00526 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7