diff options
author | Stefan Metzmacher <metze@samba.org> | 2022-10-13 14:46:14 +0200 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2022-10-19 16:14:36 +0000 |
commit | 29a65da63d730ecead1e7d4a81a76dd1c8c179ea (patch) | |
tree | 54849de861fe9f790991705385092583129a2b48 | |
parent | 9950efd83e1a4b5e711f1d36fefa8a5d5e8b2410 (diff) | |
download | samba-29a65da63d730ecead1e7d4a81a76dd1c8c179ea.tar.gz |
lib/tsocket: check for errors indicated by poll() before getsockopt(fd, SOL_SOCKET, SO_ERROR)
This also returns an error if we got TCP_FIN from the peer,
which is only reported by an explicit POLLRDHUP check.
Also on FreeBSD getsockopt(fd, SOL_SOCKET, SO_ERROR) fetches
and resets the error, so a 2nd call no longer returns an error.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
-rw-r--r-- | lib/tsocket/tsocket_bsd.c | 80 |
1 files changed, 79 insertions, 1 deletions
diff --git a/lib/tsocket/tsocket_bsd.c b/lib/tsocket/tsocket_bsd.c index a4c2d0cf336..8ab2ffb0b83 100644 --- a/lib/tsocket/tsocket_bsd.c +++ b/lib/tsocket/tsocket_bsd.c @@ -24,8 +24,10 @@ #include "replace.h" #include "system/filesys.h" #include "system/network.h" +#include "system/select.h" #include "tsocket.h" #include "tsocket_internal.h" +#include "lib/util/select.h" #include "lib/util/iov_buf.h" #include "lib/util/blocking.h" #include "lib/util/util_net.h" @@ -171,7 +173,42 @@ static ssize_t tsocket_bsd_netlink_pending(int fd) } #endif -static ssize_t tsocket_bsd_error(int fd) +static int tsocket_bsd_poll_error(int fd) +{ + struct pollfd pfd = { + .fd = fd, +#ifdef POLLRDHUP + .events = POLLRDHUP, /* POLLERR and POLLHUP are not needed */ +#endif + }; + int ret; + + errno = 0; + ret = sys_poll_intr(&pfd, 1, 0); + if (ret == 0) { + return 0; + } + if (ret != 1) { + return POLLNVAL; + } + + if (pfd.revents & POLLERR) { + return POLLERR; + } + if (pfd.revents & POLLHUP) { + return POLLHUP; + } +#ifdef POLLRDHUP + if (pfd.revents & POLLRDHUP) { + return POLLRDHUP; + } +#endif + + /* should never be reached! */ + return POLLNVAL; +} + +static int tsocket_bsd_sock_error(int fd) { int ret, error = 0; socklen_t len = sizeof(error); @@ -192,6 +229,47 @@ static ssize_t tsocket_bsd_error(int fd) return 0; } +static int tsocket_bsd_error(int fd) +{ + int ret; + int poll_error = 0; + + poll_error = tsocket_bsd_poll_error(fd); + if (poll_error == 0) { + return 0; + } + +#ifdef POLLRDHUP + if (poll_error == POLLRDHUP) { + errno = ECONNRESET; + return -1; + } +#endif + + if (poll_error == POLLHUP) { + errno = EPIPE; + return -1; + } + + /* + * POLLERR and POLLNVAL fallback to + * getsockopt(fd, SOL_SOCKET, SO_ERROR) + * and force EPIPE as fallback. + */ + + errno = 0; + ret = tsocket_bsd_sock_error(fd); + if (ret == 0) { + errno = EPIPE; + } + + if (errno == 0) { + errno = EPIPE; + } + + return -1; +} + static ssize_t tsocket_bsd_pending(int fd) { int ret; |