diff options
Diffstat (limited to 'ext/socket/basicsocket.c')
| -rw-r--r-- | ext/socket/basicsocket.c | 305 |
1 files changed, 158 insertions, 147 deletions
diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c index 2ef4f32692..2fcae8eb54 100644 --- a/ext/socket/basicsocket.c +++ b/ext/socket/basicsocket.c @@ -10,6 +10,28 @@ #include "rubysocket.h" +#ifdef _WIN32 +#define is_socket(fd) rb_w32_is_socket(fd) +#else +static int +is_socket(int fd) +{ + struct stat sbuf; + + if (fstat(fd, &sbuf) < 0) + rb_sys_fail("fstat(2)"); + return S_ISSOCK(sbuf.st_mode); +} +#endif + +static void +rsock_validate_descriptor(int descriptor) +{ + if (!is_socket(descriptor) || rb_reserved_fd_p(descriptor)) { + rb_syserr_fail(EBADF, "not a socket file descriptor"); + } +} + /* * call-seq: * BasicSocket.for_fd(fd) => basicsocket @@ -22,10 +44,14 @@ * */ static VALUE -bsock_s_for_fd(VALUE klass, VALUE fd) +bsock_s_for_fd(VALUE klass, VALUE _descriptor) { rb_io_t *fptr; - VALUE sock = rsock_init_sock(rb_obj_alloc(klass), NUM2INT(fd)); + + int descriptor = RB_NUM2INT(_descriptor); + rsock_validate_descriptor(descriptor); + + VALUE sock = rsock_init_sock(rb_obj_alloc(klass), descriptor); GetOpenFile(sock, fptr); @@ -66,21 +92,18 @@ bsock_shutdown(int argc, VALUE *argv, VALUE sock) int how; rb_io_t *fptr; - if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) { - rb_raise(rb_eSecurityError, "Insecure: can't shutdown socket"); - } rb_scan_args(argc, argv, "01", &howto); if (howto == Qnil) - how = SHUT_RDWR; + how = SHUT_RDWR; else { - how = rsock_shutdown_how_arg(howto); + how = rsock_shutdown_how_arg(howto); if (how != SHUT_WR && how != SHUT_RD && how != SHUT_RDWR) { - rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR"); - } + rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR"); + } } GetOpenFile(sock, fptr); if (shutdown(fptr->fd, how) == -1) - rb_sys_fail(0); + rb_sys_fail("shutdown(2)"); return INT2FIX(0); } @@ -100,13 +123,10 @@ bsock_close_read(VALUE sock) { rb_io_t *fptr; - if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) { - rb_raise(rb_eSecurityError, "Insecure: can't close socket"); - } GetOpenFile(sock, fptr); - shutdown(fptr->fd, 0); + shutdown(fptr->fd, SHUT_RD); if (!(fptr->mode & FMODE_WRITABLE)) { - return rb_io_close(sock); + return rb_io_close(sock); } fptr->mode &= ~FMODE_READABLE; @@ -133,14 +153,11 @@ bsock_close_write(VALUE sock) { rb_io_t *fptr; - if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) { - rb_raise(rb_eSecurityError, "Insecure: can't close socket"); - } GetOpenFile(sock, fptr); if (!(fptr->mode & FMODE_READABLE)) { - return rb_io_close(sock); + return rb_io_close(sock); } - shutdown(fptr->fd, 1); + shutdown(fptr->fd, SHUT_WR); fptr->mode &= ~FMODE_WRITABLE; return Qnil; @@ -167,10 +184,10 @@ bsock_close_write(VALUE sock) * * +optval+ is the value of the option, it is passed to the underlying * setsockopt() as a pointer to a certain number of bytes. How this is * done depends on the type: - * - Fixnum: value is assigned to an int, and a pointer to the int is + * - Integer: value is assigned to an int, and a pointer to the int is * passed, with length of sizeof(int). * - true or false: 1 or 0 (respectively) is assigned to an int, and the - * int is passed as for a Fixnum. Note that +false+ must be passed, + * int is passed as for an Integer. Note that +false+ must be passed, * not +nil+. * - String: the string's data and length is passed to the socket. * * +socketoption+ is an instance of Socket::Option @@ -222,41 +239,37 @@ bsock_setsockopt(int argc, VALUE *argv, VALUE sock) rb_scan_args(argc, argv, "30", &lev, &optname, &val); } - rb_secure(2); GetOpenFile(sock, fptr); - family = rsock_getfamily(fptr->fd); + family = rsock_getfamily(fptr); level = rsock_level_arg(family, lev); option = rsock_optname_arg(family, level, optname); switch (TYPE(val)) { case T_FIXNUM: - i = FIX2INT(val); - goto numval; + i = FIX2INT(val); + goto numval; case T_FALSE: - i = 0; - goto numval; + i = 0; + goto numval; case T_TRUE: - i = 1; + i = 1; numval: - v = (char*)&i; vlen = (int)sizeof(i); - break; + v = (char*)&i; vlen = (int)sizeof(i); + break; default: - StringValue(val); - v = RSTRING_PTR(val); - vlen = RSTRING_LENINT(val); - break; + StringValue(val); + v = RSTRING_PTR(val); + vlen = RSTRING_SOCKLEN(val); + break; } -#define rb_sys_fail_path(path) rb_sys_fail_str(path) - rb_io_check_closed(fptr); if (setsockopt(fptr->fd, level, option, v, vlen) < 0) - rb_sys_fail_path(fptr->pathv); + rsock_sys_fail_path("setsockopt(2)", fptr->pathv); return INT2FIX(0); } -#if !defined(__BEOS__) /* * Document-method: getsockopt * call-seq: @@ -293,7 +306,7 @@ bsock_setsockopt(int argc, VALUE *argv, VALUE sock) * ipttl = sock.getsockopt(:IP, :TTL).int * * optval = sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL) - * ipttl = optval.unpack("i")[0] + * ipttl = optval.unpack1("i") * * Option values may be structs. Decoding them can be complex as it involves * examining your system headers to determine the correct definition. An @@ -323,22 +336,31 @@ bsock_getsockopt(VALUE sock, VALUE lev, VALUE optname) int family; GetOpenFile(sock, fptr); - family = rsock_getfamily(fptr->fd); + family = rsock_getfamily(fptr); level = rsock_level_arg(family, lev); option = rsock_optname_arg(family, level, optname); len = 256; +#ifdef _AIX + switch (option) { + case SO_DEBUG: + case SO_REUSEADDR: + case SO_KEEPALIVE: + case SO_DONTROUTE: + case SO_BROADCAST: + case SO_OOBINLINE: + /* AIX doesn't set len for boolean options */ + len = sizeof(int); + } +#endif buf = ALLOCA_N(char,len); rb_io_check_closed(fptr); if (getsockopt(fptr->fd, level, option, buf, &len) < 0) - rb_sys_fail_path(fptr->pathv); + rsock_sys_fail_path("getsockopt(2)", fptr->pathv); return rsock_sockopt_new(family, level, option, rb_str_new(buf, len)); } -#else -#define bsock_getsockopt rb_f_notimplement -#endif /* * call-seq: @@ -356,14 +378,14 @@ bsock_getsockopt(VALUE sock, VALUE lev, VALUE optname) static VALUE bsock_getsockname(VALUE sock) { - struct sockaddr_storage buf; + union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); - if (getsockname(fptr->fd, (struct sockaddr*)&buf, &len) < 0) - rb_sys_fail("getsockname(2)"); + if (getsockname(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getsockname(2)"); if (len0 < len) len = len0; return rb_str_new((char*)&buf, len); } @@ -387,14 +409,14 @@ bsock_getsockname(VALUE sock) static VALUE bsock_getpeername(VALUE sock) { - struct sockaddr_storage buf; + union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); - if (getpeername(fptr->fd, (struct sockaddr*)&buf, &len) < 0) - rb_sys_fail("getpeername(2)"); + if (getpeername(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getpeername(2)"); if (len0 < len) len = len0; return rb_str_new((char*)&buf, len); } @@ -431,7 +453,7 @@ bsock_getpeereid(VALUE self) gid_t egid; GetOpenFile(self, fptr); if (getpeereid(fptr->fd, &euid, &egid) == -1) - rb_sys_fail("getpeereid"); + rb_sys_fail("getpeereid(3)"); return rb_assoc_new(UIDT2NUM(euid), GIDT2NUM(egid)); #elif defined(SO_PEERCRED) /* GNU/Linux */ rb_io_t *fptr; @@ -439,7 +461,7 @@ bsock_getpeereid(VALUE self) socklen_t len = sizeof(cred); GetOpenFile(self, fptr); if (getsockopt(fptr->fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) - rb_sys_fail("getsockopt(SO_PEERCRED)"); + rb_sys_fail("getsockopt(SO_PEERCRED)"); return rb_assoc_new(UIDT2NUM(cred.uid), GIDT2NUM(cred.gid)); #elif defined(HAVE_GETPEERUCRED) /* Solaris */ rb_io_t *fptr; @@ -447,7 +469,7 @@ bsock_getpeereid(VALUE self) VALUE ret; GetOpenFile(self, fptr); if (getpeerucred(fptr->fd, &uc) == -1) - rb_sys_fail("getpeerucred"); + rb_sys_fail("getpeerucred(3C)"); ret = rb_assoc_new(UIDT2NUM(ucred_geteuid(uc)), GIDT2NUM(ucred_getegid(uc))); ucred_free(uc); return ret; @@ -477,16 +499,16 @@ bsock_getpeereid(VALUE self) static VALUE bsock_local_address(VALUE sock) { - struct sockaddr_storage buf; + union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); - if (getsockname(fptr->fd, (struct sockaddr*)&buf, &len) < 0) - rb_sys_fail("getsockname(2)"); + if (getsockname(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getsockname(2)"); if (len0 < len) len = len0; - return rsock_fd_socket_addrinfo(fptr->fd, (struct sockaddr *)&buf, len); + return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len); } /* @@ -511,16 +533,16 @@ bsock_local_address(VALUE sock) static VALUE bsock_remote_address(VALUE sock) { - struct sockaddr_storage buf; + union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); - if (getpeername(fptr->fd, (struct sockaddr*)&buf, &len) < 0) - rb_sys_fail("getpeername(2)"); + if (getpeername(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getpeername(2)"); if (len0 < len) len = len0; - return rsock_fd_socket_addrinfo(fptr->fd, (struct sockaddr *)&buf, len); + return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len); } /* @@ -541,39 +563,50 @@ bsock_remote_address(VALUE sock) * } */ VALUE -rsock_bsock_send(int argc, VALUE *argv, VALUE sock) +rsock_bsock_send(int argc, VALUE *argv, VALUE socket) { struct rsock_send_arg arg; VALUE flags, to; rb_io_t *fptr; - int n; rb_blocking_function_t *func; + const char *funcname; - rb_secure(4); rb_scan_args(argc, argv, "21", &arg.mesg, &flags, &to); StringValue(arg.mesg); if (!NIL_P(to)) { - SockAddrStringValue(to); - to = rb_str_new4(to); - arg.to = (struct sockaddr *)RSTRING_PTR(to); - arg.tolen = (socklen_t)RSTRING_LENINT(to); - func = rsock_sendto_blocking; + SockAddrStringValue(to); + to = rb_str_new4(to); + arg.to = (struct sockaddr *)RSTRING_PTR(to); + arg.tolen = RSTRING_SOCKLEN(to); + func = rsock_sendto_blocking; + funcname = "sendto(2)"; } else { - func = rsock_send_blocking; + func = rsock_send_blocking; + funcname = "send(2)"; } - GetOpenFile(sock, fptr); + + RB_IO_POINTER(socket, fptr); + arg.fd = fptr->fd; arg.flags = NUM2INT(flags); - while (rb_thread_fd_writable(arg.fd), - (n = (int)BLOCKING_REGION_FD(func, &arg)) < 0) { - if (rb_io_wait_writable(arg.fd)) { - continue; - } - rb_sys_fail("send(2)"); + + while (true) { +#ifdef RSOCK_WAIT_BEFORE_BLOCKING + rb_io_wait(socket, RB_INT2NUM(RUBY_IO_WRITABLE), Qnil); +#endif + + ssize_t n = (ssize_t)rb_io_blocking_region(fptr, func, &arg); + + if (n >= 0) return SSIZET2NUM(n); + + if (rb_io_maybe_wait_writable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT)) { + continue; + } + + rb_sys_fail(funcname); } - return INT2FIX(n); } /* @@ -582,11 +615,15 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE sock) * * Gets the do_not_reverse_lookup flag of _basicsocket_. * + * require 'socket' + * + * BasicSocket.do_not_reverse_lookup = false * TCPSocket.open("www.ruby-lang.org", 80) {|sock| * p sock.do_not_reverse_lookup #=> false - * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] - * sock.do_not_reverse_lookup = true - * p sock.peeraddr #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] + * } + * BasicSocket.do_not_reverse_lookup = true + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.do_not_reverse_lookup #=> true * } */ static VALUE @@ -604,10 +641,12 @@ bsock_do_not_reverse_lookup(VALUE sock) * * Sets the do_not_reverse_lookup flag of _basicsocket_. * - * BasicSocket.do_not_reverse_lookup = false - * p TCPSocket.new("127.0.0.1", 80).do_not_reverse_lookup #=> false - * BasicSocket.do_not_reverse_lookup = true - * p TCPSocket.new("127.0.0.1", 80).do_not_reverse_lookup #=> true + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.do_not_reverse_lookup #=> true + * p sock.peeraddr #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] + * sock.do_not_reverse_lookup = false + * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "54.163.249.195"] + * } * */ static VALUE @@ -615,21 +654,19 @@ bsock_do_not_reverse_lookup_set(VALUE sock, VALUE state) { rb_io_t *fptr; - rb_secure(4); GetOpenFile(sock, fptr); if (RTEST(state)) { - fptr->mode |= FMODE_NOREVLOOKUP; + fptr->mode |= FMODE_NOREVLOOKUP; } else { - fptr->mode &= ~FMODE_NOREVLOOKUP; + fptr->mode &= ~FMODE_NOREVLOOKUP; } return sock; } /* * call-seq: - * basicsocket.recv(maxlen) => mesg - * basicsocket.recv(maxlen, flags) => mesg + * basicsocket.recv(maxlen[, flags[, outbuf]]) => mesg * * Receives a message. * @@ -637,6 +674,9 @@ bsock_do_not_reverse_lookup_set(VALUE sock, VALUE state) * * _flags_ should be a bitwise OR of Socket::MSG_* constants. * + * _outbuf_ will contain only the received data after the method call + * even if it is not empty at the beginning. + * * UNIXSocket.pair {|s1, s2| * s1.puts "Hello World" * p s2.recv(4) #=> "Hell" @@ -651,55 +691,11 @@ bsock_recv(int argc, VALUE *argv, VALUE sock) return rsock_s_recvfrom(sock, argc, argv, RECV_RECV); } -/* - * call-seq: - * basicsocket.recv_nonblock(maxlen) => mesg - * basicsocket.recv_nonblock(maxlen, flags) => mesg - * - * Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after - * O_NONBLOCK is set for the underlying file descriptor. - * _flags_ is zero or more of the +MSG_+ options. - * The result, _mesg_, is the data received. - * - * When recvfrom(2) returns 0, Socket#recv_nonblock returns - * an empty string as data. - * The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc. - * - * === Parameters - * * +maxlen+ - the number of bytes to receive from the socket - * * +flags+ - zero or more of the +MSG_+ options - * - * === Example - * serv = TCPServer.new("127.0.0.1", 0) - * af, port, host, addr = serv.addr - * c = TCPSocket.new(addr, port) - * s = serv.accept - * c.send "aaa", 0 - * begin # emulate blocking recv. - * p s.recv_nonblock(10) #=> "aaa" - * rescue IO::WaitReadable - * IO.select([s]) - * retry - * end - * - * Refer to Socket#recvfrom for the exceptions that may be thrown if the call - * to _recv_nonblock_ fails. - * - * BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure, - * including Errno::EWOULDBLOCK. - * - * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN, - * it is extended by IO::WaitReadable. - * So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock. - * - * === See - * * Socket#recvfrom - */ - +/* :nodoc: */ static VALUE -bsock_recv_nonblock(int argc, VALUE *argv, VALUE sock) +bsock_recv_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex) { - return rsock_s_recvfrom_nonblock(sock, argc, argv, RECV_RECV); + return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_RECV); } /* @@ -711,7 +707,7 @@ bsock_recv_nonblock(int argc, VALUE *argv, VALUE sock) * BasicSocket.do_not_reverse_lookup #=> false */ static VALUE -bsock_do_not_rev_lookup(void) +bsock_do_not_rev_lookup(VALUE _) { return rsock_do_not_reverse_lookup?Qtrue:Qfalse; } @@ -735,7 +731,6 @@ bsock_do_not_rev_lookup(void) static VALUE bsock_do_not_rev_lookup_set(VALUE self, VALUE val) { - rb_secure(4); rsock_do_not_reverse_lookup = RTEST(val); return val; } @@ -752,9 +747,9 @@ rsock_init_basicsocket(void) rb_undef_method(rb_cBasicSocket, "initialize"); rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup", - bsock_do_not_rev_lookup, 0); + bsock_do_not_rev_lookup, 0); rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup=", - bsock_do_not_rev_lookup_set, 1); + bsock_do_not_rev_lookup_set, 1); rb_define_singleton_method(rb_cBasicSocket, "for_fd", bsock_s_for_fd, 1); rb_define_method(rb_cBasicSocket, "close_read", bsock_close_read, 0); @@ -769,13 +764,29 @@ rsock_init_basicsocket(void) rb_define_method(rb_cBasicSocket, "remote_address", bsock_remote_address, 0); rb_define_method(rb_cBasicSocket, "send", rsock_bsock_send, -1); rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1); - rb_define_method(rb_cBasicSocket, "recv_nonblock", bsock_recv_nonblock, -1); + rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup", bsock_do_not_reverse_lookup, 0); rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup=", bsock_do_not_reverse_lookup_set, 1); - rb_define_method(rb_cBasicSocket, "sendmsg", rsock_bsock_sendmsg, -1); /* in ancdata.c */ - rb_define_method(rb_cBasicSocket, "sendmsg_nonblock", rsock_bsock_sendmsg_nonblock, -1); /* in ancdata.c */ - rb_define_method(rb_cBasicSocket, "recvmsg", rsock_bsock_recvmsg, -1); /* in ancdata.c */ - rb_define_method(rb_cBasicSocket, "recvmsg_nonblock", rsock_bsock_recvmsg_nonblock, -1); /* in ancdata.c */ + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cBasicSocket, + "__recv_nonblock", bsock_recv_nonblock, 4); + +#if MSG_DONTWAIT_RELIABLE + rb_define_private_method(rb_cBasicSocket, + "__read_nonblock", rsock_read_nonblock, 3); + rb_define_private_method(rb_cBasicSocket, + "__write_nonblock", rsock_write_nonblock, 2); +#endif + + /* in ancdata.c */ + rb_define_private_method(rb_cBasicSocket, "__sendmsg", + rsock_bsock_sendmsg, 4); + rb_define_private_method(rb_cBasicSocket, "__sendmsg_nonblock", + rsock_bsock_sendmsg_nonblock, 5); + rb_define_private_method(rb_cBasicSocket, "__recvmsg", + rsock_bsock_recvmsg, 4); + rb_define_private_method(rb_cBasicSocket, "__recvmsg_nonblock", + rsock_bsock_recvmsg_nonblock, 5); } |
