]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
lib/tsocket: check for errors indicated by poll() before getsockopt(fd, SOL_SOCKET...
authorStefan Metzmacher <metze@samba.org>
Thu, 13 Oct 2022 12:46:14 +0000 (14:46 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 19 Oct 2022 16:14:36 +0000 (16:14 +0000)
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>
lib/tsocket/tsocket_bsd.c

index a4c2d0cf336b3cce6b5ef855e69226e5ca28033b..8ab2ffb0b839b311d874ea17dda87731c61d2092 100644 (file)
 #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;