summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2022-10-13 14:46:14 +0200
committerStefan Metzmacher <metze@samba.org>2022-10-19 16:14:36 +0000
commit29a65da63d730ecead1e7d4a81a76dd1c8c179ea (patch)
tree54849de861fe9f790991705385092583129a2b48
parent9950efd83e1a4b5e711f1d36fefa8a5d5e8b2410 (diff)
downloadsamba-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.c80
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;