summaryrefslogtreecommitdiff
path: root/ext/socket/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/socket/socket.c')
-rw-r--r--ext/socket/socket.c1369
1 files changed, 738 insertions, 631 deletions
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 3df435bbc9..a8e5ae8119 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -10,6 +10,106 @@
#include "rubysocket.h"
+static VALUE sym_wait_writable;
+
+static VALUE sock_s_unpack_sockaddr_in(VALUE, VALUE);
+
+ID tcp_fast_fallback;
+
+void
+rsock_sys_fail_host_port(const char *mesg, VALUE host, VALUE port)
+{
+ rsock_syserr_fail_host_port(errno, mesg, host, port);
+}
+
+void
+rsock_syserr_fail_host_port(int err, const char *mesg, VALUE host, VALUE port)
+{
+ VALUE message;
+
+ message = rb_sprintf("%s for %+"PRIsVALUE" port % "PRIsVALUE"",
+ mesg, host, port);
+
+ if (err == ETIMEDOUT) {
+ rb_exc_raise(rb_exc_new3(rb_eIOTimeoutError, message));
+ }
+
+ rb_syserr_fail_str(err, message);
+}
+
+void
+rsock_sys_fail_path(const char *mesg, VALUE path)
+{
+ rsock_syserr_fail_path(errno, mesg, path);
+}
+
+void
+rsock_syserr_fail_path(int err, const char *mesg, VALUE path)
+{
+ VALUE message;
+
+ if (RB_TYPE_P(path, T_STRING)) {
+ message = rb_sprintf("%s for % "PRIsVALUE"", mesg, path);
+ rb_syserr_fail_str(err, message);
+ }
+ else {
+ rb_syserr_fail(err, mesg);
+ }
+}
+
+void
+rsock_sys_fail_sockaddr(const char *mesg, struct sockaddr *addr, socklen_t len)
+{
+ rsock_syserr_fail_sockaddr(errno, mesg, addr, len);
+}
+
+void
+rsock_syserr_fail_sockaddr(int err, const char *mesg, struct sockaddr *addr, socklen_t len)
+{
+ VALUE rai;
+
+ rai = rsock_addrinfo_new(addr, len, PF_UNSPEC, 0, 0, Qnil, Qnil);
+
+ rsock_syserr_fail_raddrinfo(err, mesg, rai);
+}
+
+void
+rsock_sys_fail_raddrinfo(const char *mesg, VALUE rai)
+{
+ rsock_syserr_fail_raddrinfo(errno, mesg, rai);
+}
+
+void
+rsock_syserr_fail_raddrinfo(int err, const char *mesg, VALUE rai)
+{
+ VALUE str, message;
+
+ str = rsock_addrinfo_inspect_sockaddr(rai);
+ message = rb_sprintf("%s for %"PRIsVALUE"", mesg, str);
+
+ rb_syserr_fail_str(err, message);
+}
+
+void
+rsock_sys_fail_raddrinfo_or_sockaddr(const char *mesg, VALUE addr, VALUE rai)
+{
+ rsock_syserr_fail_raddrinfo_or_sockaddr(errno, mesg, addr, rai);
+}
+
+void
+rsock_syserr_fail_raddrinfo_or_sockaddr(int err, const char *mesg, VALUE addr, VALUE rai)
+{
+ if (NIL_P(rai)) {
+ StringValue(addr);
+
+ rsock_syserr_fail_sockaddr(err, mesg,
+ (struct sockaddr *)RSTRING_PTR(addr),
+ (socklen_t)RSTRING_LEN(addr)); /* overflow should be checked already */
+ }
+ else
+ rsock_syserr_fail_raddrinfo(err, mesg, rai);
+}
+
static void
setup_domain_and_type(VALUE domain, int *dv, VALUE type, int *tv)
{
@@ -46,7 +146,6 @@ sock_initialize(int argc, VALUE *argv, VALUE sock)
if (NIL_P(protocol))
protocol = INT2FIX(0);
- rb_secure(3);
setup_domain_and_type(domain, &d, type, &t);
fd = rsock_socket(d, t, NUM2INT(protocol));
if (fd < 0) rb_sys_fail("socket(2)");
@@ -58,7 +157,7 @@ sock_initialize(int argc, VALUE *argv, VALUE sock)
static VALUE
io_call_close(VALUE io)
{
- return rb_funcall(io, rb_intern("close"), 0, 0);
+ return rb_funcallv(io, rb_intern("close"), 0, 0);
}
static VALUE
@@ -75,57 +174,47 @@ pair_yield(VALUE pair)
#endif
#if defined HAVE_SOCKETPAIR
-
static int
-rsock_socketpair0(int domain, int type, int protocol, int sv[2])
+rsock_socketpair0(int domain, int type, int protocol, int descriptors[2])
{
- int ret;
-
#ifdef SOCK_CLOEXEC
- static int try_sock_cloexec = 1;
- if (try_sock_cloexec) {
- ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv);
- if (ret == -1 && errno == EINVAL) {
- /* SOCK_CLOEXEC is available since Linux 2.6.27. Linux 2.6.18 fails with EINVAL */
- ret = socketpair(domain, type, protocol, sv);
- if (ret != -1) {
- /* The reason of EINVAL may be other than SOCK_CLOEXEC.
- * So disable SOCK_CLOEXEC only if socketpair() succeeds without SOCK_CLOEXEC.
- * Ex. Socket.pair(:UNIX, 0xff) fails with EINVAL.
- */
- try_sock_cloexec = 0;
- }
- }
- }
- else {
- ret = socketpair(domain, type, protocol, sv);
- }
-#else
- ret = socketpair(domain, type, protocol, sv);
+ type |= SOCK_CLOEXEC;
#endif
- if (ret == -1) {
+#ifdef SOCK_NONBLOCK
+ type |= SOCK_NONBLOCK;
+#endif
+
+ int result = socketpair(domain, type, protocol, descriptors);
+
+ if (result == -1)
return -1;
- }
- rb_fd_fix_cloexec(sv[0]);
- rb_fd_fix_cloexec(sv[1]);
+#ifndef SOCK_CLOEXEC
+ rb_fd_fix_cloexec(descriptors[0]);
+ rb_fd_fix_cloexec(descriptors[1]);
+#endif
+
+#ifndef SOCK_NONBLOCK
+ rsock_make_fd_nonblock(descriptors[0]);
+ rsock_make_fd_nonblock(descriptors[1]);
+#endif
- return ret;
+ return result;
}
static int
-rsock_socketpair(int domain, int type, int protocol, int sv[2])
+rsock_socketpair(int domain, int type, int protocol, int descriptors[2])
{
- int ret;
+ int result;
- ret = rsock_socketpair0(domain, type, protocol, sv);
- if (ret < 0 && (errno == EMFILE || errno == ENFILE)) {
- rb_gc();
- ret = rsock_socketpair0(domain, type, protocol, sv);
+ result = rsock_socketpair0(domain, type, protocol, descriptors);
+
+ if (result < 0 && rb_gc_for_fd(errno)) {
+ result = rsock_socketpair0(domain, type, protocol, descriptors);
}
- return ret;
+ return result;
}
/*
@@ -142,6 +231,14 @@ rsock_socketpair(int domain, int type, int protocol, int sv[2])
* _protocol_ should be a protocol defined in the domain,
* defaults to 0 for the domain.
*
+ * s1, s2 = Socket.pair(:UNIX, :STREAM, 0)
+ * s1.send "a", 0
+ * s1.send "b", 0
+ * s1.close
+ * p s2.recv(10) #=> "ab"
+ * p s2.recv(10) #=> ""
+ * p s2.recv(10) #=> ""
+ *
* s1, s2 = Socket.pair(:UNIX, :DGRAM, 0)
* s1.send "a", 0
* s1.send "b", 0
@@ -165,10 +262,8 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
- rb_sys_fail("socketpair(2)");
+ rb_sys_fail("socketpair(2)");
}
- rb_fd_fix_cloexec(sp[0]);
- rb_fd_fix_cloexec(sp[1]);
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
@@ -184,7 +279,7 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * socket.connect(remote_sockaddr) => 0
+ * socket.connect(remote_sockaddr) => 0
*
* Requests a connection to be made on the given +remote_sockaddr+. Returns 0 if
* successful, otherwise an exception is raised.
@@ -193,14 +288,14 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
* * +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object
*
* === Example:
- * # Pull down Google's web page
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
- * socket.connect( sockaddr )
- * socket.write( "GET / HTTP/1.0\r\n\r\n" )
- * results = socket.read
+ * # Pull down Google's web page
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
+ * socket.connect( sockaddr )
+ * socket.write( "GET / HTTP/1.0\r\n\r\n" )
+ * results = socket.read
*
* === Unix-based Exceptions
* On unix-based systems the following system exceptions may be raised if
@@ -241,7 +336,7 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
* * Errno::EOPNOTSUPP - the calling +socket+ is listening and cannot be connected
* * Errno::EPROTOTYPE - the _sockaddr_ has a different type than the socket
* bound to the specified peer address
- * * Errno::ETIMEDOUT - the attempt to connect time out before a connection
+ * * Errno::ETIMEDOUT - the attempt to connect timed out before a connection
* was made.
*
* On unix-based systems if the address family of the calling +socket+ is
@@ -282,7 +377,7 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
* * Errno::EHOSTUNREACH - no route to the network is present
* * Errno::ENOBUFS - no buffer space is available
* * Errno::ENOTSOCK - the +socket+ argument does not refer to a socket
- * * Errno::ETIMEDOUT - the attempt to connect time out before a connection
+ * * Errno::ETIMEDOUT - the attempt to connect timed out before a connection
* was made.
* * Errno::EWOULDBLOCK - the socket is marked as nonblocking and the
* connection cannot be completed immediately
@@ -294,80 +389,49 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
* * connect function in Microsoft's Winsock functions reference
*/
static VALUE
-sock_connect(VALUE sock, VALUE addr)
+sock_connect(VALUE self, VALUE addr)
{
- rb_io_t *fptr;
- int fd, n;
+ VALUE rai;
- SockAddrStringValue(addr);
+ SockAddrStringValueWithAddrinfo(addr, rai);
addr = rb_str_new4(addr);
- GetOpenFile(sock, fptr);
- fd = fptr->fd;
- n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LENINT(addr), 0);
- if (n < 0) {
- rb_sys_fail("connect(2)");
+
+ int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT);
+
+ if (result < 0) {
+ rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
- return INT2FIX(n);
+ return INT2FIX(result);
}
-/*
- * call-seq:
- * socket.connect_nonblock(remote_sockaddr) => 0
- *
- * Requests a connection to be made on the given +remote_sockaddr+ after
- * O_NONBLOCK is set for the underlying file descriptor.
- * Returns 0 if successful, otherwise an exception is raised.
- *
- * === Parameter
- * * +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object
- *
- * === Example:
- * # Pull down Google's web page
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(80, 'www.google.com')
- * begin # emulate blocking connect
- * socket.connect_nonblock(sockaddr)
- * rescue IO::WaitWritable
- * IO.select(nil, [socket]) # wait 3-way handshake completion
- * begin
- * socket.connect_nonblock(sockaddr) # check connection failure
- * rescue Errno::EISCONN
- * end
- * end
- * socket.write("GET / HTTP/1.0\r\n\r\n")
- * results = socket.read
- *
- * Refer to Socket#connect for the exceptions that may be thrown if the call
- * to _connect_nonblock_ fails.
- *
- * Socket#connect_nonblock may raise any error corresponding to connect(2) failure,
- * including Errno::EINPROGRESS.
- *
- * If the exception is Errno::EINPROGRESS,
- * it is extended by IO::WaitWritable.
- * So IO::WaitWritable can be used to rescue the exceptions for retrying connect_nonblock.
- *
- * === See
- * * Socket#connect
- */
+/* :nodoc: */
static VALUE
-sock_connect_nonblock(VALUE sock, VALUE addr)
+sock_connect_nonblock(VALUE sock, VALUE addr, VALUE ex)
{
+ VALUE rai;
rb_io_t *fptr;
int n;
- SockAddrStringValue(addr);
+ SockAddrStringValueWithAddrinfo(addr, rai);
addr = rb_str_new4(addr);
GetOpenFile(sock, fptr);
rb_io_set_nonblock(fptr);
- n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LENINT(addr));
+ n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr));
if (n < 0) {
- if (errno == EINPROGRESS)
- rb_mod_sys_fail(rb_mWaitWritable, "connect(2) would block");
- rb_sys_fail("connect(2)");
+ int e = errno;
+ if (e == EINPROGRESS) {
+ if (ex == Qfalse) {
+ return sym_wait_writable;
+ }
+ rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, "connect(2) would block");
+ }
+ if (e == EISCONN) {
+ if (ex == Qfalse) {
+ return INT2FIX(0);
+ }
+ }
+ rsock_syserr_fail_raddrinfo_or_sockaddr(e, "connect(2)", addr, rai);
}
return INT2FIX(n);
@@ -375,7 +439,7 @@ sock_connect_nonblock(VALUE sock, VALUE addr)
/*
* call-seq:
- * socket.bind(local_sockaddr) => 0
+ * socket.bind(local_sockaddr) => 0
*
* Binds to the given local address.
*
@@ -383,18 +447,18 @@ sock_connect_nonblock(VALUE sock, VALUE addr)
* * +local_sockaddr+ - the +struct+ sockaddr contained in a string or an Addrinfo object
*
* === Example
- * require 'socket'
+ * require 'socket'
*
- * # use Addrinfo
- * socket = Socket.new(:INET, :STREAM, 0)
- * socket.bind(Addrinfo.tcp("127.0.0.1", 2222))
- * p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>
+ * # use Addrinfo
+ * socket = Socket.new(:INET, :STREAM, 0)
+ * socket.bind(Addrinfo.tcp("127.0.0.1", 2222))
+ * p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>
*
- * # use struct sockaddr
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
- * socket.bind( sockaddr )
+ * # use struct sockaddr
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
+ * socket.bind( sockaddr )
*
* === Unix-based Exceptions
* On unix-based based systems the following system exceptions may be raised if
@@ -462,19 +526,20 @@ sock_connect_nonblock(VALUE sock, VALUE addr)
static VALUE
sock_bind(VALUE sock, VALUE addr)
{
+ VALUE rai;
rb_io_t *fptr;
- SockAddrStringValue(addr);
+ SockAddrStringValueWithAddrinfo(addr, rai);
GetOpenFile(sock, fptr);
- if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_LENINT(addr)) < 0)
- rb_sys_fail("bind(2)");
+ if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0)
+ rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
return INT2FIX(0);
}
/*
* call-seq:
- * socket.listen( int ) => 0
+ * socket.listen( int ) => 0
*
* Listens for connections, using the specified +int+ as the backlog. A call
* to _listen_ only applies if the +socket+ is of type SOCK_STREAM or
@@ -484,18 +549,18 @@ sock_bind(VALUE sock, VALUE addr)
* * +backlog+ - the maximum length of the queue for pending connections.
*
* === Example 1
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
- * socket.bind( sockaddr )
- * socket.listen( 5 )
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
+ * socket.bind( sockaddr )
+ * socket.listen( 5 )
*
* === Example 2 (listening on an arbitrary port, unix-based systems only):
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * socket.listen( 1 )
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * socket.listen( 1 )
*
* === Unix-based Exceptions
* On unix based systems the above will work because a new +sockaddr+ struct
@@ -548,19 +613,18 @@ rsock_sock_listen(VALUE sock, VALUE log)
rb_io_t *fptr;
int backlog;
- rb_secure(4);
backlog = NUM2INT(log);
GetOpenFile(sock, fptr);
if (listen(fptr->fd, backlog) < 0)
- rb_sys_fail("listen(2)");
+ rb_sys_fail("listen(2)");
return INT2FIX(0);
}
/*
* call-seq:
- * socket.recvfrom(maxlen) => [mesg, sender_addrinfo]
- * socket.recvfrom(maxlen, flags) => [mesg, sender_addrinfo]
+ * socket.recvfrom(maxlen) => [mesg, sender_addrinfo]
+ * socket.recvfrom(maxlen, flags) => [mesg, sender_addrinfo]
*
* Receives up to _maxlen_ bytes from +socket+. _flags_ is zero or more
* of the +MSG_+ options. The first element of the results, _mesg_, is the data
@@ -572,27 +636,27 @@ rsock_sock_listen(VALUE sock, VALUE log)
* * +flags+ - zero or more of the +MSG_+ options
*
* === Example
- * # In one file, start this first
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
- * socket.bind( sockaddr )
- * socket.listen( 5 )
- * client, client_addrinfo = socket.accept
- * data = client.recvfrom( 20 )[0].chomp
- * puts "I only received 20 bytes '#{data}'"
- * sleep 1
- * socket.close
- *
- * # In another file, start this second
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
- * socket.connect( sockaddr )
- * socket.puts "Watch this get cut short!"
- * socket.close
+ * # In one file, start this first
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
+ * socket.bind( sockaddr )
+ * socket.listen( 5 )
+ * client, client_addrinfo = socket.accept
+ * data = client.recvfrom( 20 )[0].chomp
+ * puts "I only received 20 bytes '#{data}'"
+ * sleep 1
+ * socket.close
+ *
+ * # In another file, start this second
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
+ * socket.connect( sockaddr )
+ * socket.puts "Watch this get cut short!"
+ * socket.close
*
* === Unix-based Exceptions
* On unix-based based systems the following system exceptions may be raised if the
@@ -668,72 +732,11 @@ sock_recvfrom(int argc, VALUE *argv, VALUE sock)
return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET);
}
-/*
- * call-seq:
- * socket.recvfrom_nonblock(maxlen) => [mesg, sender_addrinfo]
- * socket.recvfrom_nonblock(maxlen, flags) => [mesg, sender_addrinfo]
- *
- * 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 first element of the results, _mesg_, is the data received.
- * The second element, _sender_addrinfo_, contains protocol-specific address
- * information of the sender.
- *
- * When recvfrom(2) returns 0, Socket#recvfrom_nonblock returns
- * an empty string as data.
- * The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
- *
- * === Parameters
- * * +maxlen+ - the maximum number of bytes to receive from the socket
- * * +flags+ - zero or more of the +MSG_+ options
- *
- * === Example
- * # In one file, start this first
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.bind(sockaddr)
- * socket.listen(5)
- * client, client_addrinfo = socket.accept
- * begin # emulate blocking recvfrom
- * pair = client.recvfrom_nonblock(20)
- * rescue IO::WaitReadable
- * IO.select([client])
- * retry
- * end
- * data = pair[0].chomp
- * puts "I only received 20 bytes '#{data}'"
- * sleep 1
- * socket.close
- *
- * # In another file, start this second
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.connect(sockaddr)
- * socket.puts "Watch this get cut short!"
- * socket.close
- *
- * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
- * to _recvfrom_nonblock_ fails.
- *
- * Socket#recvfrom_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 recvfrom_nonblock.
- *
- * === See
- * * Socket#recvfrom
- */
+/* :nodoc: */
static VALUE
-sock_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock)
+sock_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex)
{
- return rsock_s_recvfrom_nonblock(sock, argc, argv, RECV_SOCKET);
+ return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_SOCKET);
}
/*
@@ -751,114 +754,65 @@ sock_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock)
*
*/
static VALUE
-sock_accept(VALUE sock)
+sock_accept(VALUE server)
{
- rb_io_t *fptr;
- VALUE sock2;
- struct sockaddr_storage buf;
- socklen_t len = (socklen_t)sizeof buf;
+ union_sockaddr buffer;
+ socklen_t length = (socklen_t)sizeof(buffer);
- GetOpenFile(sock, fptr);
- sock2 = rsock_s_accept(rb_cSocket,fptr->fd,(struct sockaddr*)&buf,&len);
+ VALUE peer = rsock_s_accept(rb_cSocket, server, &buffer.addr, &length);
- return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, (struct sockaddr*)&buf, len));
+ return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
}
-/*
- * call-seq:
- * socket.accept_nonblock => [client_socket, client_addrinfo]
- *
- * Accepts an incoming connection using accept(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * It returns an array containing the accepted socket
- * for the incoming connection, _client_socket_,
- * and an Addrinfo, _client_addrinfo_.
- *
- * === Example
- * # In one script, start this first
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.bind(sockaddr)
- * socket.listen(5)
- * begin # emulate blocking accept
- * client_socket, client_addrinfo = socket.accept_nonblock
- * rescue IO::WaitReadable, Errno::EINTR
- * IO.select([socket])
- * retry
- * end
- * puts "The client said, '#{client_socket.readline.chomp}'"
- * client_socket.puts "Hello from script one!"
- * socket.close
- *
- * # In another script, start this second
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new(AF_INET, SOCK_STREAM, 0)
- * sockaddr = Socket.sockaddr_in(2200, 'localhost')
- * socket.connect(sockaddr)
- * socket.puts "Hello from script 2."
- * puts "The server said, '#{socket.readline.chomp}'"
- * socket.close
- *
- * Refer to Socket#accept for the exceptions that may be thrown if the call
- * to _accept_nonblock_ fails.
- *
- * Socket#accept_nonblock may raise any error corresponding to accept(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK, Errno::AGAIN, Errno::ECONNABORTED or Errno::EPROTO,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
- *
- * === See
- * * Socket#accept
- */
+/* :nodoc: */
static VALUE
-sock_accept_nonblock(VALUE sock)
+sock_accept_nonblock(VALUE sock, VALUE ex)
{
rb_io_t *fptr;
VALUE sock2;
- struct sockaddr_storage buf;
+ union_sockaddr buf;
+ struct sockaddr *addr = &buf.addr;
socklen_t len = (socklen_t)sizeof buf;
GetOpenFile(sock, fptr);
- sock2 = rsock_s_accept_nonblock(rb_cSocket, fptr, (struct sockaddr *)&buf, &len);
- return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, (struct sockaddr*)&buf, len));
+ sock2 = rsock_s_accept_nonblock(rb_cSocket, ex, fptr, addr, &len);
+
+ if (SYMBOL_P(sock2)) /* :wait_readable */
+ return sock2;
+ return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
}
/*
* call-seq:
- * socket.sysaccept => [client_socket_fd, client_addrinfo]
+ * socket.sysaccept => [client_socket_fd, client_addrinfo]
*
* Accepts an incoming connection returning an array containing the (integer)
* file descriptor for the incoming connection, _client_socket_fd_,
* and an Addrinfo, _client_addrinfo_.
*
* === Example
- * # In one script, start this first
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
- * socket.bind( sockaddr )
- * socket.listen( 5 )
- * client_fd, client_addrinfo = socket.sysaccept
- * client_socket = Socket.for_fd( client_fd )
- * puts "The client said, '#{client_socket.readline.chomp}'"
- * client_socket.puts "Hello from script one!"
- * socket.close
- *
- * # In another script, start this second
- * require 'socket'
- * include Socket::Constants
- * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
- * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
- * socket.connect( sockaddr )
- * socket.puts "Hello from script 2."
- * puts "The server said, '#{socket.readline.chomp}'"
- * socket.close
+ * # In one script, start this first
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
+ * socket.bind( sockaddr )
+ * socket.listen( 5 )
+ * client_fd, client_addrinfo = socket.sysaccept
+ * client_socket = Socket.for_fd( client_fd )
+ * puts "The client said, '#{client_socket.readline.chomp}'"
+ * client_socket.puts "Hello from script one!"
+ * socket.close
+ *
+ * # In another script, start this second
+ * require 'socket'
+ * include Socket::Constants
+ * socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
+ * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
+ * socket.connect( sockaddr )
+ * socket.puts "Hello from script 2."
+ * puts "The server said, '#{socket.readline.chomp}'"
+ * socket.close
*
* Refer to Socket#accept for the exceptions that may be thrown if the call
* to _sysaccept_ fails.
@@ -867,17 +821,14 @@ sock_accept_nonblock(VALUE sock)
* * Socket#accept
*/
static VALUE
-sock_sysaccept(VALUE sock)
+sock_sysaccept(VALUE server)
{
- rb_io_t *fptr;
- VALUE sock2;
- struct sockaddr_storage buf;
- socklen_t len = (socklen_t)sizeof buf;
+ union_sockaddr buffer;
+ socklen_t length = (socklen_t)sizeof(buffer);
- GetOpenFile(sock, fptr);
- sock2 = rsock_s_accept(0,fptr->fd,(struct sockaddr*)&buf,&len);
+ VALUE peer = rsock_s_accept(0, server, &buffer.addr, &length);
- return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, (struct sockaddr*)&buf, len));
+ return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
}
#ifdef HAVE_GETHOSTNAME
@@ -895,17 +846,35 @@ sock_sysaccept(VALUE sock)
static VALUE
sock_gethostname(VALUE obj)
{
-#ifndef HOST_NAME_MAX
-# define HOST_NAME_MAX 1024
+#if defined(NI_MAXHOST)
+# define RUBY_MAX_HOST_NAME_LEN NI_MAXHOST
+#elif defined(HOST_NAME_MAX)
+# define RUBY_MAX_HOST_NAME_LEN HOST_NAME_MAX
+#else
+# define RUBY_MAX_HOST_NAME_LEN 1024
#endif
- char buf[HOST_NAME_MAX+1];
- rb_secure(3);
- if (gethostname(buf, (int)sizeof buf - 1) < 0)
- rb_sys_fail("gethostname");
-
- buf[sizeof buf - 1] = '\0';
- return rb_str_new2(buf);
+ long len = RUBY_MAX_HOST_NAME_LEN;
+ VALUE name;
+
+ name = rb_str_new(0, len);
+ while (gethostname(RSTRING_PTR(name), len) < 0) {
+ int e = errno;
+ switch (e) {
+ case ENAMETOOLONG:
+#ifdef __linux__
+ case EINVAL:
+ /* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
+#endif
+ break;
+ default:
+ rb_syserr_fail(e, "gethostname(3)");
+ }
+ rb_str_modify_expand(name, len);
+ len += len;
+ }
+ rb_str_resize(name, strlen(RSTRING_PTR(name)));
+ return name;
}
#else
#ifdef HAVE_UNAME
@@ -917,7 +886,6 @@ sock_gethostname(VALUE obj)
{
struct utsname un;
- rb_secure(3);
uname(&un);
return rb_str_new2(un.nodename);
}
@@ -927,47 +895,47 @@ sock_gethostname(VALUE obj)
#endif
static VALUE
-make_addrinfo(struct addrinfo *res0, int norevlookup)
+make_addrinfo(struct rb_addrinfo *res0, int norevlookup)
{
VALUE base, ary;
struct addrinfo *res;
if (res0 == NULL) {
- rb_raise(rb_eSocket, "host not found");
+ rb_raise(rb_eSocket, "host not found");
}
base = rb_ary_new();
- for (res = res0; res; res = res->ai_next) {
- ary = rsock_ipaddr(res->ai_addr, res->ai_addrlen, norevlookup);
- if (res->ai_canonname) {
- RARRAY_PTR(ary)[2] = rb_str_new2(res->ai_canonname);
- }
- rb_ary_push(ary, INT2FIX(res->ai_family));
- rb_ary_push(ary, INT2FIX(res->ai_socktype));
- rb_ary_push(ary, INT2FIX(res->ai_protocol));
- rb_ary_push(base, ary);
+ for (res = res0->ai; res; res = res->ai_next) {
+ ary = rsock_ipaddr(res->ai_addr, res->ai_addrlen, norevlookup);
+ if (res->ai_canonname) {
+ RARRAY_ASET(ary, 2, rb_str_new2(res->ai_canonname));
+ }
+ rb_ary_push(ary, INT2FIX(res->ai_family));
+ rb_ary_push(ary, INT2FIX(res->ai_socktype));
+ rb_ary_push(ary, INT2FIX(res->ai_protocol));
+ rb_ary_push(base, ary);
}
return base;
}
static VALUE
-sock_sockaddr(struct sockaddr *addr, size_t len)
+sock_sockaddr(struct sockaddr *addr, socklen_t len)
{
char *ptr;
switch (addr->sa_family) {
case AF_INET:
- ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr;
- len = sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr);
- break;
+ ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr;
+ len = (socklen_t)sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr);
+ break;
#ifdef AF_INET6
case AF_INET6:
- ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr;
- len = sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr);
- break;
+ ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr;
+ len = (socklen_t)sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr);
+ break;
#endif
default:
rb_raise(rb_eSocket, "unknown socket family:%d", addr->sa_family);
- break;
+ break;
}
return rb_str_new(ptr, len);
}
@@ -976,7 +944,18 @@ sock_sockaddr(struct sockaddr *addr, size_t len)
* call-seq:
* Socket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list]
*
- * Obtains the host information for _hostname_.
+ * Use Addrinfo.getaddrinfo instead.
+ * This method is deprecated for the following reasons:
+ *
+ * - The 3rd element of the result is the address family of the first address.
+ * The address families of the rest of the addresses are not returned.
+ * - Uncommon address representation:
+ * 4/16-bytes binary string to represent IPv4/IPv6 address.
+ * - gethostbyname() may take a long time and it may block other threads.
+ * (GVL cannot be released since gethostbyname() is not thread safe.)
+ * - This method uses gethostbyname() function already removed from POSIX.
+ *
+ * This method obtains the host information for _hostname_.
*
* p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"]
*
@@ -984,21 +963,39 @@ sock_sockaddr(struct sockaddr *addr, size_t len)
static VALUE
sock_s_gethostbyname(VALUE obj, VALUE host)
{
- rb_secure(3);
- return rsock_make_hostent(host, rsock_addrinfo(host, Qnil, SOCK_STREAM, AI_CANONNAME), sock_sockaddr);
+ rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead.");
+ struct rb_addrinfo *res =
+ rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil);
+ return rsock_make_hostent(host, res, sock_sockaddr);
}
/*
* call-seq:
* Socket.gethostbyaddr(address_string [, address_family]) => hostent
*
- * Obtains the host information for _address_.
+ * Use Addrinfo#getnameinfo instead.
+ * This method is deprecated for the following reasons:
+ *
+ * - Uncommon address representation:
+ * 4/16-bytes binary string to represent IPv4/IPv6 address.
+ * - gethostbyaddr() may take a long time and it may block other threads.
+ * (GVL cannot be released since gethostbyname() is not thread safe.)
+ * - This method uses gethostbyname() function already removed from POSIX.
+ *
+ * This method obtains the host information for _address_.
*
* p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
* #=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
+ *
+ * p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
+ * ["localhost", [], 2, "\x7F\x00\x00\x01"]
+ * p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
+ * #=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
+ * "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"]
+ *
*/
static VALUE
-sock_s_gethostbyaddr(int argc, VALUE *argv)
+sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _)
{
VALUE addr, family;
struct hostent *h;
@@ -1006,23 +1003,25 @@ sock_s_gethostbyaddr(int argc, VALUE *argv)
VALUE ary, names;
int t = AF_INET;
+ rb_warn("Socket.gethostbyaddr is deprecated; use Addrinfo#getnameinfo instead.");
+
rb_scan_args(argc, argv, "11", &addr, &family);
StringValue(addr);
if (!NIL_P(family)) {
- t = rsock_family_arg(family);
+ t = rsock_family_arg(family);
}
#ifdef AF_INET6
else if (RSTRING_LEN(addr) == 16) {
- t = AF_INET6;
+ t = AF_INET6;
}
#endif
- h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_LENINT(addr), t);
+ h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t);
if (h == NULL) {
#ifdef HAVE_HSTRERROR
- extern int h_errno;
- rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
+ extern int h_errno;
+ rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
#else
- rb_raise(rb_eSocket, "host not found");
+ rb_raise(rb_eSocket, "host not found");
#endif
}
ary = rb_ary_new();
@@ -1030,14 +1029,14 @@ sock_s_gethostbyaddr(int argc, VALUE *argv)
names = rb_ary_new();
rb_ary_push(ary, names);
if (h->h_aliases != NULL) {
- for (pch = h->h_aliases; *pch; pch++) {
- rb_ary_push(names, rb_str_new2(*pch));
- }
+ for (pch = h->h_aliases; *pch; pch++) {
+ rb_ary_push(names, rb_str_new2(*pch));
+ }
}
rb_ary_push(ary, INT2NUM(h->h_addrtype));
#ifdef h_addr
for (pch = h->h_addr_list; *pch; pch++) {
- rb_ary_push(ary, rb_str_new(*pch, h->h_length));
+ rb_ary_push(ary, rb_str_new(*pch, h->h_length));
}
#else
rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length));
@@ -1060,7 +1059,7 @@ sock_s_gethostbyaddr(int argc, VALUE *argv)
* Socket.getservbyname("syslog", "udp") #=> 514
*/
static VALUE
-sock_s_getservbyname(int argc, VALUE *argv)
+sock_s_getservbyname(int argc, VALUE *argv, VALUE _)
{
VALUE service, proto;
struct servent *sp;
@@ -1074,15 +1073,15 @@ sock_s_getservbyname(int argc, VALUE *argv)
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyname(servicename, protoname);
if (sp) {
- port = ntohs(sp->s_port);
+ port = ntohs(sp->s_port);
}
else {
- char *end;
+ char *end;
- port = STRTOUL(servicename, &end, 0);
- if (*end != '\0') {
- rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
- }
+ port = STRTOUL(servicename, &end, 0);
+ if (*end != '\0') {
+ rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
+ }
}
return INT2FIX(port);
}
@@ -1101,7 +1100,7 @@ sock_s_getservbyname(int argc, VALUE *argv)
*
*/
static VALUE
-sock_s_getservbyport(int argc, VALUE *argv)
+sock_s_getservbyport(int argc, VALUE *argv, VALUE _)
{
VALUE port, proto;
struct servent *sp;
@@ -1111,16 +1110,16 @@ sock_s_getservbyport(int argc, VALUE *argv)
rb_scan_args(argc, argv, "11", &port, &proto);
portnum = NUM2LONG(port);
if (portnum != (uint16_t)portnum) {
- const char *s = portnum > 0 ? "big" : "small";
- rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
+ const char *s = portnum > 0 ? "big" : "small";
+ rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
}
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyport((int)htons((uint16_t)portnum), protoname);
if (!sp) {
- rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
+ rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
}
- return rb_tainted_str_new2(sp->s_name);
+ return rb_str_new2(sp->s_name);
}
/*
@@ -1129,7 +1128,10 @@ sock_s_getservbyport(int argc, VALUE *argv)
*
* Obtains address information for _nodename_:_servname_.
*
- * _family_ should be an address family such as: :INET, :INET6, :UNIX, etc.
+ * Note that Addrinfo.getaddrinfo provides the same functionality in
+ * an object oriented style.
+ *
+ * _family_ should be an address family such as: :INET, :INET6, etc.
*
* _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc.
*
@@ -1150,16 +1152,17 @@ sock_s_getservbyport(int argc, VALUE *argv)
* be one of below. If _reverse_lookup_ is omitted, the default value is +nil+.
*
* +true+, +:hostname+: hostname is obtained from numeric address using reverse lookup, which may take a time.
- * +false+, +:numeric+: hostname is same as numeric address.
+ * +false+, +:numeric+: hostname is the same as numeric address.
* +nil+: obey to the current +do_not_reverse_lookup+ flag.
*
* If Addrinfo object is preferred, use Addrinfo.getaddrinfo.
*/
static VALUE
-sock_s_getaddrinfo(int argc, VALUE *argv)
+sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _)
{
VALUE host, port, family, socktype, protocol, flags, ret, revlookup;
- struct addrinfo hints, *res;
+ struct addrinfo hints;
+ struct rb_addrinfo *res;
int norevlookup;
rb_scan_args(argc, argv, "25", &host, &port, &family, &socktype, &protocol, &flags, &revlookup);
@@ -1168,21 +1171,22 @@ sock_s_getaddrinfo(int argc, VALUE *argv)
hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family);
if (!NIL_P(socktype)) {
- hints.ai_socktype = rsock_socktype_arg(socktype);
+ hints.ai_socktype = rsock_socktype_arg(socktype);
}
if (!NIL_P(protocol)) {
- hints.ai_protocol = NUM2INT(protocol);
+ hints.ai_protocol = NUM2INT(protocol);
}
if (!NIL_P(flags)) {
- hints.ai_flags = NUM2INT(flags);
+ hints.ai_flags = NUM2INT(flags);
}
if (NIL_P(revlookup) || !rsock_revlookup_flag(revlookup, &norevlookup)) {
- norevlookup = rsock_do_not_reverse_lookup;
+ norevlookup = rsock_do_not_reverse_lookup;
}
- res = rsock_getaddrinfo(host, port, &hints, 0);
+
+ res = rsock_getaddrinfo(host, port, &hints, 0, Qnil);
ret = make_addrinfo(res, norevlookup);
- freeaddrinfo(res);
+ rb_freeaddrinfo(res);
return ret;
}
@@ -1209,15 +1213,15 @@ sock_s_getaddrinfo(int argc, VALUE *argv)
* If Addrinfo object is preferred, use Addrinfo#getnameinfo.
*/
static VALUE
-sock_s_getnameinfo(int argc, VALUE *argv)
+sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
{
VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp;
- char *hptr, *pptr;
char hbuf[1024], pbuf[1024];
int fl;
- struct addrinfo hints, *res = NULL, *r;
+ struct rb_addrinfo *res = NULL;
+ struct addrinfo hints, *r;
int error, saved_errno;
- struct sockaddr_storage ss;
+ union_sockaddr ss;
struct sockaddr *sap;
socklen_t salen;
@@ -1226,122 +1230,92 @@ sock_s_getnameinfo(int argc, VALUE *argv)
fl = 0;
if (!NIL_P(flags)) {
- fl = NUM2INT(flags);
+ fl = NUM2INT(flags);
}
tmp = rb_check_sockaddr_string_type(sa);
if (!NIL_P(tmp)) {
- sa = tmp;
- if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
- rb_raise(rb_eTypeError, "sockaddr length too big");
- }
- memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
- if ((size_t)RSTRING_LEN(sa) != SS_LEN(&ss)) {
- rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
- }
- sap = (struct sockaddr*)&ss;
- salen = RSTRING_LEN(sa);
- goto call_nameinfo;
+ sa = tmp;
+ if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
+ rb_raise(rb_eTypeError, "sockaddr length too big");
+ }
+ memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
+ if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) {
+ rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
+ }
+ sap = &ss.addr;
+ salen = RSTRING_SOCKLEN(sa);
+ goto call_nameinfo;
}
tmp = rb_check_array_type(sa);
if (!NIL_P(tmp)) {
- sa = tmp;
- MEMZERO(&hints, struct addrinfo, 1);
- if (RARRAY_LEN(sa) == 3) {
- af = RARRAY_PTR(sa)[0];
- port = RARRAY_PTR(sa)[1];
- host = RARRAY_PTR(sa)[2];
- }
- else if (RARRAY_LEN(sa) >= 4) {
- af = RARRAY_PTR(sa)[0];
- port = RARRAY_PTR(sa)[1];
- host = RARRAY_PTR(sa)[3];
- if (NIL_P(host)) {
- host = RARRAY_PTR(sa)[2];
- }
- else {
- /*
- * 4th element holds numeric form, don't resolve.
- * see rsock_ipaddr().
- */
+ sa = tmp;
+ MEMZERO(&hints, struct addrinfo, 1);
+ if (RARRAY_LEN(sa) == 3) {
+ af = RARRAY_AREF(sa, 0);
+ port = RARRAY_AREF(sa, 1);
+ host = RARRAY_AREF(sa, 2);
+ }
+ else if (RARRAY_LEN(sa) >= 4) {
+ af = RARRAY_AREF(sa, 0);
+ port = RARRAY_AREF(sa, 1);
+ host = RARRAY_AREF(sa, 3);
+ if (NIL_P(host)) {
+ host = RARRAY_AREF(sa, 2);
+ }
+ else {
+ /*
+ * 4th element holds numeric form, don't resolve.
+ * see rsock_ipaddr().
+ */
#ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */
- hints.ai_flags |= AI_NUMERICHOST;
+ hints.ai_flags |= AI_NUMERICHOST;
#endif
- }
- }
- else {
- rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
- RARRAY_LEN(sa));
- }
- /* host */
- if (NIL_P(host)) {
- hptr = NULL;
- }
- else {
- strncpy(hbuf, StringValuePtr(host), sizeof(hbuf));
- hbuf[sizeof(hbuf) - 1] = '\0';
- hptr = hbuf;
- }
- /* port */
- if (NIL_P(port)) {
- strcpy(pbuf, "0");
- pptr = NULL;
- }
- else if (FIXNUM_P(port)) {
- snprintf(pbuf, sizeof(pbuf), "%ld", NUM2LONG(port));
- pptr = pbuf;
- }
- else {
- strncpy(pbuf, StringValuePtr(port), sizeof(pbuf));
- pbuf[sizeof(pbuf) - 1] = '\0';
- pptr = pbuf;
- }
- hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
- /* af */
+ }
+ }
+ else {
+ rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
+ RARRAY_LEN(sa));
+ }
+ hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
+ /* af */
hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
- error = rb_getaddrinfo(hptr, pptr, &hints, &res);
- if (error) goto error_exit_addr;
- sap = res->ai_addr;
- salen = res->ai_addrlen;
+ res = rsock_getaddrinfo(host, port, &hints, 0, Qnil);
+ sap = res->ai->ai_addr;
+ salen = res->ai->ai_addrlen;
}
else {
- rb_raise(rb_eTypeError, "expecting String or Array");
+ rb_raise(rb_eTypeError, "expecting String or Array");
}
call_nameinfo:
error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf),
- pbuf, sizeof(pbuf), fl);
+ pbuf, sizeof(pbuf), fl);
if (error) goto error_exit_name;
if (res) {
- for (r = res->ai_next; r; r = r->ai_next) {
- char hbuf2[1024], pbuf2[1024];
+ for (r = res->ai->ai_next; r; r = r->ai_next) {
+ char hbuf2[1024], pbuf2[1024];
- sap = r->ai_addr;
+ sap = r->ai_addr;
salen = r->ai_addrlen;
- error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
- pbuf2, sizeof(pbuf2), fl);
- if (error) goto error_exit_name;
- if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
- freeaddrinfo(res);
- rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
- }
- }
- freeaddrinfo(res);
+ error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
+ pbuf2, sizeof(pbuf2), fl);
+ if (error) goto error_exit_name;
+ if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
+ rb_freeaddrinfo(res);
+ rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
+ }
+ }
+ rb_freeaddrinfo(res);
}
return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
- error_exit_addr:
- saved_errno = errno;
- if (res) freeaddrinfo(res);
- errno = saved_errno;
- rsock_raise_socket_error("getaddrinfo", error);
-
error_exit_name:
saved_errno = errno;
- if (res) freeaddrinfo(res);
+ if (res) rb_freeaddrinfo(res);
errno = saved_errno;
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
}
/*
@@ -1361,12 +1335,10 @@ sock_s_getnameinfo(int argc, VALUE *argv)
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
- struct addrinfo *res = rsock_addrinfo(host, port, 0, 0);
- VALUE addr = rb_str_new((char*)res->ai_addr, res->ai_addrlen);
+ struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0, Qnil);
+ VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
- freeaddrinfo(res);
- OBJ_INFECT(addr, port);
- OBJ_INFECT(addr, host);
+ rb_freeaddrinfo(res);
return addr;
}
@@ -1407,12 +1379,11 @@ sock_s_unpack_sockaddr_in(VALUE self, VALUE addr)
rb_raise(rb_eArgError, "not an AF_INET sockaddr");
#endif
}
- host = rsock_make_ipaddr((struct sockaddr*)sockaddr, RSTRING_LEN(addr));
- OBJ_INFECT(host, addr);
+ host = rsock_make_ipaddr((struct sockaddr*)sockaddr, RSTRING_SOCKLEN(addr));
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
}
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
/*
* call-seq:
@@ -1431,15 +1402,13 @@ sock_s_pack_sockaddr_un(VALUE self, VALUE path)
VALUE addr;
StringValue(path);
- MEMZERO(&sockaddr, struct sockaddr_un, 1);
- sockaddr.sun_family = AF_UNIX;
+ INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
- OBJ_INFECT(addr, path);
return addr;
}
@@ -1472,72 +1441,99 @@ sock_s_unpack_sockaddr_un(VALUE self, VALUE addr)
rb_raise(rb_eArgError, "not an AF_UNIX sockaddr");
}
if (sizeof(struct sockaddr_un) < (size_t)RSTRING_LEN(addr)) {
- rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
- RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un));
+ rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
+ RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un));
}
- path = rsock_unixpath_str(sockaddr, RSTRING_LENINT(addr));
- OBJ_INFECT(path, addr);
+ path = rsock_unixpath_str(sockaddr, RSTRING_SOCKLEN(addr));
return path;
}
#endif
#if defined(HAVE_GETIFADDRS) || defined(SIOCGLIFCONF) || defined(SIOCGIFCONF) || defined(_WIN32)
-static VALUE
-sockaddr_obj(struct sockaddr *addr)
-{
- socklen_t len;
-#if defined(AF_INET6) && defined(__KAME__)
- struct sockaddr_in6 addr6;
-#endif
+static socklen_t
+sockaddr_len(struct sockaddr *addr)
+{
if (addr == NULL)
- return Qnil;
+ return 0;
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ if (addr->sa_len != 0)
+ return addr->sa_len;
+#endif
switch (addr->sa_family) {
case AF_INET:
- len = (socklen_t)sizeof(struct sockaddr_in);
- break;
+ return (socklen_t)sizeof(struct sockaddr_in);
#ifdef AF_INET6
case AF_INET6:
- len = (socklen_t)sizeof(struct sockaddr_in6);
-# ifdef __KAME__
- /* KAME uses the 2nd 16bit word of link local IPv6 address as interface index internally */
- /* http://orange.kame.net/dev/cvsweb.cgi/kame/IMPLEMENTATION */
- /* convert fe80:1::1 to fe80::1%1 */
- memcpy(&addr6, addr, len);
- addr = (struct sockaddr *)&addr6;
- if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
- addr6.sin6_scope_id == 0 &&
- (addr6.sin6_addr.s6_addr[2] || addr6.sin6_addr.s6_addr[3])) {
- addr6.sin6_scope_id = (addr6.sin6_addr.s6_addr[2] << 8) | addr6.sin6_addr.s6_addr[3];
- addr6.sin6_addr.s6_addr[2] = 0;
- addr6.sin6_addr.s6_addr[3] = 0;
- }
-# endif
- break;
+ return (socklen_t)sizeof(struct sockaddr_in6);
#endif
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
case AF_UNIX:
- len = (socklen_t)sizeof(struct sockaddr_un);
- break;
+ return (socklen_t)sizeof(struct sockaddr_un);
+#endif
+
+#ifdef AF_PACKET
+ case AF_PACKET:
+ return (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + ((struct sockaddr_ll *)addr)->sll_halen);
#endif
default:
- len = (socklen_t)sizeof(struct sockaddr_in);
- break;
+ return (socklen_t)(offsetof(struct sockaddr, sa_family) + sizeof(addr->sa_family));
+ }
+}
+
+socklen_t
+rsock_sockaddr_len(struct sockaddr *addr)
+{
+ return sockaddr_len(addr);
+}
+
+static VALUE
+sockaddr_obj(struct sockaddr *addr, socklen_t len)
+{
+#if defined(AF_INET6) && defined(__KAME__)
+ struct sockaddr_in6 addr6;
+#endif
+
+ if (addr == NULL)
+ return Qnil;
+
+ len = sockaddr_len(addr);
+
+#if defined(__KAME__) && defined(AF_INET6)
+ if (addr->sa_family == AF_INET6) {
+ /* KAME uses the 2nd 16bit word of link local IPv6 address as interface index internally */
+ /* http://orange.kame.net/dev/cvsweb.cgi/kame/IMPLEMENTATION */
+ /* convert fe80:1::1 to fe80::1%1 */
+ len = (socklen_t)sizeof(struct sockaddr_in6);
+ memcpy(&addr6, addr, len);
+ addr = (struct sockaddr *)&addr6;
+ if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
+ addr6.sin6_scope_id == 0 &&
+ (addr6.sin6_addr.s6_addr[2] || addr6.sin6_addr.s6_addr[3])) {
+ addr6.sin6_scope_id = (addr6.sin6_addr.s6_addr[2] << 8) | addr6.sin6_addr.s6_addr[3];
+ addr6.sin6_addr.s6_addr[2] = 0;
+ addr6.sin6_addr.s6_addr[3] = 0;
+ }
}
-#ifdef SA_LEN
- if (len < (socklen_t)SA_LEN(addr))
- len = (socklen_t)SA_LEN(addr);
#endif
return rsock_addrinfo_new(addr, len, addr->sa_family, 0, 0, Qnil, Qnil);
}
+
+VALUE
+rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len)
+{
+ return sockaddr_obj(addr, len);
+}
+
#endif
-#if defined(HAVE_GETIFADDRS) || (defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && !defined(__hpux)) || defined(SIOCGIFCONF) || defined(_WIN32)
+#if defined(HAVE_GETIFADDRS) || (defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)) || defined(SIOCGIFCONF) || defined(_WIN32)
/*
* call-seq:
* Socket.ip_address_list => array
@@ -1570,21 +1566,41 @@ socket_s_ip_address_list(VALUE self)
list = rb_ary_new();
for (p = ifp; p; p = p->ifa_next) {
if (p->ifa_addr != NULL && IS_IP_FAMILY(p->ifa_addr->sa_family)) {
- rb_ary_push(list, sockaddr_obj(p->ifa_addr));
+ struct sockaddr *addr = p->ifa_addr;
+#if defined(AF_INET6) && defined(__sun)
+ /*
+ * OpenIndiana SunOS 5.11 getifaddrs() returns IPv6 link local
+ * address with sin6_scope_id == 0.
+ * So fill it from the interface name (ifa_name).
+ */
+ struct sockaddr_in6 addr6;
+ if (addr->sa_family == AF_INET6) {
+ socklen_t len = (socklen_t)sizeof(struct sockaddr_in6);
+ memcpy(&addr6, addr, len);
+ addr = (struct sockaddr *)&addr6;
+ if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
+ addr6.sin6_scope_id == 0) {
+ unsigned int ifindex = if_nametoindex(p->ifa_name);
+ if (ifindex != 0) {
+ addr6.sin6_scope_id = ifindex;
+ }
+ }
+ }
+#endif
+ rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
}
freeifaddrs(ifp);
return list;
-#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && !defined(__hpux)
+#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)
/* Solaris if_tcp(7P) */
- /* HP-UX has SIOCGLIFCONF too. But it uses different struct */
int fd = -1;
int ret;
struct lifnum ln;
struct lifconf lc;
- char *reason = NULL;
+ const char *reason = NULL;
int save_errno;
int i;
VALUE list = Qnil;
@@ -1593,15 +1609,15 @@ socket_s_ip_address_list(VALUE self)
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
- rb_sys_fail("socket");
+ rb_sys_fail("socket(2)");
memset(&ln, 0, sizeof(ln));
ln.lifn_family = AF_UNSPEC;
ret = ioctl(fd, SIOCGLIFNUM, &ln);
if (ret == -1) {
- reason = "SIOCGLIFNUM";
- goto finish;
+ reason = "SIOCGLIFNUM";
+ goto finish;
}
memset(&lc, 0, sizeof(lc));
@@ -1612,13 +1628,13 @@ socket_s_ip_address_list(VALUE self)
ret = ioctl(fd, SIOCGLIFCONF, &lc);
if (ret == -1) {
- reason = "SIOCGLIFCONF";
- goto finish;
+ reason = "SIOCGLIFCONF";
+ goto finish;
}
list = rb_ary_new();
for (i = 0; i < ln.lifn_count; i++) {
- struct lifreq *req = &lc.lifc_req[i];
+ struct lifreq *req = &lc.lifc_req[i];
if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
if (req->lifr_addr.ss_family == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) &&
@@ -1632,26 +1648,25 @@ socket_s_ip_address_list(VALUE self)
}
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id = req2.lifr_index;
}
- rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr));
+ rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr, req->lifr_addrlen));
}
}
finish:
save_errno = errno;
- if (lc.lifc_buf != NULL)
- xfree(lc.lifc_req);
+ xfree(lc.lifc_req);
if (fd != -1)
- close(fd);
+ close(fd);
errno = save_errno;
if (reason)
- rb_sys_fail(reason);
+ rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
int fd = -1;
int ret;
-#define EXTRA_SPACE (sizeof(struct ifconf) + sizeof(struct sockaddr_storage))
+#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
char initbuf[4096+EXTRA_SPACE];
char *buf = initbuf;
int bufsize;
@@ -1663,7 +1678,7 @@ socket_s_ip_address_list(VALUE self)
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
- rb_sys_fail("socket");
+ rb_sys_fail("socket(2)");
bufsize = sizeof(initbuf);
buf = initbuf;
@@ -1683,17 +1698,17 @@ socket_s_ip_address_list(VALUE self)
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
- if (bufsize < conf.ifc_len) {
- /* NetBSD returns required size for all interfaces. */
- bufsize = conf.ifc_len + EXTRA_SPACE;
- }
- else {
- bufsize = bufsize << 1;
- }
- if (buf == initbuf)
- buf = NULL;
- buf = xrealloc(buf, bufsize);
- goto retry;
+ if (bufsize < conf.ifc_len) {
+ /* NetBSD returns required size for all interfaces. */
+ bufsize = conf.ifc_len + EXTRA_SPACE;
+ }
+ else {
+ bufsize = bufsize << 1;
+ }
+ if (buf == initbuf)
+ buf = NULL;
+ buf = xrealloc(buf, bufsize);
+ goto retry;
}
close(fd);
@@ -1702,11 +1717,11 @@ socket_s_ip_address_list(VALUE self)
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
- struct sockaddr *addr = &req->ifr_addr;
+ struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
- rb_ary_push(list, sockaddr_obj(addr));
- }
-#ifdef HAVE_SA_LEN
+ rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
+ }
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
(sizeof(struct ifreq) + \
@@ -1714,9 +1729,9 @@ socket_s_ip_address_list(VALUE self)
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
- req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
+ req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
- req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
+ req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
@@ -1726,57 +1741,57 @@ socket_s_ip_address_list(VALUE self)
if (buf != initbuf)
xfree(buf);
if (fd != -1)
- close(fd);
+ close(fd);
errno = save_errno;
if (reason)
- rb_sys_fail(reason);
+ rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
- unsigned LONG_LONG dummy0;
- struct ip_adapter_unicast_address_st *Next;
- struct {
- struct sockaddr *lpSockaddr;
- int iSockaddrLength;
- } Address;
- int dummy1;
- int dummy2;
- int dummy3;
- long dummy4;
- long dummy5;
- long dummy6;
+ unsigned LONG_LONG dummy0;
+ struct ip_adapter_unicast_address_st *Next;
+ struct {
+ struct sockaddr *lpSockaddr;
+ int iSockaddrLength;
+ } Address;
+ int dummy1;
+ int dummy2;
+ int dummy3;
+ long dummy4;
+ long dummy5;
+ long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
- unsigned LONG_LONG dummy0;
- struct ip_adapter_anycast_address_st *Next;
- struct {
- struct sockaddr *lpSockaddr;
- int iSockaddrLength;
- } Address;
+ unsigned LONG_LONG dummy0;
+ struct ip_adapter_anycast_address_st *Next;
+ struct {
+ struct sockaddr *lpSockaddr;
+ int iSockaddrLength;
+ } Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
- unsigned LONG_LONG dummy0;
- struct ip_adapter_addresses_st *Next;
- void *dummy1;
- ip_adapter_unicast_address_t *FirstUnicastAddress;
- ip_adapter_anycast_address_t *FirstAnycastAddress;
- void *dummy2;
- void *dummy3;
- void *dummy4;
- void *dummy5;
- void *dummy6;
- BYTE dummy7[8];
- DWORD dummy8;
- DWORD dummy9;
- DWORD dummy10;
- DWORD IfType;
- int OperStatus;
- DWORD dummy12;
- DWORD dummy13[16];
- void *dummy14;
+ unsigned LONG_LONG dummy0;
+ struct ip_adapter_addresses_st *Next;
+ void *dummy1;
+ ip_adapter_unicast_address_t *FirstUnicastAddress;
+ ip_adapter_anycast_address_t *FirstAnycastAddress;
+ void *dummy2;
+ void *dummy3;
+ void *dummy4;
+ void *dummy5;
+ void *dummy6;
+ BYTE dummy7[8];
+ DWORD dummy8;
+ DWORD dummy9;
+ DWORD dummy10;
+ DWORD IfType;
+ int OperStatus;
+ DWORD dummy12;
+ DWORD dummy13[16];
+ void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
@@ -1788,49 +1803,49 @@ socket_s_ip_address_list(VALUE self)
h = LoadLibrary("iphlpapi.dll");
if (!h)
- rb_notimplement();
+ rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
- FreeLibrary(h);
- rb_notimplement();
+ FreeLibrary(h);
+ rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
- errno = rb_w32_map_errno(ret);
- FreeLibrary(h);
- rb_sys_fail("GetAdaptersAddresses");
+ errno = rb_w32_map_errno(ret);
+ FreeLibrary(h);
+ rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
- errno = rb_w32_map_errno(ret);
- FreeLibrary(h);
- rb_sys_fail("GetAdaptersAddresses");
+ errno = rb_w32_map_errno(ret);
+ FreeLibrary(h);
+ rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
- ip_adapter_unicast_address_t *uni;
- ip_adapter_anycast_address_t *any;
- if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
- continue;
- for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
+ ip_adapter_unicast_address_t *uni;
+ ip_adapter_anycast_address_t *any;
+ if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
+ continue;
+ for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
- if (uni->Address.lpSockaddr->sa_family == AF_INET)
+ if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
- if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
+ if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
- rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr));
- }
- for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
+ rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
+ }
+ for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
- if (any->Address.lpSockaddr->sa_family == AF_INET)
+ if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
- if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
+ if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
- rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr));
- }
+ rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
+ }
}
FreeLibrary(h);
@@ -1841,9 +1856,72 @@ socket_s_ip_address_list(VALUE self)
#define socket_s_ip_address_list rb_f_notimplement
#endif
+/*
+ * call-seq:
+ * Socket.tcp_fast_fallback -> true or false
+ *
+ * Returns whether Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]),
+ * which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp,
+ * is enabled or disabled.
+ *
+ * If true, it is enabled for TCPSocket.new and Socket.tcp.
+ * (Note: Happy Eyeballs Version 2 is not provided when using TCPSocket.new on Windows.)
+ *
+ * If false, Happy Eyeballs Version 2 is disabled.
+ *
+ * For details on Happy Eyeballs Version 2,
+ * see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=].
+ */
+VALUE socket_s_tcp_fast_fallback(VALUE self) {
+ return rb_ivar_get(rb_cSocket, tcp_fast_fallback);
+}
+
+/*
+ * call-seq:
+ * Socket.tcp_fast_fallback= -> true or false
+ *
+ * Enable or disable Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305])
+ * globally, which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp.
+ *
+ * When set to true, the feature is enabled for both `TCPSocket.new` and `Socket.tcp`.
+ * (Note: This feature is not available when using TCPSocket.new on Windows.)
+ *
+ * When set to false, the behavior reverts to that of Ruby 3.3 or earlier.
+ *
+ * The default value is true if no value is explicitly set by calling this method.
+ * However, when the environment variable RUBY_TCP_NO_FAST_FALLBACK=1 is set,
+ * the default is false.
+ *
+ * To control the setting on a per-method basis, use the fast_fallback keyword argument for each method.
+ *
+ * === Happy Eyeballs Version 2
+ * Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305])
+ * is an algorithm designed to improve client socket connectivity.<br>
+ * It aims for more reliable and efficient connections by performing hostname resolution
+ * and connection attempts in parallel, instead of serially.
+ *
+ * Starting from Ruby 3.4, this method operates as follows with this algorithm:
+ *
+ * 1. Start resolving both IPv6 and IPv4 addresses concurrently.
+ * 2. Start connecting to the one of the addresses that are obtained first.<br>If IPv4 addresses are obtained first,
+ * the method waits 50 ms for IPv6 name resolution to prioritize IPv6 connections.
+ * 3. After starting a connection attempt, wait 250 ms for the connection to be established.<br>
+ * If no connection is established within this time, a new connection is started every 250 ms<br>
+ * until a connection is established or there are no more candidate addresses.<br>
+ * (Although RFC 8305 strictly specifies sorting addresses,<br>
+ * this method only alternates between IPv6 / IPv4 addresses due to the performance concerns)
+ * 4. Once a connection is established, all remaining connection attempts are canceled.
+ */
+VALUE socket_s_tcp_fast_fallback_set(VALUE self, VALUE value) {
+ rb_ivar_set(rb_cSocket, tcp_fast_fallback, value);
+ return value;
+}
+
void
-Init_socket()
+Init_socket(void)
{
+ rb_ext_ractor_safe(true);
+
rsock_init_basicsocket();
/*
@@ -1860,7 +1938,7 @@ Init_socket()
*
* === What's a socket?
*
- * Sockets are endpoints of a bidirectionnal communication channel.
+ * Sockets are endpoints of a bidirectional communication channel.
* Sockets can communicate within a process, between processes on the same
* machine or between different machines. There are many types of socket:
* TCPSocket, UDPSocket or UNIXSocket for example.
@@ -1885,7 +1963,7 @@ Init_socket()
*
* *hostname:*
* The identifier of a network interface:
- * * a string (hostname, IPv4 or IPv6 adress or +broadcast+
+ * * a string (hostname, IPv4 or IPv6 address or +broadcast+
* which specifies a broadcast address)
* * a zero-length string which specifies INADDR_ANY
* * an integer (interpreted as binary address in host byte order).
@@ -1897,6 +1975,8 @@ Init_socket()
*
* Let's create an internet socket using the IPv4 protocol in a C-like manner:
*
+ * require 'socket'
+ *
* s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
* s.connect Socket.pack_sockaddr_in(80, 'example.com')
*
@@ -1965,17 +2045,38 @@ Init_socket()
rsock_init_socket_init();
+ const char *tcp_no_fast_fallback_config = getenv("RUBY_TCP_NO_FAST_FALLBACK");
+ VALUE fast_fallback_default;
+ if (tcp_no_fast_fallback_config == NULL || strcmp(tcp_no_fast_fallback_config, "0") == 0) {
+ fast_fallback_default = Qtrue;
+ } else {
+ fast_fallback_default = Qfalse;
+ }
+ tcp_fast_fallback = rb_intern_const("tcp_fast_fallback");
+ rb_ivar_set(rb_cSocket, tcp_fast_fallback, fast_fallback_default);
+
rb_define_method(rb_cSocket, "initialize", sock_initialize, -1);
rb_define_method(rb_cSocket, "connect", sock_connect, 1);
- rb_define_method(rb_cSocket, "connect_nonblock", sock_connect_nonblock, 1);
+
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cSocket,
+ "__connect_nonblock", sock_connect_nonblock, 2);
+
rb_define_method(rb_cSocket, "bind", sock_bind, 1);
rb_define_method(rb_cSocket, "listen", rsock_sock_listen, 1);
rb_define_method(rb_cSocket, "accept", sock_accept, 0);
- rb_define_method(rb_cSocket, "accept_nonblock", sock_accept_nonblock, 0);
+
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cSocket,
+ "__accept_nonblock", sock_accept_nonblock, 1);
+
rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1);
- rb_define_method(rb_cSocket, "recvfrom_nonblock", sock_recvfrom_nonblock, -1);
+
+ /* for ext/socket/lib/socket.rb use only: */
+ rb_define_private_method(rb_cSocket,
+ "__recvfrom_nonblock", sock_recvfrom_nonblock, 4);
rb_define_singleton_method(rb_cSocket, "socketpair", rsock_sock_s_socketpair, -1);
rb_define_singleton_method(rb_cSocket, "pair", rsock_sock_s_socketpair, -1);
@@ -1989,11 +2090,17 @@ Init_socket()
rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2);
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2);
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1);
-#ifdef HAVE_SYS_UN_H
+#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN
rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1);
rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1);
rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1);
#endif
rb_define_singleton_method(rb_cSocket, "ip_address_list", socket_s_ip_address_list, 0);
+
+ rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback", socket_s_tcp_fast_fallback, 0);
+ rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback=", socket_s_tcp_fast_fallback_set, 1);
+
+#undef rb_intern
+ sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
}