From 69f23482ff4fbbba76a1bb6909cbd7e9bf9c7a0c Mon Sep 17 00:00:00 2001 From: anonymouspage <123-anonymouspage@users.noreply.gitlab.synchro.net> Date: Tue, 18 Oct 2022 18:10:17 -0700 Subject: [PATCH] Use shutdown() ahead of close() for rlogin/ssh Using only close() on a socket has a few issues: (1) poll is not required to wake on such events leading to hangs if infinite timeouts are used. This can be observed in macOS: (a) syncterm -iC (b) connect via telnet to some system (c) use the menu to disconnect from the system Note: socket must be idle to reproduce. (2) If a blocked recv/send receives an error in response to a close from another thread, the context of the socket has been lost: the descriptor is free to be reallocated under the thread performing I/O by the system. Any retry logic that may be written in response to the error condition could lead to operations being performed on a valid but incorrect resource. (3) Processes that inherited the socket via fork() will still have a valid descriptor to the socket. i.e., the socket does not globally shutdown via a call to close(); however, shutdown() acts on the connection by triggering a TCP FIN. This will lead to the actual shutdown of the socket by the remote connection as well. Solution: Use shutdown() ahead of close(). This will initiate a proper shutdown sequence on the socket while leaving the descriptor open. poll() will wake, and a subsequent read or write will yield the desired EOF/EPIPE. --- src/syncterm/rlogin.c | 3 ++- src/syncterm/ssh.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/syncterm/rlogin.c b/src/syncterm/rlogin.c index e309b1eae8..68fac96517 100644 --- a/src/syncterm/rlogin.c +++ b/src/syncterm/rlogin.c @@ -212,11 +212,12 @@ int rlogin_close(void) char garbage[1024]; conn_api.terminate=1; - closesocket(rlogin_sock); + shutdown(rlogin_sock, SHUT_RDWR); while(conn_api.input_thread_running == 1 || conn_api.output_thread_running == 1) { conn_recv_upto(garbage, sizeof(garbage), 0); SLEEP(1); } + closesocket(rlogin_sock); destroy_conn_buf(&conn_inbuf); destroy_conn_buf(&conn_outbuf); FREE_AND_NULL(conn_api.rd_buf); diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c index 0b45edaac5..c9e46c390d 100644 --- a/src/syncterm/ssh.c +++ b/src/syncterm/ssh.c @@ -355,6 +355,7 @@ int ssh_close(void) SLEEP(1); } cl.DestroySession(ssh_session); + shutdown(SHUT_RDWR, ssh_sock); closesocket(ssh_sock); ssh_sock=INVALID_SOCKET; destroy_conn_buf(&conn_inbuf); -- GitLab