mirror of https://github.com/wolfSSL/wolfssh.git
3913 lines
102 KiB
C
3913 lines
102 KiB
C
/* wolfsftp.c
|
|
*
|
|
* Copyright (C) 2014-2016 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSH.
|
|
*
|
|
* wolfSSH is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSH is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with wolfSSH. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <wolfssh/wolfsftp.h>
|
|
|
|
#ifdef WOLFSSH_SFTP
|
|
|
|
#include <wolfssh/internal.h>
|
|
#include <wolfssh/log.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssh/misc.h>
|
|
#else
|
|
#define WOLFSSH_MISC_INCLUDED
|
|
#include "src/misc.c"
|
|
#endif
|
|
|
|
static int SendPacketType(WOLFSSH* ssh, byte type, byte* buf, word32 bufSz);
|
|
static int SFTP_ParseAtributes(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr);
|
|
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 WS_SFTPNAME* wolfSSH_SFTPNAME_new(void* heap);
|
|
|
|
/* 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
|
|
*/
|
|
static int SFTP_GetHeader(WOLFSSH* ssh, word32* reqId, byte* type)
|
|
{
|
|
int ret;
|
|
word32 len;
|
|
byte buf[WOLFSSH_SFTP_HEADER];
|
|
|
|
if (type == NULL || reqId == NULL || ssh == NULL) {
|
|
WLOG(WS_LOG_SFTP, "NULL argument error");
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
ret = wolfSSH_stream_read(ssh, buf, sizeof(buf));
|
|
if (ret < WOLFSSH_SFTP_HEADER) {
|
|
WLOG(WS_LOG_SFTP, "Unable to read SFTP header");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ato32(buf, &len);
|
|
*type = buf[LENGTH_SZ];
|
|
ato32(buf + UINT32_SZ + MSG_ID_SZ, reqId);
|
|
|
|
return len - UINT32_SZ - MSG_ID_SZ;
|
|
}
|
|
|
|
|
|
/* Sets the packet header information to the front of buf. It is assumed that
|
|
* buf is at least WOLFSSH_SFTP_HEADER (9).
|
|
*
|
|
* reqId request ID to place in header of packet
|
|
* type type of SFTP packet i.e. WOLFSSH_FTP_OPEN
|
|
* len length of all data in packet
|
|
* buf buffer to place packet header at front of
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_SetHeader(WOLFSSH* ssh, word32 reqId, byte type, word32 len,
|
|
byte* buf) {
|
|
c32toa(len + LENGTH_SZ + MSG_ID_SZ, buf);
|
|
buf[LENGTH_SZ] = type;
|
|
c32toa(reqId, buf + LENGTH_SZ + MSG_ID_SZ);
|
|
|
|
(void)ssh;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
#ifndef NO_WOLFSSH_SERVER
|
|
/* unique from other packets because the request ID is not also sent.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
|
|
int len;
|
|
byte id;
|
|
word32 sz = 0;
|
|
word32 version = 0;
|
|
byte buf[LENGTH_SZ + MSG_ID_SZ + UINT32_SZ];
|
|
|
|
if ((len = wolfSSH_stream_read(ssh, buf, sizeof(buf))) != sizeof(buf)) {
|
|
return len;
|
|
}
|
|
|
|
ato32(buf, &sz);
|
|
if (sz < MSG_ID_SZ + UINT32_SZ) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
/* compare versions supported */
|
|
id = buf[LENGTH_SZ];
|
|
if (id != WOLFSSH_FTP_INIT) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected SFTP type received");
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version);
|
|
if (version != WOLFSSH_SFTP_VERSION) {
|
|
WLOG(WS_LOG_SFTP, "Unsupported SFTP version");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* silently ignore extensions if not supported */
|
|
sz = sz - MSG_ID_SZ - UINT32_SZ;
|
|
if (sz > 0) {
|
|
byte* data = (byte*)WMALLOC(sz, NULL, DYNTYPE_BUFFER);
|
|
if (data == NULL) return WS_MEMORY_E;
|
|
if ((len = wolfSSH_stream_read(ssh, data, sz)) != (int)sz) {
|
|
return len;
|
|
}
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
}
|
|
|
|
ssh->reqId++;
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* unique from SendPacketType because the request ID is not also sent.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_ServerSendInit(WOLFSSH* ssh) {
|
|
int ret;
|
|
byte buf[LENGTH_SZ + MSG_ID_SZ + UINT32_SZ];
|
|
|
|
c32toa(MSG_ID_SZ + UINT32_SZ, buf);
|
|
buf[LENGTH_SZ] = WOLFSSH_FTP_VERSION;
|
|
|
|
/* version */
|
|
c32toa((word32)WOLFSSH_SFTP_VERSION, buf + LENGTH_SZ + MSG_ID_SZ);
|
|
if ((ret = wolfSSH_stream_send(ssh, buf, sizeof(buf))) != sizeof(buf)) {
|
|
return ret;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* @TODO
|
|
* state machine for SFTP protocol
|
|
* returns WS_SFTP_COMPLETE on success
|
|
*/
|
|
int wolfSSH_SFTP_accept(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_SFTP_COMPLETE;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* check accept is done, if not call wolfSSH accept */
|
|
if (ssh->acceptState < ACCEPT_CLIENT_SESSION_ESTABLISHED) {
|
|
byte name[] = "sftp";
|
|
|
|
WLOG(WS_LOG_SFTP, "Trying to do SSH accept first");
|
|
if ((ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM,
|
|
name, sizeof(name) - 1)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Unable to set subsystem channel type");
|
|
return ret;
|
|
}
|
|
|
|
if ((ret = wolfSSH_accept(ssh)) != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
switch (ssh->sftpState) {
|
|
case SFTP_BEGIN:
|
|
if ((ssh->error = SFTP_ServerRecvInit(ssh)) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ssh->connectState = SFTP_RECV;
|
|
FALL_THROUGH /* no break */
|
|
|
|
case SFTP_RECV:
|
|
if ((ssh->error = SFTP_ServerSendInit(ssh)) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ssh->connectState = SFTP_DONE;
|
|
WLOG(WS_LOG_SFTP, "SFTP connection established");
|
|
break;
|
|
|
|
default:
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* returns the size of buffer needed to hold attributes */
|
|
static int SFTP_AtributesSz(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
word32 sz = 0;
|
|
|
|
(void)ssh;
|
|
|
|
sz += UINT32_SZ; /* flag */
|
|
|
|
/* check if size attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_SIZE) {
|
|
sz += UINT32_SZ * 2;
|
|
}
|
|
|
|
/* check if uid and gid attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_UIDGID) {
|
|
sz += UINT32_SZ * 2;
|
|
}
|
|
|
|
/* check if permissions attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
|
|
sz += UINT32_SZ;
|
|
}
|
|
|
|
/* check if time attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_TIME) {
|
|
sz += UINT32_SZ * 2;
|
|
}
|
|
|
|
/* check if extended attributes are present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_EXT) {
|
|
sz += UINT32_SZ;
|
|
|
|
/* @TODO handle extended attributes */
|
|
}
|
|
|
|
return sz;
|
|
}
|
|
|
|
|
|
/* set attributes in buffer
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_SetAtributes(WOLFSSH* ssh, byte* buf, word32 bufSz,
|
|
WS_SFTP_FILEATRB* atr)
|
|
{
|
|
word32 idx = 0;
|
|
|
|
(void)ssh;
|
|
(void)bufSz;
|
|
|
|
/* get flags */
|
|
c32toa(atr->flags, buf); idx += UINT32_SZ;
|
|
|
|
/* check if size attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_SIZE) {
|
|
c32toa((word32)(atr->sz << 32), buf + idx); idx += UINT32_SZ;
|
|
c32toa((word32)(atr->sz & 0xFFFFFFFF), buf + idx); idx += UINT32_SZ;
|
|
}
|
|
|
|
/* check if uid and gid attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_UIDGID) {
|
|
c32toa(atr->uid, buf + idx); idx += UINT32_SZ;
|
|
c32toa(atr->gid, buf + idx); idx += UINT32_SZ;
|
|
}
|
|
|
|
/* check if permissions attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
|
|
c32toa(atr->per, buf + idx); idx += UINT32_SZ;
|
|
}
|
|
|
|
|
|
/* check if time attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_TIME) {
|
|
c32toa(atr->atime, buf + idx); idx += UINT32_SZ;
|
|
c32toa(atr->mtime, buf + idx); idx += UINT32_SZ;
|
|
}
|
|
|
|
/* check if extended attributes are present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_EXT) {
|
|
/* @TODO handle attribute extensions */
|
|
c32toa(atr->extCount, buf + idx);
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* returns WS_SUCCESS on success */
|
|
static int wolfSSH_SFTP_RecvRealPath(WOLFSSH* ssh, int reqId, int maxSz)
|
|
{
|
|
WS_SFTP_FILEATRB atr;
|
|
char* dir;
|
|
char r[WOLFSSH_MAX_FILENAME];
|
|
word32 rSz;
|
|
word32 idx = 0;
|
|
word32 i;
|
|
byte* out;
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_REALPATH");
|
|
|
|
if (ssh == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Bad argument passed in");
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* get directory from wire */
|
|
dir = (char*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (dir == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
if (wolfSSH_stream_read(ssh, (byte*)dir, maxSz) < 0) {
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ato32((byte*)dir, &rSz);
|
|
if (rSz > WOLFSSH_MAX_FILENAME || (int)(rSz + UINT32_SZ) > maxSz) {
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
WMEMCPY(r, dir + UINT32_SZ, rSz);
|
|
r[rSz] = '\0';
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
/* get working directory in the case of receiving non absolute path */
|
|
if (r[0] != '/' && r[1] != ':') {
|
|
char wd[WOLFSSH_MAX_FILENAME];
|
|
|
|
if (WGETCWD(wd, WOLFSSH_MAX_FILENAME) == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Unable to get current working directory");
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Directory error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
WSTRNCAT(wd, "/", sizeof("/"));
|
|
WSTRNCAT(wd, r, WOLFSSH_MAX_FILENAME - 1);
|
|
WMEMCPY(r, wd, WOLFSSH_MAX_FILENAME);
|
|
}
|
|
|
|
clean_path(r);
|
|
rSz = (int)WSTRLEN(r);
|
|
|
|
/* for real path always send '/' chars */
|
|
for (i = 0; i < rSz; i++) {
|
|
if (r[i] == WS_DELIM) r[i] = '/';
|
|
}
|
|
WLOG(WS_LOG_SFTP, "Real Path Directory = %s", r);
|
|
|
|
/* send response */
|
|
maxSz = WOLFSSH_SFTP_HEADER + (UINT32_SZ * 3) + (rSz * 2);
|
|
|
|
WMEMSET(&atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
maxSz += SFTP_AtributesSz(ssh, &atr);
|
|
|
|
out = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (out == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_NAME,
|
|
maxSz - WOLFSSH_SFTP_HEADER, out);
|
|
idx += WOLFSSH_SFTP_HEADER;
|
|
|
|
/* set number of files */
|
|
c32toa(1, out + idx); idx += UINT32_SZ; /* only sending one file name */
|
|
|
|
/* set file name size and string */
|
|
c32toa(rSz, out + idx); idx += UINT32_SZ;
|
|
WMEMCPY(out + idx, r, rSz); idx += rSz;
|
|
|
|
/* set long name size and string */
|
|
c32toa(rSz, out + idx); idx += UINT32_SZ;
|
|
WMEMCPY(out + idx, r, rSz); idx += rSz;
|
|
|
|
/* set attributes */
|
|
SFTP_SetAtributes(ssh, out + idx, maxSz - idx, &atr);
|
|
|
|
/* send out buffer */
|
|
if (wolfSSH_stream_send(ssh, out, maxSz) < 0) {
|
|
WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Look for incoming packet and handle it
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_read(WOLFSSH* ssh)
|
|
{
|
|
int maxSz;
|
|
word32 reqId;
|
|
byte type = 0;
|
|
|
|
|
|
/* wait for packet to come in then handle it */
|
|
maxSz = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (maxSz <= 0) {
|
|
return maxSz;
|
|
}
|
|
|
|
ssh->reqId = reqId;
|
|
switch (type) {
|
|
case WOLFSSH_FTP_REALPATH:
|
|
return wolfSSH_SFTP_RecvRealPath(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_RMDIR:
|
|
return wolfSSH_SFTP_RecvRMDIR(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_MKDIR:
|
|
return wolfSSH_SFTP_RecvMKDIR(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_STAT:
|
|
return wolfSSH_SFTP_RecvSTAT(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_LSTAT:
|
|
return wolfSSH_SFTP_RecvLSTAT(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_FSTAT:
|
|
return wolfSSH_SFTP_RecvFSTAT(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_OPEN:
|
|
return wolfSSH_SFTP_RecvOpen(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_READ:
|
|
return wolfSSH_SFTP_RecvRead(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_WRITE:
|
|
return wolfSSH_SFTP_RecvWrite(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_CLOSE:
|
|
return wolfSSH_SFTP_RecvClose(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_REMOVE:
|
|
return wolfSSH_SFTP_RecvRemove(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_RENAME:
|
|
return wolfSSH_SFTP_RecvRename(ssh, reqId, maxSz);
|
|
|
|
#ifndef NO_WOLFSSL_DIR
|
|
case WOLFSSH_FTP_OPENDIR:
|
|
return wolfSSH_SFTP_RecvOpenDir(ssh, reqId, maxSz);
|
|
|
|
case WOLFSSH_FTP_READDIR:
|
|
return wolfSSH_SFTP_RecvReadDir(ssh, reqId, maxSz);
|
|
#endif
|
|
|
|
default:
|
|
/* read rest of data off the wire and send error status to client */
|
|
{
|
|
byte* data;
|
|
WLOG(WS_LOG_SFTP, "Unknown packet type [%d] received", type);
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data != NULL) {
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
}
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Unknown/Unsupported packet type", "English");
|
|
}
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* send a status packet
|
|
*
|
|
* structure of status packet is as follows
|
|
* {
|
|
* uint32 error code
|
|
* string error msg
|
|
* string language
|
|
* }
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_SendStatus(WOLFSSH* ssh, word32 status, word32 reqId,
|
|
const char* reason, const char* lang)
|
|
{
|
|
byte* buf;
|
|
word32 sz;
|
|
word32 maxSz;
|
|
word32 idx = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
maxSz = WOLFSSH_SFTP_HEADER + (UINT32_SZ * 3);
|
|
if (reason != NULL) {
|
|
maxSz += WSTRLEN(reason);
|
|
}
|
|
if (lang != NULL) {
|
|
maxSz += WSTRLEN(lang);
|
|
}
|
|
|
|
buf = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (buf == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
idx += WOLFSSH_SFTP_HEADER;
|
|
|
|
if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_STATUS, maxSz - idx, buf)
|
|
!= WS_SUCCESS) {
|
|
WFREE(buf, ssh->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
c32toa(status, buf + idx); idx += UINT32_SZ;
|
|
|
|
sz = (reason != NULL)? (int)WSTRLEN(reason): 0;
|
|
if (sz + idx + UINT32_SZ > maxSz) {
|
|
WFREE(buf, ssh->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
c32toa(sz, buf + idx); idx += UINT32_SZ;
|
|
if (reason != NULL) {
|
|
WMEMCPY(buf + idx, reason, sz); idx += sz;
|
|
}
|
|
|
|
|
|
sz = (lang != NULL)? (int)WSTRLEN(lang): 0;
|
|
if (sz + idx + UINT32_SZ > maxSz) {
|
|
WFREE(buf, ssh->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
c32toa(sz, buf + idx); idx += UINT32_SZ;
|
|
if (lang != NULL) {
|
|
WMEMCPY(buf + idx, lang, sz);
|
|
}
|
|
|
|
if (wolfSSH_stream_send(ssh, buf, maxSz) <= 0) {
|
|
WFREE(buf, ssh->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
WFREE(buf, ssh->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles packet to remove a directory
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvRMDIR(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
word32 sz;
|
|
int ret;
|
|
byte* data;
|
|
char* dir;
|
|
word32 idx = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_RMDIR");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
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 */
|
|
dir = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (dir == NULL) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(dir, data + idx, sz);
|
|
dir[sz] = '\0';
|
|
|
|
clean_path(dir);
|
|
ret = WRMDIR(dir);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
if (ret != 0) {
|
|
/* @TODO errno holds reason for rmdir failure. Status sent could be
|
|
* better if using errno value to send reason i.e. permissions .. */
|
|
WLOG(WS_LOG_SFTP, "Error removing directory %s", dir);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Remove Directory Error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
else {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId,
|
|
"Removed Directory", "English");
|
|
return WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
/* Handles packet to make a directory
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvMKDIR(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
word32 sz;
|
|
int ret;
|
|
byte* data;
|
|
char* dir;
|
|
word32 mode = 0;
|
|
word32 idx = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_MKDIR");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
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 */
|
|
dir = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (dir == NULL) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(dir, data + idx, sz);
|
|
dir[sz] = '\0';
|
|
idx += sz;
|
|
|
|
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");
|
|
}
|
|
else {
|
|
ato32(data + idx, &mode);
|
|
}
|
|
clean_path(dir);
|
|
ret = WMKDIR(dir, mode);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_SFTP, "Error creating directory %s", dir);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Create Directory Error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
else {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId,
|
|
"Created Directory", "English");
|
|
return WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
/* Handles packet to open a file
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WS_SFTP_FILEATRB atr;
|
|
WFD fd;
|
|
word32 sz;
|
|
byte* data;
|
|
char* dir;
|
|
word32 reason;
|
|
word32 idx = 0;
|
|
int m = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_OPEN");
|
|
|
|
if (sizeof(WFD) > WOLFSSH_MAX_HANDLE) {
|
|
WLOG(WS_LOG_SFTP, "Handle size is too large");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
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 */
|
|
dir = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (dir == NULL) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(dir, data + idx, sz);
|
|
dir[sz] = '\0';
|
|
idx += sz;
|
|
|
|
/* get reason for opening file */
|
|
ato32(data + idx, &reason); idx += UINT32_SZ;
|
|
|
|
|
|
/* @TODO handle attributes */
|
|
SFTP_ParseAtributes_buffer(ssh, &atr, data + idx, maxSz);
|
|
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
if ((reason & WOLFSSH_FXF_READ) && (reason & WOLFSSH_FXF_WRITE)) {
|
|
m |= WOLFSSH_O_RDWR;
|
|
}
|
|
else {
|
|
if (reason & WOLFSSH_FXF_READ) {
|
|
m |= WOLFSSH_O_RDONLY;
|
|
}
|
|
if (reason & WOLFSSH_FXF_WRITE) {
|
|
m |= WOLFSSH_O_WRONLY;
|
|
}
|
|
}
|
|
|
|
if (reason & WOLFSSH_FXF_APPEND) {
|
|
m |= WOLFSSH_O_APPEND;
|
|
}
|
|
if (reason & WOLFSSH_FXF_CREAT) {
|
|
m |= WOLFSSH_O_CREAT;
|
|
}
|
|
if (reason & WOLFSSH_FXF_TRUNC) {
|
|
m |= WOLFSSH_O_TRUNC;
|
|
}
|
|
if (reason & WOLFSSH_FXF_EXCL) {
|
|
m |= WOLFSSH_O_EXCL;
|
|
}
|
|
|
|
/* if file permissions not set then use default */
|
|
if (!(atr.flags & WOLFSSH_FILEATRB_PERM)) {
|
|
atr.per = 0644;
|
|
}
|
|
|
|
clean_path(dir);
|
|
fd = WOPEN(dir, m, atr.per);
|
|
if (fd < 0) {
|
|
WLOG(WS_LOG_SFTP, "Error opening file %s", dir);
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Open File Error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
|
|
#ifdef WOLFSSH_STOREHANDLE
|
|
if (SFTP_AddHandleNode(ssh, (byte*)&fd, sizeof(WFD), dir) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Unable to store handle");
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Internal Failure", "English");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
#endif
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&fd, sizeof(WFD));
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
#ifndef NO_WOLFSSL_DIR
|
|
|
|
/* hold pointers to directory handles */
|
|
typedef struct DIR_HANDLE {
|
|
WDIR dir;
|
|
byte isEof; /* flag for if read everything */
|
|
word64 id; /* handle ID */
|
|
struct DIR_HANDLE* next;
|
|
} DIR_HANDLE;
|
|
static DIR_HANDLE* dirList = NULL;
|
|
static word64 idCount = 0;
|
|
/* @TODO add locking for thread safety */
|
|
|
|
|
|
/* Handles packet to open a directory
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WDIR ctx;
|
|
word32 sz;
|
|
byte* data;
|
|
char* dir;
|
|
word32 idx = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_OPENDIR");
|
|
|
|
if (sizeof(WFD) > WOLFSSH_MAX_HANDLE) {
|
|
WLOG(WS_LOG_SFTP, "Handle size is too large");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
/* get directory name */
|
|
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 */
|
|
dir = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (dir == NULL) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(dir, data + idx, sz);
|
|
dir[sz] = '\0';
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
/* get directory handle */
|
|
clean_path(dir);
|
|
if (WOPENDIR(&ctx, dir) != 0) {
|
|
WLOG(WS_LOG_SFTP, "Error with opening directory");
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_NOFILE, reqId,
|
|
"Unable To Open Directory", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
(void)reqId;
|
|
|
|
/* add to directory list @TODO locking for thread safety */
|
|
if (dirList == NULL) {
|
|
dirList = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE), ssh->ctx->heap,
|
|
DYNTYPE_SFTP);
|
|
if (dirList == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
WMEMCPY(&dirList->dir, &ctx, sizeof(WDIR));
|
|
#else
|
|
dirList->dir = ctx;
|
|
#endif
|
|
dirList->id = idCount++;
|
|
dirList->isEof = 0;
|
|
dirList->next = NULL;
|
|
SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&dirList->id,
|
|
sizeof(word64));
|
|
}
|
|
else {
|
|
DIR_HANDLE* cur = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE),
|
|
ssh->ctx->heap, DYNTYPE_SFTP);
|
|
if (cur == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
WMEMCPY(&cur->dir, &ctx, sizeof(WDIR));
|
|
#else
|
|
cur->dir = ctx;
|
|
#endif
|
|
cur->id = idCount++;
|
|
cur->isEof = 0;
|
|
cur->next = dirList;
|
|
dirList = cur;
|
|
SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&cur->id,
|
|
sizeof(word64));
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
/* For Nucleus port
|
|
* helper function that gets file information from reading directory
|
|
* @TODO allow user to override
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
|
|
{
|
|
int sz;
|
|
|
|
if (dir == NULL || ssh == NULL || out == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
sz = (int)WSTRLEN(dir->sfname);
|
|
out->fName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP);
|
|
if (out->fName == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(out->fName, dir->sfname, sz);
|
|
out->fName[sz] = '\0';
|
|
out->fSz = sz;
|
|
|
|
sz = (int)WSTRLEN(dir->lfname);
|
|
out->lName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP);
|
|
if (out ->lName == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(out->lName, dir->lfname, sz);
|
|
out->lName[sz] = '\0';
|
|
out->lSz = sz;
|
|
|
|
SFTP_GetAttributes(out->fName, &out->atrb, 0);
|
|
|
|
if ((WREADDIR(dir)) == NULL) {
|
|
return WS_NEXT_ERROR;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
#else
|
|
/* helper function that gets file information from reading directory
|
|
* @TODO allow user to override
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out)
|
|
{
|
|
struct dirent* dp;
|
|
int sz;
|
|
|
|
if (dir == NULL || ssh == NULL || out == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
dp = WREADDIR(dir);
|
|
if (dp == NULL) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
sz = (int)WSTRLEN(dp->d_name);
|
|
out->fName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP);
|
|
if (out->fName == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
out->lName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP);
|
|
if (out ->lName == NULL) {
|
|
WFREE(out->fName, out->heap, DYNTYPE_SFTP);
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
WMEMCPY(out->fName, dp->d_name, sz);
|
|
WMEMCPY(out->lName, dp->d_name, sz);
|
|
out->fName[sz] = '\0';
|
|
out->lName[sz] = '\0';
|
|
out->fSz = sz;
|
|
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);
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* helper function to create a name packet. out buffer will have the following
|
|
* format on success
|
|
*
|
|
* [ SFTP header ] [ count ] [ file [ name | long name | attribs ] ].
|
|
*
|
|
* outSz gets set to the size of resulting buffer
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int wolfSSH_SFTP_SendName(WOLFSSH* ssh, WS_SFTPNAME* list, word32 count,
|
|
byte* out, word32* outSz, int reqId)
|
|
{
|
|
WS_SFTPNAME* cur = list;
|
|
word32 i, idx = 0;
|
|
|
|
if (out == NULL || outSz == NULL || ssh == NULL || list == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (*outSz < WOLFSSH_SFTP_HEADER + UINT32_SZ) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_NAME,
|
|
*outSz - WOLFSSH_SFTP_HEADER, out) != WS_SUCCESS) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
idx += WOLFSSH_SFTP_HEADER;
|
|
|
|
c32toa(count, out + idx); idx += UINT32_SZ;
|
|
|
|
for (i = 0; i < count && cur != NULL; i++) {
|
|
if (*outSz - idx < cur->fSz + cur->lSz + UINT32_SZ * 2) {
|
|
/* not enough space for the buffer */
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
c32toa(cur->fSz, out + idx); idx += UINT32_SZ;
|
|
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) !=
|
|
WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
idx += SFTP_AtributesSz(ssh, &cur->atrb);
|
|
cur = cur->next;
|
|
}
|
|
|
|
*outSz = idx;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles packet to read a directory
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WDIR dir;
|
|
word64 handle = 0;
|
|
word32 sz;
|
|
byte* data;
|
|
word32 idx = 0;
|
|
int count = 0;
|
|
int ret;
|
|
WS_SFTPNAME* name = NULL;
|
|
WS_SFTPNAME* list = NULL;
|
|
word32 outSz = 0;
|
|
DIR_HANDLE* cur = dirList;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_READDIR");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
/* get directory handle */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
WMEMCPY((byte*)&handle, data + idx, sz);
|
|
|
|
/* find DIR given handle */
|
|
while (cur != NULL) {
|
|
if (cur->id == handle) {
|
|
dir = cur->dir;
|
|
break;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
if (cur == NULL) {
|
|
/* unable to find handle */
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* this closes the directory before returning */
|
|
//wc_ReadDirFirst(&ctx, dir, NULL);
|
|
|
|
/* get directory information */
|
|
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);
|
|
if (ret == WS_SUCCESS || ret == WS_NEXT_ERROR) {
|
|
count++;
|
|
outSz += name->fSz + name->lSz + (UINT32_SZ * 2);
|
|
outSz += SFTP_AtributesSz(ssh, &name->atrb);
|
|
if (list != NULL) {
|
|
name->next = list;
|
|
list = name;
|
|
}
|
|
else {
|
|
list = name;
|
|
}
|
|
}
|
|
else {
|
|
wolfSSH_SFTPNAME_free(name);
|
|
}
|
|
} while (ret == WS_SUCCESS);
|
|
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (list == NULL || cur->isEof) {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_EOF, reqId,
|
|
"No More Files In Directory", "English");
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
/* if next state would cause an error then set EOF flag for when called
|
|
* again */
|
|
if (ret == WS_NEXT_ERROR) {
|
|
cur->isEof = 1;
|
|
}
|
|
|
|
data = (byte*)WMALLOC(outSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (wolfSSH_SFTP_SendName(ssh, list, count, data, &outSz, reqId)
|
|
!= WS_SUCCESS) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
wolfSSH_SFTPNAME_list_free(list);
|
|
wolfSSH_stream_send(ssh, data, outSz);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles packet to close a directory
|
|
*
|
|
* returns 0 on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz)
|
|
{
|
|
DIR_HANDLE* cur = dirList;
|
|
WDIR* dir = NULL;
|
|
|
|
if (ssh == NULL || handle == NULL || handleSz != sizeof(word64)) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_CLOSE Directory");
|
|
|
|
/* find DIR given handle */
|
|
while (cur != NULL) {
|
|
if (cur->id == *((word64*)handle)) {
|
|
dir = &cur->dir;
|
|
break;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
if (cur == NULL) {
|
|
/* unable to find handle */
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
WCLOSEDIR(dir);
|
|
|
|
/* remove directory from list */
|
|
if (cur != NULL) {
|
|
DIR_HANDLE* pre = dirList;
|
|
|
|
WLOG(WS_LOG_SFTP, "Free'ing and closing handle %ld pointer of [%p]",
|
|
(long)cur->id, cur);
|
|
/* case where node is at head of list */
|
|
if (pre == cur) {
|
|
dirList = cur->next;
|
|
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
|
|
}
|
|
else {
|
|
while (pre->next != NULL && pre->next != cur) pre = pre->next;
|
|
if (pre->next != cur) {
|
|
/* error case where current handle is not in list? */
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
pre->next = cur->next;
|
|
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
|
|
}
|
|
}
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
#endif /* NO_WOLFSSL_DIR */
|
|
|
|
/* Handles packet to write a file
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvWrite(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WFD fd;
|
|
word32 sz;
|
|
int ret;
|
|
byte* data;
|
|
word32 idx = 0;
|
|
word64 ofst = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_WRITE");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
sz = 0;
|
|
do {
|
|
ret = wolfSSH_stream_read(ssh, data + sz, maxSz - sz);
|
|
if (ret > 0) {
|
|
sz += ret;
|
|
}
|
|
} while (sz < maxSz && ret > 0);
|
|
if (ret < 0) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/* get file handle */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) {
|
|
WLOG(WS_LOG_SFTP, "Error with file handle size");
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Write File Error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
WMEMSET((byte*)&fd, 0, sizeof(WFD));
|
|
WMEMCPY((byte*)&fd, data + idx, sz); idx += sz;
|
|
|
|
/* get offset into file */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
ofst = (word64)sz << 32 & 0xFFFFFFFF00000000;
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
ofst |= (word64)sz & 0xFFFFFFFF;
|
|
|
|
/* get length to be written */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
|
|
ret = (int)WPWRITE(fd, data + idx, sz, ofst);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (ret < 0) {
|
|
#if defined(WOLFSSL_NUCLEUS) && defined(DEBUG_WOLFSSH)
|
|
if (ret == NUF_NOSPC) {
|
|
WLOG(WS_LOG_SFTP, "Ran out of memory");
|
|
}
|
|
#endif
|
|
WLOG(WS_LOG_SFTP, "Error writing to file");
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Write File Error", "English");
|
|
return WS_INVALID_STATE_E;
|
|
}
|
|
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId,
|
|
"Write File Success", "English");
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles packet to read a file
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvRead(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WFD fd;
|
|
word32 sz;
|
|
int ret;
|
|
byte* data;
|
|
word32 idx = 0;
|
|
word64 ofst = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_READ");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
if ((ret = wolfSSH_stream_read(ssh, data, maxSz)) < 0) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/* get file handle */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
WMEMSET((byte*)&fd, 0, sizeof(WFD));
|
|
WMEMCPY((byte*)&fd, data + idx, sz); idx += sz;
|
|
|
|
/* get offset into file */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
ofst = (word64)sz << 32 & 0xFFFFFFFF00000000;
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
ofst |= (word64)sz & 0xFFFFFFFF;
|
|
|
|
/* get length to be read */
|
|
ato32(data + idx, &sz);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
|
|
/* read from handle and send data back to client */
|
|
data = (byte*)WMALLOC(sz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
ret = (int)WPREAD(fd, data, sz, ofst);
|
|
if (ret < 0) {
|
|
WLOG(WS_LOG_SFTP, "Error reading from file");
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Read File Error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
|
|
/* eof */
|
|
if (ret == 0) {
|
|
WLOG(WS_LOG_SFTP, "Error reading from file");
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_EOF, reqId,
|
|
"Read EOF", "English");
|
|
return WS_SUCCESS; /* end of file is not fatal error */
|
|
}
|
|
|
|
SendPacketType(ssh, WOLFSSH_FTP_DATA, data, ret);
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles packet to close a file
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvClose(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WFD fd;
|
|
word32 sz;
|
|
byte* data;
|
|
word32 idx = 0;
|
|
int ret;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_CLOSE");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
if ((ret = wolfSSH_stream_read(ssh, data, maxSz)) < 0) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/* get file handle */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
#ifndef NO_WOLFSSL_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 */
|
|
if (sz == sizeof(WFD)) {
|
|
WMEMSET((byte*)&fd, 0, sizeof(WFD));
|
|
WMEMCPY((byte*)&fd, data + idx, sz);
|
|
ret = WCLOSE(fd);
|
|
#ifdef WOLFSSH_STOREHANDLE
|
|
if (SFTP_RemoveHandleNode(ssh, data + idx, sz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Unable to remove handle from list");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
if (ret < 0) {
|
|
WLOG(WS_LOG_SFTP, "Error closing file");
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Close File Error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
else {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId,
|
|
"Closed File", "English");
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/* Handles packet to remove a file
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvRemove(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
word32 sz;
|
|
byte* data;
|
|
char* name;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_REMOVE");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
if ((ret = wolfSSH_stream_read(ssh, data, maxSz)) < 0) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/* get file name */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
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);
|
|
name[sz] = '\0';
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
clean_path(name);
|
|
if ((ret = WREMOVE(name)) < 0) {
|
|
WLOG(WS_LOG_SFTP, "Error removing file");
|
|
#if defined(WOLFSSL_NUCLEUS) && defined(DEBUG_WOLFSSH)
|
|
if (ret == NUF_ACCES)
|
|
WLOG(WS_LOG_SFTP, "access error");
|
|
if (ret == NUF_BAD_USER)
|
|
WLOG(WS_LOG_SFTP, "bad user");
|
|
if (ret == NUF_IO_ERROR)
|
|
WLOG(WS_LOG_SFTP, "io error");
|
|
if (ret == NUF_NOFILE)
|
|
WLOG(WS_LOG_SFTP, "%s file not found", name);
|
|
#endif
|
|
ret = WS_BAD_FILE_E;
|
|
}
|
|
else {
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
/* Let the client know the results from trying to remove the file */
|
|
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (ret != WS_SUCCESS) {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Remove File Error", "English");
|
|
}
|
|
else {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId,
|
|
"Removed File", "English");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Handles packet to rename a file
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvRename(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
word32 sz = 0;
|
|
byte* data;
|
|
char* old;
|
|
char* nw;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_RENAME");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
/* @TODO for now trying to read all of expected data */
|
|
while (ret == WS_SUCCESS && sz < maxSz) {
|
|
ret = wolfSSH_stream_read(ssh, data + sz, maxSz - sz);
|
|
if (ret > 0) {
|
|
sz += ret;
|
|
ret = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* get old file name */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
old = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (old == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMCPY(old, data + idx, sz); idx += sz;
|
|
old[sz] = '\0';
|
|
}
|
|
|
|
/* get new file name */
|
|
ato32(data + idx, &sz); idx += UINT32_SZ;
|
|
if (sz + idx > maxSz) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
nw = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (nw == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMCPY(nw, data + idx, sz);
|
|
nw[sz] = '\0';
|
|
}
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
clean_path(old);
|
|
clean_path(nw);
|
|
if (ret == WS_SUCCESS && WRENAME(old, nw) < 0) {
|
|
WLOG(WS_LOG_SFTP, "Error renaming file");
|
|
ret = WS_BAD_FILE_E;
|
|
}
|
|
|
|
/* Let the client know the results from trying to rename the file */
|
|
WFREE(old, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
WFREE(nw, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (ret != WS_SUCCESS) {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"Rename File Error", "English");
|
|
}
|
|
else {
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId,
|
|
"Renamed File", "English");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_STOREHANDLE
|
|
/* some systems do not have a fstat function to allow for attribute lookup given
|
|
* a file descriptor. In those cases we keep track of an internal list matching
|
|
* handles to file names */
|
|
|
|
typedef 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)
|
|
{
|
|
WS_HANDLE_LIST* cur = handleList;
|
|
|
|
if (handle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* for Nucleus need to find name from handle */
|
|
while (cur != NULL) {
|
|
if(handleSz == cur->handleSz && WMEMCMP(handle, cur->handle, handleSz) == 0) {
|
|
break; /* found handle */
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
|
|
/* add a name and handle to the handle list
|
|
* return WS_SUCCESS on success */
|
|
int SFTP_AddHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz, char* name)
|
|
{
|
|
WS_HANDLE_LIST* cur;
|
|
int sz;
|
|
|
|
if (handle == NULL || name == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
cur = (WS_HANDLE_LIST*)WMALLOC(sizeof(WS_HANDLE_LIST), ssh->ctx->heap,
|
|
DYNTYPE_SFTP);
|
|
if (cur == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
WMEMCPY(cur->handle, handle, handleSz);
|
|
cur->handleSz = handleSz;
|
|
|
|
sz = (int)WSTRLEN(name);
|
|
if (sz + 1 >= WOLFSSH_MAX_FILENAME) {
|
|
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
|
|
return WS_BUFFER_E;
|
|
}
|
|
WMEMCPY(cur->name, name, sz);
|
|
cur->name[sz] = '\0';
|
|
|
|
cur->prev = NULL;
|
|
cur->next = handleList;
|
|
if (handleList != NULL) {
|
|
handleList->prev = cur;
|
|
}
|
|
handleList = cur;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* remove a handle node from the list
|
|
* returns WS_SUCCESS on success */
|
|
int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz)
|
|
{
|
|
WS_HANDLE_LIST* cur;
|
|
|
|
if (ssh == NULL || handle == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
cur = SFTP_GetHandleNode(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;
|
|
}
|
|
|
|
if (cur->next != NULL) {
|
|
cur->next->prev = cur->prev;
|
|
}
|
|
|
|
if (cur->prev != NULL) {
|
|
cur->prev->next = cur->next;
|
|
}
|
|
|
|
if (cur->next == NULL && cur->prev == NULL) {
|
|
handleList = NULL;
|
|
}
|
|
|
|
WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
#endif /* WOLFSSH_STOREHANDLE */
|
|
|
|
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
/* @TODO can be overriden by user for portability
|
|
* NOTE: if atr->flags is set to a value of 0 then no attributes are set.
|
|
* Fills out a WS_SFTP_FILEATRB structure
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr, byte link)
|
|
{
|
|
DSTAT stats;
|
|
int sz = (int)WSTRLEN(fileName);
|
|
int ret;
|
|
|
|
if (link) {
|
|
ret = WLSTAT(fileName, &stats);
|
|
}
|
|
else {
|
|
ret = WSTAT(fileName, &stats);
|
|
}
|
|
|
|
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
if (sz > 2 && fileName[sz - 2] == ':' && ret == NUF_NOFILE) {
|
|
atr->flags |= WOLFSSH_FILEATRB_PERM;
|
|
atr->per |= 0x4000;
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
if (ret != NU_SUCCESS) {
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_SIZE;
|
|
atr->sz = (word64)stats.fsize;
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* @TODO handle attribute extensions */
|
|
|
|
NU_Done(&stats);
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* @TODO can be overriden by user for portability
|
|
* Gets attributes based on file descriptor
|
|
* NOTE: if atr->flags is set to a value of 0 then no attributes are set.
|
|
* Fills out a WS_SFTP_FILEATRB structure
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
DSTAT stats;
|
|
WS_HANDLE_LIST* cur;
|
|
|
|
if (handle == NULL || atr == NULL) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
cur = SFTP_GetHandleNode(handle, handleSz);
|
|
if (cur == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Unknown handle");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
|
|
if (WSTAT(cur->name, &stats) != NU_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_SIZE;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
/* @TODO handle attribute extensions */
|
|
|
|
NU_Done(&stats);
|
|
return WS_SUCCESS;
|
|
}
|
|
#else
|
|
|
|
/* @TODO can be overriden by user for portability
|
|
* NOTE: if atr->flags is set to a value of 0 then no attributes are set.
|
|
* Fills out a WS_SFTP_FILEATRB structure
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr, byte link)
|
|
{
|
|
struct stat stats;
|
|
|
|
if (link) {
|
|
if (WLSTAT(fileName, &stats) != 0) {
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
}
|
|
else {
|
|
if (WSTAT(fileName, &stats) != 0) {
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
}
|
|
|
|
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_SIZE;
|
|
atr->sz = (word64)stats.st_size;
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_UIDGID;
|
|
atr->uid = (word32)stats.st_uid;
|
|
atr->gid = (word32)stats.st_gid;
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_PERM;
|
|
atr->per = (word32)stats.st_mode;
|
|
|
|
#if 0
|
|
/* @TODO porting time from stat structure */
|
|
atr->flags |= WOLFSSH_FILEATRB_TIME;
|
|
atr->atime = (word32)stats.st_atimespec.tv_sec;
|
|
atr->mtime = (word32)stats.st_mtimespec.tv_sec;
|
|
#endif
|
|
|
|
/* @TODO handle attribute extensions */
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* @TODO can be overriden by user for portability
|
|
* Gets attributes based on file descriptor
|
|
* NOTE: if atr->flags is set to a value of 0 then no attributes are set.
|
|
* Fills out a WS_SFTP_FILEATRB structure
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
struct stat stats;
|
|
|
|
if (handleSz != sizeof(word32)) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected handle size SFTP_GetAttributes_Handle()");
|
|
}
|
|
|
|
if (fstat(*(int*)handle, &stats) != 0) {
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
|
|
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_SIZE;
|
|
atr->sz = (word64)stats.st_size;
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_UIDGID;
|
|
atr->uid = (word32)stats.st_uid;
|
|
atr->gid = (word32)stats.st_gid;
|
|
|
|
atr->flags |= WOLFSSH_FILEATRB_PERM;
|
|
atr->per = (word32)stats.st_mode;
|
|
|
|
#if 0
|
|
/* @TODO porting time from stat structure */
|
|
atr->flags |= WOLFSSH_FILEATRB_TIME;
|
|
atr->atime = (word32)stats.st_atimespec.tv_sec;
|
|
atr->mtime = (word32)stats.st_mtimespec.tv_sec;
|
|
#endif
|
|
|
|
/* @TODO handle attribute extensions */
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Handles receiving fstat packet
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WS_SFTP_FILEATRB atr;
|
|
word32 handleSz;
|
|
word32 sz;
|
|
byte* data;
|
|
byte* handle;
|
|
word32 idx = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_FSTAT");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
ato32(data + idx, &handleSz); idx += UINT32_SZ;
|
|
if (handleSz + idx > maxSz) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_BUFFER_E;
|
|
}
|
|
handle = data + idx;
|
|
|
|
/* 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) {
|
|
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,
|
|
"STAT error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
sz = SFTP_AtributesSz(ssh, &atr);
|
|
data = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER, ssh->ctx->heap,
|
|
DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, data) != WS_SUCCESS) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
SFTP_SetAtributes(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;
|
|
}
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles receiving stat packet
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, word32 maxSz)
|
|
{
|
|
WS_SFTP_FILEATRB atr;
|
|
char* name = NULL;
|
|
|
|
word32 sz;
|
|
byte* data;
|
|
word32 idx = 0;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_STAT");
|
|
|
|
data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
wolfSSH_stream_read(ssh, data, maxSz);
|
|
|
|
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);
|
|
name[sz] = '\0';
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
/* try to get file attributes and send back to client */
|
|
clean_path(name);
|
|
WMEMSET((byte*)&atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
if (SFTP_GetAttributes(name, &atr, 0) != WS_SUCCESS) {
|
|
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
WLOG(WS_LOG_SFTP, "Unable to get stat of file/directory");
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"STAT error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
sz = SFTP_AtributesSz(ssh, &atr);
|
|
data = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER, ssh->ctx->heap,
|
|
DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, data) != WS_SUCCESS) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
SFTP_SetAtributes(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;
|
|
}
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Handles receiving lstat packet
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RecvLSTAT(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_LSTAT");
|
|
|
|
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);
|
|
name[sz] = '\0';
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
clean_path(name);
|
|
|
|
/* try to get file attributes and send back to client */
|
|
WMEMSET((byte*)&atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
if ((ret = SFTP_GetAttributes(name, &atr, 1)) != WS_SUCCESS) {
|
|
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
/* tell peer that was not ok */
|
|
WLOG(WS_LOG_SFTP, "Unable to get lstat of file/directory");
|
|
wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId,
|
|
"LSTAT error", "English");
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
sz = SFTP_AtributesSz(ssh, &atr);
|
|
data = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER, ssh->ctx->heap,
|
|
DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, data) != WS_SUCCESS) {
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
SFTP_SetAtributes(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;
|
|
}
|
|
WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
#endif /* !NO_WOLFSSH_SERVER */
|
|
|
|
|
|
#ifndef NO_WOLFSSH_CLIENT
|
|
|
|
/* unique from other packets because the request ID is not also sent.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_ClientRecvInit(WOLFSSH* ssh) {
|
|
int len;
|
|
byte id;
|
|
word32 sz = 0;
|
|
word32 version = 0;
|
|
byte buf[LENGTH_SZ + MSG_ID_SZ + UINT32_SZ];
|
|
|
|
if ((len = wolfSSH_stream_read(ssh, buf, sizeof(buf))) != sizeof(buf)) {
|
|
return len;
|
|
}
|
|
|
|
ato32(buf, &sz);
|
|
if (sz < MSG_ID_SZ + UINT32_SZ) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
/* expecting */
|
|
id = buf[LENGTH_SZ];
|
|
if (id != WOLFSSH_FTP_VERSION) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected SFTP type received");
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version);
|
|
|
|
/* silently ignore extensions if not supported */
|
|
sz = sz - MSG_ID_SZ - UINT32_SZ;
|
|
if (sz > 0) {
|
|
byte* data = (byte*)WMALLOC(sz, NULL, DYNTYPE_BUFFER);
|
|
if (data == NULL) return WS_MEMORY_E;
|
|
if ((len = wolfSSH_stream_read(ssh, data, sz)) != (int)sz) {
|
|
return len;
|
|
}
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
}
|
|
|
|
ssh->reqId++;
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* unique from SendPacketType because the request ID is not also sent.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_ClientSendInit(WOLFSSH* ssh) {
|
|
int ret;
|
|
byte buf[LENGTH_SZ + MSG_ID_SZ + UINT32_SZ];
|
|
|
|
c32toa(MSG_ID_SZ + UINT32_SZ, buf);
|
|
buf[LENGTH_SZ] = WOLFSSH_FTP_INIT;
|
|
|
|
/* version */
|
|
c32toa((word32)WOLFSSH_SFTP_VERSION, buf + LENGTH_SZ + MSG_ID_SZ);
|
|
if ((ret = wolfSSH_stream_send(ssh, buf, sizeof(buf))) != sizeof(buf)) {
|
|
return ret;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Completes SFTP connection to server
|
|
* returns WS_SFTP_COMPLETE on success
|
|
*/
|
|
int wolfSSH_SFTP_connect(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_SFTP_COMPLETE;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* check connect is done, if not call wolfSSH connect */
|
|
if (ssh->connectState < CONNECT_SERVER_CHANNEL_REQUEST_DONE) {
|
|
byte name[] = "sftp";
|
|
|
|
WLOG(WS_LOG_SFTP, "Trying to do SSH connect first");
|
|
if ((ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM,
|
|
name, sizeof(name) - 1)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Unable to set subsystem channel type");
|
|
return ret;
|
|
}
|
|
|
|
if ((ret = wolfSSH_connect(ssh)) != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
switch (ssh->sftpState) {
|
|
case SFTP_BEGIN:
|
|
if ((ssh->error = SFTP_ClientSendInit(ssh)) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ssh->connectState = SFTP_RECV;
|
|
FALL_THROUGH /* no break */
|
|
|
|
case SFTP_RECV:
|
|
if ((ssh->error = SFTP_ClientRecvInit(ssh)) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ssh->connectState = SFTP_DONE;
|
|
WLOG(WS_LOG_SFTP, "SFTP connection established");
|
|
break;
|
|
|
|
default:
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* NO_WOLFSSH_CLIENT */
|
|
|
|
|
|
/* Tries to do SFTP accept or connect based on server/client side
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_negotiate(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_FATAL_ERROR;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
#ifndef NO_WOLFSSH_SERVER
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
|
|
WLOG(WS_LOG_SFTP, "Trying to do SSH accept first");
|
|
ret = wolfSSH_SFTP_accept(ssh);
|
|
}
|
|
#endif
|
|
|
|
#ifndef NO_WOLFSSH_CLIENT
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
|
|
ret = wolfSSH_SFTP_connect(ssh);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Sends generic packet structure for packets that only have one field in type
|
|
* specific data.
|
|
* [ packet header ] [ size ] [ type specific data (buf) ]
|
|
*
|
|
* type is the type of packet to send
|
|
* buf holds the type specific data
|
|
* bufSz is amount of type specific data
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SendPacketType(WOLFSSH* ssh, byte type, byte* buf, word32 bufSz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
word32 idx;
|
|
word32 sent = 0;
|
|
|
|
if (ssh == NULL || buf == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ssh->connectState != SFTP_DONE) {
|
|
WLOG(WS_LOG_SFTP, "SFTP connection not complete, trying to finish");
|
|
ret = wolfSSH_SFTP_negotiate(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte* data = (byte*)WMALLOC(bufSz + WOLFSSH_SFTP_HEADER +
|
|
UINT32_SZ, NULL, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, ssh->reqId, type, bufSz + UINT32_SZ, data)
|
|
!= WS_SUCCESS) {
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
idx = WOLFSSH_SFTP_HEADER;
|
|
c32toa(bufSz, data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, buf, bufSz); idx += bufSz;
|
|
|
|
/* send header and type specific data, looping over send because
|
|
* channel could have restrictions on how much data can be sent at
|
|
* one time */
|
|
do {
|
|
ret = wolfSSH_stream_send(ssh, data + sent, idx - sent);
|
|
sent += (word32)ret;
|
|
} while (ret > 0 && sent < idx);
|
|
|
|
if (ret > 0) {
|
|
ret = WS_SUCCESS;
|
|
}
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* process a status packet
|
|
*
|
|
* assuming that request ID has already been read
|
|
*
|
|
* structure of status packet is as follows
|
|
* {
|
|
* uint32 error code
|
|
* string error msg
|
|
* string language
|
|
* }
|
|
*
|
|
* returns error code, i.e. WOLFSSH_FTP_OK, WOLFSSH_FTP_EOF ...
|
|
*/
|
|
static int wolfSSH_SFTP_DoStatus(WOLFSSH* ssh, word32 reqId)
|
|
{
|
|
byte buf[UINT32_SZ];
|
|
int ret;
|
|
word32 sz;
|
|
word32 status = WOLFSSH_FTP_FAILURE;
|
|
(void)reqId;
|
|
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &status);
|
|
|
|
/* read error message */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &sz);
|
|
|
|
{
|
|
byte* s = (byte*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (s == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
ret = wolfSSH_stream_read(ssh, s, sz);
|
|
if (ret < 0) {
|
|
WFREE(s, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
s[sz] = '\0';
|
|
WLOG(WS_LOG_SFTP, "Status Recv : %s", s);
|
|
WFREE(s, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
}
|
|
|
|
/* read language tag */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &sz);
|
|
|
|
if (sz > 0)
|
|
{
|
|
byte* s = (byte*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (s == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
ret = wolfSSH_stream_read(ssh, s, sz);
|
|
if (ret < 0) {
|
|
WFREE(s, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
s[sz] = '\0';
|
|
WLOG(WS_LOG_SFTP, "Status Language : %s", s);
|
|
WFREE(s, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
/* create a new initialized sftp name structure
|
|
* returns a new structure on success and null on fail */
|
|
WS_SFTPNAME* wolfSSH_SFTPNAME_new(void* heap)
|
|
{
|
|
WS_SFTPNAME* n = (WS_SFTPNAME*)WMALLOC(sizeof(WS_SFTPNAME), heap,
|
|
DYNTYPE_SFTP );
|
|
if (n != NULL) {
|
|
WMEMSET(n, 0, sizeof(WS_SFTPNAME));
|
|
n->heap = heap;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
/* free's a single sftp name structure. Note that this could destroy a list if
|
|
* used on a single node in the list.
|
|
*/
|
|
void wolfSSH_SFTPNAME_free(WS_SFTPNAME* n)
|
|
{
|
|
if (n != NULL) {
|
|
WFREE(n->fName, n->heap, DYNTYPE_SFTP);
|
|
WFREE(n->lName, n->heap, DYNTYPE_SFTP);
|
|
WFREE(n, n->heap, DYNTYPE_SFTP);
|
|
}
|
|
}
|
|
|
|
|
|
/* free's linked list of sftp name structures */
|
|
void wolfSSH_SFTPNAME_list_free(WS_SFTPNAME* n)
|
|
{
|
|
WS_SFTPNAME* tmp = NULL;
|
|
|
|
while (n != NULL) {
|
|
tmp = n->next;
|
|
wolfSSH_SFTPNAME_free(n);
|
|
n = tmp;
|
|
}
|
|
}
|
|
|
|
|
|
/* parse out file attributes from a buffer
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SFTP_ParseAtributes_buffer(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr, byte* buf,
|
|
word32 bufSz)
|
|
{
|
|
word32 idx = 0;
|
|
|
|
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
/* get flags */
|
|
ato32(buf, &atr->flags); idx += UINT32_SZ;
|
|
|
|
/* check if size attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_SIZE) {
|
|
word32 tmp;
|
|
|
|
ato32(buf + idx, &tmp); idx += UINT32_SZ;
|
|
atr->sz = tmp; atr->sz = atr->sz << 32;
|
|
ato32(buf + idx, &tmp); idx += UINT32_SZ;
|
|
atr->sz |= tmp;
|
|
}
|
|
|
|
/* check if uid and gid attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_UIDGID) {
|
|
ato32(buf + idx, &atr->uid); idx += UINT32_SZ;
|
|
ato32(buf + idx, &atr->gid); idx += UINT32_SZ;
|
|
}
|
|
|
|
/* check if permissions attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
|
|
ato32(buf + idx, &atr->per); idx += UINT32_SZ;
|
|
}
|
|
|
|
/* check if time attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_TIME) {
|
|
ato32(buf + idx, &atr->atime); idx += UINT32_SZ;
|
|
ato32(buf + idx, &atr->mtime); idx += UINT32_SZ;
|
|
}
|
|
|
|
/* check if extended attributes are present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_EXT) {
|
|
word32 i;
|
|
word32 sz;
|
|
|
|
ato32(buf + idx, &atr->extCount); idx += UINT32_SZ;
|
|
|
|
for (i = 0; i < atr->extCount; i++) {
|
|
/* @TODO in the process of storing attributes */
|
|
ato32(buf + idx, &sz); idx += UINT32_SZ;
|
|
|
|
if (sz > 0) {
|
|
/* @TODO extension type */
|
|
idx += sz;
|
|
}
|
|
|
|
/* @TODO in the process of storing attributes */
|
|
ato32(buf + idx, &sz); idx += UINT32_SZ;
|
|
|
|
if (sz > 0) {
|
|
/* @TODO extension data */
|
|
idx += sz;
|
|
}
|
|
}
|
|
}
|
|
|
|
(void)ssh;
|
|
(void)bufSz;
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* parse out file attributes from I/O stream
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int SFTP_ParseAtributes(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
byte buf[UINT32_SZ * 2];
|
|
int ret;
|
|
|
|
WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
/* get flags */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &atr->flags);
|
|
|
|
/* check if size attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_SIZE) {
|
|
word32 tmp;
|
|
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ * 2);
|
|
if (ret != UINT32_SZ * 2) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &tmp);
|
|
atr->sz = tmp; atr->sz = atr->sz << 32;
|
|
ato32(buf + UINT32_SZ, &tmp);
|
|
atr->sz |= tmp;
|
|
}
|
|
|
|
/* check if uid and gid attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_UIDGID) {
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ*2);
|
|
if (ret != UINT32_SZ*2) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &atr->uid);
|
|
ato32(buf+UINT32_SZ, &atr->gid);
|
|
}
|
|
|
|
/* check if permissions attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &atr->per);
|
|
}
|
|
|
|
/* check if time attribute present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_TIME) {
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ*2);
|
|
if (ret != UINT32_SZ*2) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &atr->atime);
|
|
ato32(buf+UINT32_SZ, &atr->mtime);
|
|
}
|
|
|
|
/* check if extended attributes are present */
|
|
if (atr->flags & WOLFSSH_FILEATRB_EXT) {
|
|
word32 i;
|
|
word32 sz;
|
|
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ato32(buf, &atr->extCount);
|
|
|
|
for (i = 0; i < atr->extCount; i++) {
|
|
/* read extension [ string ] [ string ] */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* @TODO in the process of storing attributes */
|
|
ato32(buf, &sz);
|
|
|
|
if (sz > 0) {
|
|
/* extension type */
|
|
byte* tmp = (byte*)WMALLOC(sz, NULL, DYNTYPE_BUFFER);
|
|
ret = wolfSSH_stream_read(ssh, tmp, sz);
|
|
if (ret < 0) {
|
|
WFREE(tmp, NULL, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
WFREE(tmp, NULL, DYNTYPE_BUFFER);
|
|
}
|
|
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* @TODO in the process of storing attributes */
|
|
ato32(buf, &sz);
|
|
|
|
if (sz > 0) {
|
|
/* extension data */
|
|
byte* tmp = (byte*)WMALLOC(sz, NULL, DYNTYPE_BUFFER);
|
|
ret = wolfSSH_stream_read(ssh, tmp, sz);
|
|
WFREE(tmp, NULL, DYNTYPE_BUFFER);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* process a name packet. Creates a linked list of WS_SFTPNAME structures
|
|
*
|
|
* Syntax of name packet is as follows
|
|
* {
|
|
* uint32 id
|
|
* uint32 count
|
|
* for count times:
|
|
* string filename
|
|
* string longname
|
|
* ATTRS atributes
|
|
* }
|
|
*
|
|
* A pointer to a malloc'd WS_SFTPNAME list is returned on success and NULL is
|
|
* returned with failure.
|
|
*/
|
|
static WS_SFTPNAME* wolfSSH_SFTP_DoName(WOLFSSH* ssh)
|
|
{
|
|
/* process name */
|
|
WS_SFTPNAME* n = NULL;
|
|
byte buf[UINT32_SZ];
|
|
word32 maxSz;
|
|
word32 count;
|
|
word32 reqId;
|
|
byte type = 0;
|
|
int ret;
|
|
|
|
maxSz = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (type != WOLFSSH_FTP_NAME) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type %d", type);
|
|
/* check for status msg */
|
|
if (type == WOLFSSH_FTP_STATUS) {
|
|
wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "unexpected ID");
|
|
return NULL;
|
|
}
|
|
ssh->reqId += 1;
|
|
|
|
/* get number of files */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) return NULL;
|
|
|
|
ato32(buf, &count);
|
|
while (count > 0) {
|
|
word32 sz;
|
|
WS_SFTPNAME* tmp = wolfSSH_SFTPNAME_new(ssh->ctx->heap);
|
|
|
|
count--;
|
|
if (tmp == NULL) {
|
|
/* error case free list and exit */
|
|
WLOG(WS_LOG_SFTP, "Memory error when creating new name structure");
|
|
ret = WS_MEMORY_E;
|
|
break;
|
|
}
|
|
|
|
/* push tmp onto front of name list */
|
|
tmp->next = n;
|
|
n = tmp;
|
|
|
|
/* get filename size and name */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
ret = WS_FATAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
ato32(buf, &sz);
|
|
tmp->fSz = sz;
|
|
if (sz > 0) {
|
|
tmp->fName = (char*)XMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
|
|
if (tmp->fName == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
break;
|
|
}
|
|
ret = wolfSSH_stream_read(ssh, (byte*)tmp->fName, sz);
|
|
if (ret < 0 || (word32)ret != sz) {
|
|
ret = WS_FATAL_ERROR;
|
|
break;
|
|
}
|
|
tmp->fName[sz] = '\0';
|
|
}
|
|
|
|
/* get longname size and name */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret != UINT32_SZ) {
|
|
ret = WS_FATAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
ato32(buf, &sz);
|
|
tmp->lSz = sz;
|
|
if (sz > 0) {
|
|
tmp->lName = (char*)XMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
|
|
if (tmp->lName == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
break;
|
|
}
|
|
ret = wolfSSH_stream_read(ssh, (byte*)tmp->lName, sz);
|
|
if (ret < 0 || (word32)ret != sz) {
|
|
ret = WS_FATAL_ERROR;
|
|
break;
|
|
}
|
|
tmp->lName[sz] = '\0';
|
|
}
|
|
|
|
/* get attributes */
|
|
ret = SFTP_ParseAtributes(ssh, &tmp->atrb);
|
|
if (ret != WS_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error with reading file names");
|
|
wolfSSH_SFTPNAME_list_free(n);
|
|
return NULL;
|
|
}
|
|
|
|
(void)maxSz;
|
|
return n;
|
|
}
|
|
|
|
|
|
/* get the file handle from SSH stream
|
|
*
|
|
* handle buffer to hold result
|
|
* handleSz gets set to size of resulting handle. Initially this should be
|
|
* passed in set to the size of "handle" buffer.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int wolfSSH_SFTP_GetHandle(WOLFSSH* ssh, byte* handle, word32* handleSz)
|
|
{
|
|
/* process handle*/
|
|
byte buf[WOLFSSH_MAX_HANDLE + UINT32_SZ];
|
|
word32 reqId;
|
|
word32 bufSz;
|
|
byte type = 0;
|
|
|
|
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_GetHandle");
|
|
bufSz = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (type != WOLFSSH_FTP_HANDLE) {
|
|
if (type == WOLFSSH_FTP_STATUS) {
|
|
return wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
}
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type with getting handle");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* @TODO packets do not need to be in order, may need mechanisim to
|
|
* handle out of order ID's ? */
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected ID");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ssh->reqId += 1;
|
|
|
|
if (bufSz > sizeof(buf)) {
|
|
WLOG(WS_LOG_SFTP, "Handle found is too large for buffer");
|
|
return WS_BUFFER_E;
|
|
}
|
|
if (wolfSSH_stream_read(ssh, buf, bufSz) != (int)bufSz) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* RFC specifies that handle size should not be larger than max size */
|
|
ato32(buf, &bufSz);
|
|
if (bufSz > WOLFSSH_MAX_HANDLE || *handleSz < bufSz) {
|
|
WLOG(WS_LOG_SFTP, "Handle size found was too big");
|
|
return WS_BUFFER_E;
|
|
}
|
|
*handleSz = bufSz;
|
|
WMEMCPY(handle, (buf + UINT32_SZ), *handleSz);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Used to get a list of all files and their attributes from a directory.
|
|
*
|
|
* dir NULL terminated string of the directory to list
|
|
*
|
|
* returns a linked list of files in the directory on success, NULL on failure
|
|
*/
|
|
WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
|
|
{
|
|
WS_SFTPNAME* name;
|
|
byte handle[WOLFSSH_MAX_HANDLE];
|
|
word32 handleSz;
|
|
|
|
if (ssh == NULL || dir == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Bad argument passed in");
|
|
return NULL;
|
|
}
|
|
|
|
name = wolfSSH_SFTP_RealPath(ssh, dir);
|
|
if (name == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (wolfSSH_SFTP_OpenDir(ssh, (byte*)name->fName, name->fSz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Unable to open directory");
|
|
wolfSSH_SFTPNAME_list_free(name); name = NULL;
|
|
return NULL;
|
|
}
|
|
wolfSSH_SFTPNAME_list_free(name); name = NULL;
|
|
|
|
/* get the handle from opening the directory and read with it */
|
|
handleSz = WOLFSSH_MAX_HANDLE;
|
|
if (wolfSSH_SFTP_GetHandle(ssh, handle, &handleSz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Unable to get handle");
|
|
return NULL;
|
|
}
|
|
|
|
/* now read the dir */
|
|
name = wolfSSH_SFTP_ReadDir(ssh, handle, handleSz);
|
|
if (name == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Error reading directory");
|
|
/* fall through because the handle should always be closed */
|
|
}
|
|
|
|
/* close dir when finished */
|
|
if (wolfSSH_SFTP_Close(ssh, handle, handleSz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error closing handle");
|
|
wolfSSH_SFTPNAME_list_free(name);
|
|
name = NULL;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
/* helper function for common code between LSTAT and STAT
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
static int SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr, byte type)
|
|
{
|
|
int ret;
|
|
word32 reqId;
|
|
|
|
if (ssh == NULL || dir == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_[L]STAT");
|
|
|
|
if (SendPacketType(ssh, type, (byte*)dir, (word32)WSTRLEN(dir))
|
|
!= WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* get attributes response */
|
|
if (SFTP_GetHeader(ssh, &reqId, &type) <= 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* check request ID */
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "Bad request ID received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
ssh->reqId++;
|
|
}
|
|
|
|
if (type == WOLFSSH_FTP_ATTRS) {
|
|
ret = SFTP_ParseAtributes(ssh, atr);
|
|
if (ret != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
}
|
|
else if (type == WOLFSSH_FTP_STATUS) {
|
|
if ((ret = wolfSSH_SFTP_DoStatus(ssh, reqId)) != WOLFSSH_FTP_OK) {
|
|
if (ret == WOLFSSH_FTP_PERMISSION) {
|
|
return WS_PERMISSIONS;
|
|
}
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* follows symbolic links
|
|
*
|
|
* dir NULL terminated string of file name
|
|
* atr structure to hold parsed file attributes
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
return SFTP_STAT(ssh, dir, atr, WOLFSSH_FTP_STAT);
|
|
}
|
|
|
|
|
|
/* does not follow symbolic links
|
|
*
|
|
* dir NULL terminated string of file name
|
|
* atr structure to hold parsed file attributes
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_LSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
return SFTP_STAT(ssh, dir, atr, WOLFSSH_FTP_LSTAT);
|
|
}
|
|
|
|
|
|
/* Open a file for reading/writing
|
|
*
|
|
* dir NULL terminated string holding file name
|
|
* reason the reason file is being opened for i.e. WOLFSSH_FXF_READ,
|
|
* WOLFSSH_FXF_WRITE, ....
|
|
* atr attributes of file
|
|
* handle resulting handle from opening the file
|
|
* handleSz gets set to resulting handle size. Should initially be size of
|
|
* handle buffer when passed in
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_Open(WOLFSSH* ssh, char* dir, word32 reason,
|
|
WS_SFTP_FILEATRB* atr, byte* handle, word32* handleSz)
|
|
{
|
|
int sz;
|
|
byte* data;
|
|
word32 idx;
|
|
|
|
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_Open()");
|
|
if (ssh == NULL || dir == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
sz = (int)WSTRLEN(dir);
|
|
data = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER + UINT32_SZ * 3 ,
|
|
ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_OPEN,
|
|
sz + UINT32_SZ * 3, data) != WS_SUCCESS) {
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
idx = WOLFSSH_SFTP_HEADER;
|
|
c32toa(sz, data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, (byte*)dir, sz); idx += sz;
|
|
c32toa(reason, data + idx); idx += UINT32_SZ;
|
|
|
|
/* @TODO handle adding attributes here */
|
|
(void)atr;
|
|
c32toa(0x00000000, data + idx); idx += UINT32_SZ;
|
|
|
|
/* 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);
|
|
|
|
if (wolfSSH_SFTP_GetHandle(ssh, handle, handleSz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error getting handle");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Writes data from buffer to the file handle
|
|
*
|
|
* handle file handle given by sftp server
|
|
* handleSz size of handle
|
|
* ofst offset to start writing at
|
|
* in data to be written
|
|
* inSz amount of data to be written from "in" buffer
|
|
*
|
|
* returns the amount written on success
|
|
*/
|
|
int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
|
|
word64 ofst, byte* in, word32 inSz)
|
|
{
|
|
int ret;
|
|
int status;
|
|
byte* data;
|
|
byte type;
|
|
word32 reqId;
|
|
word32 idx;
|
|
|
|
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_SendWritePacket()");
|
|
if (ssh == NULL || handle == NULL || in == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
data = (byte*)WMALLOC(handleSz + WOLFSSH_SFTP_HEADER + UINT32_SZ * 4,
|
|
ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_WRITE,
|
|
handleSz + UINT32_SZ * 4 + inSz, data) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
idx = WOLFSSH_SFTP_HEADER;
|
|
c32toa(handleSz, data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, (byte*)handle, handleSz); idx += handleSz;
|
|
|
|
/* offset to start reading from */
|
|
c32toa((word32)(ofst >> 32), data + idx); idx += UINT32_SZ;
|
|
c32toa((word32)ofst, data + idx); idx += UINT32_SZ;
|
|
|
|
/* data to be written */
|
|
c32toa(inSz, data + idx); idx += UINT32_SZ;
|
|
|
|
/* send header and type specific data */
|
|
ret = wolfSSH_stream_send(ssh, data, idx);
|
|
if (ret < 0) {
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
ret = wolfSSH_stream_send(ssh, in, inSz);
|
|
if (ret < 0) {
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
return ret;
|
|
}
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
|
|
/* Get response */
|
|
if (SFTP_GetHeader(ssh, &reqId, &type) <= 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* check request ID */
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "Bad request ID received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
ssh->reqId++;
|
|
}
|
|
|
|
if (type == WOLFSSH_FTP_STATUS) {
|
|
status = wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
if (status == WOLFSSH_FTP_OK) {
|
|
/* a okay */
|
|
}
|
|
else {
|
|
/* @TODO better error value description i.e permissions... */
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Reads data from file and places it in "out" buffer
|
|
*
|
|
* handle file handle given by sftp server
|
|
* handleSz size of handle
|
|
* ofst offset to start reading at
|
|
* out buffer to hold resulting data read
|
|
* outSz size of "out" buffer
|
|
*
|
|
* returns the number of bytes read on success
|
|
*/
|
|
int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
|
|
word64 ofst, byte* out, word32 outSz)
|
|
{
|
|
int ret;
|
|
byte* data;
|
|
byte type;
|
|
word32 reqId;
|
|
word32 idx;
|
|
|
|
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_SendReadPacket()");
|
|
if (ssh == NULL || handle == NULL || out == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
data = (byte*)WMALLOC(handleSz + WOLFSSH_SFTP_HEADER + UINT32_SZ * 4,
|
|
ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_READ,
|
|
handleSz + UINT32_SZ * 4, data) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
idx = WOLFSSH_SFTP_HEADER;
|
|
c32toa(handleSz, data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, (byte*)handle, handleSz); idx += handleSz;
|
|
|
|
/* offset to start reading from */
|
|
c32toa((word32)(ofst >> 32), data + idx) ; idx += UINT32_SZ;
|
|
c32toa((word32)ofst, data + idx); idx += UINT32_SZ;
|
|
|
|
/* max length to read */
|
|
c32toa(outSz, data + idx); idx += UINT32_SZ;
|
|
|
|
/* send header and type specific data */
|
|
ret = wolfSSH_stream_send(ssh, data, idx);
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* Get response */
|
|
if (SFTP_GetHeader(ssh, &reqId, &type) <= 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* check request ID */
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "Bad request ID received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
ssh->reqId++;
|
|
}
|
|
|
|
if (type == WOLFSSH_FTP_DATA) {
|
|
byte buf[UINT32_SZ];
|
|
word32 sz;
|
|
|
|
/* get size of string and place it into out buffer */
|
|
ret = wolfSSH_stream_read(ssh, buf, UINT32_SZ);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ato32(buf, &sz);
|
|
if (sz > outSz) {
|
|
WLOG(WS_LOG_SFTP, "Server sent more data then expected");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ret = wolfSSH_stream_read(ssh, out, sz);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
ret = sz;
|
|
}
|
|
else if (type == WOLFSSH_FTP_STATUS) {
|
|
ret = wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
if (ret == WOLFSSH_FTP_OK || ret == WOLFSSH_FTP_EOF) {
|
|
WLOG(WS_LOG_SFTP, "OK or EOF found");
|
|
ret = 0; /* nothing was read */
|
|
}
|
|
else {
|
|
/* @TODO better error value description i.e permissions... */
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Sends packet to make a directory
|
|
*
|
|
* dir NULL terminated string with the name of the new directory to create
|
|
* atr attributes of the new directory
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_MKDIR(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
|
|
{
|
|
int sz;
|
|
int ret;
|
|
byte* data;
|
|
word32 reqId;
|
|
byte type;
|
|
word32 idx;
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_MKDIR");
|
|
if (ssh == NULL || dir == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
sz = (int)WSTRLEN(dir);
|
|
data = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER + UINT32_SZ * 3 ,
|
|
ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_MKDIR,
|
|
sz + UINT32_SZ * 3, data) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
idx = WOLFSSH_SFTP_HEADER;
|
|
c32toa(sz, data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, (byte*)dir, sz); idx += sz;
|
|
c32toa(UINT32_SZ, data + idx); idx += UINT32_SZ;
|
|
|
|
/* @TODO handle setting attributes */
|
|
(void)atr;
|
|
c32toa(0x000001FF, data + idx); idx += UINT32_SZ;
|
|
|
|
/* send header and type specific data */
|
|
ret = wolfSSH_stream_send(ssh, data, idx);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
|
|
/* Get response */
|
|
if (SFTP_GetHeader(ssh, &reqId, &type) <= 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
if (type != WOLFSSH_FTP_STATUS) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* check request ID */
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "Bad request ID received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
ssh->reqId++;
|
|
}
|
|
|
|
if ((ret = wolfSSH_SFTP_DoStatus(ssh, reqId)) != WOLFSSH_FTP_OK) {
|
|
if (ret == WOLFSSH_FTP_PERMISSION) {
|
|
return WS_PERMISSIONS;
|
|
}
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Reads an open handle and returns a name list on success
|
|
*
|
|
* handle the directory handle to read
|
|
* handleSz size of handle passed in
|
|
*
|
|
* returns a pointer to linked name list on success and NULL on failure
|
|
*/
|
|
WS_SFTPNAME* wolfSSH_SFTP_ReadDir(WOLFSSH* ssh, byte* handle,
|
|
word32 handleSz)
|
|
{
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_READDIR");
|
|
if (ssh == NULL || handle == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Bad argument passed in");
|
|
return NULL;
|
|
}
|
|
|
|
ret = SendPacketType(ssh, WOLFSSH_FTP_READDIR, handle, handleSz);
|
|
if (ret != WS_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
|
|
return wolfSSH_SFTP_DoName(ssh);
|
|
}
|
|
|
|
|
|
/* Sends close dir command on a handle
|
|
*
|
|
* handle the directory handle to read
|
|
* handleSz size of handle passed in
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_Close(WOLFSSH* ssh, byte* handle, word32 handleSz)
|
|
{
|
|
int ret;
|
|
word32 reqId;
|
|
byte type = 0;
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_CLOSE");
|
|
if (ssh == NULL || handle == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
ret = SendPacketType(ssh, WOLFSSH_FTP_CLOSE, handle, handleSz);
|
|
if (ret != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
ret = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (type != WOLFSSH_FTP_STATUS || ret <= 0) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
if (ret == WOLFSSH_FTP_OK) {
|
|
return WS_SUCCESS;
|
|
}
|
|
else {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
/* Gets the real path name from a directory
|
|
*
|
|
* dir NULL terminated string with path to get real path of
|
|
*
|
|
* returns a WS_SFTPNAME structure on success and NULL on failure
|
|
*/
|
|
WS_SFTPNAME* wolfSSH_SFTP_RealPath(WOLFSSH* ssh, char* dir)
|
|
{
|
|
int sz;
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_REALPATH");
|
|
if (ssh == NULL || dir == NULL) {
|
|
WLOG(WS_LOG_SFTP, "Bad argument passed in");
|
|
return NULL;
|
|
}
|
|
|
|
sz = (int)WSTRLEN(dir);
|
|
if (SendPacketType(ssh, WOLFSSH_FTP_REALPATH, (byte*)dir, sz) !=
|
|
WS_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
|
|
/* read name response from Real Path packet */
|
|
return wolfSSH_SFTP_DoName(ssh);
|
|
}
|
|
|
|
|
|
/* Send open directory packet, currently this function leaves handling response
|
|
* to the caller
|
|
*
|
|
* dir name of directory to read
|
|
* dirSz size of "dir" buffer
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_OpenDir(WOLFSSH* ssh, byte* dir, word32 dirSz)
|
|
{
|
|
WLOG(WS_LOG_SFTP, "Entering WOLFSSH_FTP_OPENDIR");
|
|
if (ssh == NULL || dir == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
return SendPacketType(ssh, WOLFSSH_FTP_OPENDIR, dir, dirSz);
|
|
}
|
|
|
|
|
|
/* rename a file path
|
|
*
|
|
* old null terminated old name
|
|
* nw null terminated resulting name after rename
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_Rename(WOLFSSH* ssh, const char* old, const char* nw)
|
|
{
|
|
WS_SFTP_FILEATRB atrb;
|
|
byte* data;
|
|
int sz;
|
|
int ret;
|
|
word32 reqId;
|
|
word32 idx;
|
|
byte type;
|
|
|
|
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_Rename");
|
|
if (ssh == NULL || old == NULL || nw == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* check that file exists */
|
|
if ((ret = wolfSSH_SFTP_STAT(ssh, (char*)old, &atrb)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error finding file to rename");
|
|
return ret;
|
|
}
|
|
|
|
sz = (int)(WSTRLEN(old) + WSTRLEN(nw));
|
|
data = (byte*)WMALLOC(sz + WOLFSSH_SFTP_HEADER + UINT32_SZ * 2,
|
|
ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (data == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_RENAME,
|
|
sz + UINT32_SZ * 2, data) != WS_SUCCESS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* add old name to the packet */
|
|
idx = WOLFSSH_SFTP_HEADER;
|
|
c32toa((word32)WSTRLEN(old), data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, (byte*)old, WSTRLEN(old)); idx += WSTRLEN(old);
|
|
|
|
/* add new name to the packet */
|
|
c32toa((word32)WSTRLEN(nw), data + idx); idx += UINT32_SZ;
|
|
WMEMCPY(data + idx, (byte*)nw, WSTRLEN(nw)); idx += WSTRLEN(nw);
|
|
|
|
/* send header and type specific data */
|
|
ret = wolfSSH_stream_send(ssh, data, idx);
|
|
WFREE(data, NULL, DYNTYPE_BUFFER);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* Get response */
|
|
ret = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (ret <= 0 || type != WOLFSSH_FTP_STATUS) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* check request ID */
|
|
if (reqId != ssh->reqId) {
|
|
WLOG(WS_LOG_SFTP, "Bad request ID received");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
ssh->reqId++;
|
|
}
|
|
|
|
if ((ret = wolfSSH_SFTP_DoStatus(ssh, reqId)) != WOLFSSH_FTP_OK) {
|
|
if (ret == WOLFSSH_FTP_PERMISSION) {
|
|
return WS_PERMISSIONS;
|
|
}
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* removes a file
|
|
*
|
|
* f file name to be removed
|
|
* fSz size of file name
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_Remove(WOLFSSH* ssh, char* f)
|
|
{
|
|
WS_SFTP_FILEATRB atrb;
|
|
int ret;
|
|
word32 reqId;
|
|
byte type;
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_REMOVE");
|
|
if (ssh == NULL || f == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* check file is there to be removed */
|
|
if ((ret = wolfSSH_SFTP_LSTAT(ssh, f, &atrb)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error verifying file");
|
|
return ret;
|
|
}
|
|
|
|
ret = SendPacketType(ssh, WOLFSSH_FTP_REMOVE, (byte*)f, (word32)WSTRLEN(f));
|
|
if (ret != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
ret = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (ret <= 0 || type != WOLFSSH_FTP_STATUS) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
if (ret == WOLFSSH_FTP_OK) {
|
|
return WS_SUCCESS;
|
|
}
|
|
else {
|
|
/* @TODO can return better error value i.e. permissions */
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
/* removes a directory
|
|
*
|
|
* dir name of directory to remove
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_RMDIR(WOLFSSH* ssh, char* dir)
|
|
{
|
|
int ret;
|
|
word32 reqId;
|
|
byte type;
|
|
|
|
WLOG(WS_LOG_SFTP, "Sending WOLFSSH_FTP_RMDIR");
|
|
if (ssh == NULL || dir == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
ret = SendPacketType(ssh, WOLFSSH_FTP_RMDIR, (byte*)dir,
|
|
(word32)WSTRLEN(dir));
|
|
if (ret != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
ret = SFTP_GetHeader(ssh, &reqId, &type);
|
|
if (ret <= 0 || type != WOLFSSH_FTP_STATUS) {
|
|
WLOG(WS_LOG_SFTP, "Unexpected packet type");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_DoStatus(ssh, reqId);
|
|
if (ret == WOLFSSH_FTP_OK) {
|
|
return WS_SUCCESS;
|
|
}
|
|
else {
|
|
/* @TODO can return better error value i.e. permissions */
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
/* save an offset for later reget or reput command
|
|
* frm and to should be null terminated strings
|
|
*
|
|
* frm NULL terminated string holding the from name
|
|
* to NULL terminated string holding the to name
|
|
* ofst offset into file that should be saved
|
|
*
|
|
* return WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_SaveOfst(WOLFSSH* ssh, char* frm, char* to, word64 ofst)
|
|
{
|
|
int idx;
|
|
SFTP_OFST* current;
|
|
int frmSz, toSz;
|
|
|
|
if (ssh == NULL || frm == NULL || to == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
frmSz = (int)WSTRLEN(frm);
|
|
toSz = (int)WSTRLEN(to);
|
|
|
|
/* find if able to save */
|
|
for (idx = 0; idx < WOLFSSH_MAX_SFTPOFST; idx++) {
|
|
if (ssh->sftpOfst[idx].offset == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (idx == WOLFSSH_MAX_SFTPOFST) {
|
|
WLOG(WS_LOG_SFTP, "No free save spots found");
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
if (frmSz > WOLFSSH_MAX_FILENAME || toSz > WOLFSSH_MAX_FILENAME) {
|
|
WLOG(WS_LOG_SFTP, "File name is too large");
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
current = &ssh->sftpOfst[idx];
|
|
WMEMCPY(current->from, frm, frmSz);
|
|
current->from[frmSz] = '\0';
|
|
WMEMCPY(current->to, to, toSz);
|
|
current->to[toSz] = '\0';
|
|
current->offset = ofst;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Compares the from and to name to stored values and if a match is found the
|
|
* stored offset is returned.
|
|
*
|
|
* frm NULL terminated string holding the from name
|
|
* to NULL terminated string holding the to name
|
|
*
|
|
* returns ofst size, 0 is returned if no saved offset was found
|
|
*/
|
|
word64 wolfSSH_SFTP_GetOfst(WOLFSSH* ssh, char* frm, char* to)
|
|
{
|
|
int idx;
|
|
word64 ofst = 0;
|
|
int frmSz, toSz;
|
|
|
|
if (ssh == NULL || frm == NULL || to == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
frmSz = (int)WSTRLEN(frm);
|
|
toSz = (int)WSTRLEN(to);
|
|
|
|
/* check if in saved list */
|
|
for (idx = 0; idx < WOLFSSH_MAX_SFTPOFST; idx++) {
|
|
/* check "from" file name is same */
|
|
if ((frmSz == (int)WSTRLEN(ssh->sftpOfst[idx].from)) &&
|
|
(WMEMCMP(frm, ssh->sftpOfst[idx].from, frmSz) == 0)) {
|
|
/* check "to" file name is same */
|
|
if ((toSz == (int)WSTRLEN(ssh->sftpOfst[idx].to)) &&
|
|
(WMEMCMP(to, ssh->sftpOfst[idx].to, toSz) == 0)) {
|
|
WLOG(WS_LOG_SFTP, "Found saved offset");
|
|
ofst = ssh->sftpOfst[idx].offset;
|
|
/* clear offset */
|
|
WMEMSET(&ssh->sftpOfst[idx], 0, sizeof(SFTP_OFST));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ofst;
|
|
}
|
|
|
|
|
|
/* clears out all get/put offset status stored
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_ClearOfst(WOLFSSH* ssh)
|
|
{
|
|
int i;
|
|
|
|
if (ssh == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
for (i = 0; i < WOLFSSH_MAX_SFTPOFST; i++) {
|
|
WMEMSET(&ssh->sftpOfst[i], 0, sizeof(SFTP_OFST));
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* set interrupt for get/put. This can be called by the application to break out
|
|
* of and store the current offset of a wolfSSH_SFTP_Get or wolfSSH_SFTP_Put
|
|
* call
|
|
*/
|
|
void wolfSSH_SFTP_Interrupt(WOLFSSH* ssh)
|
|
{
|
|
if (ssh != NULL) {
|
|
ssh->sftpInt = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Continuously loops over wolfSSH_SFTP_SendReadPacket() getting all data.
|
|
*
|
|
* resume if set to 1 then stored offsets are searched for from -> to
|
|
* statusCb can be NULL. If not NULL then callback function is called on each
|
|
* loop with bytes written.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from,
|
|
char* to, byte resume, WS_STATUS_CB* statusCb)
|
|
{
|
|
WS_SFTP_FILEATRB atrb;
|
|
byte handle[WOLFSSH_MAX_HANDLE];
|
|
WFILE* fl;
|
|
long gOfst = 0;
|
|
word32 handleSz;
|
|
int ret;
|
|
|
|
if (ssh == NULL || from == NULL || to == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_LSTAT(ssh, from, &atrb);
|
|
if ((ret |= wolfSSH_SFTP_STAT(ssh, from, &atrb)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error verifying file");
|
|
return ret;
|
|
}
|
|
|
|
/* open file and get handle */
|
|
handleSz = WOLFSSH_MAX_HANDLE;
|
|
if ((ret = wolfSSH_SFTP_Open(ssh, from, WOLFSSH_FXF_READ, NULL,
|
|
handle, &handleSz)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error getting handle");
|
|
return ret;
|
|
}
|
|
|
|
/* if resuming then check for saved offset */
|
|
if (resume) {
|
|
gOfst = wolfSSH_SFTP_GetOfst(ssh, from, to);
|
|
}
|
|
|
|
if (gOfst > 0) {
|
|
ret = WFOPEN(&fl, to, "ab");
|
|
}
|
|
else {
|
|
ret = WFOPEN(&fl, to, "wb");
|
|
}
|
|
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_SFTP, "Unable to open output file");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
long sz;
|
|
byte r[WOLFSSH_MAX_SFTP_RW];
|
|
|
|
ret = WS_SUCCESS;
|
|
do {
|
|
sz = wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz, gOfst,
|
|
r, WOLFSSH_MAX_SFTP_RW);
|
|
if (sz > 0) {
|
|
if ((long)WFWRITE(r, 1, sz, fl) != sz) {
|
|
WLOG(WS_LOG_SFTP, "Error writing to file");
|
|
ret = WS_FATAL_ERROR;
|
|
break;
|
|
}
|
|
gOfst += sz;
|
|
if (statusCb != NULL) {
|
|
statusCb(ssh, gOfst, from);
|
|
}
|
|
}
|
|
} while (sz > 0 && ssh->sftpInt == 0);
|
|
if (ssh->sftpInt) {
|
|
WLOG(WS_LOG_SFTP, "Interrupted, trying to save offset");
|
|
wolfSSH_SFTP_SaveOfst(ssh, from, to, gOfst);
|
|
}
|
|
ssh->sftpInt = 0;
|
|
WFCLOSE(fl);
|
|
}
|
|
|
|
if (wolfSSH_SFTP_Close(ssh, handle, handleSz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error closing handle");
|
|
if (ret == WS_SUCCESS) ret = WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Higher level command for pushing data to SFTP server. Loops over
|
|
* wolfSSH_SFTP_Write
|
|
*
|
|
* resume if set to 1 then stored offsets are searched for from -> to
|
|
* statusCb can be NULL. If not NULL then callback function is called on each
|
|
* loop with bytes written.
|
|
*
|
|
* returns WS_SUCCESS on success
|
|
*/
|
|
int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
|
|
WS_STATUS_CB* statusCb)
|
|
{
|
|
byte handle[WOLFSSH_MAX_HANDLE];
|
|
WFILE* fl;
|
|
long pOfst = 0;
|
|
word32 handleSz;
|
|
int ret;
|
|
|
|
if (ssh == NULL || from == NULL || to == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (resume) {
|
|
/* check if offset was stored */
|
|
pOfst = wolfSSH_SFTP_GetOfst(ssh, from, to);
|
|
}
|
|
|
|
/* open file and get handle */
|
|
handleSz = WOLFSSH_MAX_HANDLE;
|
|
if ((ret = wolfSSH_SFTP_Open(ssh, to, (WOLFSSH_FXF_WRITE |
|
|
WOLFSSH_FXF_CREAT | WOLFSSH_FXF_TRUNC), NULL,
|
|
handle, &handleSz)) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error getting handle");
|
|
return ret;
|
|
}
|
|
|
|
ret = WFOPEN(&fl, from, "rb");
|
|
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_SFTP, "Unable to open file");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
byte r[WOLFSSH_MAX_SFTP_RW];
|
|
int rSz;
|
|
int sz;
|
|
|
|
ret = WS_SUCCESS;
|
|
do {
|
|
rSz = (int)WFREAD(r, 1, WOLFSSH_MAX_SFTP_RW, fl);
|
|
sz = wolfSSH_SFTP_SendWritePacket(ssh, handle, handleSz, pOfst,
|
|
r, rSz);
|
|
if (sz > 0) {
|
|
pOfst += sz;
|
|
if (statusCb != NULL) {
|
|
statusCb(ssh, pOfst, from);
|
|
}
|
|
}
|
|
} while (sz > 0 && ssh->sftpInt == 0);
|
|
if (ssh->sftpInt) {
|
|
wolfSSH_SFTP_SaveOfst(ssh, from, to, pOfst);
|
|
}
|
|
ssh->sftpInt = 0;
|
|
WFCLOSE(fl);
|
|
}
|
|
|
|
if (wolfSSH_SFTP_Close(ssh, handle, handleSz) != WS_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Error closing handle");
|
|
if (ret == WS_SUCCESS) ret = WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WOLFSSH_SFTP */
|