From 99795d8dfea0b1394e376c4c13e0ada2e44e6717 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Thu, 22 Oct 2026 16:03:21 +0300 Subject: [PATCH] sockrecvfrom: poll again when a readable socket has nothing to read sockrecvfrom() polled once and then read in a loop for as long as the read failed with EAGAIN. Over TLS a socket is readable when only a part of a record has come, and the read fails with EAGAIN until the rest does: a client sending half a record and then nothing kept a thread taking all of a CPU, and the read never timed out. On EAGAIN the socket is now polled again, the read repeated at once only for EINTR. Co-Authored-By: Claude Opus 5.5 --- src/sockgetchar.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/sockgetchar.c b/src/sockgetchar.c index a48a628..5340c5b 100644 --- a/src/sockgetchar.c +++ b/src/sockgetchar.c @@ -60,15 +60,21 @@ int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin, fds.fd = sock; fds.events = POLLIN; - if(conf.timetoexit) return EOF; - res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to); - if (res<1) return 0; - sasize = sizeof(PROXYSOCKADDRTYPE); - /* sin NULL for a stream socket: nothing to learn the address into */ - do { - res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL); - } while (res < 0 && (errno == EAGAIN || errno == EINTR)); - return res; + for(;;){ + if(conf.timetoexit) return EOF; + res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to); + if (res<1) return 0; + sasize = sizeof(PROXYSOCKADDRTYPE); + /* sin NULL for a stream socket: nothing to learn the address into */ + do { + res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL); + } while (res < 0 && errno == EINTR); + /* readable and still nothing to read: over TLS a record not yet + whole. Read again, without polling, it would only be the same until + the rest came, a loop taking all of a CPU and never timing out: the + socket is polled for it instead. */ + if(res >= 0 || errno != EAGAIN) return res; + } } int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){