/* * call-seq: * basicsocket.send(mesg, flags [, dest_sockaddr]) => numbytes_sent * * send _mesg_ via _basicsocket_. * * _mesg_ should be a string. * * _flags_ should be a bitwise OR of Socket::MSG_* constants. * * _dest_sockaddr_ should be a packed sockaddr string or an addrinfo. * * TCPSocket.open("localhost", 80) {|s| * s.send "GET / HTTP/1.0\r\n\r\n", 0 * p s.read * } */ VALUE rsock_bsock_send(int argc, VALUE *argv, VALUE sock) { struct rsock_send_arg arg; VALUE flags, to; rb_io_t *fptr; int n; rb_blocking_function_t *func; 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; } else { func = rsock_send_blocking; } GetOpenFile(sock, 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)"); } return INT2FIX(n); }
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); }
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks) { int status; rb_blocking_function_t *func = connect_blocking; struct connect_arg arg; arg.fd = fd; arg.sockaddr = sockaddr; arg.len = len; #if defined(SOCKS) && !defined(SOCKS5) if (socks) func = socks_connect_blocking; #endif status = (int)BLOCKING_REGION_FD(func, &arg); if (status < 0) { switch (errno) { case EINTR: #ifdef ERESTART case ERESTART: #endif case EAGAIN: #ifdef EINPROGRESS case EINPROGRESS: #endif return wait_connectable(fd); } } return status; }
/* * call-seq: * udpsocket.send(mesg, flags, host, port) => numbytes_sent * udpsocket.send(mesg, flags, sockaddr_to) => numbytes_sent * udpsocket.send(mesg, flags) => numbytes_sent * * Sends _mesg_ via _udpsocket_. * * _flags_ should be a bitwise OR of Socket::MSG_* constants. * * u1 = UDPSocket.new * u1.bind("127.0.0.1", 4913) * * u2 = UDPSocket.new * u2.send "hi", 0, "127.0.0.1", 4913 * * mesg, addr = u1.recvfrom(10) * u1.send mesg, 0, addr[3], addr[1] * * p u2.recv(100) #=> "hi" * */ static VALUE udp_send(int argc, VALUE *argv, VALUE sock) { VALUE flags, host, port; rb_io_t *fptr; int n; struct addrinfo *res0, *res; struct rsock_send_arg arg; if (argc == 2 || argc == 3) { return rsock_bsock_send(argc, argv, sock); } rb_scan_args(argc, argv, "4", &arg.mesg, &flags, &host, &port); StringValue(arg.mesg); res0 = rsock_addrinfo(host, port, SOCK_DGRAM, 0); GetOpenFile(sock, fptr); arg.fd = fptr->fd; arg.flags = NUM2INT(flags); for (res = res0; res; res = res->ai_next) { retry: arg.to = res->ai_addr; arg.tolen = res->ai_addrlen; rb_thread_fd_writable(arg.fd); n = (int)BLOCKING_REGION_FD(rsock_sendto_blocking, &arg); if (n >= 0) { freeaddrinfo(res0); return INT2FIX(n); } if (rb_io_wait_writable(fptr->fd)) { goto retry; } } freeaddrinfo(res0); rsock_sys_fail_host_port("sendto(2)", host, port); return INT2FIX(n); }
/* * 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); } }
/* * call-seq: * unixsocket.send_io(io) => nil * * Sends _io_ as file descriptor passing. * * s1, s2 = UNIXSocket.pair * * s1.send_io STDOUT * stdout = s2.recv_io * * p STDOUT.fileno #=> 1 * p stdout.fileno #=> 6 * * stdout.puts "hello" # outputs "hello\n" to standard output. */ static VALUE unix_send_io(VALUE sock, VALUE val) { int fd; rb_io_t *fptr; struct iomsg_arg arg; struct iovec vec[1]; char buf[1]; #if FD_PASSING_BY_MSG_CONTROL struct { struct cmsghdr hdr; char pad[8+sizeof(int)+8]; } cmsg; #endif if (rb_obj_is_kind_of(val, rb_cIO)) { rb_io_t *valfptr; GetOpenFile(val, valfptr); fd = valfptr->fd; } else if (FIXNUM_P(val)) { fd = FIX2INT(val); } else { rb_raise(rb_eTypeError, "neither IO nor file descriptor"); } GetOpenFile(sock, fptr); arg.msg.msg_name = NULL; arg.msg.msg_namelen = 0; /* Linux and Solaris doesn't work if msg_iov is NULL. */ buf[0] = '\0'; vec[0].iov_base = buf; vec[0].iov_len = 1; 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_LEN(sizeof(int)); arg.msg.msg_flags = 0; MEMZERO((char*)&cmsg, char, sizeof(cmsg)); cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int)); #else arg.msg.msg_accrights = (caddr_t)&fd; arg.msg.msg_accrightslen = sizeof(fd); #endif arg.fd = fptr->fd; while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) { if (!rb_io_wait_writable(arg.fd)) rb_sys_fail("sendmsg(2)"); } return Qnil; }
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"); } }
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks) { int status; rb_blocking_function_t *func = connect_blocking; struct connect_arg arg; #if WAIT_IN_PROGRESS > 0 int wait_in_progress = -1; int sockerr; socklen_t sockerrlen; #endif arg.fd = fd; arg.sockaddr = sockaddr; arg.len = len; #if defined(SOCKS) && !defined(SOCKS5) if (socks) func = socks_connect_blocking; #endif for (;;) { status = (int)BLOCKING_REGION_FD(func, &arg); if (status < 0) { switch (errno) { case EINTR: #if defined(ERESTART) case ERESTART: #endif continue; case EAGAIN: #ifdef EINPROGRESS case EINPROGRESS: #endif #if WAIT_IN_PROGRESS > 0 sockerrlen = (socklen_t)sizeof(sockerr); status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen); if (status) break; if (sockerr) { status = -1; errno = sockerr; break; } #endif #ifdef EALREADY case EALREADY: #endif #if WAIT_IN_PROGRESS > 0 wait_in_progress = WAIT_IN_PROGRESS; #endif status = wait_connectable(fd); if (status) { break; } errno = 0; continue; #if WAIT_IN_PROGRESS > 0 case EINVAL: if (wait_in_progress-- > 0) { /* * connect() after EINPROGRESS returns EINVAL on * some platforms, need to check true error * status. */ sockerrlen = (socklen_t)sizeof(sockerr); status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen); if (!status && !sockerr) { struct timeval tv = {0, 100000}; rb_thread_wait_for(tv); continue; } status = -1; errno = sockerr; } break; #endif #ifdef EISCONN case EISCONN: status = 0; errno = 0; break; #endif default: break; } } return status; } }