diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 196f3140..4671f110 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -1132,11 +1132,21 @@ static int sftp_worker(thread_ctx_t* threadCtx) WS_SOCKET_T sockfd; int select_ret = 0; + error = wolfSSH_get_error(threadCtx->ssh); sockfd = (WS_SOCKET_T)wolfSSH_get_fd(threadCtx->ssh); do { if (threadCtx->nonBlock) { - if (error == WS_WANT_READ) + if (error == WS_WANT_READ) { + WOLFSSH_CHANNEL* c; printf("... sftp server would read block\n"); + + /* if all channels are closed then close connection */ + c = wolfSSH_ChannelNext(threadCtx->ssh, NULL); + if (c && wolfSSH_ChannelGetEof(c)) { + ret = 0; + break; + } + } else if (error == WS_WANT_WRITE) { word32 c; printf("... sftp server would write block\n"); @@ -1148,24 +1158,31 @@ static int sftp_worker(thread_ctx_t* threadCtx) } } - if (wolfSSH_stream_peek(threadCtx->ssh, tmp, 1) > 0) { - select_ret = WS_SELECT_RECV_READY; - } - else { - select_ret = tcp_select(sockfd, TEST_SFTP_TIMEOUT); - } - - if (select_ret == WS_SELECT_RECV_READY || - select_ret == WS_SELECT_ERROR_READY || - error == WS_WANT_WRITE) - { - ret = wolfSSH_SFTP_read(threadCtx->ssh); + /* if there is a current send in progress then continue to process it */ + if (wolfSSH_SFTP_PendingSend(threadCtx->ssh)) { + ret = wolfSSH_SFTP_read(threadCtx->ssh); error = wolfSSH_get_error(threadCtx->ssh); } - else if (select_ret == WS_SELECT_TIMEOUT) - error = WS_WANT_READ; - else - error = WS_FATAL_ERROR; + else { + if (wolfSSH_stream_peek(threadCtx->ssh, tmp, 1) > 0) { + select_ret = WS_SELECT_RECV_READY; + } + else { + select_ret = tcp_select(sockfd, TEST_SFTP_TIMEOUT); + } + + if (select_ret == WS_SELECT_RECV_READY || + select_ret == WS_SELECT_ERROR_READY || + error == WS_WANT_WRITE) + { + ret = wolfSSH_SFTP_read(threadCtx->ssh); + error = wolfSSH_get_error(threadCtx->ssh); + } + else if (select_ret == WS_SELECT_TIMEOUT) + error = WS_WANT_READ; + else + error = WS_FATAL_ERROR; + } if (error == WS_WANT_READ || error == WS_WANT_WRITE || error == WS_CHAN_RXD || error == WS_REKEYING || @@ -1181,7 +1198,7 @@ static int sftp_worker(thread_ctx_t* threadCtx) } } - } while (ret != WS_FATAL_ERROR); + } while (ret != WS_FATAL_ERROR && ret != WS_SOCKET_ERROR_E); return ret; } @@ -1279,8 +1296,43 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs) } if (error != WS_SOCKET_ERROR_E && error != WS_FATAL_ERROR) { - if (wolfSSH_shutdown(threadCtx->ssh) != WS_SUCCESS) { - fprintf(stderr, "Error with SSH shutdown.\n"); + ret = wolfSSH_shutdown(threadCtx->ssh); + + /* peer hung up, stop shutdown */ + if (ret == WS_SOCKET_ERROR_E) { + 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 succesfully closed down gracefully */ + if (ret == WS_CHANNEL_CLOSED) { + 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) { + break; + } + } + + if (attempt == maxAttempt) { + printf("Gave up on gracefull shutdown, closing the socket\n"); + } } } diff --git a/src/internal.c b/src/internal.c index 77ffc32c..22354e71 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1436,6 +1436,12 @@ int ChannelPutData(WOLFSSH_CHANNEL* channel, byte* data, word32 dataSz) inBuf = &channel->inputBuffer; + /* sanity check the current state to see if is too much data */ + if (dataSz > channel->windowSz) { + WLOG(WS_LOG_ERROR, "Internal state error, too much data"); + return WS_FATAL_ERROR; + } + if (inBuf->length < inBuf->bufferSz && inBuf->length + dataSz <= inBuf->bufferSz) { @@ -1445,7 +1451,7 @@ int ChannelPutData(WOLFSSH_CHANNEL* channel, byte* data, word32 dataSz) WLOG(WS_LOG_INFO, " dataSz = %u", dataSz); WLOG(WS_LOG_INFO, " windowSz = %u", channel->windowSz); channel->windowSz -= dataSz; - WLOG(WS_LOG_INFO, " windowSz = %u", channel->windowSz); + WLOG(WS_LOG_INFO, " update windowSz = %u", channel->windowSz); } else { return WS_RECV_OVERFLOW_E; @@ -9856,6 +9862,18 @@ int SendChannelData(WOLFSSH* ssh, word32 channelId, ret = WS_REKEYING; } + /* if already having data pending try to flush it first and do not continue + * to que more on fail */ + if (ret == WS_SUCCESS && ssh->outputBuffer.plainSz > 0) { + WLOG(WS_LOG_DEBUG, "Flushing out want write data"); + ret = wolfSSH_SendPacket(ssh); + if (ret != WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "Leaving SendChannelData(), ret = %d", ret); + return ret; + } + + } + if (ret == WS_SUCCESS) { if (ssh->outputBuffer.length != 0) ret = wolfSSH_SendPacket(ssh); @@ -9914,10 +9932,12 @@ int SendChannelData(WOLFSSH* ssh, word32 channelId, WLOG(WS_LOG_INFO, " update peerWindowSz = %u", channel->peerWindowSz); } + /* at this point the data has been loaded into WOLFSSH structure and is + * considered consumed */ if (ret == WS_SUCCESS) ret = wolfSSH_SendPacket(ssh); - if (ret == WS_SUCCESS) + if (ret == WS_SUCCESS || ret == WS_WANT_WRITE) ret = dataSz; if (ssh && ssh->error == WS_WANT_WRITE) @@ -10453,18 +10473,9 @@ int wolfSSH_CleanPath(WOLFSSH* ssh, char* in) if (path[i] == '/') path[i] = '\\'; } #endif - - /* remove any ./ patterns */ - for (i = 1; i < sz - 1; i++) { - if (path[i] == '.' && path[i - 1] != '.' && path[i + 1] == WS_DELIM) { - WMEMMOVE(path + i, path + i + 1, sz - i - 1); - path[sz - 1] = '\0'; - i--; - } - } sz = (int)WSTRLEN(path); - /* remove any /./ patterns */ + /* remove any /./ patterns, direcotries, exclude cases like ./ok./test */ for (i = 1; i + 1 < sz; i++) { if (path[i] == '.' && path[i - 1] == WS_DELIM && path[i + 1] == WS_DELIM) { WMEMMOVE(path + i, path + i + 1, sz - i + 1); diff --git a/src/io.c b/src/io.c index f3be6f9c..43fed38b 100644 --- a/src/io.c +++ b/src/io.c @@ -404,11 +404,7 @@ int wsEmbedSend(WOLFSSH* ssh, void* data, word32 sz, void* ctx) #endif /* MICROCHIP_MPLAB_HARMONY */ sent = (int)SEND_FUNCTION(sd, buf, sz, ssh->wflags); - sent = TranslateReturnCode(sent, sd); - - WLOG(WS_LOG_DEBUG,"Embed Send sent %d", sent); - if (sent < 0) { err = LastError(); WLOG(WS_LOG_DEBUG,"Embed Send error"); @@ -434,6 +430,7 @@ int wsEmbedSend(WOLFSSH* ssh, void* data, word32 sz, void* ctx) return WS_CBIO_ERR_GENERAL; } } + WLOG(WS_LOG_DEBUG,"Embed Send sent %d", sent); return sent; } diff --git a/src/ssh.c b/src/ssh.c index e24e8770..067281af 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -947,10 +947,14 @@ int wolfSSH_shutdown(WOLFSSH* ssh) if (ret == WS_SUCCESS) ret = SendChannelEof(ssh, ssh->channelList->peerChannel); - if (ret == WS_SUCCESS) + /* continue on success and in case where queing up send packets */ + if (ret == WS_SUCCESS || + (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) ret = SendChannelExit(ssh, ssh->channelList->peerChannel, 0); - if (ret == WS_SUCCESS) + /* continue on success and in case where queing up send packets */ + if (ret == WS_SUCCESS || + (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) ret = SendChannelClose(ssh, ssh->channelList->peerChannel); if (ssh != NULL && ssh->channelList == NULL) { @@ -1013,14 +1017,21 @@ static int wolfSSH_stream_adjust_window(WOLFSSH* ssh) bytesToAdd = inputBuffer->idx; WLOG(WS_LOG_DEBUG, "Making more room: %u", usedSz); + WLOG(WS_LOG_DEBUG, " Current index into buffer = %u", inputBuffer->idx); + WLOG(WS_LOG_DEBUG, " Current max index for available data = %u", + inputBuffer->length); + WLOG(WS_LOG_DEBUG, " Current total buffer size = %u", + inputBuffer->bufferSz); if (usedSz) { - WLOG(WS_LOG_DEBUG, " ...moving data down"); + WLOG(WS_LOG_DEBUG, " ...moving %d used bytes down", usedSz); WMEMMOVE(inputBuffer->buffer, inputBuffer->buffer + bytesToAdd, usedSz); + inputBuffer->length = usedSz; + inputBuffer->idx = 0; } ret = SendChannelWindowAdjust(ssh, ssh->channelList->channel, bytesToAdd); - if (ret != WS_SUCCESS) { + if (ret != WS_SUCCESS && ret != WS_WANT_WRITE) { WLOG(WS_LOG_ERROR, "Error adjusting window"); } else { @@ -1065,7 +1076,11 @@ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz) if (ret == WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, " Stream read index of %u", inputBuffer->idx); + WLOG(WS_LOG_DEBUG, " Stream read ava data %u", inputBuffer->length); while (inputBuffer->length - inputBuffer->idx == 0) { + WLOG(WS_LOG_DEBUG, "Starting to recieve data at current index of %u", + inputBuffer->idx); ret = DoReceive(ssh); if (ssh->channelList == NULL || ssh->channelList->eofRxd) ret = WS_EOF; @@ -1109,19 +1124,6 @@ int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz) if (ssh == NULL || buf == NULL || ssh->channelList == NULL) return WS_BAD_ARGUMENT; - /* case of WANT WRITE and data stored in output buffer */ - if (ssh->outputBuffer.plainSz && ssh->outputBuffer.length != 0) { - int ret; - - bytesTxd = ssh->outputBuffer.plainSz; - WLOG(WS_LOG_DEBUG, "Trying to resend %d bytes", bytesTxd); - ssh->error = WS_SUCCESS; - ret = wolfSSH_SendPacket(ssh); - - /* return the amount sent on success otherwise return error found */ - return (ret == WS_SUCCESS)? bytesTxd : ret; - } - bytesTxd = SendChannelData(ssh, ssh->channelList->channel, buf, bufSz); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_send(), txd = %d", bytesTxd); @@ -2029,6 +2031,8 @@ static int _UpdateChannelWindow(WOLFSSH_CHANNEL* channel) WLOG(WS_LOG_DEBUG, " ...moving data down"); WMEMMOVE(inputBuffer->buffer, inputBuffer->buffer + bytesToAdd, usedSz); + inputBuffer->length = usedSz; + inputBuffer->idx = 0; } sendResult = SendChannelWindowAdjust(channel->ssh, channel->channel, diff --git a/src/wolfsftp.c b/src/wolfsftp.c index bdc9138a..37575a79 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -492,7 +492,7 @@ static void wolfSSH_SFTP_buffer_rewind(WS_SFTP_BUFFER* buffer) * increments idx with amount sent */ static int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer) { - int ret; + int ret = WS_SUCCESS; if (buffer == NULL) { return WS_BAD_ARGUMENT; @@ -502,25 +502,20 @@ static int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer) return WS_BUFFER_E; } - do { + while (buffer->idx < buffer->sz && (ret > 0 || ret == WS_SUCCESS)) { ret = wolfSSH_stream_send(ssh, buffer->data + buffer->idx, buffer->sz - buffer->idx); - if (ret == WS_WINDOW_FULL || ret == WS_REKEYING) { - ret = wolfSSH_worker(ssh, NULL); - if (ret == WS_SUCCESS) - continue; /* skip past increment and send more */ - } if (ret > 0) { buffer->idx += ret; } + WLOG(WS_LOG_SFTP, "SFTP buffer sent %d / %d bytes", buffer->idx, + buffer->sz); - if (ret == WS_WANT_WRITE) { - /* data was stored in out buffer of ssh struct but not sent - * still advance the SFTP buffer index */ - buffer->idx += buffer->sz - buffer->idx; + /* interupt sending for a rekey or full window */ + if (ret == WS_WINDOW_FULL || ret == WS_REKEYING) { + ret = wolfSSH_worker(ssh, NULL); } - - } while (buffer->idx < buffer->sz && (ret > 0 || ret == WS_SUCCESS)); + } return ret; } @@ -557,6 +552,8 @@ static int wolfSSH_SFTP_buffer_read(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer, return WS_FATAL_ERROR; } buffer->idx += (word32)ret; + WLOG(WS_LOG_SFTP, "SFTP buffer read %d / %d bytes", buffer->idx, + buffer->sz); } while (buffer->idx < buffer->sz); return buffer->sz; @@ -763,6 +760,19 @@ static void wolfSSH_SFTP_ClearState(WOLFSSH* ssh, enum WS_SFTP_STATE_ID state) } +/* Returns 1 if there is pending data to be sent and 0 if not */ +int wolfSSH_SFTP_PendingSend(WOLFSSH* ssh) +{ + int isSet = 0; + + if (ssh) { + if (ssh->recvState != NULL && ssh->recvState->toSend) + isSet = 1; + } + return isSet; +} + + /* Gets packet header information * request Id, type, and size of type specific data * return value is length of type specific data still on the wire to be read @@ -996,8 +1006,13 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) { } ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version); - if (version != WOLFSSH_SFTP_VERSION) { + /* versions greater than WOLFSSH_SFTP_VERSION should fall back to ours + * versions less than WOLFSSH_SFTP_VERSION we should bail out on or + * implement a fall back */ + if (version < WOLFSSH_SFTP_VERSION) { WLOG(WS_LOG_SFTP, "Unsupported SFTP version, sending version 3"); + wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); + return WS_VERSION_E; } /* silently ignore extensions if not supported */ @@ -1032,7 +1047,6 @@ static int SFTP_ServerSendInit(WOLFSSH* ssh) { if ((ret = wolfSSH_stream_send(ssh, buf, sizeof(buf))) != sizeof(buf)) { return ret; } - return WS_SUCCESS; } @@ -1107,6 +1121,7 @@ static void wolfSSH_SFTP_RecvSetSend(WOLFSSH* ssh, byte* buf, int sz) if (state == NULL) { return; } + WLOG(WS_LOG_SFTP, "Loading up send buffer"); /* free up existing data if needed */ if (buf != state->buffer.data && state->buffer.data != NULL) { @@ -1463,40 +1478,13 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh) case STATE_RECV_SEND: if (state->toSend) { - do { - if (state->toSend != 2) { - ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer); - if (ret < 0) { - if (ssh->error != WS_WANT_READ && - ssh->error != WS_WANT_WRITE && - ssh->error != WS_REKEYING && - ssh->error != WS_CHAN_RXD && - ssh->error != WS_WINDOW_FULL) - wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV); - return WS_FATAL_ERROR; - } - } - state->toSend = 1; - - if (wolfSSH_SFTP_buffer_idx(&state->buffer) - < wolfSSH_SFTP_buffer_size(&state->buffer)) { - ret = wolfSSH_worker(ssh, NULL); - if (ret != WS_SUCCESS && - (ssh->error == WS_WANT_READ || - ssh->error == WS_REKEYING || - ssh->error == WS_CHAN_RXD || - ssh->error == WS_WINDOW_FULL)) { - /* was something there to read, try again */ - state->toSend = 2; - return WS_FATAL_ERROR; - } - } - } while (wolfSSH_SFTP_buffer_idx(&state->buffer) - < wolfSSH_SFTP_buffer_size(&state->buffer)); - ret = WS_SUCCESS; - state->toSend = 0; + ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer); + if (ret == WS_SUCCESS || ret > 0) { + ret = WS_SUCCESS; + state->toSend = 0; + wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV); + } } - wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV); return ret; default: @@ -3276,9 +3264,9 @@ int wolfSSH_SFTP_RecvWrite(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) } } - if (sz > maxSz - idx) { - return WS_BUFFER_E; - } + if (sz > maxSz - idx) { + return WS_BUFFER_E; + } if (wolfSSH_SFTP_CreateStatus(ssh, type, reqId, res, "English", NULL, &outSz) != WS_SIZE_ONLY) { return WS_FATAL_ERROR; @@ -8501,6 +8489,7 @@ int wolfSSH_SFTP_free(WOLFSSH* ssh) WFREE(toFree->dirName, ssh->ctx->heap, DYNTYPE_SFTP); WFREE(toFree, ssh->ctx->heap, DYNTYPE_SFTP); } + dirList = NULL; } #endif /* NO_WOLFSSH_DIR */ diff --git a/wolfssh/wolfsftp.h b/wolfssh/wolfsftp.h index 647021ac..93cdf766 100644 --- a/wolfssh/wolfsftp.h +++ b/wolfssh/wolfsftp.h @@ -215,6 +215,7 @@ WOLFSSH_API int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, /* SFTP server functions */ WOLFSSH_API int wolfSSH_SFTP_read(WOLFSSH* ssh); +WOLFSSH_API int wolfSSH_SFTP_PendingSend(WOLFSSH* ssh); WOLFSSH_LOCAL int wolfSSH_SFTP_CreateStatus(WOLFSSH* ssh, word32 status,