VALUE rsock_s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len) { int fd2; int retry = 0; struct accept_arg arg; rb_secure(3); arg.fd = fd; arg.sockaddr = sockaddr; arg.len = len; retry: rsock_maybe_wait_fd(fd); fd2 = (int)BLOCKING_REGION_FD(accept_blocking, &arg); if (fd2 < 0) { switch (errno) { case EMFILE: case ENFILE: if (retry) break; rb_gc(); retry = 1; goto retry; default: if (!rb_io_wait_readable(fd)) break; retry = 0; goto retry; } rb_sys_fail("accept(2)"); } rb_update_max_fd(fd2); if (!klass) return INT2NUM(fd2); return rsock_init_sock(rb_obj_alloc(klass), fd2); }
static VALUE ossl_start_ssl(VALUE self, int (*func)(), const char *funcname) { SSL *ssl; rb_io_t *fptr; int ret, ret2; VALUE cb_state; rb_ivar_set(self, ID_callback_state, Qnil); Data_Get_Struct(self, SSL, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); for(;;){ if((ret = func(ssl)) > 0) break; switch((ret2 = ssl_get_error(ssl, ret))){ case SSL_ERROR_WANT_WRITE: rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(funcname); ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); default: ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); } } cb_state = rb_ivar_get(self, ID_callback_state); if (!NIL_P(cb_state)) rb_jump_tag(NUM2INT(cb_state)); return self; }
VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { int fd = io_spec_get_fd(io); set_non_blocking(fd); char buf[RB_IO_WAIT_READABLE_BUF]; wait_bool ret; if(RTEST(read_p)) { rb_ivar_set(self, rb_intern("@write_data"), Qtrue); if(read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) { return Qnil; } } ret = rb_io_wait_readable(fd); if(RTEST(read_p)) { if(read(fd, buf, RB_IO_WAIT_READABLE_BUF) != 13) { return Qnil; } rb_ivar_set(self, rb_intern("@read_data"), rb_str_new(buf, RB_IO_WAIT_READABLE_BUF)); } return wait_bool_to_ruby_bool(ret); }
/* * call-seq: * ssl.sysread(length) => string * ssl.sysread(length, buffer) => buffer * * === Parameters * * +length+ is a positive integer. * * +buffer+ is a string used to store the result. */ static VALUE ossl_ssl_read(int argc, VALUE *argv, VALUE self) { SSL *ssl; int ilen, nread = 0; VALUE len, str; rb_scan_args(argc, argv, "11", &len, &str); ilen = NUM2INT(len); if(NIL_P(str)) { str = rb_str_new(0, ilen); } else { StringValue(str); rb_str_modify(str); rb_str_resize(str, ilen); } if(ilen == 0) return str; Data_Get_Struct(self, SSL, ssl); int fd = rb_io_fd(ossl_ssl_get_io(self)); if (ssl) { if(SSL_pending(ssl) <= 0) rb_thread_wait_fd(fd); for (;;) { nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LEN(str)); switch(ssl_get_error(ssl, nread)) { case SSL_ERROR_NONE: goto end; case SSL_ERROR_ZERO_RETURN: rb_eof_error(); case SSL_ERROR_WANT_WRITE: rb_io_wait_writable(fd); continue; case SSL_ERROR_WANT_READ: rb_io_wait_readable(fd); continue; case SSL_ERROR_SYSCALL: if(ERR_peek_error() == 0 && nread == 0) rb_eof_error(); rb_sys_fail(0); default: ossl_raise(eSSLError, "SSL_read:"); } } } else { ID id_sysread = rb_intern("sysread"); rb_warning("SSL session is not started yet."); return rb_funcall(ossl_ssl_get_io(self), id_sysread, 2, len, str); } end: rb_str_set_len(str, nread); OBJ_TAINT(str); return str; }
/* * Blocks the running Thread indefinitely until +self+ IO object is readable. * This method is automatically called by default whenever kgio_read needs * to block on input. * * Users of alternative threading/fiber libraries are * encouraged to override this method in their subclasses or modules to * work with their threading/blocking methods. */ static VALUE kgio_wait_readable(VALUE self) { int fd = my_fileno(self); errno = EAGAIN; if (!rb_io_wait_readable(fd)) rb_sys_fail("kgio_wait_readable"); return self; }
VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io) { int fd = io_spec_get_fd(io); set_non_blocking(fd); char buf[256]; int ret; ret = read(fd, buf, 256); if (ret < 1) return rb_io_wait_readable(fd) ? Qtrue : Qfalse; else return Qnil; }
static int kgio_wait(int argc, VALUE *argv, VALUE self, int write_p) { int fd; VALUE timeout; if (rb_scan_args(argc, argv, "01", &timeout) == 1 && !NIL_P(timeout)) return kgio_timedwait(self, timeout, write_p); fd = my_fileno(self); errno = EAGAIN; write_p ? rb_io_wait_writable(fd) : rb_io_wait_readable(fd); return 1; }
static void set_blocking_or_block(int fd) { static time_t last_set_blocking; time_t now = time(NULL); if (last_set_blocking == 0) { last_set_blocking = now; (void)rb_io_wait_readable(fd); } else if ((now - last_set_blocking) <= 5) { (void)rb_io_wait_readable(fd); } else { int flags = fcntl(fd, F_GETFL); if (flags == -1) rb_sys_fail("fcntl(F_GETFL)"); if (flags & O_NONBLOCK) { flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); if (flags == -1) rb_sys_fail("fcntl(F_SETFL)"); } last_set_blocking = now; } }
static VALUE wait_for_thread(void *data) { struct BlockingThread* thr = (struct BlockingThread *) data; char c; if (read(thr->rdfd, &c, 1) < 1) { rb_thread_wait_fd(thr->rdfd); while (read(thr->rdfd, &c, 1) < 1 && rb_io_wait_readable(thr->rdfd) == Qtrue) { ; } } return Qnil; }
static VALUE ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock) { SSL *ssl; int nwrite = 0; rb_io_t *fptr; StringValue(str); Data_Get_Struct(self, SSL, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); if (ssl) { for (;;){ nwrite = SSL_write(ssl, RSTRING_PTR(str), RSTRING_LEN(str)); switch(ssl_get_error(ssl, nwrite)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_WANT_WRITE: write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(0); default: ossl_raise(eSSLError, "SSL_write:"); } } } else { ID id_syswrite = rb_intern("syswrite"); rb_warning("SSL session is not started yet."); return rb_funcall(ossl_ssl_get_io(self), id_syswrite, 1, str); } end: return INT2NUM(nwrite); }
static VALUE kl_read_loop(VALUE vfd) { int fd = FIX2INT(vfd); int ret, done; struct input_event ev; int brek = 0; while(!brek) { done = 0; rb_thread_wait_fd(fd); while(!done) { ret = read(fd, &ev, sizeof(struct input_event)); if(ret == -1) { if (rb_io_wait_readable(fd) == Qfalse) rb_raise(rb_eStandardError, "read error"); } else { if(ev.type == 1) { if(rb_yield_values(2, INT2FIX(ev.code), INT2FIX(ev.value)) != Qtrue) brek = 1; } done = 1; } } } return Qnil; }
/* * call-seq: * unixsocket.recv_io([klass [, mode]]) => io * * UNIXServer.open("/tmp/sock") {|serv| * UNIXSocket.open("/tmp/sock") {|c| * s = serv.accept * * c.send_io STDOUT * stdout = s.recv_io * * p STDOUT.fileno #=> 1 * p stdout.fileno #=> 7 * * stdout.puts "hello" # outputs "hello\n" to standard output. * } * } * */ static VALUE unix_recv_io(int argc, VALUE *argv, VALUE sock) { VALUE klass, mode; rb_io_t *fptr; struct iomsg_arg arg; struct iovec vec[2]; char buf[1]; int fd; #if FD_PASSING_BY_MSG_CONTROL struct { struct cmsghdr hdr; char pad[8+sizeof(int)+8]; } cmsg; #endif rb_scan_args(argc, argv, "02", &klass, &mode); if (argc == 0) klass = rb_cIO; if (argc <= 1) mode = Qnil; GetOpenFile(sock, fptr); arg.msg.msg_name = NULL; arg.msg.msg_namelen = 0; vec[0].iov_base = buf; vec[0].iov_len = sizeof(buf); arg.msg.msg_iov = vec; arg.msg.msg_iovlen = 1; #if FD_PASSING_BY_MSG_CONTROL arg.msg.msg_control = (caddr_t)&cmsg; arg.msg.msg_controllen = (socklen_t)CMSG_SPACE(sizeof(int)); arg.msg.msg_flags = 0; cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_RIGHTS; fd = -1; memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int)); #else arg.msg.msg_accrights = (caddr_t)&fd; arg.msg.msg_accrightslen = sizeof(fd); fd = -1; #endif arg.fd = fptr->fd; while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) { if (!rb_io_wait_readable(arg.fd)) rb_sys_fail("recvmsg(2)"); } #if FD_PASSING_BY_MSG_CONTROL if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) { rb_raise(rb_eSocket, "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)", (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr)); } if (cmsg.hdr.cmsg_level != SOL_SOCKET) { rb_raise(rb_eSocket, "file descriptor was not passed (cmsg_level=%d, %d expected)", cmsg.hdr.cmsg_level, SOL_SOCKET); } if (cmsg.hdr.cmsg_type != SCM_RIGHTS) { rb_raise(rb_eSocket, "file descriptor was not passed (cmsg_type=%d, %d expected)", cmsg.hdr.cmsg_type, SCM_RIGHTS); } if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) { rb_raise(rb_eSocket, "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)", (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int))); } if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) { rb_raise(rb_eSocket, "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)", (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int))); } if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) { rsock_discard_cmsg_resource(&arg.msg, 0); rb_raise(rb_eSocket, "file descriptor was not passed (cmsg_len=%d, %d expected)", (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int))); } #else if (arg.msg.msg_accrightslen != sizeof(fd)) { rb_raise(rb_eSocket, "file descriptor was not passed (accrightslen) : %d != %d", arg.msg.msg_accrightslen, (int)sizeof(fd)); } #endif #if FD_PASSING_BY_MSG_CONTROL memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int)); #endif rb_update_max_fd(fd); if (klass == Qnil) return INT2FIX(fd); else { ID for_fd; int ff_argc; VALUE ff_argv[2]; CONST_ID(for_fd, "for_fd"); ff_argc = mode == Qnil ? 1 : 2; ff_argv[0] = INT2FIX(fd); ff_argv[1] = mode; return rb_funcall2(klass, for_fd, ff_argc, ff_argv); } }
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from) { rb_io_t *fptr; VALUE str, klass; struct recvfrom_arg arg; VALUE len, flg; long buflen; long slen; rb_scan_args(argc, argv, "11", &len, &flg); if (flg == Qnil) arg.flags = 0; else arg.flags = NUM2INT(flg); buflen = NUM2INT(len); GetOpenFile(sock, fptr); if (rb_io_read_pending(fptr)) { rb_raise(rb_eIOError, "recv for buffered IO"); } arg.fd = fptr->fd; arg.alen = (socklen_t)sizeof(arg.buf); arg.str = str = rb_tainted_str_new(0, buflen); klass = RBASIC(str)->klass; rb_obj_hide(str); while (rb_io_check_closed(fptr), rsock_maybe_wait_fd(arg.fd), (slen = BLOCKING_REGION_FD(recvfrom_blocking, &arg)) < 0) { if (!rb_io_wait_readable(fptr->fd)) { rb_sys_fail("recvfrom(2)"); } if (RBASIC(str)->klass || RSTRING_LEN(str) != buflen) { rb_raise(rb_eRuntimeError, "buffer string modified"); } } rb_obj_reveal(str, klass); if (slen < RSTRING_LEN(str)) { rb_str_set_len(str, slen); } rb_obj_taint(str); switch (from) { case RECV_RECV: return str; case RECV_IP: #if 0 if (arg.alen != sizeof(struct sockaddr_in)) { rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); } #endif if (arg.alen && arg.alen != sizeof(arg.buf)) /* OSX doesn't return a from result for connection-oriented sockets */ return rb_assoc_new(str, rsock_ipaddr(&arg.buf.addr, arg.alen, fptr->mode & FMODE_NOREVLOOKUP)); else return rb_assoc_new(str, Qnil); #ifdef HAVE_SYS_UN_H case RECV_UNIX: return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen)); #endif case RECV_SOCKET: return rb_assoc_new(str, rsock_io_socket_addrinfo(sock, &arg.buf.addr, arg.alen)); default: rb_bug("rsock_s_recvfrom called with bad value"); } }
static VALUE ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) { SSL *ssl; int ilen, nread = 0; VALUE len, str; rb_io_t *fptr; rb_scan_args(argc, argv, "11", &len, &str); ilen = NUM2INT(len); if(NIL_P(str)) { str = rb_bstr_new(); } else{ StringValue(str); rb_str_modify(str); str = rb_str_bstr(str); } rb_bstr_resize(str, ilen); if(ilen == 0) return str; Data_Get_Struct(self, SSL, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); if (ssl) { if(!nonblock && SSL_pending(ssl) <= 0) rb_thread_wait_fd(FPTR_TO_FD(fptr)); for (;;){ nread = SSL_read(ssl, rb_bstr_bytes(str), rb_bstr_length(str)); switch(ssl_get_error(ssl, nread)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_ZERO_RETURN: rb_eof_error(); case SSL_ERROR_WANT_WRITE: write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if(ERR_peek_error() == 0 && nread == 0) rb_eof_error(); rb_sys_fail(0); default: ossl_raise(eSSLError, "SSL_read:"); } } } else { ID meth = nonblock ? rb_intern("read_nonblock") : rb_intern("sysread"); rb_warning("SSL session is not started yet."); return rb_funcall(ossl_ssl_get_io(self), meth, 2, len, str); } end: rb_bstr_resize(str, nread); OBJ_TAINT(str); return str; }