Merge pull request #80 from JacobBarthelmeh/sftp-nuc

fix when reading directory
pull/83/head
John Safranek 2018-07-16 10:28:25 -07:00 committed by GitHub
commit 419efeb59c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 561 additions and 87 deletions

View File

@ -35,6 +35,7 @@
#ifdef WOLFSSL_NUCLEUS
/* use buffers for keys with server */
#define NO_FILESYSTEM
#define WOLFSSH_NO_EXIT
#endif
#ifdef NO_FILESYSTEM
@ -210,6 +211,12 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
break;
}
if (ret == WS_FATAL_ERROR && wolfSSH_get_error(threadCtx->ssh) ==
WS_VERSION_E) {
ret = 0; /* don't break out of loop with version miss match */
printf("Unsupported version error\n");
}
if (wolfSSH_shutdown(threadCtx->ssh) != WS_SUCCESS) {
fprintf(stderr, "Error with SSH shutdown.\n");
}
@ -220,7 +227,9 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
if (ret != 0) {
fprintf(stderr, "Error [%d] \"%s\" with handling connection.\n", ret,
wolfSSH_ErrorToName(ret));
#ifndef WOLFSSH_NO_EXIT
exit(EXIT_FAILURE);
#endif
}
free(threadCtx);

View File

@ -7151,3 +7151,59 @@ void DumpOctetString(const byte* input, word32 inputSz)
}
#endif
/* converts the octal input to decimal. Input is in string format i.e. 0666
* returns the decimal value on success or negative value on failure */
int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz)
{
int ret;
#if defined(WOLFSSL_KEY_GEN) || defined(HAVE_COMP_KEY) || \
defined(WOLFSSL_DEBUG_MATH) || defined(DEBUG_WOLFSSL) || \
defined(WOLFSSL_PUBLIC_MP)
mp_int tmp;
char decimalString[WOLFSSH_MAX_OCTET_LEN + 1];
#else
int i;
#endif
if (octSz > WOLFSSH_MAX_OCTET_LEN || ssh == NULL || oct == NULL) {
return WS_BAD_ARGUMENT;
}
#if defined(WOLFSSL_KEY_GEN) || defined(HAVE_COMP_KEY) || \
defined(WOLFSSL_DEBUG_MATH) || defined(DEBUG_WOLFSSL) || \
defined(WOLFSSL_PUBLIC_MP)
ret = mp_init(&tmp);
if (ret == MP_OKAY) {
ret = mp_read_radix(&tmp, (const char*)oct, 8);
}
if (ret == MP_OKAY) {
/* convert octal to decimal */
ret = mp_todecimal(&tmp, decimalString);
if (ret == MP_OKAY) {
/* convert string to int */
ret = atoi(decimalString);
}
}
mp_clear(&tmp);
#else
/* convert octal string to int without mp_read_radix() */
ret = 0;
for (i = 0; i < WOLFSSH_MAX_OCTET_LEN; i++)
{
if (oct[i] < '0' || oct[0] > '7') {
ret = WS_BAD_ARGUMENT;
break;
}
ret <<= 3;
ret |= (oct[i] - '0');
}
#endif
return ret;
}

View File

@ -128,6 +128,11 @@ void wolfSSH_free(WOLFSSH* ssh)
if (ssh) {
void* heap = ssh->ctx ? ssh->ctx->heap : NULL;
#ifdef WOLFSSH_SFTP
if (wolfSSH_SFTP_free(ssh) != WS_SUCCESS) {
WLOG(WS_LOG_SFTP, "Error cleaning up SFTP connection");
}
#endif
SshResourceFree(ssh, heap);
WFREE(ssh, heap, DYNTYPE_SSH);
}
@ -930,5 +935,3 @@ const char* wolfSSH_GetSessionCommand(const WOLFSSH* ssh)
return NULL;
}

View File

