summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau2026-02-13 08:50:58 +0000
committerFelix Fietkau2026-02-13 09:02:45 +0000
commit0efa2cd3b74ca295362341f7b21f0449066141f5 (patch)
tree95eaa58c36f176351c5dd92951b48d7b57923ba1
parent5ec7ff2effb36bed32de122629b8c1404bd5157a (diff)
downloadlibubox-0efa2cd3b74ca295362341f7b21f0449066141f5.tar.gz
usock: check SO_ERROR after poll in usock_inet_timeout()
After poll() returns with POLLOUT on a non-blocking connect, the connection may have failed (ECONNREFUSED, ENETUNREACH, etc.). Check the socket error via getsockopt(SO_ERROR) before accepting the connection as successful. Without this, a fast-failing IPv6 connection would be returned to the caller instead of falling back to IPv4. Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--usock.c29
1 files changed, 24 insertions, 5 deletions
diff --git a/usock.c b/usock.c
index 15b6988..8aafbc3 100644
--- a/usock.c
+++ b/usock.c
@@ -131,6 +131,17 @@ static int poll_restart(struct pollfd *fds, int nfds, int timeout)
}
}
+static int usock_check_connect(int fd)
+{
+ int err = 0;
+ socklen_t len = sizeof(err);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len))
+ return -1;
+
+ return err ? -1 : 0;
+}
+
int usock_inet_timeout(int type, const char *host, const char *service,
void *addr, int timeout)
{
@@ -184,9 +195,15 @@ int usock_inet_timeout(int type, const char *host, const char *service,
if (timeout > 300) {
if (poll_restart(pfds, 1, 300) == 1) {
- rp = rp_v6;
- sock = pfds[0].fd;
- goto out;
+ if (usock_check_connect(pfds[0].fd) == 0) {
+ rp = rp_v6;
+ sock = pfds[0].fd;
+ goto out;
+ }
+ close(pfds[0].fd);
+ pfds[0].fd = -1;
+ rp_v6 = NULL;
+ goto try_v4;
}
}
timeout -= 300;
@@ -208,13 +225,15 @@ try_v4:
wait:
poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout);
- if (pfds[0].revents & POLLOUT) {
+ if ((pfds[0].revents & POLLOUT) &&
+ usock_check_connect(pfds[0].fd) == 0) {
rp = rp_v6;
sock = pfds[0].fd;
goto out;
}
- if (pfds[1].revents & POLLOUT) {
+ if ((pfds[1].revents & POLLOUT) &&
+ usock_check_connect(pfds[1].fd) == 0) {
rp = rp_v4;
sock = pfds[1].fd;
goto out;