From fa95270d7cde2681e6a599f2a2bdc42b611d93b1 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Sun, 11 Oct 2026 00:47:32 +0300 Subject: [PATCH] Windows: no linger on non-blocking sockets, no shutdown before close, no sleep before close Test branch, not for merging as is. --- src/common.c | 6 +++++- src/ftppr.c | 9 ++++++++- src/proxymain.c | 21 ++++++++++++++++++++- src/sockmap.c | 3 --- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/common.c b/src/common.c index 6dacf65..10bf296 100644 --- a/src/common.c +++ b/src/common.c @@ -387,7 +387,6 @@ int return shutdown(s, how); } int WINAPI def_closesocket(void* state, SOCKET s){ - usleep(SLEEPTIME); return closesocket(s); } #else @@ -715,7 +714,9 @@ int doconnect(struct clientparam * param){ if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsr, &size)==-1) {return (14);} } else { +#ifndef _WIN32 struct linger lg = {1,conf.timeouts[LINGER_TO]}; +#endif int tries = RANGETRIES; if(SAISNULL(¶m->sinsr)){ @@ -747,7 +748,10 @@ int doconnect(struct clientparam * param){ *SAPORT(¶m->sinsl) = 0; setopts(param->remsock, param->srv->srvsockopts); +#ifndef _WIN32 + /* not on Windows, see the accepting side in proxymain.c */ param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); +#endif if(extbindtodevice(param, param->remsock)) return 12; #ifdef WITH_UN if(*SAFAMILY(¶m->sinsl) != AF_UNIX) diff --git a/src/ftppr.c b/src/ftppr.c index f7d4ce6..e6f8d42 100644 --- a/src/ftppr.c +++ b/src/ftppr.c @@ -23,14 +23,18 @@ void * ftpprchild(struct clientparam* param) { SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET; SASIZETYPE sasize; char * req = NULL; +#ifndef _WIN32 struct linger lg; +#endif struct pollfd fds; if(!(buf = malloc(BUFSIZE))) RETURN(876); param->ctrlsock = param->clisock; param->operation = CONNECT; +#ifndef _WIN32 lg.l_onoff = 1; - lg.l_linger = conf.timeouts[STRING_L];; + lg.l_linger = conf.timeouts[STRING_L]; +#endif if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, conf.timeouts[STRING_S])!=11) {RETURN (801);} for(;;){ i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]); @@ -262,8 +266,11 @@ void * ftpprchild(struct clientparam* param) { } sc = param->remsock; param->remsock = ss; +#ifndef _WIN32 + /* not on Windows, see the accepting side in proxymain.c */ param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); param->srv->so._setsockopt(param->sostate, clidatasock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); +#endif param->clisock = clidatasock; res = mapsocket(param, conf.timeouts[CONNECTION_S]); if(param->remsock != INVALID_SOCKET) { diff --git a/src/proxymain.c b/src/proxymain.c index be3d6dd..6a531bf 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -306,7 +306,9 @@ int MODULEMAINFUNC (int argc, char** argv){ unsigned char *cbc_string = NULL, *cbl_string = NULL; PROXYSOCKADDRTYPE cbsa; FILE *fp = NULL; +#ifndef _WIN32 struct linger lg; +#endif int nlog = 5000; int haveslot = 0, nfull = 0; #ifdef __linux__ @@ -1117,10 +1119,14 @@ int MODULEMAINFUNC (int argc, char** argv){ #else fcntl(new_sock,F_SETFL,O_NONBLOCK | fcntl(new_sock,F_GETFL)); #endif +#ifndef _WIN32 + /* Not on Windows: there closesocket() on a non-blocking socket with a linger + timeout fails with WSAEWOULDBLOCK while data is queued, leaving the socket + open. Without it the close completes in the background, data first. */ lg.l_onoff = 1; lg.l_linger = conf.timeouts[STRING_L]; - srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); +#endif srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int)); } #ifndef NOUDPMAIN @@ -1413,21 +1419,34 @@ void freeparam(struct clientparam * param) { } if(param->clibuf) free(param->clibuf); if(param->srvbuf) free(param->srvbuf); + /* Not shut down on Windows: there shutting down receiving resets the + connection as soon as the peer sends anything more, and close() + alone sends whatever is queued before its FIN. Elsewhere it stays: + on the BSDs it discards what is left unread, so the close that + follows is not turned into a reset. */ if(param->srv) { if(param->ctrlsocksrv != INVALID_SOCKET && param->ctrlsocksrv != param->remsock) { +#ifndef _WIN32 param->srv->so._shutdown(param->sostate, param->ctrlsocksrv, SHUT_RDWR); +#endif param->srv->so._closesocket(param->sostate, param->ctrlsocksrv); } if(param->ctrlsock != INVALID_SOCKET && param->ctrlsock != param->clisock) { +#ifndef _WIN32 param->srv->so._shutdown(param->sostate, param->ctrlsock, SHUT_RDWR); +#endif param->srv->so._closesocket(param->sostate, param->ctrlsock); } if(param->remsock != INVALID_SOCKET) { +#ifndef _WIN32 param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR); +#endif param->srv->so._closesocket(param->sostate, param->remsock); } if(param->clisock != INVALID_SOCKET) { +#ifndef _WIN32 param->srv->so._shutdown(param->sostate, param->clisock, SHUT_RDWR); +#endif param->srv->so._closesocket(param->sostate, param->clisock); } } diff --git a/src/sockmap.c b/src/sockmap.c index df7ad16..0e993bb 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -819,9 +819,6 @@ log("timeout"); else if(inclientpipe || inserverpipe) res = 94; #endif - if((param->nwrites > 0 && !SERVERTERMWRITE) || (param->nreads > 0 && !CLIENTTERMWRITE)) - usleep(SLEEPTIME * 10); - CLEANRET: #ifdef WITHSPLICE