summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau2026-02-13 08:52:09 +0000
committerFelix Fietkau2026-02-13 09:02:45 +0000
commit1a73ded9f738d403784aa448910cf5c9f9b05e18 (patch)
tree5fc0414dc2ecb1694d53731b2036d53d4d864d95
parent0efa2cd3b74ca295362341f7b21f0449066141f5 (diff)
usock: fix timeout handling in usock_inet_timeout()
Replace relative timeout arithmetic with an absolute CLOCK_MONOTONIC deadline computed once at function entry. This avoids the case where the IPv6 head-start delay subtraction produces a negative timeout for the subsequent combined poll, causing it to return immediately. Also give IPv6 a head start of min(300ms, remaining_timeout) instead of skipping the head start entirely when the total timeout is <= 300ms. Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--usock.c50
1 files changed, 36 insertions, 14 deletions
diff --git a/usock.c b/usock.c
index 8aafbc3..4a0cc67 100644
--- a/usock.c
+++ b/usock.c
@@ -142,6 +142,18 @@ static int usock_check_connect(int fd)
return err ? -1 : 0;
}
+static int usock_timeout_remaining(struct timespec *deadline)
+{
+ struct timespec cur;
+ int msec;
+
+ clock_gettime(CLOCK_MONOTONIC, &cur);
+ msec = (deadline->tv_sec - cur.tv_sec) * 1000;
+ msec += (deadline->tv_nsec - cur.tv_nsec) / 1000000;
+
+ return msec > 0 ? msec : 0;
+}
+
int usock_inet_timeout(int type, const char *host, const char *service,
void *addr, int timeout)
{
@@ -162,8 +174,9 @@ int usock_inet_timeout(int type, const char *host, const char *service,
{ .fd = -1, .events = POLLOUT },
{ .fd = -1, .events = POLLOUT },
};
+ struct timespec deadline;
int sock = -1;
- int i;
+ int delay, i;
if (getaddrinfo(host, service, &hints, &result))
return -1;
@@ -173,6 +186,14 @@ int usock_inet_timeout(int type, const char *host, const char *service,
goto free_addrinfo;
}
+ clock_gettime(CLOCK_MONOTONIC, &deadline);
+ deadline.tv_nsec += (timeout % 1000) * 1000000;
+ if (deadline.tv_nsec >= 1000000000) {
+ deadline.tv_sec++;
+ deadline.tv_nsec -= 1000000000;
+ }
+ deadline.tv_sec += timeout / 1000;
+
for (rp = result; rp != NULL; rp = rp->ai_next) {
if (rp->ai_family == AF_INET6 && !rp_v6)
rp_v6 = rp;
@@ -193,20 +214,20 @@ int usock_inet_timeout(int type, const char *host, const char *service,
goto try_v4;
}
- if (timeout > 300) {
- if (poll_restart(pfds, 1, 300) == 1) {
- 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;
+ delay = usock_timeout_remaining(&deadline);
+ if (delay > 300)
+ delay = 300;
+ if (delay > 0 && poll_restart(pfds, 1, delay) == 1) {
+ 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;
}
try_v4:
@@ -224,7 +245,8 @@ try_v4:
}
wait:
- poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout);
+ poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4,
+ usock_timeout_remaining(&deadline));
if ((pfds[0].revents & POLLOUT) &&
usock_check_connect(pfds[0].fd) == 0) {
rp = rp_v6;