Windows: no linger on non-blocking sockets, no shutdown before close, no sleep before close

Test branch, not for merging as is.
This commit is contained in:
Vladimir Dubrovin 2026-10-11 00:47:32 +03:00
parent 36382bda96
commit fa95270d7c
4 changed files with 33 additions and 6 deletions

View File

@ -387,7 +387,6 @@ int
return shutdown(s, how); return shutdown(s, how);
} }
int WINAPI def_closesocket(void* state, SOCKET s){ int WINAPI def_closesocket(void* state, SOCKET s){
usleep(SLEEPTIME);
return closesocket(s); return closesocket(s);
} }
#else #else
@ -715,7 +714,9 @@ int doconnect(struct clientparam * param){
if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)&param->sinsr, &size)==-1) {return (14);} if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)&param->sinsr, &size)==-1) {return (14);}
} }
else { else {
#ifndef _WIN32
struct linger lg = {1,conf.timeouts[LINGER_TO]}; struct linger lg = {1,conf.timeouts[LINGER_TO]};
#endif
int tries = RANGETRIES; int tries = RANGETRIES;
if(SAISNULL(&param->sinsr)){ if(SAISNULL(&param->sinsr)){
@ -747,7 +748,10 @@ int doconnect(struct clientparam * param){
*SAPORT(&param->sinsl) = 0; *SAPORT(&param->sinsl) = 0;
setopts(param->remsock, param->srv->srvsockopts); 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)); param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#endif
if(extbindtodevice(param, param->remsock)) return 12; if(extbindtodevice(param, param->remsock)) return 12;
#ifdef WITH_UN #ifdef WITH_UN
if(*SAFAMILY(&param->sinsl) != AF_UNIX) if(*SAFAMILY(&param->sinsl) != AF_UNIX)

View File

@ -23,14 +23,18 @@ void * ftpprchild(struct clientparam* param) {
SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET; SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET;
SASIZETYPE sasize; SASIZETYPE sasize;
char * req = NULL; char * req = NULL;
#ifndef _WIN32
struct linger lg; struct linger lg;
#endif
struct pollfd fds; struct pollfd fds;
if(!(buf = malloc(BUFSIZE))) RETURN(876); if(!(buf = malloc(BUFSIZE))) RETURN(876);
param->ctrlsock = param->clisock; param->ctrlsock = param->clisock;
param->operation = CONNECT; param->operation = CONNECT;
#ifndef _WIN32
lg.l_onoff = 1; 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);} if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, conf.timeouts[STRING_S])!=11) {RETURN (801);}
for(;;){ for(;;){
i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]); i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]);
@ -262,8 +266,11 @@ void * ftpprchild(struct clientparam* param) {
} }
sc = param->remsock; sc = param->remsock;
param->remsock = ss; 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, 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)); param->srv->so._setsockopt(param->sostate, clidatasock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#endif
param->clisock = clidatasock; param->clisock = clidatasock;
res = mapsocket(param, conf.timeouts[CONNECTION_S]); res = mapsocket(param, conf.timeouts[CONNECTION_S]);
if(param->remsock != INVALID_SOCKET) { if(param->remsock != INVALID_SOCKET) {

View File

@ -306,7 +306,9 @@ int MODULEMAINFUNC (int argc, char** argv){
unsigned char *cbc_string = NULL, *cbl_string = NULL; unsigned char *cbc_string = NULL, *cbl_string = NULL;
PROXYSOCKADDRTYPE cbsa; PROXYSOCKADDRTYPE cbsa;
FILE *fp = NULL; FILE *fp = NULL;
#ifndef _WIN32
struct linger lg; struct linger lg;
#endif
int nlog = 5000; int nlog = 5000;
int haveslot = 0, nfull = 0; int haveslot = 0, nfull = 0;
#ifdef __linux__ #ifdef __linux__
@ -1117,10 +1119,14 @@ int MODULEMAINFUNC (int argc, char** argv){
#else #else
fcntl(new_sock,F_SETFL,O_NONBLOCK | fcntl(new_sock,F_GETFL)); fcntl(new_sock,F_SETFL,O_NONBLOCK | fcntl(new_sock,F_GETFL));
#endif #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_onoff = 1;
lg.l_linger = conf.timeouts[STRING_L]; lg.l_linger = conf.timeouts[STRING_L];
srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); 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)); srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int));
} }
#ifndef NOUDPMAIN #ifndef NOUDPMAIN
@ -1413,21 +1419,34 @@ void freeparam(struct clientparam * param) {
} }
if(param->clibuf) free(param->clibuf); if(param->clibuf) free(param->clibuf);
if(param->srvbuf) free(param->srvbuf); 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->srv) {
if(param->ctrlsocksrv != INVALID_SOCKET && param->ctrlsocksrv != param->remsock) { if(param->ctrlsocksrv != INVALID_SOCKET && param->ctrlsocksrv != param->remsock) {
#ifndef _WIN32
param->srv->so._shutdown(param->sostate, param->ctrlsocksrv, SHUT_RDWR); param->srv->so._shutdown(param->sostate, param->ctrlsocksrv, SHUT_RDWR);
#endif
param->srv->so._closesocket(param->sostate, param->ctrlsocksrv); param->srv->so._closesocket(param->sostate, param->ctrlsocksrv);
} }
if(param->ctrlsock != INVALID_SOCKET && param->ctrlsock != param->clisock) { if(param->ctrlsock != INVALID_SOCKET && param->ctrlsock != param->clisock) {
#ifndef _WIN32
param->srv->so._shutdown(param->sostate, param->ctrlsock, SHUT_RDWR); param->srv->so._shutdown(param->sostate, param->ctrlsock, SHUT_RDWR);
#endif
param->srv->so._closesocket(param->sostate, param->ctrlsock); param->srv->so._closesocket(param->sostate, param->ctrlsock);
} }
if(param->remsock != INVALID_SOCKET) { if(param->remsock != INVALID_SOCKET) {
#ifndef _WIN32
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR); param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR);
#endif
param->srv->so._closesocket(param->sostate, param->remsock); param->srv->so._closesocket(param->sostate, param->remsock);
} }
if(param->clisock != INVALID_SOCKET) { if(param->clisock != INVALID_SOCKET) {
#ifndef _WIN32
param->srv->so._shutdown(param->sostate, param->clisock, SHUT_RDWR); param->srv->so._shutdown(param->sostate, param->clisock, SHUT_RDWR);
#endif
param->srv->so._closesocket(param->sostate, param->clisock); param->srv->so._closesocket(param->sostate, param->clisock);
} }
} }

View File

@ -819,9 +819,6 @@ log("timeout");
else if(inclientpipe || inserverpipe) res = 94; else if(inclientpipe || inserverpipe) res = 94;
#endif #endif
if((param->nwrites > 0 && !SERVERTERMWRITE) || (param->nreads > 0 && !CLIENTTERMWRITE))
usleep(SLEEPTIME * 10);
CLEANRET: CLEANRET:
#ifdef WITHSPLICE #ifdef WITHSPLICE