summaryrefslogtreecommitdiff
path: root/ext/socket
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2022-09-20 16:10:56 +0200
committerJean Boussier <jean.boussier@gmail.com>2023-08-30 10:07:18 +0200
commitbcc905100f1079e191632cfd02319c10af82dac0 (patch)
treee8dbe37eb4de741c51210f65ffaa0336ce579c8a /ext/socket
parentacedbcb1b4eb6b362f11e783bff53c237d05afc6 (diff)
BasicSocket#recv* return `nil` rather than an empty packet
[Bug #19012] man recvmsg(2) states: > Return Value > These calls return the number of bytes received, or -1 if an error occurred. > The return value will be 0 when the peer has performed an orderly shutdown. Not too sure how one is supposed to make the difference between a packet of size 0 and a closed connection.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/6407
Diffstat (limited to 'ext/socket')
-rw-r--r--ext/socket/ancdata.c4
-rw-r--r--ext/socket/init.c20
-rw-r--r--ext/socket/rubysocket.h2
3 files changed, 26 insertions, 0 deletions
diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index 7406177de2..6ef040b692 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -1555,6 +1555,10 @@ bsock_recvmsg_internal(VALUE sock,
ss = rb_recvmsg(fptr->fd, &mh, flags);
+ if (ss == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
+
if (ss == -1) {
int e;
if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) {
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 557d4374a5..e9dc6f8483 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -116,6 +116,7 @@ recvfrom_blocking(void *data)
ssize_t ret;
ret = recvfrom(arg->fd, RSTRING_PTR(arg->str), arg->length,
arg->flags, &arg->buf.addr, &arg->alen);
+
if (ret != -1 && len0 < arg->alen)
arg->alen = len0;
@@ -147,6 +148,18 @@ recvfrom_locktmp(VALUE v)
return rb_thread_io_blocking_region(recvfrom_blocking, arg, arg->fd);
}
+int
+rsock_is_dgram(rb_io_t *fptr)
+{
+ int socktype;
+ socklen_t optlen = (socklen_t)sizeof(socktype);
+ int ret = getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen);
+ if (ret == -1) {
+ rb_sys_fail("getsockopt(SO_TYPE)");
+ }
+ return socktype == SOCK_DGRAM;
+}
+
VALUE
rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
{
@@ -187,6 +200,9 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
slen = (long)rb_str_locktmp_ensure(str, recvfrom_locktmp, (VALUE)&arg);
+ if (slen == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
if (slen >= 0) break;
if (!rb_io_maybe_wait_readable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT))
@@ -259,6 +275,10 @@ rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
if (slen != -1 && len0 < alen)
alen = len0;
+ if (slen == 0 && !rsock_is_dgram(fptr)) {
+ return Qnil;
+ }
+
if (slen < 0) {
int e = errno;
switch (e) {
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index 5f803ba0da..7c5739808d 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -459,6 +459,8 @@ VALUE rsock_write_nonblock(VALUE sock, VALUE buf, VALUE ex);
void rsock_make_fd_nonblock(int fd);
+int rsock_is_dgram(rb_io_t *fptr);
+
#if !defined HAVE_INET_NTOP && ! defined _WIN32
const char *inet_ntop(int, const void *, char *, size_t);
#elif defined __MINGW32__