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
69 changes: 47 additions & 22 deletions examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,17 +508,28 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
appCtx->state = APP_STATE_CONNECT;
}
else if (action == WOLFSSH_FWD_LOCAL_CLEANUP) {
WCLOSESOCKET(appCtx->appFd);
appCtx->appFd = -1;
if (fwdCbCtx->hostName) {
WFREE(fwdCbCtx->hostName, NULL, 0);
fwdCbCtx->hostName = NULL;
}
if (fwdCbCtx->originName) {
WFREE(fwdCbCtx->originName, NULL, 0);
fwdCbCtx->originName = NULL;
/* The channel id rides in the port parameter. A channel can outlive
* its turn in the slot, so only the holder may tear it down. */
if (port == appCtx->channelId) {
/* This runs now, so the socket may already be gone: the open can
* fail after the setup, before anything connected. */
if (appCtx->appFd != (WS_SOCKET_T)-1) {
WCLOSESOCKET(appCtx->appFd);
appCtx->appFd = -1;
}
if (fwdCbCtx->hostName) {
WFREE(fwdCbCtx->hostName, NULL, 0);
fwdCbCtx->hostName = NULL;
}
if (fwdCbCtx->originName) {
WFREE(fwdCbCtx->originName, NULL, 0);
fwdCbCtx->originName = NULL;
}
/* A refused connect leaves this set; retire it with the
* channel. */
fwdCbCtx->isDirect = 0;
appCtx->state = APP_STATE_INIT;
}
appCtx->state = APP_STATE_INIT;
}
else if (action == WOLFSSH_FWD_REMOTE_SETUP) {
struct sockaddr_in addr;
Expand Down Expand Up @@ -1181,21 +1192,35 @@ static int ssh_worker(thread_ctx_t* threadCtx)
}
else if (rc == WS_CHANNEL_CLOSED) {
#ifdef WOLFSSH_FWD
if (threadCtx->fwdCtx.state == APP_STATE_CONNECTED &&
lastChannel == threadCtx->fwdCtx.channelId) {
/* Read zero-returned. Socket is closed. Go back
to listening. */
if (fwdFd != -1) {
WCLOSESOCKET(fwdFd);
/* wolfSSH_worker() names the channel only for the
* data and EOF statuses; DoChannelClose() recorded
* the id it retired. */
wolfSSH_GetLastRxId(ssh, &lastChannel);
if (lastChannel == threadCtx->fwdCtx.channelId) {
Comment thread
ejohnstown marked this conversation as resolved.
if (threadCtx->fwdCtx.appFd == -1) {
/* The LOCAL_CLEANUP handler ran ahead of
* this and closed the socket; only this
* copy of the descriptor is stale. */
fwdFd = -1;
threadCtx->fwdCtx.appFd = -1;
}
if (threadCtx->fwdCbCtx.originName != NULL) {
WFREE(threadCtx->fwdCbCtx.originName,
NULL, 0);
threadCtx->fwdCbCtx.originName = NULL;
else if (threadCtx->fwdCtx.state
== APP_STATE_CONNECTED) {
/* A locally opened forward is armed by no
* LOCAL_SETUP and so draws no cleanup. Its
* teardown is still ours: go back to
* listening. */
if (fwdFd != -1) {
WCLOSESOCKET(fwdFd);
fwdFd = -1;
threadCtx->fwdCtx.appFd = -1;
}
if (threadCtx->fwdCbCtx.originName != NULL) {
WFREE(threadCtx->fwdCbCtx.originName,
NULL, 0);
threadCtx->fwdCbCtx.originName = NULL;
}
threadCtx->fwdCtx.state = APP_STATE_LISTEN;
}
threadCtx->fwdCtx.state = APP_STATE_LISTEN;
}
#endif
continue;
Expand Down
24 changes: 20 additions & 4 deletions examples/portfwd/portfwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ typedef struct PortfwdState {
SOCKET_T appFd; /* socket to the local target, -1 when idle */
word32 channelId; /* id of the inbound forwarded-tcpip channel */
int pending; /* a new channel is waiting to be wired up */
int cleanupRxd; /* LOCAL_CLEANUP closed appFd for us */
int replied; /* peer answered the tcpip-forward request */
int refused; /* ...and the answer was a refusal */
int badPort; /* ...or named a port outside 1..65535 */
Expand Down Expand Up @@ -319,15 +320,17 @@ static int portfwdRemoteFwdCb(WS_FwdCbAction action, void* ctx,
st->pending = 1;
break;
case WOLFSSH_FWD_LOCAL_CLEANUP:
/* The library does not currently emit this action, so this branch
* never runs. The target socket is closed when portfwd_worker()
* leaves its loop. Kept so the handler is right if that changes. */
/* Paired with the LOCAL_SETUP that opened the target socket.
* portfwd_worker() keeps its own copy of the descriptor, so tell
* it not to close what has already been closed. The closing
* channel's id arrives in the port argument. */
(void)address;
(void)port;
if (st->appFd != (SOCKET_T)-1) {
WCLOSESOCKET(st->appFd);
st->appFd = (SOCKET_T)-1;
}
st->cleanupRxd = 1;
break;
case WOLFSSH_FWD_REMOTE_SETUP:
case WOLFSSH_FWD_REMOTE_CLEANUP:
Expand Down Expand Up @@ -746,6 +749,9 @@ THREAD_RETURN WOLFSSH_THREAD portfwd_worker(void* args)
WS_CHANNEL_ID_SELF);
if (fwdState.appFd != (SOCKET_T)-1 && newChannel != NULL) {
appFd = fwdState.appFd;
/* The latch describes this descriptor now, not one an
* earlier failed open already cleaned up. */
fwdState.cleanupRxd = 0;
fwdChannel = newChannel;
fwdChannelId = fwdState.channelId;
FD_SET(appFd, &templateFds);
Expand Down Expand Up @@ -885,7 +891,17 @@ THREAD_RETURN WOLFSSH_THREAD portfwd_worker(void* args)
WCLOSESOCKET(sshFd);
if (listenFd != (SOCKET_T)-1)
WCLOSESOCKET(listenFd);
WCLOSESOCKET(appFd);
/* Skip a descriptor the cleanup callback already closed; closing it
* twice can take down whatever has been handed the number since. */
if (fwdState.cleanupRxd)
appFd = (SOCKET_T)-1;
if (appFd != (SOCKET_T)-1) {
WCLOSESOCKET(appFd);
/* The loop can leave with the channel still open, and freeing the
* session below runs the cleanup handler on this same descriptor. */
if (fwdState.appFd == appFd)
fwdState.appFd = (SOCKET_T)-1;
}
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
#ifdef WOLFSSH_SMALL_STACK
Expand Down
36 changes: 36 additions & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3840,12 +3840,45 @@ WOLFSSH_CHANNEL* ChannelNew(WOLFSSH* ssh, byte channelType,
}


#ifdef WOLFSSH_FWD
/* Counterpart of the WOLFSSH_FWD_LOCAL_SETUP sent when a forwarding channel
* was opened, so the application can release what it set up there. Gated on
* that setup having succeeded: a locally opened forward draws no setup, and
* one that reported failure set nothing up. Cleaning up after either would
* free what the application does not own. Runs from ChannelDelete() so every
* way a channel goes, a peer close, a refused open, wolfSSH_ChannelFree(),
* or the session being freed, reports it once.
* The channel's id rides in the port parameter, the way CHANNEL_ID passes
* it. */
static void NotifyFwdLocalCleanup(WOLFSSH_CHANNEL* channel)
{
WOLFSSH* ssh;
int ret;

if (channel == NULL || !channel->fwdSetupTxd)
return;
ssh = channel->ssh;
if (ssh == NULL || ssh->ctx->fwdCb == NULL)
return;

channel->fwdSetupTxd = 0;
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_LOCAL_CLEANUP, ssh->fwdCbCtx,
NULL, channel->channel);
if (ret != WS_SUCCESS) {
WLOG(WS_LOG_WARN, "Forward cleanup failed for channel %u, ret = %d",
channel->channel, ret);
}
}
#endif /* WOLFSSH_FWD */


void ChannelDelete(WOLFSSH_CHANNEL* channel, void* heap)
{
WOLFSSH_UNUSED(heap);

if (channel) {
#ifdef WOLFSSH_FWD
NotifyFwdLocalCleanup(channel);
if (channel->host)
WFREE(channel->host, heap, DYNTYPE_STRING);
if (channel->origin)
Expand Down Expand Up @@ -12053,6 +12086,9 @@ static int DoChannelOpen(WOLFSSH* ssh,
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_LOCAL_SETUP,
ssh->fwdCbCtx, host, hostPort);
if (ret == WS_SUCCESS) {
/* The application now owns whatever the setup made,
* so it is owed the matching cleanup. */
newChannel->fwdSetupTxd = 1;
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_CHANNEL_ID,
ssh->fwdCbCtx, NULL, newChannel->channel);
}
Expand Down
Loading
Loading