summaryrefslogtreecommitdiff
path: root/ext/socket/ancdata.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/socket/ancdata.c')
-rw-r--r--ext/socket/ancdata.c586
1 files changed, 251 insertions, 335 deletions
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index 37d2860224..f1e9e42524 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -2,7 +2,9 @@
#include <time.h>
-#if defined(HAVE_ST_MSG_CONTROL)
+static VALUE sym_wait_readable, sym_wait_writable;
+
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
static VALUE rb_cAncillaryData;
static VALUE
@@ -86,9 +88,9 @@ ancillary_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE vtype, VALUE
static VALUE
ancdata_new(int family, int level, int type, VALUE data)
{
- NEWOBJ_OF(obj, struct RObject, rb_cAncillaryData, T_OBJECT);
+ VALUE obj = rb_obj_alloc(rb_cAncillaryData);
StringValue(data);
- ancillary_initialize((VALUE)obj, INT2NUM(family), INT2NUM(level), INT2NUM(type), data);
+ ancillary_initialize(obj, INT2NUM(family), INT2NUM(level), INT2NUM(type), data);
return (VALUE)obj;
}
@@ -204,7 +206,7 @@ ancillary_s_unix_rights(int argc, VALUE *argv, VALUE klass)
str = rb_str_buf_new(sizeof(int) * argc);
for (i = 0 ; i < argc; i++) {
- VALUE obj = RARRAY_PTR(ary)[i];
+ VALUE obj = RARRAY_AREF(ary, i);
rb_io_t *fptr;
int fd;
GetOpenFile(obj, fptr);
@@ -276,8 +278,8 @@ ancillary_unix_rights(VALUE self)
* returns the timestamp as a time object.
*
* _ancillarydata_ should be one of following type:
- * - SOL_SOCKET/SCM_TIMESTAMP (micro second) GNU/Linux, FreeBSD, NetBSD, OpenBSD, Solaris, MacOS X
- * - SOL_SOCKET/SCM_TIMESTAMPNS (nano second) GNU/Linux
+ * - SOL_SOCKET/SCM_TIMESTAMP (microsecond) GNU/Linux, FreeBSD, NetBSD, OpenBSD, Solaris, MacOS X
+ * - SOL_SOCKET/SCM_TIMESTAMPNS (nanosecond) GNU/Linux
* - SOL_SOCKET/SCM_BINTIME (2**(-64) second) FreeBSD
*
* Addrinfo.udp("127.0.0.1", 0).bind {|s1|
@@ -331,11 +333,11 @@ ancillary_timestamp(VALUE self)
if (level == SOL_SOCKET && type == SCM_BINTIME &&
RSTRING_LEN(data) == sizeof(struct bintime)) {
struct bintime bt;
- VALUE d, timev;
+ VALUE d, timev;
memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt));
- d = ULL2NUM(0x100000000ULL);
- d = mul(d,d);
- timev = add(TIMET2NUM(bt.sec), quo(ULL2NUM(bt.frac), d));
+ d = ULL2NUM(0x100000000ULL);
+ d = mul(d,d);
+ timev = add(TIMET2NUM(bt.sec), quo(ULL2NUM(bt.frac), d));
result = rb_time_num_new(timev, Qnil);
}
# endif
@@ -357,6 +359,8 @@ ancillary_timestamp(VALUE self)
*
* The size and endian is dependent on the host.
*
+ * require 'socket'
+ *
* p Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, STDERR.fileno)
* #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2>
*/
@@ -573,9 +577,7 @@ extract_ipv6_pktinfo(VALUE self, struct in6_pktinfo *pktinfo_ptr, struct sockadd
memcpy(pktinfo_ptr, RSTRING_PTR(data), sizeof(*pktinfo_ptr));
- memset(sa_ptr, 0, sizeof(*sa_ptr));
- SET_SA_LEN((struct sockaddr *)sa_ptr, sizeof(struct sockaddr_in6));
- sa_ptr->sin6_family = AF_INET6;
+ INIT_SOCKADDR((struct sockaddr *)sa_ptr, AF_INET6, sizeof(*sa_ptr));
memcpy(&sa_ptr->sin6_addr, &pktinfo_ptr->ipi6_addr, sizeof(sa_ptr->sin6_addr));
if (IN6_IS_ADDR_LINKLOCAL(&sa_ptr->sin6_addr))
sa_ptr->sin6_scope_id = pktinfo_ptr->ipi6_ifindex;
@@ -695,7 +697,7 @@ anc_inspect_passcred_credentials(int level, int type, VALUE data, VALUE ret)
struct ucred cred;
memcpy(&cred, RSTRING_PTR(data), sizeof(struct ucred));
rb_str_catf(ret, " pid=%u uid=%u gid=%u", cred.pid, cred.uid, cred.gid);
- rb_str_cat2(ret, " (ucred)");
+ rb_str_cat2(ret, " (ucred)");
return 1;
}
else {
@@ -710,7 +712,7 @@ static int
anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret)
{
if (level != SOL_SOCKET && type != SCM_CREDS)
- return 0;
+ return 0;
/*
* FreeBSD has struct cmsgcred and struct sockcred.
@@ -725,46 +727,46 @@ anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret)
#if defined(HAVE_TYPE_STRUCT_CMSGCRED) /* FreeBSD */
if (RSTRING_LEN(data) == sizeof(struct cmsgcred)) {
- struct cmsgcred cred;
+ struct cmsgcred cred;
memcpy(&cred, RSTRING_PTR(data), sizeof(struct cmsgcred));
rb_str_catf(ret, " pid=%u", cred.cmcred_pid);
rb_str_catf(ret, " uid=%u", cred.cmcred_uid);
rb_str_catf(ret, " euid=%u", cred.cmcred_euid);
rb_str_catf(ret, " gid=%u", cred.cmcred_gid);
- if (cred.cmcred_ngroups) {
- int i;
- const char *sep = " groups=";
- for (i = 0; i < cred.cmcred_ngroups; i++) {
- rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]);
- sep = ",";
- }
- }
- rb_str_cat2(ret, " (cmsgcred)");
+ if (cred.cmcred_ngroups) {
+ int i;
+ const char *sep = " groups=";
+ for (i = 0; i < cred.cmcred_ngroups; i++) {
+ rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]);
+ sep = ",";
+ }
+ }
+ rb_str_cat2(ret, " (cmsgcred)");
return 1;
}
#endif
#if defined(HAVE_TYPE_STRUCT_SOCKCRED) /* FreeBSD, NetBSD */
if ((size_t)RSTRING_LEN(data) >= SOCKCREDSIZE(0)) {
- struct sockcred cred0, *cred;
+ struct sockcred cred0, *cred;
memcpy(&cred0, RSTRING_PTR(data), SOCKCREDSIZE(0));
- if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) {
- cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups));
- memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups));
- rb_str_catf(ret, " uid=%u", cred->sc_uid);
- rb_str_catf(ret, " euid=%u", cred->sc_euid);
- rb_str_catf(ret, " gid=%u", cred->sc_gid);
- rb_str_catf(ret, " egid=%u", cred->sc_egid);
- if (cred0.sc_ngroups) {
- int i;
- const char *sep = " groups=";
- for (i = 0; i < cred0.sc_ngroups; i++) {
- rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]);
- sep = ",";
- }
- }
- rb_str_cat2(ret, " (sockcred)");
- return 1;
- }
+ if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) {
+ cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups));
+ memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups));
+ rb_str_catf(ret, " uid=%u", cred->sc_uid);
+ rb_str_catf(ret, " euid=%u", cred->sc_euid);
+ rb_str_catf(ret, " gid=%u", cred->sc_gid);
+ rb_str_catf(ret, " egid=%u", cred->sc_egid);
+ if (cred0.sc_ngroups) {
+ int i;
+ const char *sep = " groups=";
+ for (i = 0; i < cred0.sc_ngroups; i++) {
+ rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]);
+ sep = ",";
+ }
+ }
+ rb_str_cat2(ret, " (sockcred)");
+ return 1;
+ }
}
#endif
return 0;
@@ -849,6 +851,12 @@ anc_inspect_ipv6_pktinfo(int level, int type, VALUE data, VALUE ret)
}
#endif
+#ifdef HAVE_GMTIME_R
+# define LOCALTIME(time, tm) localtime_r(&(time), &(tm))
+#else
+# define LOCALTIME(time, tm) ((tm) = *localtime(&(time)))
+#endif
+
#if defined(SCM_TIMESTAMP) /* GNU/Linux, FreeBSD, NetBSD, OpenBSD, MacOS X, Solaris */
static int
inspect_timeval_as_abstime(int level, int optname, VALUE data, VALUE ret)
@@ -860,7 +868,7 @@ inspect_timeval_as_abstime(int level, int optname, VALUE data, VALUE ret)
char buf[32];
memcpy((char*)&tv, RSTRING_PTR(data), sizeof(tv));
time = tv.tv_sec;
- tm = *localtime(&time);
+ LOCALTIME(time, tm);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
rb_str_catf(ret, " %s.%06ld", buf, (long)tv.tv_usec);
return 1;
@@ -880,7 +888,7 @@ inspect_timespec_as_abstime(int level, int optname, VALUE data, VALUE ret)
struct tm tm;
char buf[32];
memcpy((char*)&ts, RSTRING_PTR(data), sizeof(ts));
- tm = *localtime(&ts.tv_sec);
+ LOCALTIME(ts.tv_sec, tm);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
rb_str_catf(ret, " %s.%09ld", buf, (long)ts.tv_nsec);
return 1;
@@ -898,37 +906,37 @@ inspect_bintime_as_abstime(int level, int optname, VALUE data, VALUE ret)
if (RSTRING_LEN(data) == sizeof(struct bintime)) {
struct bintime bt;
struct tm tm;
- uint64_t frac_h, frac_l;
- uint64_t scale_h, scale_l;
- uint64_t tmp1, tmp2;
- uint64_t res_h, res_l;
+ uint64_t frac_h, frac_l;
+ uint64_t scale_h, scale_l;
+ uint64_t tmp1, tmp2;
+ uint64_t res_h, res_l;
char buf[32];
memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt));
- tm = *localtime(&bt.sec);
+ LOCALTIME(bt.sec, tm);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
- /* res_h = frac * 10**19 / 2**64 */
+ /* res_h = frac * 10**19 / 2**64 */
- frac_h = bt.frac >> 32;
- frac_l = bt.frac & 0xffffffff;
+ frac_h = bt.frac >> 32;
+ frac_l = bt.frac & 0xffffffff;
- scale_h = 0x8ac72304; /* 0x8ac7230489e80000 == 10**19 */
- scale_l = 0x89e80000;
+ scale_h = 0x8ac72304; /* 0x8ac7230489e80000 == 10**19 */
+ scale_l = 0x89e80000;
- res_h = frac_h * scale_h;
- res_l = frac_l * scale_l;
+ res_h = frac_h * scale_h;
+ res_l = frac_l * scale_l;
- tmp1 = frac_h * scale_l;
- res_h += tmp1 >> 32;
- tmp2 = res_l;
- res_l += tmp1 & 0xffffffff;
- if (res_l < tmp2) res_h++;
+ tmp1 = frac_h * scale_l;
+ res_h += tmp1 >> 32;
+ tmp2 = res_l;
+ res_l += tmp1 & 0xffffffff;
+ if (res_l < tmp2) res_h++;
- tmp1 = frac_l * scale_h;
- res_h += tmp1 >> 32;
- tmp2 = res_l;
- res_l += tmp1 & 0xffffffff;
- if (res_l < tmp2) res_h++;
+ tmp1 = frac_l * scale_h;
+ res_h += tmp1 >> 32;
+ tmp2 = res_l;
+ res_l += tmp1 & 0xffffffff;
+ if (res_l < tmp2) res_h++;
rb_str_catf(ret, " %s.%019"PRIu64, buf, res_h);
return 1;
@@ -989,7 +997,7 @@ ancillary_inspect(VALUE self)
vtype = ip_cmsg_type_to_sym(level, type);
if (SYMBOL_P(vtype))
- rb_str_catf(ret, " %s", rb_id2name(SYM2ID(vtype)));
+ rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(vtype));
else
rb_str_catf(ret, " cmsg_type:%d", type);
}
@@ -1054,7 +1062,7 @@ ancillary_inspect(VALUE self)
# if defined(IPPROTO_IPV6)
case IPPROTO_IPV6:
switch (type) {
-# if defined(IPV6_PKTINFO) /* RFC 3542 */
+# if defined(IPV6_PKTINFO) && defined(HAVE_TYPE_STRUCT_IN6_PKTINFO) /* RFC 3542 */
case IPV6_PKTINFO: inspected = anc_inspect_ipv6_pktinfo(level, type, data, ret); break;
# endif
}
@@ -1105,8 +1113,8 @@ ancillary_cmsg_is_p(VALUE self, VALUE vlevel, VALUE vtype)
#if defined(HAVE_SENDMSG)
struct sendmsg_args_struct {
int fd;
- const struct msghdr *msg;
int flags;
+ const struct msghdr *msg;
};
static void *
@@ -1127,42 +1135,40 @@ rb_sendmsg(int fd, const struct msghdr *msg, int flags)
}
static VALUE
-bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
+bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags,
+ VALUE dest_sockaddr, VALUE controls, VALUE ex,
+ int nonblock)
{
rb_io_t *fptr;
- VALUE data, vflags, dest_sockaddr;
- VALUE *controls_ptr;
- int controls_num;
struct msghdr mh;
struct iovec iov;
-#if defined(HAVE_ST_MSG_CONTROL)
- volatile VALUE controls_str = 0;
+ VALUE tmp;
+ int controls_num;
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ VALUE controls_str = 0;
+ int family;
#endif
int flags;
ssize_t ss;
- int family;
- rb_secure(4);
GetOpenFile(sock, fptr);
- family = rsock_getfamily(fptr->fd);
-
- data = vflags = dest_sockaddr = Qnil;
- controls_ptr = NULL;
- controls_num = 0;
-
- if (argc == 0)
- rb_raise(rb_eArgError, "mesg argument required");
- data = argv[0];
- if (1 < argc) vflags = argv[1];
- if (2 < argc) dest_sockaddr = argv[2];
- if (3 < argc) { controls_ptr = &argv[3]; controls_num = argc - 3; }
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ family = rsock_getfamily(fptr);
+#endif
StringValue(data);
+ tmp = rb_str_tmp_frozen_acquire(data);
+
+ if (!RB_TYPE_P(controls, T_ARRAY)) {
+ controls = rb_ary_new();
+ }
+ controls_num = RARRAY_LENINT(controls);
if (controls_num) {
-#if defined(HAVE_ST_MSG_CONTROL)
- int i;
- size_t last_pad = 0;
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ int i;
+ size_t last_pad = 0;
+ const VALUE *controls_ptr = RARRAY_CONST_PTR(controls);
#if defined(__NetBSD__)
int last_level = 0;
int last_type = 0;
@@ -1209,9 +1215,9 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
last_level = cmh.cmsg_level;
last_type = cmh.cmsg_type;
#endif
- last_pad = cspace - cmh.cmsg_len;
+ last_pad = cspace - cmh.cmsg_len;
}
- if (last_pad) {
+ if (last_pad) {
/*
* This code removes the last padding from msg_controllen.
*
@@ -1236,9 +1242,10 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
if (last_level == SOL_SOCKET && last_type == SCM_RIGHTS)
rb_str_set_len(controls_str, RSTRING_LEN(controls_str)-last_pad);
#endif
- }
+ }
+ RB_GC_GUARD(controls);
#else
- rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented");
+ rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented");
#endif
}
@@ -1249,7 +1256,7 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
#endif
if (!NIL_P(dest_sockaddr))
- SockAddrStringValue(dest_sockaddr);
+ SockAddrStringValue(dest_sockaddr);
rb_io_check_closed(fptr);
@@ -1257,117 +1264,91 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
memset(&mh, 0, sizeof(mh));
if (!NIL_P(dest_sockaddr)) {
mh.msg_name = RSTRING_PTR(dest_sockaddr);
- mh.msg_namelen = RSTRING_LENINT(dest_sockaddr);
+ mh.msg_namelen = RSTRING_SOCKLEN(dest_sockaddr);
}
mh.msg_iovlen = 1;
mh.msg_iov = &iov;
- iov.iov_base = RSTRING_PTR(data);
- iov.iov_len = RSTRING_LEN(data);
-#if defined(HAVE_ST_MSG_CONTROL)
+ iov.iov_base = RSTRING_PTR(tmp);
+ iov.iov_len = RSTRING_LEN(tmp);
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (controls_str) {
mh.msg_control = RSTRING_PTR(controls_str);
- mh.msg_controllen = RSTRING_LENINT(controls_str);
- }
- else {
- mh.msg_control = NULL;
- mh.msg_controllen = 0;
+ mh.msg_controllen = RSTRING_SOCKLEN(controls_str);
}
#endif
rb_io_check_closed(fptr);
- if (nonblock)
+ if (nonblock && !MSG_DONTWAIT_RELIABLE)
rb_io_set_nonblock(fptr);
ss = rb_sendmsg(fptr->fd, &mh, flags);
if (ss == -1) {
- if (!nonblock && rb_io_wait_writable(fptr->fd)) {
+ int e;
+ if (!nonblock && rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
goto retry;
}
- if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN))
- rb_mod_sys_fail(rb_mWaitWritable, "sendmsg(2) would block");
- rb_sys_fail("sendmsg(2)");
+ e = errno;
+ if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
+ if (ex == Qfalse) {
+ return sym_wait_writable;
+ }
+ rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
+ "sendmsg(2) would block");
+ }
+ rb_syserr_fail(e, "sendmsg(2)");
}
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ RB_GC_GUARD(controls_str);
+#endif
+ rb_str_tmp_frozen_release(data, tmp);
return SSIZET2NUM(ss);
}
#endif
#if defined(HAVE_SENDMSG)
-/*
- * call-seq:
- * basicsocket.sendmsg(mesg, flags=0, dest_sockaddr=nil, *controls) => numbytes_sent
- *
- * sendmsg sends a message using sendmsg(2) system call in blocking manner.
- *
- * _mesg_ is a string to send.
- *
- * _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_OOB.
- *
- * _dest_sockaddr_ is a destination socket address for connection-less socket.
- * It should be a sockaddr such as a result of Socket.sockaddr_in.
- * An Addrinfo object can be used too.
- *
- * _controls_ is a list of ancillary data.
- * The element of _controls_ should be Socket::AncillaryData or
- * 3-elements array.
- * The 3-element array should contains cmsg_level, cmsg_type and data.
- *
- * The return value, _numbytes_sent_ is an integer which is the number of bytes sent.
- *
- * sendmsg can be used to implement send_io as follows:
- *
- * # use Socket::AncillaryData.
- * ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, io.fileno)
- * sock.sendmsg("a", 0, nil, ancdata)
- *
- * # use 3-element array.
- * ancdata = [:SOCKET, :RIGHTS, [io.fileno].pack("i!")]
- * sock.sendmsg("\0", 0, nil, ancdata)
- *
- */
VALUE
-rsock_bsock_sendmsg(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags, VALUE dest_sockaddr,
+ VALUE controls)
{
- return bsock_sendmsg_internal(argc, argv, sock, 0);
+ return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr, controls,
+ Qtrue, 0);
}
#endif
#if defined(HAVE_SENDMSG)
-/*
- * call-seq:
- * basicsocket.sendmsg_nonblock(mesg, flags=0, dest_sockaddr=nil, *controls) => numbytes_sent
- *
- * sendmsg_nonblock sends a message using sendmsg(2) system call in non-blocking manner.
- *
- * It is similar to BasicSocket#sendmsg
- * but the non-blocking flag is set before the system call
- * and it doesn't retry the system call.
- *
- */
VALUE
-rsock_bsock_sendmsg_nonblock(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags,
+ VALUE dest_sockaddr, VALUE controls, VALUE ex)
{
- return bsock_sendmsg_internal(argc, argv, sock, 1);
+ return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr,
+ controls, ex, 1);
}
#endif
#if defined(HAVE_RECVMSG)
struct recvmsg_args_struct {
int fd;
- struct msghdr *msg;
int flags;
+ struct msghdr *msg;
};
ssize_t
rsock_recvmsg(int socket, struct msghdr *message, int flags)
{
+ ssize_t ret;
+ socklen_t len0;
#ifdef MSG_CMSG_CLOEXEC
/* MSG_CMSG_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */
flags |= MSG_CMSG_CLOEXEC;
#endif
- return recvmsg(socket, message, flags);
+ len0 = message->msg_namelen;
+ ret = recvmsg(socket, message, flags);
+ if (ret != -1 && len0 < message->msg_namelen)
+ message->msg_namelen = len0;
+ return ret;
}
static void *
@@ -1388,7 +1369,7 @@ rb_recvmsg(int fd, struct msghdr *msg, int flags)
return (ssize_t)rb_thread_call_without_gvl(nogvl_recvmsg_func, &args, RUBY_UBF_IO, 0);
}
-#if defined(HAVE_ST_MSG_CONTROL)
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
static void
discard_cmsg(struct cmsghdr *cmh, char *msg_end, int msg_peek_p)
{
@@ -1409,7 +1390,7 @@ discard_cmsg(struct cmsghdr *cmh, char *msg_end, int msg_peek_p)
int *end = (int *)((char *)cmh + cmh->cmsg_len);
while ((char *)fdp + sizeof(int) <= (char *)end &&
(char *)fdp + sizeof(int) <= msg_end) {
- rb_fd_fix_cloexec(*fdp);
+ rb_update_max_fd(*fdp);
close(*fdp);
fdp++;
}
@@ -1420,7 +1401,7 @@ discard_cmsg(struct cmsghdr *cmh, char *msg_end, int msg_peek_p)
void
rsock_discard_cmsg_resource(struct msghdr *mh, int msg_peek_p)
{
-#if defined(HAVE_ST_MSG_CONTROL)
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
struct cmsghdr *cmh;
char *msg_end;
@@ -1435,24 +1416,25 @@ rsock_discard_cmsg_resource(struct msghdr *mh, int msg_peek_p)
#endif
}
-#if defined(HAVE_ST_MSG_CONTROL)
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
static void
make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end)
{
if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_RIGHTS) {
int *fdp, *end;
- VALUE ary = rb_ary_new();
- rb_ivar_set(ctl, rb_intern("unix_rights"), ary);
+ VALUE ary = rb_ary_new();
+ rb_ivar_set(ctl, rb_intern("unix_rights"), ary);
fdp = (int *)CMSG_DATA(cmh);
end = (int *)((char *)cmh + cmh->cmsg_len);
while ((char *)fdp + sizeof(int) <= (char *)end &&
- (char *)fdp + sizeof(int) <= msg_end) {
+ (char *)fdp + sizeof(int) <= msg_end) {
int fd = *fdp;
struct stat stbuf;
VALUE io;
if (fstat(fd, &stbuf) == -1)
rb_raise(rb_eSocket, "invalid fd in SCM_RIGHTS");
- rb_fd_fix_cloexec(fd);
+ rb_update_max_fd(fd);
+ rb_maygvl_fd_fix_cloexec(fd);
if (S_ISSOCK(stbuf.st_mode))
io = rsock_init_sock(rb_obj_alloc(rb_cSocket), fd);
else
@@ -1461,56 +1443,45 @@ make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end)
rb_ary_push(ary, io);
fdp++;
}
- OBJ_FREEZE(ary);
+ OBJ_FREEZE(ary);
}
}
#endif
static VALUE
-bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
+bsock_recvmsg_internal(VALUE sock,
+ VALUE vmaxdatlen, VALUE vflags, VALUE vmaxctllen,
+ VALUE scm_rights, VALUE ex, int nonblock)
{
rb_io_t *fptr;
- VALUE vmaxdatlen, vmaxctllen, vflags, vopts;
int grow_buffer;
size_t maxdatlen;
int flags, orig_flags;
- int request_scm_rights;
struct msghdr mh;
struct iovec iov;
- struct sockaddr_storage namebuf;
- char datbuf0[4096], *datbuf;
+ union_sockaddr namebuf;
+ char *datbuf;
VALUE dat_str = Qnil;
VALUE ret;
ssize_t ss;
-#if defined(HAVE_ST_MSG_CONTROL)
+ int request_scm_rights;
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
struct cmsghdr *cmh;
size_t maxctllen;
- union {
- char bytes[4096];
- struct cmsghdr align;
- } ctlbuf0;
char *ctlbuf;
VALUE ctl_str = Qnil;
int family;
int gc_done = 0;
#endif
- rb_secure(4);
-
- vopts = Qnil;
- if (0 < argc && RB_TYPE_P(argv[argc-1], T_HASH))
- vopts = argv[--argc];
-
- rb_scan_args(argc, argv, "03", &vmaxdatlen, &vflags, &vmaxctllen);
-
- maxdatlen = NIL_P(vmaxdatlen) ? sizeof(datbuf0) : NUM2SIZET(vmaxdatlen);
-#if defined(HAVE_ST_MSG_CONTROL)
- maxctllen = NIL_P(vmaxctllen) ? sizeof(ctlbuf0) : NUM2SIZET(vmaxctllen);
+ maxdatlen = NIL_P(vmaxdatlen) ? 4096 : NUM2SIZET(vmaxdatlen);
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ maxctllen = NIL_P(vmaxctllen) ? 4096 : NUM2SIZET(vmaxctllen);
#else
if (!NIL_P(vmaxctllen))
rb_raise(rb_eArgError, "control message not supported");
#endif
- flags = NIL_P(vflags) ? 0 : NUM2INT(vflags);
+ flags = NUM2INT(vflags);
#ifdef MSG_DONTWAIT
if (nonblock)
flags |= MSG_DONTWAIT;
@@ -1520,53 +1491,49 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
grow_buffer = NIL_P(vmaxdatlen) || NIL_P(vmaxctllen);
request_scm_rights = 0;
- if (!NIL_P(vopts) && RTEST(rb_hash_aref(vopts, ID2SYM(rb_intern("scm_rights")))))
+ if (RTEST(scm_rights))
request_scm_rights = 1;
+#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ if (request_scm_rights)
+ rb_raise(rb_eNotImpError, "control message for recvmsg is unimplemented");
+#endif
GetOpenFile(sock, fptr);
if (rb_io_read_pending(fptr)) {
rb_raise(rb_eIOError, "recvmsg for buffered IO");
}
-#if !defined(HAVE_ST_MSG_CONTROL)
+#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (grow_buffer) {
- int socktype;
- socklen_t optlen = (socklen_t)sizeof(socktype);
+ int socktype;
+ socklen_t optlen = (socklen_t)sizeof(socktype);
if (getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen) == -1) {
- rb_sys_fail("getsockopt(SO_TYPE)");
- }
- if (socktype == SOCK_STREAM)
- grow_buffer = 0;
+ rb_sys_fail("getsockopt(SO_TYPE)");
+ }
+ if (socktype == SOCK_STREAM)
+ grow_buffer = 0;
}
#endif
retry:
- if (maxdatlen <= sizeof(datbuf0))
- datbuf = datbuf0;
- else {
- if (NIL_P(dat_str))
- dat_str = rb_str_tmp_new(maxdatlen);
- else
- rb_str_resize(dat_str, maxdatlen);
- datbuf = RSTRING_PTR(dat_str);
- }
+ if (NIL_P(dat_str))
+ dat_str = rb_str_tmp_new(maxdatlen);
+ else
+ rb_str_resize(dat_str, maxdatlen);
+ datbuf = RSTRING_PTR(dat_str);
-#if defined(HAVE_ST_MSG_CONTROL)
- if (maxctllen <= sizeof(ctlbuf0))
- ctlbuf = ctlbuf0.bytes;
- else {
- if (NIL_P(ctl_str))
- ctl_str = rb_str_tmp_new(maxctllen);
- else
- rb_str_resize(ctl_str, maxctllen);
- ctlbuf = RSTRING_PTR(ctl_str);
- }
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ if (NIL_P(ctl_str))
+ ctl_str = rb_str_tmp_new(maxctllen);
+ else
+ rb_str_resize(ctl_str, maxctllen);
+ ctlbuf = RSTRING_PTR(ctl_str);
#endif
memset(&mh, 0, sizeof(mh));
memset(&namebuf, 0, sizeof(namebuf));
- mh.msg_name = (struct sockaddr *)&namebuf;
+ mh.msg_name = &namebuf.addr;
mh.msg_namelen = (socklen_t)sizeof(namebuf);
mh.msg_iov = &iov;
@@ -1574,7 +1541,7 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
iov.iov_base = datbuf;
iov.iov_len = maxdatlen;
-#if defined(HAVE_ST_MSG_CONTROL)
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
mh.msg_control = ctlbuf;
mh.msg_controllen = (socklen_t)maxctllen;
#endif
@@ -1583,20 +1550,30 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
flags |= MSG_PEEK;
rb_io_check_closed(fptr);
- if (nonblock)
+ if (nonblock && !MSG_DONTWAIT_RELIABLE)
rb_io_set_nonblock(fptr);
ss = rb_recvmsg(fptr->fd, &mh, flags);
+ if (ss == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
+
if (ss == -1) {
- if (!nonblock && rb_io_wait_readable(fptr->fd)) {
+ int e;
+ if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
rb_io_check_closed(fptr);
goto retry;
}
- if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN))
- rb_mod_sys_fail(rb_mWaitReadable, "recvmsg(2) would block");
-#if defined(HAVE_ST_MSG_CONTROL)
- if (!gc_done && (errno == EMFILE || errno == EMSGSIZE)) {
+ e = errno;
+ if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) {
+ if (ex == Qfalse) {
+ return sym_wait_readable;
+ }
+ rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvmsg(2) would block");
+ }
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ if (!gc_done && (e == EMFILE || e == EMSGSIZE)) {
/*
* When SCM_RIGHTS hit the file descriptors limit:
* - Linux 2.6.18 causes success with MSG_CTRUNC
@@ -1606,31 +1583,35 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
gc_and_retry:
rb_gc();
gc_done = 1;
- goto retry;
+ goto retry;
}
+#else
+ if (NIL_P(vmaxdatlen) && grow_buffer && e == EMSGSIZE)
+ ss = (ssize_t)iov.iov_len;
+ else
#endif
- rb_sys_fail("recvmsg(2)");
+ rb_syserr_fail(e, "recvmsg(2)");
}
if (grow_buffer) {
- int grown = 0;
-#if defined(HAVE_ST_MSG_CONTROL)
- if (NIL_P(vmaxdatlen) && (mh.msg_flags & MSG_TRUNC)) {
+ int grown = 0;
+ if (NIL_P(vmaxdatlen) && ss != -1 && ss == (ssize_t)iov.iov_len) {
if (SIZE_MAX/2 < maxdatlen)
rb_raise(rb_eArgError, "max data length too big");
- maxdatlen *= 2;
- grown = 1;
- }
+ maxdatlen *= 2;
+ grown = 1;
+ }
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
if (NIL_P(vmaxctllen) && (mh.msg_flags & MSG_CTRUNC)) {
#define BIG_ENOUGH_SPACE 65536
if (BIG_ENOUGH_SPACE < maxctllen &&
- mh.msg_controllen < (socklen_t)(maxctllen - BIG_ENOUGH_SPACE)) {
+ (socklen_t)mh.msg_controllen < (socklen_t)(maxctllen - BIG_ENOUGH_SPACE)) {
/* there are big space bug truncated.
* file descriptors limit? */
if (!gc_done) {
- rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
+ rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
goto gc_and_retry;
- }
+ }
}
else {
if (SIZE_MAX/2 < maxctllen)
@@ -1639,20 +1620,13 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
grown = 1;
}
#undef BIG_ENOUGH_SPACE
- }
-#else
- if (NIL_P(vmaxdatlen) && ss != -1 && ss == (ssize_t)iov.iov_len) {
- if (SIZE_MAX/2 < maxdatlen)
- rb_raise(rb_eArgError, "max data length too big");
- maxdatlen *= 2;
- grown = 1;
- }
+ }
#endif
- if (grown) {
+ if (grown) {
rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
- goto retry;
- }
- else {
+ goto retry;
+ }
+ else {
grow_buffer = 0;
if (flags != orig_flags) {
rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0);
@@ -1663,42 +1637,42 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
}
if (NIL_P(dat_str))
- dat_str = rb_tainted_str_new(datbuf, ss);
+ dat_str = rb_str_new(datbuf, ss);
else {
rb_str_resize(dat_str, ss);
- OBJ_TAINT(dat_str);
- RBASIC(dat_str)->klass = rb_cString;
+ rb_obj_reveal(dat_str, rb_cString);
}
- ret = rb_ary_new3(3, dat_str,
- rsock_io_socket_addrinfo(sock, mh.msg_name, mh.msg_namelen),
-#if defined(HAVE_ST_MSG_CONTROL)
- INT2NUM(mh.msg_flags)
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ VALUE msg_flags = INT2NUM(mh.msg_flags);
#else
- Qnil
+ VALUE msg_flags = Qnil;
#endif
- );
+ ret = rb_ary_new3(3, dat_str,
+ rsock_io_socket_addrinfo(sock, mh.msg_name, mh.msg_namelen),
+ msg_flags);
-#if defined(HAVE_ST_MSG_CONTROL)
- family = rsock_getfamily(fptr->fd);
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
+ family = rsock_getfamily(fptr);
if (mh.msg_controllen) {
- char *msg_end = (char *)mh.msg_control + mh.msg_controllen;
+ char *msg_end = (char *)mh.msg_control + mh.msg_controllen;
for (cmh = CMSG_FIRSTHDR(&mh); cmh != NULL; cmh = CMSG_NXTHDR(&mh, cmh)) {
VALUE ctl;
- char *ctl_end;
+ char *ctl_end;
size_t clen;
if (cmh->cmsg_len == 0) {
rb_raise(rb_eTypeError, "invalid control message (cmsg_len == 0)");
}
ctl_end = (char*)cmh + cmh->cmsg_len;
- clen = (ctl_end <= msg_end ? ctl_end : msg_end) - (char*)CMSG_DATA(cmh);
- ctl = ancdata_new(family, cmh->cmsg_level, cmh->cmsg_type, rb_tainted_str_new((char*)CMSG_DATA(cmh), clen));
+ clen = (ctl_end <= msg_end ? ctl_end : msg_end) - (char*)CMSG_DATA(cmh);
+ ctl = ancdata_new(family, cmh->cmsg_level, cmh->cmsg_type, rb_str_new((char*)CMSG_DATA(cmh), clen));
if (request_scm_rights)
make_io_for_unix_rights(ctl, cmh, msg_end);
else
discard_cmsg(cmh, msg_end, (flags & MSG_PEEK) != 0);
rb_ary_push(ret, ctl);
}
+ RB_GC_GUARD(ctl_str);
}
#endif
@@ -1707,89 +1681,28 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
#endif
#if defined(HAVE_RECVMSG)
-/*
- * call-seq:
- * basicsocket.recvmsg(maxmesglen=nil, flags=0, maxcontrollen=nil, opts={}) => [mesg, sender_addrinfo, rflags, *controls]
- *
- * recvmsg receives a message using recvmsg(2) system call in blocking manner.
- *
- * _maxmesglen_ is the maximum length of mesg to receive.
- *
- * _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_PEEK.
- *
- * _maxcontrollen_ is the maximum length of controls (ancillary data) to receive.
- *
- * _opts_ is option hash.
- * Currently :scm_rights=>bool is the only option.
- *
- * :scm_rights option specifies that application expects SCM_RIGHTS control message.
- * If the value is nil or false, application don't expects SCM_RIGHTS control message.
- * In this case, recvmsg closes the passed file descriptors immediately.
- * This is the default behavior.
- *
- * If :scm_rights value is neither nil nor false, application expects SCM_RIGHTS control message.
- * In this case, recvmsg creates IO objects for each file descriptors for
- * Socket::AncillaryData#unix_rights method.
- *
- * The return value is 4-elements array.
- *
- * _mesg_ is a string of the received message.
- *
- * _sender_addrinfo_ is a sender socket address for connection-less socket.
- * It is an Addrinfo object.
- * For connection-oriented socket such as TCP, sender_addrinfo is platform dependent.
- *
- * _rflags_ is a flags on the received message which is bitwise OR of MSG_* constants such as Socket::MSG_TRUNC.
- * It will be nil if the system uses 4.3BSD style old recvmsg system call.
- *
- * _controls_ is ancillary data which is an array of Socket::AncillaryData objects such as:
- *
- * #<Socket::AncillaryData: AF_UNIX SOCKET RIGHTS 7>
- *
- * _maxmesglen_ and _maxcontrollen_ can be nil.
- * In that case, the buffer will be grown until the message is not truncated.
- * Internally, MSG_PEEK is used and MSG_TRUNC/MSG_CTRUNC are checked.
- *
- * recvmsg can be used to implement recv_io as follows:
- *
- * mesg, sender_sockaddr, rflags, *controls = sock.recvmsg(:scm_rights=>true)
- * controls.each {|ancdata|
- * if ancdata.cmsg_is?(:SOCKET, :RIGHTS)
- * return ancdata.unix_rights[0]
- * end
- * }
- *
- */
VALUE
-rsock_bsock_recvmsg(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
+ VALUE scm_rights)
{
- return bsock_recvmsg_internal(argc, argv, sock, 0);
+ VALUE ex = Qtrue;
+ return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 0);
}
#endif
#if defined(HAVE_RECVMSG)
-/*
- * call-seq:
- * basicsocket.recvmsg_nonblock(maxdatalen=nil, flags=0, maxcontrollen=nil, opts={}) => [data, sender_addrinfo, rflags, *controls]
- *
- * recvmsg receives a message using recvmsg(2) system call in non-blocking manner.
- *
- * It is similar to BasicSocket#recvmsg
- * but non-blocking flag is set before the system call
- * and it doesn't retry the system call.
- *
- */
VALUE
-rsock_bsock_recvmsg_nonblock(int argc, VALUE *argv, VALUE sock)
+rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE flags, VALUE clen,
+ VALUE scm_rights, VALUE ex)
{
- return bsock_recvmsg_internal(argc, argv, sock, 1);
+ return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 1);
}
#endif
void
rsock_init_ancdata(void)
{
-#if defined(HAVE_ST_MSG_CONTROL)
+#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
/*
* Document-class: Socket::AncillaryData
*
@@ -1823,4 +1736,7 @@ rsock_init_ancdata(void)
rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_addr", ancillary_ipv6_pktinfo_addr, 0);
rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_ifindex", ancillary_ipv6_pktinfo_ifindex, 0);
#endif
+#undef rb_intern
+ sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
+ sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
}