mirror of https://github.com/wolfSSL/wolfssh.git
Non-blocking SFTP and SSH fixes, handle edge case with files having '.' in name, alter want write case to return consumed size
parent
90827e95b0
commit
dba42d3dc7
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
5
src/io.c
5
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;
|
||||
}
|
||||
|
||||
|
|
38
src/ssh.c
38
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,
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue