Non-blocking SFTP and SSH fixes, handle edge case with files having '.' in name, alter want write case to return consumed size

pull/431/head
JacobBarthelmeh 2022-07-18 13:31:10 -07:00
parent 90827e95b0
commit dba42d3dc7
6 changed files with 158 additions and 104 deletions

View File

@ -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");
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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 */

View File

@ -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,