Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 25 additions & 35 deletions apps/wolfssh/wolfssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,7 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
socklen_t clientAddrSz = sizeof(clientAddr);
int ret = 0;
int ioErr = 0;
int waitRet = WS_SUCCESS;
byte keepOpen;
#ifdef USE_WINDOWS_API
byte rawMode = 0;
Expand Down Expand Up @@ -1389,51 +1390,40 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
if (ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
if (ret != WS_SUCCESS && ret != WS_WANT_WRITE && ret != WS_WANT_READ &&
ret != WS_CHANNEL_CLOSED && ret != WS_EOF &&
ret != WS_SOCKET_ERROR_E && ret != WS_DISCONNECT) {
WLOG(WS_LOG_DEBUG, "Sending the shutdown messages failed.");
}

/* do not continue on with shutdown process if peer already disconnected */
if (ret != WS_SOCKET_ERROR_E
&& wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) {
#ifndef WOLFSSL_NUCLEUS
if (ret == WS_WANT_WRITE) {
/* The close messages are queued and the threads are done, no
* one else is going to send them. */
ret = FlushQueuedSend(ssh, NULL);
if (ret == WS_WANT_WRITE) {
/* The peer stopped reading. The socket closes next, there is
* nothing left to push the messages out with. */
ret = WS_SUCCESS;
}
}
#endif

if (ret == WS_SUCCESS) {
ret = wolfSSH_worker(ssh, NULL);
if (ret == WS_FATAL_ERROR) {
ret = wolfSSH_get_error(ssh);
}
if (ret == WS_WANT_WRITE) {
/* The close messages are already out, whatever the drain
* still wants to send is a reply to the peer. */
ret = WS_SUCCESS;
}
}
else if (ret != WS_CHANNEL_CLOSED && ret != WS_WANT_READ
&& ret != WS_EOF) {
WLOG(WS_LOG_DEBUG, "Sending the shutdown messages failed.");
/* The exit status read below comes ahead of the peer's close, and
* the drain would discard it. A peer that disconnected, hung up or
* never closed still ended the session; any other error broke it. */
if (ret != WS_CHANNEL_CLOSED) {
waitRet = WaitForChannelClose(ssh, sockFd);
if (waitRet == WS_WANT_READ || waitRet == WS_DISCONNECT ||
waitRet == WS_SOCKET_ERROR_E)
waitRet = WS_SUCCESS;
}

if (ret == WS_CHANNEL_CLOSED || ret == WS_WANT_READ
|| ret == WS_EOF) {
/* Shutting down. The channel closing or the peer's EOF isn't a
* fail, and neither is the peer having nothing ready on this
* non-blocking socket; either way there is nothing left to wait
* for. */
/* The close messages a short send left queued go out with the
* disconnect: the threads are done, no one else is going to send
* them. */
ret = SendDisconnectAndDrain(ssh, sockFd);
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {
/* The socket closes next, so there is nothing left to push a
* queued disconnect out with, and a peer that never hung up is
* not a fail. */
ret = WS_SUCCESS;
}
else if (ret != WS_SUCCESS) {
WLOG(WS_LOG_DEBUG,
"Failed to listen for close messages from the peer.");
WLOG(WS_LOG_DEBUG, "Sending the disconnect failed.");
}
if (ret == WS_SUCCESS)
ret = waitRet;
}
WCLOSESOCKET(sockFd);

Expand Down
63 changes: 15 additions & 48 deletions apps/wolfsshd/wolfsshd.c
Original file line number Diff line number Diff line change
Expand Up @@ -3792,6 +3792,7 @@ static void* HandleConnection(void* arg)
{
int ret = WS_SUCCESS;
int error;
int graceExpired = 0;
#ifdef _WIN32
byte threaded = 0;
#endif
Expand Down Expand Up @@ -3894,6 +3895,7 @@ static void* HandleConnection(void* arg)
ret != WS_SFTP_COMPLETE && ret != WS_SCP_INIT) {
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] Failed login within grace period");
graceExpired = 1;
}

#ifdef _WIN32
Expand Down Expand Up @@ -4080,63 +4082,28 @@ static void* HandleConnection(void* arg)
if (error != WS_SOCKET_ERROR_E && error != WS_FATAL_ERROR) {
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Attempting to close down connection");
ret = wolfSSH_shutdown(ssh);
error = wolfSSH_get_error(ssh);

/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
if (ret == WS_SOCKET_ERROR_E || error == WS_SOCKET_ERROR_E) {
ret = 0;
}
}

error = wolfSSH_get_error(ssh);
if (error != WS_SOCKET_ERROR_E &&
(error == WS_WANT_READ || error == WS_WANT_WRITE)) {
int maxAttempt = 10; /* make 10 attempts max before giving up */
int attempt;

for (attempt = 0; attempt < maxAttempt; attempt++) {
ret = wolfSSH_worker(ssh, NULL);
error = wolfSSH_get_error(ssh);

/* peer successfully closed down gracefully */
if (ret == WS_CHANNEL_CLOSED || ret == WS_EOF) {
ret = 0;
break;
}

/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
ret = 0;
break;
}

if (ret == WS_FATAL_ERROR &&
(error != WS_WANT_READ &&
error != WS_WANT_WRITE)) {
break;
}
#ifdef _WIN32
Sleep(1);
#else
usleep(100000);
#endif
}

if (attempt == maxAttempt) {
wolfSSH_Log(WS_LOG_INFO,
"[SSHD] Gave up on graceful shutdown, closing the socket");
}
if (conn != NULL) {
/* The close exchange ended the session, so the half close alone
* says the daemon is done: OpenSSH's client exits 255 on a
* disconnect that lands before it is finished. A peer that let the
* grace time run out is not waited on a second time. */
if (error != WS_SOCKET_ERROR_E && !graceExpired &&
HalfCloseAndDrain(ssh, conn->fd) == WS_WANT_READ) {
wolfSSH_Log(WS_LOG_INFO,
"[SSHD] Gave up waiting for the peer to hang up, "
"closing the socket");
}
}

/* check if there is a response to the shutdown */
wolfSSH_free(ssh);
if (conn != NULL) {
byte sc[1024];
shutdown(conn->fd, 1);
/* Spin until socket closes. */
do {
ret = (int)recv(conn->fd, (char*)sc, 1024, 0);
} while (ret > 0);

WCLOSESOCKET(conn->fd);
}
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Return from closing connection = %d", ret);
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ AC_ARG_WITH(wolfssl,
)

AC_CHECK_LIB([wolfssl],[wolfCrypt_Init],,[AC_MSG_ERROR([libwolfssl is required for ${PACKAGE}. It can be obtained from https://www.wolfssl.com/download.html/ .])])
AC_CHECK_FUNCS([gethostbyname getaddrinfo gettimeofday inet_ntoa memset socket wc_ecc_set_rng])
AC_CHECK_FUNCS([gethostbyname getaddrinfo gettimeofday inet_ntoa memset socket socketpair wc_ecc_set_rng])

# futimens()/utimensat() declarations are feature-test-macro gated, so a plain
# AC_CHECK_FUNCS link test can pass while the prototype stays hidden at compile
Expand Down
53 changes: 35 additions & 18 deletions examples/client/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
socklen_t clientAddrSz = sizeof(clientAddr);
char rxBuf[80];
int ret = 0;
int error = 0;
int waitRet = WS_SUCCESS;
int ch;
int userEcc = 0;
word16 port = wolfSshPort;
Expand Down Expand Up @@ -1250,26 +1252,41 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
#endif
}
ret = wolfSSH_shutdown(ssh);
/* do not continue on with shutdown process if peer already disconnected.
* A peer EOF is not a disconnect: the channel is still open and its close
* is still owed, so the drain below is exactly what is wanted. */
if (ret != WS_SOCKET_ERROR_E && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E
&& wolfSSH_get_error(ssh) != WS_CHANNEL_CLOSED) {
if (ret != WS_SUCCESS && ret != WS_WANT_WRITE) {
ClientFreeBuffers(pubKeyName, privKeyName, NULL);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
err_sys("Sending the shutdown messages failed.");
}
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E &&
ret != WS_CHANNEL_CLOSED && ret != WS_EOF) {
ClientFreeBuffers(pubKeyName, privKeyName, NULL);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
err_sys("Failed to listen for close messages from the peer.");
error = wolfSSH_get_error(ssh);
/* WS_FATAL_ERROR only says to go look, the session has the detail. The
* teardown sends are the point of the call, so an unexpected result is
* worth a line, but the drain below still runs: the peer's close is
* still owed. */
if (ret == WS_FATAL_ERROR)
ret = error;
if (ret != WS_SUCCESS && ret != WS_WANT_WRITE && ret != WS_WANT_READ &&
ret != WS_CHANNEL_CLOSED && ret != WS_EOF &&
ret != WS_SOCKET_ERROR_E && ret != WS_DISCONNECT)
printf("Sending the shutdown messages failed, error %d\n", ret);
/* The exit status read below comes ahead of the peer's close, and the
* drain would discard it. A peer that disconnected, hung up or never
* closed still ended the session; any other error broke it. */
if (ret != WS_CHANNEL_CLOSED && error != WS_SOCKET_ERROR_E) {
waitRet = WaitForChannelClose(ssh, sockFd);
if (waitRet == WS_WANT_READ || waitRet == WS_DISCONNECT ||
waitRet == WS_SOCKET_ERROR_E)
waitRet = WS_SUCCESS;
}
ret = WS_SUCCESS;

/* peer already hung up, just close. A peer EOF is not a hang up: the
* channel is still open and its close is still owed, so the drain is
* exactly what is wanted there. */
if (error != WS_SOCKET_ERROR_E) {
ret = SendDisconnectAndDrain(ssh, sockFd);
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {
printf("Gave up on a graceful shutdown, "
"closing the socket\n");
ret = WS_SUCCESS;
}
}
if (ret == WS_SUCCESS)
ret = waitRet;
WCLOSESOCKET(sockFd);

#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
Expand Down
54 changes: 11 additions & 43 deletions examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -2352,55 +2352,23 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
}

if (error != WS_SOCKET_ERROR_E && error != WS_FATAL_ERROR) {
ret = wolfSSH_shutdown(threadCtx->ssh);
(void)wolfSSH_shutdown(threadCtx->ssh);
error = wolfSSH_get_error(threadCtx->ssh);

/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
if (error == WS_SOCKET_ERROR_E) {
ret = 0;
}

/* The peer's close already retired the channel: a completed
* shutdown, not a failure. Left non-zero it sets quit, taking the
* server down after one session. */
if (ret == WS_CHANNEL_CLOSED) {
else {
/* The close exchange ended the session, and OpenSSH's client
* exits 255 on a disconnect that lands before it is done, so
* the half close alone says we are finished. */
ret = HalfCloseAndDrain(threadCtx->ssh, threadCtx->fd);
if (ret == WS_WANT_READ)
printf("Gave up waiting for the peer to hang up, "
"closing the socket\n");
ret = 0;
}

error = wolfSSH_get_error(threadCtx->ssh);
if (error != WS_SOCKET_ERROR_E &&
(error == WS_WANT_READ || error == WS_WANT_WRITE)) {
int maxAttempt = 10; /* make 10 attempts max before giving up */
int attempt;

for (attempt = 0; attempt < maxAttempt; attempt++) {
ret = wolfSSH_worker(threadCtx->ssh, NULL);
error = wolfSSH_get_error(threadCtx->ssh);

/* peer successfully closed down gracefully */
if (ret == WS_CHANNEL_CLOSED || ret == WS_EOF) {
ret = 0;
break;
}

/* peer hung up, stop shutdown */
if (ret == WS_SOCKET_ERROR_E) {
ret = 0;
break;
}

if (error == WS_WANT_READ || error == WS_WANT_WRITE) {
/* Wanting read or wanting write. Clear ret. */
ret = 0;
}
else {
break;
}
}

if (attempt == maxAttempt) {
printf("Gave up on graceful shutdown, closing the socket\n");
}
}
}

if (threadCtx->fd != -1) {
Expand Down
15 changes: 13 additions & 2 deletions examples/portfwd/portfwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -934,11 +934,22 @@ THREAD_RETURN WOLFSSH_THREAD portfwd_worker(void* args)
}

ret = wolfSSH_shutdown(ssh);
/* The socket closes next, so a queued write and a retired channel are
* both done as far as this teardown is concerned. */
/* A queued write and a retired channel are both done as far as this
* teardown is concerned: the disconnect carries the write out. */
if (ret != WS_SUCCESS && ret != WS_WANT_WRITE && ret != WS_CHANNEL_CLOSED)
err_sys("Closing port forward stream failed.");

/* peer already hung up, just close */
if (wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) {
ret = SendDisconnectAndDrain(ssh, sshFd);
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {
printf("Gave up on a graceful shutdown, closing the socket\n");
ret = WS_SUCCESS;
}
else if (ret != WS_SUCCESS)
err_sys("Sending the disconnect failed.");
}

WCLOSESOCKET(sshFd);
if (listenFd != (SOCKET_T)-1)
WCLOSESOCKET(listenFd);
Expand Down
33 changes: 17 additions & 16 deletions examples/scpclient/scpclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,22 +317,23 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
}

ret = wolfSSH_shutdown(ssh);
/* do not continue on with shutdown process if peer already disconnected.
* A peer EOF is not a disconnect: the channel is still open and its close
* is still owed, so the drain below is exactly what is wanted. */
if (ret != WS_CHANNEL_CLOSED && ret != WS_SOCKET_ERROR_E &&
wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E &&
wolfSSH_get_error(ssh) != WS_CHANNEL_CLOSED) {
if (ret != WS_SUCCESS && ret != WS_WANT_WRITE) {
WLOG(WS_LOG_DEBUG, "Sending the shutdown messages failed.");
}
else {
ret = wolfSSH_worker(ssh, NULL);
if (ret != WS_SUCCESS && ret != WS_CHANNEL_CLOSED
&& ret != WS_EOF) {
WLOG(WS_LOG_DEBUG,
"Failed to listen for close messages from the peer.");
}
/* WS_FATAL_ERROR only says to go look, the session has the detail. */
if (ret == WS_FATAL_ERROR)
ret = wolfSSH_get_error(ssh);
if (ret != WS_SUCCESS && ret != WS_WANT_WRITE && ret != WS_WANT_READ &&
ret != WS_CHANNEL_CLOSED && ret != WS_EOF &&
ret != WS_SOCKET_ERROR_E && ret != WS_DISCONNECT) {
WLOG(WS_LOG_DEBUG, "Sending the shutdown messages failed.");
}
ret = WS_SUCCESS;

/* peer already hung up, just close */
if (wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) {
ret = SendDisconnectAndDrain(ssh, sockFd);
if (ret == WS_WANT_READ || ret == WS_WANT_WRITE) {
WLOG(WS_LOG_DEBUG,
"Gave up on a graceful shutdown, closing the socket");
ret = WS_SUCCESS;
}
}
WCLOSESOCKET(sockFd);
Expand Down
Loading
Loading