summaryrefslogtreecommitdiff
path: root/ext/socket/basicsocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/socket/basicsocket.c')
-rw-r--r--ext/socket/basicsocket.c305
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);
}