@ -827,6 +827,7 @@ static int GetScpFileMode(WOLFSSH* ssh, byte* buf, word32 bufSz,
return ret;
}
/* Locates first space present in given string (buf) and sets inOutIdx
* to that offset.
*

View File

@ -43,7 +43,8 @@ static int SFTP_ParseAtributes_buffer(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr,
byte* buf, word32 bufSz);
static int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr,
byte link);
static int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr);
static int SFTP_GetAttributes_Handle(WOLFSSH* ssh, byte* handle, int handleSz,
WS_SFTP_FILEATRB* atr);
static WS_SFTPNAME* wolfSSH_SFTPNAME_new(void* heap);
/* Gets packet header information
@ -127,8 +128,7 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version);
if (version != WOLFSSH_SFTP_VERSION) {
WLOG(WS_LOG_SFTP, "Unsupported SFTP version");
return WS_FATAL_ERROR;
WLOG(WS_LOG_SFTP, "Unsupported SFTP version, sending version 3");
}
/* silently ignore extensions if not supported */
@ -263,7 +263,7 @@ static int SFTP_AtributesSz(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr)
*
* returns WS_SUCCESS on success
*/
static int SFTP_SetAtributes(WOLFSSH* ssh, byte* buf, word32 bufSz,
static int SFTP_SetAttributes(WOLFSSH* ssh, byte* buf, word32 bufSz,
WS_SFTP_FILEATRB* atr)
{
word32 idx = 0;
@ -396,7 +396,7 @@ static int wolfSSH_SFTP_RecvRealPath(WOLFSSH* ssh, int reqId, int maxSz)
WMEMCPY(out + idx, r, rSz); idx += rSz;
/* set attributes */
SFTP_SetAtributes(ssh, out + idx, maxSz - idx, &atr);
SFTP_SetAttributes(ssh, out + idx, maxSz - idx, &atr);
/* send out buffer */
if (wolfSSH_stream_send(ssh, out, maxSz) < 0) {
@ -464,7 +464,10 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh)
case WOLFSSH_FTP_RENAME:
return wolfSSH_SFTP_RecvRename(ssh, reqId, maxSz);
#ifndef NO_WOLFSSL_DIR
case WOLFSSH_FTP_SETSTAT:
return wolfSSH_SFTP_RecvSetSTAT(ssh, reqId, maxSz);
#ifndef NO_WOLFSSH_DIR
case WOLFSSH_FTP_OPENDIR:
return wolfSSH_SFTP_RecvOpenDir(ssh, reqId, maxSz);
@ -673,7 +676,8 @@ int wolfSSH_SFTP_RecvMKDIR(WOLFSSH* ssh, int reqId, word32 maxSz)
ato32(data + idx, &sz); idx += UINT32_SZ;
if (sz != UINT32_SZ) {
WLOG(WS_LOG_SFTP, "Attribute size larger than 4 not yet supported");
WLOG(WS_LOG_SFTP, "Skipping over attribute");
WLOG(WS_LOG_SFTP, "Skipping over attribute and using default");
mode = 0x41ED;
}
else {
ato32(data + idx, &mode);
@ -811,13 +815,14 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, word32 maxSz)
}
#ifndef NO_WOLFSSL_DIR
#ifndef NO_WOLFSSH_DIR
/* hold pointers to directory handles */
typedef struct DIR_HANDLE {
WDIR dir;
byte isEof; /* flag for if read everything */
word64 id; /* handle ID */
char* dirName; /* base name of directory */
byte isEof; /* flag for if read everything */
word64 id; /* handle ID */
struct DIR_HANDLE* next;
} DIR_HANDLE;
static DIR_HANDLE* dirList = NULL;
@ -881,7 +886,6 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
"Unable To Open Directory", "English");
return WS_BAD_FILE_E;
}
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
(void)reqId;
@ -890,6 +894,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
dirList = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE), ssh->ctx->heap,
DYNTYPE_SFTP);
if (dirList == NULL) {
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_MEMORY_E;
}
#ifdef WOLFSSL_NUCLEUS
@ -900,6 +905,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
dirList->id = idCount++;
dirList->isEof = 0;
dirList->next = NULL;
dirList->dirName = dir; /* take over ownership of buffer */
SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&dirList->id,
sizeof(word64));
}
@ -907,6 +913,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
DIR_HANDLE* cur = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE),
ssh->ctx->heap, DYNTYPE_SFTP);
if (cur == NULL) {
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_MEMORY_E;
}
#ifdef WOLFSSL_NUCLEUS
@ -918,6 +925,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
cur->isEof = 0;
cur->next = dirList;
dirList = cur;
dirList->dirName = dir; /* take over ownership of buffer */
SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&cur->id,
sizeof(word64));
}
@ -933,7 +941,8 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
*
* returns WS_SUCCESS on success
*/
static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
char* dirName)
{
int sz;
@ -941,12 +950,14 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
return WS_BAD_ARGUMENT;
}
sz = (int)WSTRLEN(dir->sfname);
/* use long name on Nucleus because sfname has only the file name and in all
* caps */
sz = (int)WSTRLEN(dir->lfname);
out->fName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP);
if (out->fName == NULL) {
return WS_MEMORY_E;
}
WMEMCPY(out->fName, dir->sfname, sz);
WMEMCPY(out->fName, dir->lfname, sz);
out->fName[sz] = '\0';
out->fSz = sz;
@ -959,7 +970,33 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
out->lName[sz] = '\0';
out->lSz = sz;
SFTP_GetAttributes(out->fName, &out->atrb, 0);
{
char* buf;
int bufSz;
int tmpSz;
bufSz = out->fSz + WSTRLEN(dirName) + sizeof(WS_DELIM);
buf = (char*)WMALLOC(bufSz + 1, out->heap, DYNTYPE_SFTP);
if (buf == NULL) {
return WS_MEMORY_E;
}
buf[0] = '\0';
WSTRNCAT(buf, dirName, bufSz);
tmpSz = WSTRLEN(buf);
/* add delimiter between path and file/dir name */
if (tmpSz + 1 < bufSz) {
buf[tmpSz] = WS_DELIM;
buf[tmpSz+1] = '\0';
}
WSTRNCAT(buf, out->fName, bufSz);
clean_path(buf);
if (SFTP_GetAttributes(buf, &out->atrb, 0) != WS_SUCCESS) {
WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s", buf);
}
WFREE(buf, out->heap, DYNTYPE_SFTP);
}
if ((WREADDIR(dir)) == NULL) {
return WS_NEXT_ERROR;
@ -973,7 +1010,8 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
*
* returns WS_SUCCESS on success
*/
static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
char* dirName)
{
struct dirent* dp;
int sz;
@ -1006,8 +1044,33 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
out->lSz = sz;
/* attempt to get file attributes. Could be directory or have none */
if (SFTP_GetAttributes(out->fName, &out->atrb, 0) != WS_SUCCESS) {
WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s", out->fName);
{
char* buf;
int bufSz;
int tmpSz;
bufSz = out->fSz + (int)WSTRLEN(dirName) + sizeof(WS_DELIM);
buf = (char*)WMALLOC(bufSz + 1, out->heap, DYNTYPE_SFTP);
if (buf == NULL) {
return WS_MEMORY_E;
}
buf[0] = '\0';
WSTRNCAT(buf, dirName, bufSz);
tmpSz = (int)WSTRLEN(buf);
/* add delimiter between path and file/dir name */
if (tmpSz + 1 < bufSz) {
buf[tmpSz] = WS_DELIM;
buf[tmpSz+1] = '\0';
}
WSTRNCAT(buf, out->fName, bufSz);
clean_path(buf);
if (SFTP_GetAttributes(buf, &out->atrb, 0) != WS_SUCCESS) {
WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s",
out->fName);
}
WFREE(buf, out->heap, DYNTYPE_SFTP);
}
return WS_SUCCESS;
@ -1055,7 +1118,7 @@ static int wolfSSH_SFTP_SendName(WOLFSSH* ssh, WS_SFTPNAME* list, word32 count,
WMEMCPY(out + idx, cur->fName, cur->fSz); idx += cur->fSz;
c32toa(cur->lSz, out + idx); idx += UINT32_SZ;
WMEMCPY(out + idx, cur->lName, cur->lSz); idx += cur->lSz;
if (SFTP_SetAtributes(ssh, out + idx, *outSz - idx, &cur->atrb) !=
if (SFTP_SetAttributes(ssh, out + idx, *outSz - idx, &cur->atrb) !=
WS_SUCCESS) {
return WS_FATAL_ERROR;
}
@ -1086,6 +1149,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz)
WS_SFTPNAME* list = NULL;
word32 outSz = 0;
DIR_HANDLE* cur = dirList;
char* dirName = NULL;
if (ssh == NULL) {
return WS_BAD_ARGUMENT;
@ -1111,6 +1175,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz)
while (cur != NULL) {
if (cur->id == handle) {
dir = cur->dir;
dirName = cur->dirName;
break;
}
cur = cur->next;
@ -1127,7 +1192,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz)
outSz += UINT32_SZ + WOLFSSH_SFTP_HEADER; /* hold header+number of files */
do {
name = wolfSSH_SFTPNAME_new(ssh->ctx->heap);
ret = wolfSSH_SFTPNAME_readdir(ssh, &dir, name);
ret = wolfSSH_SFTPNAME_readdir(ssh, &dir, name, dirName);
if (ret == WS_SUCCESS || ret == WS_NEXT_ERROR) {
count++;
outSz += name->fSz + name->lSz + (UINT32_SZ * 2);
@ -1169,7 +1234,15 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz)
return WS_FATAL_ERROR;
}
wolfSSH_SFTPNAME_list_free(list);
wolfSSH_stream_send(ssh, data, outSz);
idx = 0;
while (idx < outSz) {
if ((ret = wolfSSH_stream_send(ssh, data + idx, outSz - idx)) < 0) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return ret;
}
idx += ret;
}
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_SUCCESS;
@ -1215,6 +1288,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz)
/* case where node is at head of list */
if (pre == cur) {
dirList = cur->next;
WFREE(cur->dirName, ssh->ctx->heap, DYNTYPE_SFTP);
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
}
else {
@ -1225,6 +1299,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz)
}
else {
pre->next = cur->next;
WFREE(cur->dirName, ssh->ctx->heap, DYNTYPE_SFTP);
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
}
}
@ -1232,7 +1307,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz)
return WS_SUCCESS;
}
#endif /* NO_WOLFSSL_DIR */
#endif /* NO_WOLFSSH_DIR */
/* Handles packet to write a file
*
@ -1424,13 +1499,13 @@ int wolfSSH_SFTP_RecvClose(WOLFSSH* ssh, int reqId, word32 maxSz)
return WS_BUFFER_E;
}
#ifndef NO_WOLFSSL_DIR
#ifndef NO_WOLFSSH_DIR
/* check if is a handle for a directory */
if (sz == sizeof(word64)) {
ret = wolfSSH_SFTP_RecvCloseDir(ssh, data + idx, sz);
}
else
#endif /* NO_WOLFSSL_DIR */
#endif /* NO_WOLFSSH_DIR */
if (sz == sizeof(WFD)) {
WMEMSET((byte*)&fd, 0, sizeof(WFD));
WMEMCPY((byte*)&fd, data + idx, sz);
@ -1629,21 +1704,21 @@ int wolfSSH_SFTP_RecvRename(WOLFSSH* ssh, int reqId, word32 maxSz)
* a file descriptor. In those cases we keep track of an internal list matching
* handles to file names */
typedef struct WS_HANDLE_LIST {
struct WS_HANDLE_LIST {
byte handle[WOLFSSH_MAX_HANDLE];
word32 handleSz;
char name[WOLFSSH_MAX_FILENAME];
struct WS_HANDLE_LIST* next;
struct WS_HANDLE_LIST* prev;
} WS_HANDLE_LIST;
static WS_HANDLE_LIST* handleList = NULL;
};
/* get a handle node from the list
* returns WS_HANDLE_LIST pointer on success and NULL on failure */
static WS_HANDLE_LIST* SFTP_GetHandleNode(byte* handle, word32 handleSz)
static WS_HANDLE_LIST* SFTP_GetHandleNode(WOLFSSH* ssh, byte* handle,
word32 handleSz)
{
WS_HANDLE_LIST* cur = handleList;
WS_HANDLE_LIST* cur = ssh->handleList;
if (handle == NULL) {
return NULL;
@ -1690,11 +1765,11 @@ int SFTP_AddHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz, char* name)
cur->name[sz] = '\0';
cur->prev = NULL;
cur->next = handleList;
if (handleList != NULL) {
handleList->prev = cur;
cur->next = ssh->handleList;
if (ssh->handleList != NULL) {
ssh->handleList->prev = cur;
}
handleList = cur;
ssh->handleList = cur;
return WS_SUCCESS;
}
@ -1710,7 +1785,7 @@ int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz)
return WS_BAD_ARGUMENT;
}
cur = SFTP_GetHandleNode(handle, handleSz);
cur = SFTP_GetHandleNode(ssh, handle, handleSz);
if (cur == NULL) {
WLOG(WS_LOG_SFTP, "Fatal Error! Trying to remove a handle that was not in the list");
return WS_FATAL_ERROR;
@ -1725,7 +1800,7 @@ int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz)
}
if (cur->next == NULL && cur->prev == NULL) {
handleList = NULL;
ssh->handleList = NULL;
}
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
@ -1770,21 +1845,19 @@ int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr, byte link)
/* get additional attributes */
{
byte atrib = 0;
if (NU_Get_Attributes(&atrib, fileName) == NU_SUCCESS) {
atr->flags |= WOLFSSH_FILEATRB_PERM;
if (atrib & ADIRENT) {
atr->per |= 0x4000;
}
else {
atr->per |= 0x8000;
}
if (atrib & ANORMAL) {
atr->per |= 0x755;
}
if (atrib & ARDONLY) {
atr->per |= 0x444;
}
byte atrib = stats.fattribute;
atr->flags |= WOLFSSH_FILEATRB_PERM;
if (atrib & ADIRENT) {
atr->per |= 0x41ED; /* 755 with directory */
}
else {
atr->per |= 0x8000;
}
if ((atrib & 0x01) == ANORMAL) {
atr->per |= 0x1ED; /* octal 755 */
}
if (atrib & ARDONLY) {
atr->per |= 0x124; /* octal 444 */
}
}
@ -1801,7 +1874,8 @@ int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr, byte link)
* Fills out a WS_SFTP_FILEATRB structure
* returns WS_SUCCESS on success
*/
int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
int SFTP_GetAttributes_Handle(WOLFSSH* ssh, byte* handle, int handleSz,
WS_SFTP_FILEATRB* atr)
{
DSTAT stats;
WS_HANDLE_LIST* cur;
@ -1810,7 +1884,7 @@ int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
return WS_FATAL_ERROR;
}
cur = SFTP_GetHandleNode(handle, handleSz);
cur = SFTP_GetHandleNode(ssh, handle, handleSz);
if (cur == NULL) {
WLOG(WS_LOG_SFTP, "Unknown handle");
return WS_BAD_FILE_E;
@ -1826,22 +1900,19 @@ int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
atr->sz = (word64)stats.fsize;
{
byte atrib = 0;
if (NU_Get_Attributes(&atrib, cur->name) == NU_SUCCESS) {
atr->flags |= WOLFSSH_FILEATRB_PERM;
if (atrib & ADIRENT) {
atr->per |= 0x4000;
}
else {
atr->per |= 0x8000;
}
if (atrib & ANORMAL) {
atr->per |= 0x755;
}
if (atrib & ARDONLY) {
atr->per |= 0x444;
}
byte atrib = stats.fattribute;
atr->flags |= WOLFSSH_FILEATRB_PERM;
if (atrib & ADIRENT) {
atr->per |= 0x41ED; /* 755 with directory */
}
else {
atr->per |= 0x8000;
}
if ((atrib & 0x01) == ANORMAL) {
atr->per |= 0x1ED; /* octal 755 */
}
if (atrib & ARDONLY) {
atr->per |= 0x124; /* octal 444 */
}
}
/* @TODO handle attribute extensions */
@ -1902,7 +1973,8 @@ int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr, byte link)
* Fills out a WS_SFTP_FILEATRB structure
* returns WS_SUCCESS on success
*/
int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
int SFTP_GetAttributes_Handle(WOLFSSH* ssh, byte* handle, int handleSz,
WS_SFTP_FILEATRB* atr)
{
struct stat stats;
@ -1935,6 +2007,7 @@ int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
/* @TODO handle attribute extensions */
(void)ssh;
return WS_SUCCESS;
}
#endif
@ -1973,7 +2046,7 @@ int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
/* try to get file attributes and send back to client */
WMEMSET((byte*)&atr, 0, sizeof(WS_SFTP_FILEATRB));
if (SFTP_GetAttributes_Handle(handle, handleSz, &atr) != WS_SUCCESS) {
if (SFTP_GetAttributes_Handle(ssh, handle, handleSz, &atr) != WS_SUCCESS) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
WLOG(WS_LOG_SFTP, "Unable to get fstat of file/directory");
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
@ -1993,7 +2066,7 @@ int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
}
SFTP_SetAtributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr);
SFTP_SetAttributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr);
if (wolfSSH_stream_send(ssh, data, sz + WOLFSSH_SFTP_HEADER) < 0) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
@ -2067,7 +2140,7 @@ int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
}
SFTP_SetAtributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr);
SFTP_SetAttributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr);
if (wolfSSH_stream_send(ssh, data, sz + WOLFSSH_SFTP_HEADER) < 0) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
@ -2147,7 +2220,7 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
}
SFTP_SetAtributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr);
SFTP_SetAttributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr);
if (wolfSSH_stream_send(ssh, data, sz + WOLFSSH_SFTP_HEADER) < 0) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
@ -2157,6 +2230,125 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
return WS_SUCCESS;
}
/* Set the files mode
* return WS_SUCCESS on success */
static int SFTP_SetMode(WOLFSSH* ssh, char* name, word32 mode) {
(void)ssh;
if (WCHMOD(name, mode) != 0) {
return WS_BAD_FILE_E;
}
return WS_SUCCESS;
}
/* sets a files attributes
* returns WS_SUCCESS on success */
static int SFTP_SetFileAttributes(WOLFSSH* ssh, char* name, WS_SFTP_FILEATRB* atr)
{
int ret = WS_SUCCESS;
/* check if size attribute present */
if (atr->flags & WOLFSSH_FILEATRB_SIZE) {
/* @TODO set file size */
}
/* check if uid and gid attribute present */
if (atr->flags & WOLFSSH_FILEATRB_UIDGID) {
/* @TODO set group and user id */
}
/* check if permissions attribute present */
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
ret = SFTP_SetMode(ssh, name, atr->per);
}
/* check if time attribute present */
if (atr->flags & WOLFSSH_FILEATRB_TIME) {
/* @TODO set time */
}
/* check if extended attributes are present */
if (atr->flags & WOLFSSH_FILEATRB_EXT) {
/* @TODO handle extensions */
}
(void)ssh;
return ret ;
}
/* Handles a packet sent to set attributes of path
*
* returns WS_SUCCESS on success
*/
int wolfSSH_SFTP_RecvSetSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
{
WS_SFTP_FILEATRB atr;
char* name = NULL;
int ret;
word32 sz;
byte* data;
word32 idx = 0;
if (ssh == NULL) {
return WS_BAD_ARGUMENT;
}
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_SETSTAT");
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
if (data == NULL) {
return WS_MEMORY_E;
}
if (wolfSSH_stream_read(ssh, data, maxSz) < 0) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
}
ato32(data + idx, &sz); idx += UINT32_SZ;
if (sz + idx > maxSz) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_BUFFER_E;
}
/* plus one to make sure is null terminated */
name = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
if (name == NULL) {
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
return WS_MEMORY_E;
}
WMEMCPY(name, data + idx, sz); idx += sz;
name[sz] = '\0';
clean_path(name);
if (SFTP_ParseAtributes_buffer(ssh, &atr, data + idx, maxSz - idx) != 0) {
printf("error parsing attributes\n");
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
"Unable to parse attributes error", "English");
return WS_BAD_FILE_E;
}
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
/* try to set file attributes and send status back to client */
if ((ret = SFTP_SetFileAttributes(ssh, name, &atr))
!= WS_SUCCESS) {
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
/* tell peer that was not ok */
WLOG(WS_LOG_SFTP, "Unable to get set attributes of file/directory");
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
"Unable to set attributes error", "English");
return WS_BAD_FILE_E;
}
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId, "Set Attirbutes",
"English");
return WS_SUCCESS;
}
#endif /* !NO_WOLFSSH_SERVER */
@ -2402,7 +2594,7 @@ static int wolfSSH_SFTP_DoStatus(WOLFSSH* ssh, word32 reqId)
}
ato32(buf, &sz);
{
if (sz > 0) {
byte* s = (byte*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
if (s == NULL) {
return WS_MEMORY_E;
@ -2426,8 +2618,7 @@ static int wolfSSH_SFTP_DoStatus(WOLFSSH* ssh, word32 reqId)
}
ato32(buf, &sz);
if (sz > 0)
{
if (sz > 0) {
byte* s = (byte*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
if (s == NULL) {
return WS_MEMORY_E;
@ -2498,6 +2689,7 @@ int SFTP_ParseAtributes_buffer(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr, byte* buf,
word32 idx = 0;
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
/* get flags */
ato32(buf, &atr->flags); idx += UINT32_SZ;
@ -2749,7 +2941,7 @@ static WS_SFTPNAME* wolfSSH_SFTP_DoName(WOLFSSH* ssh)
ato32(buf, &sz);
tmp->fSz = sz;
if (sz > 0) {
tmp->fName = (char*)XMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
tmp->fName = (char*)WMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
if (tmp->fName == NULL) {
ret = WS_MEMORY_E;
break;
@ -2772,7 +2964,7 @@ static WS_SFTPNAME* wolfSSH_SFTP_DoName(WOLFSSH* ssh)
ato32(buf, &sz);
tmp->lSz = sz;
if (sz > 0) {
tmp->lName = (char*)XMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
tmp->lName = (char*)WMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
if (tmp->lName == NULL) {
ret = WS_MEMORY_E;
break;
@ -2914,6 +3106,37 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
}
/* Takes in an octal file permissions value and sets it to the file/directory
* returns WS_SUCCESS on success
*/
int wolfSSH_SFTP_CHMOD(WOLFSSH* ssh, char* n, char* oct)
{
int ret;
int mode;
WS_SFTP_FILEATRB atr;
if (ssh == NULL || n == NULL || oct == NULL) {
return WS_BAD_ARGUMENT;
}
/* convert from octal to decimal */
mode = wolfSSH_oct2dec(ssh, (byte*)oct, (word32)WSTRLEN(oct));
if (mode < 0) {
return mode;
}
/* get current attributes of path */
WMEMSET(&atr, 0, sizeof(WS_SFTP_FILEATRB));
if ((ret = wolfSSH_SFTP_STAT(ssh, n, &atr)) != WS_SUCCESS) {
return ret;
}
/* update permissions */
atr.per = mode;
return wolfSSH_SFTP_SetSTAT(ssh, n, &atr);
}
/* helper function for common code between LSTAT and STAT
*
* returns WS_SUCCESS on success
@ -2997,6 +3220,69 @@ int wolfSSH_SFTP_LSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
}
/* Sends packet to set the attributes of path
*
* dir NULL terminated string of path
* atr structure holding file attributes to use
*
* returns WS_SUCCESS on success
*/
int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
{
int dirSz, atrSz, status;
word32 maxSz, reqId;
byte type;
byte* data;
word32 idx;
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_SetSTAT()");
if (ssh == NULL || dir == NULL || atr == NULL) {
return WS_BAD_ARGUMENT;
}
dirSz = (int)WSTRLEN(dir);
atrSz = SFTP_AtributesSz(ssh, atr);
data = (byte*)WMALLOC(dirSz + atrSz + WOLFSSH_SFTP_HEADER + UINT32_SZ,
ssh->ctx->heap, DYNTYPE_BUFFER);
if (data == NULL) {
return WS_MEMORY_E;
}
if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_SETSTAT,
dirSz + atrSz + UINT32_SZ, data) != WS_SUCCESS) {
WFREE(data, NULL, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
}
idx = WOLFSSH_SFTP_HEADER;
c32toa(dirSz, data + idx); idx += UINT32_SZ;
WMEMCPY(data + idx, (byte*)dir, dirSz); idx += dirSz;
SFTP_SetAttributes(ssh, data + idx, atrSz, atr); idx += atrSz;
/* send header and type specific data */
if (wolfSSH_stream_send(ssh, data, idx) < 0) {
WFREE(data, NULL, DYNTYPE_BUFFER);
return WS_FATAL_ERROR;
}
WFREE(data, NULL, DYNTYPE_BUFFER);
maxSz = SFTP_GetHeader(ssh, &reqId, &type);
if (type != WOLFSSH_FTP_STATUS) {
WLOG(WS_LOG_SFTP, "Unexpected packet type %d", type);
return WS_FATAL_ERROR;
}
else {
status = wolfSSH_SFTP_DoStatus(ssh, reqId);
if (status != WOLFSSH_FTP_OK) {
return WS_BAD_FILE_E;
}
}
(void)maxSz;
return WS_SUCCESS;
}
/* Open a file for reading/writing
*
* dir NULL terminated string holding file name
@ -3909,4 +4195,28 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
return ret;
}
/* called when wolfSSH_free() is called
* return WS_SUCCESS on success */
int wolfSSH_SFTP_free(WOLFSSH* ssh)
{
(void)ssh;
#ifdef WOLFSSH_STOREHANDLE
{
WS_HANDLE_LIST* cur = ssh->handleList;
/* go through and free handles and make sure files are closed */
while (cur != NULL) {
WCLOSE(*((WFD*)cur->handle));
if (SFTP_RemoveHandleNode(ssh, cur->handle, cur->handleSz)
!= WS_SUCCESS) {
return WS_FATAL_ERROR;
}
cur = ssh->handleList;
}
}
#endif
return WS_SUCCESS;
}
#endif /* WOLFSSH_SFTP */

View File

@ -204,6 +204,7 @@ static void ShowCommands(void)
{
printf("\n\nCommands :\n");
printf("\tcd <string> change directory\n");
printf("\tchmod <mode> <path> change mode\n");
printf("\tget <remote file> <local file> pulls file(s) from server\n");
printf("\tls list current directory\n");
printf("\tmkdir <dir name> creates new directory on server\n");
@ -534,6 +535,61 @@ int doCmds()
continue;
}
if ((pt = WSTRNSTR(msg, "chmod", sizeof(msg))) != NULL) {
int sz;
char* f = NULL;
char mode[WOLFSSH_MAX_OCTET_LEN];
pt += sizeof("chmod");
sz = (int)WSTRLEN(pt);
if (pt[sz - 1] == '\n') pt[sz - 1] = '\0';
/* get mode */
sz = (sz < WOLFSSH_MAX_OCTET_LEN - 1)? sz :
WOLFSSH_MAX_OCTET_LEN -1;
WMEMCPY(mode, pt, sz);
mode[WOLFSSH_MAX_OCTET_LEN - 1] = '\0';
for (i = sz; i > 0; i--) {
if (mode[i] == ' ') {
mode[i] = '\0';
break;
}
}
if (i == 0) {
printf("error with getting mode\r\n");
continue;
}
pt += (int)WSTRLEN(mode);
sz = (int)WSTRLEN(pt);
for (i = 0; i < sz && pt[0] == ' '; i++, pt++);
if (pt[0] != '/') {
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
f = XMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (f == NULL) {
err_sys("Error malloc'ing");
}
f[0] = '\0';
WSTRNCAT(f, workingDir, maxSz);
if (WSTRLEN(workingDir) > 1) {
WSTRNCAT(f, "/", maxSz);
}
WSTRNCAT(f, pt, maxSz);
pt = f;
}
/* update permissions */
if (wolfSSH_SFTP_CHMOD(ssh, pt, mode) != WS_SUCCESS) {
printf("unable to change path permissions\n");
}
XFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
continue;
}
if ((pt = WSTRNSTR(msg, "rmdir", sizeof(msg))) != NULL) {
int sz;
char* f = NULL;

View File

@ -245,6 +245,7 @@ typedef struct HandshakeInfo {
#ifdef WOLFSSH_SFTP
#define WOLFSSH_MAX_SFTPOFST 3
typedef struct WS_HANDLE_LIST WS_HANDLE_LIST;
typedef struct SFTP_OFST {
word64 offset;
char from[WOLFSSH_MAX_FILENAME];
@ -312,7 +313,6 @@ struct WOLFSSH {
ScpSendCtx scpSendCbCtx; /* used in default case to for send cb ctx */
#endif
#endif
byte connReset;
byte isClosed;
byte clientOpenSSH;
@ -369,6 +369,9 @@ struct WOLFSSH {
byte sftpState;
byte sftpInt;
SFTP_OFST sftpOfst[WOLFSSH_MAX_SFTPOFST];
#ifdef WOLFSSH_STOREHANDLE
WS_HANDLE_LIST* handleList;
#endif
#endif
};
@ -662,6 +665,7 @@ WOLFSSH_LOCAL int wsScpSendCallback(WOLFSSH*, int, const char*, char*, word32,
WOLFSSH_LOCAL void clean_path(char* path);
WOLFSSH_LOCAL void DumpOctetString(const byte*, word32);
WOLFSSH_LOCAL int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz);
#ifdef __cplusplus

View File

@ -89,6 +89,35 @@ extern "C" {
#define WCLOSE(fd) NU_Close((fd))
static inline int wChmod(const char* f, int mode) {
unsigned char atr = 0;
if (f == NULL) {
return -1;
}
if (NU_Get_Attributes(&atr, f) != NU_SUCCESS) {
return -1;
}
/* Set attribute value */
atr = atr & 0xF0; /* clear first byte */
if (mode == 0x124) {
atr |= ARDONLY; /* set read only value */
}
else {
/* if not setting read only set to normal */
atr |= ANORMAL;
}
if (NU_Set_Attributes(f, atr) != NU_SUCCESS) {
return -1;
}
else {
return 0;
}
}
#define WCHMOD(f,m) wChmod((f),(m))
#else
#define WFILE FILE
WOLFSSH_API int wfopen(WFILE**, const char*, const char*);
@ -102,6 +131,7 @@ extern "C" {
#define WREWIND(s) rewind((s))
#define WSEEK_END SEEK_END
#define WUTIMES(f,t) utimes((f),(t))
#define WCHMOD(f,m) chmod((f),(m))
#if (defined(WOLFSSH_SCP) || defined(WOLFSSH_SFTP)) && \
!defined(WOLFSSH_SCP_USER_CALLBACKS) && \
@ -247,7 +277,7 @@ extern "C" {
}
#define WUTIMES(f,t) wUtimes((f), (t))
#ifndef NO_WOLFSSL_DIR
#ifndef NO_WOLFSSH_DIR
#define WDIR DSTAT
#ifndef WOPENDIR
@ -310,7 +340,7 @@ extern "C" {
#define WCLOSEDIR(d) NU_Done((d))
#define WREADDIR(d) (NU_Get_Next((d)) == NU_SUCCESS)?(d):NULL
#endif /* NO_WOLFSSL_DIR */
#endif /* NO_WOLFSSH_DIR */
#else
#include <unistd.h> /* used for rmdir */
#include <sys/stat.h> /* used for mkdir, stat, and lstat */
@ -340,7 +370,7 @@ extern "C" {
#define WPWRITE(fd,b,s,o) pwrite((fd),(b),(s),(o))
#define WPREAD(fd,b,s,o) pread((fd),(b),(s),(o))
#ifndef NO_WOLFSSL_DIR
#ifndef NO_WOLFSSH_DIR
#include <dirent.h> /* used for opendir, readdir, and closedir */
#define WDIR DIR*
@ -348,7 +378,7 @@ extern "C" {
#define WOPENDIR(c,d) ((*(c) = opendir((d))) == NULL)
#define WCLOSEDIR(d) closedir(*(d))
#define WREADDIR(d) readdir(*(d))
#endif /* NO_WOLFSSL_DIR */
#endif /* NO_WOLFSSH_DIR */
#endif
#endif /* WOLFSSH_SFTP or WOLFSSH_SCP */

View File

@ -220,6 +220,7 @@ enum WS_DisconnectReasonCodes {
#ifndef WOLFSSH_MAX_FILENAME
#define WOLFSSH_MAX_FILENAME 256
#endif
#define WOLFSSH_MAX_OCTET_LEN 6
#ifdef __cplusplus

View File

@ -169,6 +169,7 @@ WOLFSSH_API int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle,
word32 handleSz, word64 ofst, byte* out, word32 outSz);
WOLFSSH_API int wolfSSH_SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr);
WOLFSSH_API int wolfSSH_SFTP_LSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr);
WOLFSSH_API int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr);
WOLFSSH_API void wolfSSH_SFTPNAME_free(WS_SFTPNAME* n);
WOLFSSH_API void wolfSSH_SFTPNAME_list_free(WS_SFTPNAME* n);
@ -190,6 +191,7 @@ WOLFSSH_API int wolfSSH_SFTP_RMDIR(WOLFSSH* ssh, char* dir);
WOLFSSH_API int wolfSSH_SFTP_Rename(WOLFSSH* ssh, const char* old,
const char* nw);
WOLFSSH_API WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir);
WOLFSSH_API int wolfSSH_SFTP_CHMOD(WOLFSSH* ssh, char* n, char* oct);
typedef void(WS_STATUS_CB)(WOLFSSH*, long, char*);
WOLFSSH_API int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from, char* to,
@ -215,14 +217,16 @@ WOLFSSH_LOCAL int wolfSSH_SFTP_RecvRemove(WOLFSSH* ssh, int reqId, word32 maxSz)
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvRename(WOLFSSH* ssh, int reqId, word32 maxSz);
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, word32 maxSz);
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, word32 maxSz);
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvSetSTAT(WOLFSSH* ssh, int reqId, word32 maxSz);
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, word32 maxSz);
#ifndef NO_WOLFSSL_DIR
#ifndef NO_WOLFSSH_DIR
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz);
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz);
WOLFSSH_LOCAL int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle,
word32 handleSz);
#endif /* NO_WOLFSSL_DIR */
#endif /* NO_WOLFSSH_DIR */
WOLFSSL_LOCAL int wolfSSH_SFTP_free(WOLFSSH* ssh);
WOLFSSL_LOCAL int SFTP_AddHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz, char* name);
WOLFSSL_LOCAL int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz);