From 0c80c9f526dea0e2c8b3262cbad173b7f0e8653c Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Sat, 24 Oct 2026 20:56:15 +0300 Subject: [PATCH] Connect back: a connection one client takes leaves the others waiting The clients of a -R service all wait for the connection back on the same listening socket, which was blocking: one connection woke them all, one took it, and the others were left in accept() until the next, past their time out and through a reload. The socket is non-blocking, and an accept() which finds nothing, the connection taken by another client or gone, is not one of the client's tries. nonblock() makes a socket non-blocking, acceptagain() tells such an accept(). (cherry picked from commit 126c7d0) Co-Authored-By: Claude Opus 5.5 --- src/common.c | 20 ++++++++++++++++++++ src/proxy.h | 2 ++ src/proxymain.c | 4 ++++ 3 files changed, 26 insertions(+) diff --git a/src/common.c b/src/common.c index d6c942b..ee2cece 100644 --- a/src/common.c +++ b/src/common.c @@ -634,6 +634,26 @@ int parseconnusername(char *username, struct clientparam *param, int extpasswd, } +/* What waits for a socket waits in poll(): a call on it never does. */ +void nonblock(SOCKET sock){ +#ifdef _WIN32 + unsigned long ul = 1; + ioctlsocket(sock, FIONBIO, &ul); +#else + fcntl(sock, F_SETFL, O_NONBLOCK | fcntl(sock, F_GETFL)); +#endif +} + +/* accept() with nothing to accept: another took it, or it was gone */ +int acceptagain(void){ +#ifdef _WIN32 + int err = WSAGetLastError(); + return err == WSAEWOULDBLOCK || err == WSAECONNRESET || err == WSAEINTR; +#else + return errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR; +#endif +} + int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to){ struct pollfd fds[1]; #ifdef _WIN32 diff --git a/src/proxy.h b/src/proxy.h index 1197254..a7e8407 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -405,6 +405,8 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign int readconfig(FILE * fp); void initcommands(void); int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to); +void nonblock(SOCKET sock); +int acceptagain(void); int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range); int bindtodevice(struct sockfuncs *sf, void *state, SOCKET sock, const char *device, int family); int extbindtodevice(struct clientparam *param, SOCKET sock); diff --git a/src/proxymain.c b/src/proxymain.c index 9b84206..7edcc44 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -63,6 +63,8 @@ void * threadfunc (void *p) { } param->remsock = param->srv->so._accept(param->sostate, param->srv->cbsock, (struct sockaddr*)¶m->sinsr, &size); if(param->remsock == INVALID_SOCKET) { + /* another client of the service took it: not a try */ + if(acceptagain()) { i++; continue; } dolog(param, (unsigned char *)"Connect back accept() failed"); continue; } @@ -864,6 +866,8 @@ static int cblisten(struct srvstart *s){ return -6; } setopts(srv->cbsock, &srv->cbssockopts); + /* the clients of the service all wait for it: one accepts what comes */ + nonblock(srv->cbsock); if(srv->so._bind(srv->so.state, srv->cbsock, (struct sockaddr*)&cbsa, SASIZE(&cbsa))==-1) { dolog(defparam, (unsigned char *)"Failed to bind connect back socket");