mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/socket: split files for each class.
* ext/socket/rubysocket.h: common header. * ext/socket/basicsocket.c: new file for BasicSocket. * ext/socket/ipsocket.c: new file for IPSocket. * ext/socket/tcpsocket.c: new file for TCPSocket. * ext/socket/tcpserver.c: new file for TCPServer. * ext/socket/sockssocket.c: new file for SOCKSSocket. * ext/socket/udpsocket.c: new file for UDPSocket. * ext/socket/unixsocket.c: new file for UNIXSocket. * ext/socket/unixserver.c: new file for UNIXServer. * ext/socket/socket.c: now for Socket. * ext/socket/raddrinfo.c: new file for AddrInfo and name resolution. * ext/socket/constants.c: new file for constants. * ext/socket/init.c: new file for utilities. * ext/socket/mkconstants.rb: export *_to_int. * ext/socket/extconf.rb: add new object files. * ext/socket/depend: add dependencies for new files. * ext/.document: add new files. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21619 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
8ae8afa649
commit
97cbab78dc
18 changed files with 4579 additions and 4207 deletions
515
ext/socket/init.c
Normal file
515
ext/socket/init.c
Normal file
|
@ -0,0 +1,515 @@
|
|||
/************************************************
|
||||
|
||||
init.c -
|
||||
|
||||
created at: Thu Mar 31 12:21:29 JST 1994
|
||||
|
||||
Copyright (C) 1993-2007 Yukihiro Matsumoto
|
||||
|
||||
************************************************/
|
||||
|
||||
#include "rubysocket.h"
|
||||
|
||||
VALUE rb_cBasicSocket;
|
||||
VALUE rb_cIPSocket;
|
||||
VALUE rb_cTCPSocket;
|
||||
VALUE rb_cTCPServer;
|
||||
VALUE rb_cUDPSocket;
|
||||
#ifdef AF_UNIX
|
||||
VALUE rb_cUNIXSocket;
|
||||
VALUE rb_cUNIXServer;
|
||||
#endif
|
||||
VALUE rb_cSocket;
|
||||
VALUE rb_cAddrInfo;
|
||||
|
||||
VALUE rb_eSocket;
|
||||
|
||||
#ifdef SOCKS
|
||||
VALUE rb_cSOCKSSocket;
|
||||
#endif
|
||||
|
||||
int do_not_reverse_lookup = 0;
|
||||
|
||||
void
|
||||
raise_socket_error(const char *reason, int error)
|
||||
{
|
||||
#ifdef EAI_SYSTEM
|
||||
if (error == EAI_SYSTEM) rb_sys_fail(reason);
|
||||
#endif
|
||||
rb_raise(rb_eSocket, "%s: %s", reason, gai_strerror(error));
|
||||
}
|
||||
|
||||
VALUE
|
||||
init_sock(VALUE sock, int fd)
|
||||
{
|
||||
rb_io_t *fp;
|
||||
|
||||
MakeOpenFile(sock, fp);
|
||||
fp->fd = fd;
|
||||
fp->mode = FMODE_READWRITE|FMODE_DUPLEX;
|
||||
rb_io_ascii8bit_binmode(sock);
|
||||
if (do_not_reverse_lookup) {
|
||||
fp->mode |= FMODE_NOREVLOOKUP;
|
||||
}
|
||||
rb_io_synchronized(fp);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
VALUE
|
||||
sendto_blocking(void *data)
|
||||
{
|
||||
struct send_arg *arg = data;
|
||||
VALUE mesg = arg->mesg;
|
||||
return (VALUE)sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
|
||||
arg->flags, arg->to, arg->tolen);
|
||||
}
|
||||
|
||||
VALUE
|
||||
send_blocking(void *data)
|
||||
{
|
||||
struct send_arg *arg = data;
|
||||
VALUE mesg = arg->mesg;
|
||||
return (VALUE)send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
|
||||
arg->flags);
|
||||
}
|
||||
|
||||
struct recvfrom_arg {
|
||||
int fd, flags;
|
||||
VALUE str;
|
||||
socklen_t alen;
|
||||
char buf[1024];
|
||||
};
|
||||
|
||||
static VALUE
|
||||
recvfrom_blocking(void *data)
|
||||
{
|
||||
struct recvfrom_arg *arg = data;
|
||||
return (VALUE)recvfrom(arg->fd, RSTRING_PTR(arg->str), RSTRING_LEN(arg->str),
|
||||
arg->flags, (struct sockaddr*)arg->buf, &arg->alen);
|
||||
}
|
||||
|
||||
VALUE
|
||||
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 = sizeof(arg.buf);
|
||||
|
||||
arg.str = str = rb_tainted_str_new(0, buflen);
|
||||
klass = RBASIC(str)->klass;
|
||||
RBASIC(str)->klass = 0;
|
||||
|
||||
while (rb_io_check_closed(fptr),
|
||||
rb_thread_wait_fd(arg.fd),
|
||||
(slen = BLOCKING_REGION(recvfrom_blocking, &arg)) < 0) {
|
||||
if (RBASIC(str)->klass || RSTRING_LEN(str) != buflen) {
|
||||
rb_raise(rb_eRuntimeError, "buffer string modified");
|
||||
}
|
||||
}
|
||||
|
||||
RBASIC(str)->klass = 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, ipaddr((struct sockaddr*)arg.buf, fptr->mode & FMODE_NOREVLOOKUP));
|
||||
else
|
||||
return rb_assoc_new(str, Qnil);
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
case RECV_UNIX:
|
||||
return rb_assoc_new(str, unixaddr((struct sockaddr_un*)arg.buf, arg.alen));
|
||||
#endif
|
||||
case RECV_SOCKET:
|
||||
return rb_assoc_new(str, io_socket_addrinfo(sock, (struct sockaddr*)arg.buf, arg.alen));
|
||||
default:
|
||||
rb_bug("s_recvfrom called with bad value");
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
|
||||
{
|
||||
rb_io_t *fptr;
|
||||
VALUE str;
|
||||
char buf[1024];
|
||||
socklen_t alen = sizeof buf;
|
||||
VALUE len, flg;
|
||||
long buflen;
|
||||
long slen;
|
||||
int fd, flags;
|
||||
VALUE addr = Qnil;
|
||||
|
||||
rb_scan_args(argc, argv, "11", &len, &flg);
|
||||
|
||||
if (flg == Qnil) flags = 0;
|
||||
else flags = NUM2INT(flg);
|
||||
buflen = NUM2INT(len);
|
||||
|
||||
#ifdef MSG_DONTWAIT
|
||||
/* MSG_DONTWAIT avoids the race condition between fcntl and recvfrom.
|
||||
It is not portable, though. */
|
||||
flags |= MSG_DONTWAIT;
|
||||
#endif
|
||||
|
||||
GetOpenFile(sock, fptr);
|
||||
if (rb_io_read_pending(fptr)) {
|
||||
rb_raise(rb_eIOError, "recvfrom for buffered IO");
|
||||
}
|
||||
fd = fptr->fd;
|
||||
|
||||
str = rb_tainted_str_new(0, buflen);
|
||||
|
||||
rb_io_check_closed(fptr);
|
||||
rb_io_set_nonblock(fptr);
|
||||
slen = recvfrom(fd, RSTRING_PTR(str), buflen, flags, (struct sockaddr*)buf, &alen);
|
||||
|
||||
if (slen < 0) {
|
||||
rb_sys_fail("recvfrom(2)");
|
||||
}
|
||||
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 (alen && alen != sizeof(buf)) /* connection-oriented socket may not return a from result */
|
||||
addr = ipaddr((struct sockaddr*)buf, fptr->mode & FMODE_NOREVLOOKUP);
|
||||
break;
|
||||
|
||||
case RECV_SOCKET:
|
||||
addr = io_socket_addrinfo(sock, (struct sockaddr*)buf, alen);
|
||||
break;
|
||||
|
||||
default:
|
||||
rb_bug("s_recvfrom_nonblock called with bad value");
|
||||
}
|
||||
return rb_assoc_new(str, addr);
|
||||
}
|
||||
|
||||
int
|
||||
ruby_socket(int domain, int type, int proto)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = socket(domain, type, proto);
|
||||
if (fd < 0) {
|
||||
if (errno == EMFILE || errno == ENFILE) {
|
||||
rb_gc();
|
||||
fd = socket(domain, type, proto);
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
wait_connectable0(int fd, rb_fdset_t *fds_w, rb_fdset_t *fds_e)
|
||||
{
|
||||
int sockerr;
|
||||
socklen_t sockerrlen;
|
||||
|
||||
for (;;) {
|
||||
rb_fd_zero(fds_w);
|
||||
rb_fd_zero(fds_e);
|
||||
|
||||
rb_fd_set(fd, fds_w);
|
||||
rb_fd_set(fd, fds_e);
|
||||
|
||||
rb_thread_select(fd+1, 0, rb_fd_ptr(fds_w), rb_fd_ptr(fds_e), 0);
|
||||
|
||||
if (rb_fd_isset(fd, fds_w)) {
|
||||
return 0;
|
||||
}
|
||||
else if (rb_fd_isset(fd, fds_e)) {
|
||||
sockerrlen = sizeof(sockerr);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr,
|
||||
&sockerrlen) == 0) {
|
||||
if (sockerr == 0)
|
||||
continue; /* workaround for winsock */
|
||||
errno = sockerr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct wait_connectable_arg {
|
||||
int fd;
|
||||
rb_fdset_t fds_w;
|
||||
rb_fdset_t fds_e;
|
||||
};
|
||||
|
||||
#ifdef HAVE_RB_FD_INIT
|
||||
static VALUE
|
||||
try_wait_connectable(VALUE arg)
|
||||
{
|
||||
struct wait_connectable_arg *p = (struct wait_connectable_arg *)arg;
|
||||
return (VALUE)wait_connectable0(p->fd, &p->fds_w, &p->fds_e);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
wait_connectable_ensure(VALUE arg)
|
||||
{
|
||||
struct wait_connectable_arg *p = (struct wait_connectable_arg *)arg;
|
||||
rb_fd_term(&p->fds_w);
|
||||
rb_fd_term(&p->fds_e);
|
||||
return Qnil;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
wait_connectable(int fd)
|
||||
{
|
||||
struct wait_connectable_arg arg;
|
||||
|
||||
rb_fd_init(&arg.fds_w);
|
||||
rb_fd_init(&arg.fds_e);
|
||||
#ifdef HAVE_RB_FD_INIT
|
||||
arg.fd = fd;
|
||||
return (int)rb_ensure(try_wait_connectable, (VALUE)&arg,
|
||||
wait_connectable_ensure,(VALUE)&arg);
|
||||
#else
|
||||
return wait_connectable0(fd, &arg.fds_w, &arg.fds_e);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#define WAIT_IN_PROGRESS 10
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#define WAIT_IN_PROGRESS 10
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
/* returns correct error */
|
||||
#define WAIT_IN_PROGRESS 0
|
||||
#endif
|
||||
#ifndef WAIT_IN_PROGRESS
|
||||
/* BSD origin code apparently has a problem */
|
||||
#define WAIT_IN_PROGRESS 1
|
||||
#endif
|
||||
|
||||
struct connect_arg {
|
||||
int fd;
|
||||
const struct sockaddr *sockaddr;
|
||||
socklen_t len;
|
||||
};
|
||||
|
||||
static VALUE
|
||||
connect_blocking(void *data)
|
||||
{
|
||||
struct connect_arg *arg = data;
|
||||
return (VALUE)connect(arg->fd, arg->sockaddr, arg->len);
|
||||
}
|
||||
|
||||
int
|
||||
ruby_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(func, &arg);
|
||||
if (status < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
#ifdef EINPROGRESS
|
||||
case EINPROGRESS:
|
||||
#endif
|
||||
#if WAIT_IN_PROGRESS > 0
|
||||
sockerrlen = 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 = 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;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
make_fd_nonblock(int fd)
|
||||
{
|
||||
int flags;
|
||||
#ifdef F_GETFL
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags == -1) {
|
||||
rb_sys_fail(0);
|
||||
}
|
||||
#else
|
||||
flags = 0;
|
||||
#endif
|
||||
flags |= O_NONBLOCK;
|
||||
if (fcntl(fd, F_SETFL, flags) == -1) {
|
||||
rb_sys_fail(0);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
s_accept_nonblock(VALUE klass, rb_io_t *fptr, struct sockaddr *sockaddr, socklen_t *len)
|
||||
{
|
||||
int fd2;
|
||||
|
||||
rb_secure(3);
|
||||
rb_io_set_nonblock(fptr);
|
||||
fd2 = accept(fptr->fd, (struct sockaddr*)sockaddr, len);
|
||||
if (fd2 < 0) {
|
||||
rb_sys_fail("accept(2)");
|
||||
}
|
||||
make_fd_nonblock(fd2);
|
||||
return init_sock(rb_obj_alloc(klass), fd2);
|
||||
}
|
||||
|
||||
struct accept_arg {
|
||||
int fd;
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t *len;
|
||||
};
|
||||
|
||||
static VALUE
|
||||
accept_blocking(void *data)
|
||||
{
|
||||
struct accept_arg *arg = data;
|
||||
return (VALUE)accept(arg->fd, arg->sockaddr, arg->len);
|
||||
}
|
||||
|
||||
VALUE
|
||||
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:
|
||||
rb_thread_wait_fd(fd);
|
||||
fd2 = BLOCKING_REGION(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(0);
|
||||
}
|
||||
if (!klass) return INT2NUM(fd2);
|
||||
return init_sock(rb_obj_alloc(klass), fd2);
|
||||
}
|
||||
|
||||
/*
|
||||
* SocketError is the error class for socket.
|
||||
*/
|
||||
void
|
||||
Init_socket_init()
|
||||
{
|
||||
rb_eSocket = rb_define_class("SocketError", rb_eStandardError);
|
||||
Init_ipsocket();
|
||||
Init_tcpsocket();
|
||||
Init_tcpserver();
|
||||
Init_sockssocket();
|
||||
Init_udpsocket();
|
||||
Init_unixsocket();
|
||||
Init_unixserver();
|
||||
Init_addrinfo();
|
||||
Init_socket_constants();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue