Write to a peer that hung up while it still reads, on Windows

sockmap() took a hang-up on a socket it had data to write to for a peer
it could no longer write to. With poll() a hang-up comes once both sides
are shut down, so that held. WSAPoll reports one as soon as the peer has
half-closed, still reading, and a half-closed client got nothing on
Windows: the test added with the previous fix failed there.

The write is now tried, and a peer really gone makes it fail, which
ends the connection as before. WSAPoll keeps reporting the hang-up
whether the socket has room or not, so on Windows room is waited for
with select() first unless poll() reported it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-13 12:52:47 +03:00
parent 5d1cdf8e59
commit 9f46558d0b

View File

@ -71,6 +71,28 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
#define CLIROOM ROOM(param->clibufsize, param->cliinbuf)
#define SRVROOM ROOM(param->srvbufsize, param->srvinbuf)
/* A hang-up on a socket there is data to write to. With poll() it comes
once both sides are shut down, and the write tried then fails, which ends
it. WSAPoll reports it as soon as the peer has half-closed, a peer still
reading, and keeps reporting it whether the socket has room or not: room
is waited for with select() then, up to timeo seconds. Returns whether to
try the write. */
static int hupwrite(SOCKET s, short revents, int timeo){
#ifdef _WIN32
if(!(revents & POLLOUT)){
fd_set wfd;
struct timeval tv;
FD_ZERO(&wfd);
FD_SET(s, &wfd);
tv.tv_sec = timeo;
tv.tv_usec = 0;
if(select(0, NULL, &wfd, NULL, &tv) < 1) return 0;
}
#endif
return 1;
}
/* usesplice is a mask of the directions passed with splice() rather than
through a buffer: SPLICE_DOWNLOAD, server to client, and SPLICE_UPLOAD,
client to server. */
@ -676,7 +698,10 @@ log("ready to write to client");
}
if(fds[fdsc].revents & (POLLHUP)) {
if(fds[fdsc].events & POLLIN) FROMCLIENT = 1;
if(fds[fdsc].events & POLLOUT) CLIENTTERMWRITE = 1;
if(fds[fdsc].events & POLLOUT){
if(hupwrite(param->clisock, fds[fdsc].revents, timeo)) TOCLIENT = 1;
else CLIENTTERMWRITE = 1;
}
}
}
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
@ -741,7 +766,10 @@ log("ready to write to server");
log("server terminated connection");
#endif
if(fds[fdsc].events & POLLIN) FROMSERVER = 1;
if(fds[fdsc].events & POLLOUT) SERVERTERMWRITE = 1;
if(fds[fdsc].events & POLLOUT){
if(hupwrite(param->remsock, fds[fdsc].revents, timeo)) TOSERVER = 1;
else SERVERTERMWRITE = 1;
}
}
}
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {