mirror of https://github.com/wolfSSL/wolfssh.git
2097 lines
67 KiB
C
2097 lines
67 KiB
C
/* sftpclient.c
|
|
*
|
|
* Copyright (C) 2014-2026 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
|
|
|
|
#define WOLFSSH_TEST_CLIENT
|
|
|
|
#ifdef WOLFSSL_USER_SETTINGS
|
|
#include <wolfssl/wolfcrypt/settings.h>
|
|
#else
|
|
#include <wolfssl/options.h>
|
|
#endif
|
|
|
|
#include <wolfssh/ssh.h>
|
|
#include <wolfssh/internal.h>
|
|
#include <wolfssh/wolfsftp.h>
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
#include <wolfssh/certman.h>
|
|
#endif
|
|
#include <wolfssh/test.h>
|
|
#include <wolfssh/port.h>
|
|
#include <wolfssl/wolfcrypt/ecc.h>
|
|
#include <wolfssl/wolfcrypt/coding.h>
|
|
#include "examples/sftpclient/sftpclient.h"
|
|
#include "examples/client/common.h"
|
|
#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) && \
|
|
!defined(WOLFSSH_ZEPHYR)
|
|
#include <termios.h>
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
#include <wolfssl/wolfcrypt/asn.h>
|
|
#endif
|
|
|
|
/* Shared by sftpclient_test() and the -W pre-scan in main(). */
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
#define SFTPC_OPTLIST_STORE "W:"
|
|
#else
|
|
#define SFTPC_OPTLIST_STORE ""
|
|
#endif
|
|
#define SFTPC_OPTLIST "?d:gh:i:j:k:l:p:r:u:EGNP:J:A:X" SFTPC_OPTLIST_STORE
|
|
|
|
#if defined(WOLFSSH_SFTP) && !defined(NO_WOLFSSH_CLIENT)
|
|
|
|
/* static so that signal handler can access and interrupt get/put */
|
|
static WOLFSSH* ssh = NULL;
|
|
static char* workingDir;
|
|
#define fin stdin
|
|
#define fout stdout
|
|
#define MAX_CMD_SZ 7
|
|
|
|
|
|
#define AUTOPILOT_OFF 0
|
|
#define AUTOPILOT_GET 1
|
|
#define AUTOPILOT_PUT 2
|
|
|
|
|
|
#ifdef WOLFSSH_STATIC_MEMORY
|
|
#include <wolfssl/wolfcrypt/memory.h>
|
|
|
|
typedef WOLFSSL_HEAP_HINT SFTPC_HEAP_HINT;
|
|
|
|
/* This static buffer is tuned for building with SFTP only. The static
|
|
* buffer size is calculated by multiplying the pairs of sizeList items
|
|
* and distList items and summing (32*50 + 128*100 + ...) and adding
|
|
* the sum of the distList values times the sizeof wc_Memory (rounded up
|
|
* to a word, 24). This total was 268kb plus change, rounded up to 269. */
|
|
#ifndef SFTPC_STATIC_SIZES
|
|
#define SFTPC_STATIC_SIZES 64,128,384,800,3120,8400,17552,33104,131072
|
|
#endif
|
|
#ifndef SFTPC_STATIC_DISTS
|
|
#define SFTPC_STATIC_DISTS 60,100,4,6,5,2,1,2,1
|
|
#endif
|
|
#ifndef SFTPC_STATIC_LISTSZ
|
|
#define SFTPC_STATIC_LISTSZ 9
|
|
#endif
|
|
#ifndef SFTPC_STATIC_BUFSZ
|
|
#define SFTPC_STATIC_BUFSZ (269*1024)
|
|
#endif
|
|
static const word32 static_sizeList[] = {SFTPC_STATIC_SIZES};
|
|
static const word32 static_distList[] = {SFTPC_STATIC_DISTS};
|
|
static byte static_buffer[SFTPC_STATIC_BUFSZ];
|
|
#else /* WOLFSSH_STATIC_MEMORY */
|
|
typedef void SFTPC_HEAP_HINT;
|
|
#endif /* WOLFSSH_STATIC_MEMORY */
|
|
|
|
|
|
static void err_msg(const char* s)
|
|
{
|
|
printf("%s\n", s);
|
|
}
|
|
|
|
|
|
#ifndef WOLFSSH_NO_TIMESTAMP
|
|
|
|
static char currentFile[WOLFSSH_MAX_FILENAME + 1] = "";
|
|
static word32 startTime;
|
|
#define TIMEOUT_VALUE 120
|
|
|
|
word32 current_time(int reset);
|
|
#ifdef USE_WINDOWS_API
|
|
#include <time.h>
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
word32 current_time(int reset)
|
|
{
|
|
static int init = 0;
|
|
static LARGE_INTEGER freq;
|
|
|
|
LARGE_INTEGER count;
|
|
|
|
(void)reset;
|
|
|
|
if (!init) {
|
|
QueryPerformanceFrequency(&freq);
|
|
init = 1;
|
|
}
|
|
|
|
QueryPerformanceCounter(&count);
|
|
|
|
return (word32)(count.QuadPart / freq.QuadPart);
|
|
}
|
|
#else
|
|
#include <sys/time.h>
|
|
|
|
/* return number of seconds*/
|
|
word32 current_time(int reset)
|
|
{
|
|
struct timeval tv;
|
|
|
|
(void)reset;
|
|
|
|
gettimeofday(&tv, 0);
|
|
return (word32)tv.tv_sec;
|
|
}
|
|
#endif /* USE_WINDOWS_API */
|
|
#endif /* !WOLFSSH_NO_TIMESTAMP */
|
|
|
|
|
|
static void myStatusCb(WOLFSSH* sshIn, word32* bytes, char* name)
|
|
{
|
|
/* Variables declared at function scope per wolfSSL coding standards.
|
|
* Modern compilers optimize variable access regardless of declaration
|
|
* placement, so there is no performance impact. */
|
|
word32 currentTime;
|
|
#ifndef WOLFSSH_NO_TIMESTAMP
|
|
static word32 lastOutputTime = 0;
|
|
static word32 lastPrintedBytes[2] = {0, 0};
|
|
word32 elapsedTime;
|
|
word32 nameSz;
|
|
#endif
|
|
char buf[80];
|
|
word64 longBytes = ((word64)bytes[1] << 32) | bytes[0];
|
|
|
|
#ifndef WOLFSSH_NO_TIMESTAMP
|
|
currentTime = current_time(0);
|
|
if (currentTime == lastOutputTime) {
|
|
if (bytes[0] != lastPrintedBytes[0] || bytes[1] != lastPrintedBytes[1]) {
|
|
/* Progress made in the same second - throttle but track latest */
|
|
lastPrintedBytes[0] = bytes[0];
|
|
lastPrintedBytes[1] = bytes[1];
|
|
return;
|
|
}
|
|
/* bytes unchanged: EOF final call - fall through to print */
|
|
}
|
|
else {
|
|
lastOutputTime = currentTime;
|
|
}
|
|
lastPrintedBytes[0] = bytes[0];
|
|
lastPrintedBytes[1] = bytes[1];
|
|
/* clamp over-long names: currentFile holds the truncated prefix, so the
|
|
* comparison must use the same bound or every call looks like a new file */
|
|
nameSz = (word32)WSTRLEN(name);
|
|
if (nameSz >= sizeof(currentFile))
|
|
nameSz = (word32)sizeof(currentFile) - 1;
|
|
if (WSTRNCMP(currentFile, name, nameSz) != 0 ||
|
|
currentFile[nameSz] != '\0') {
|
|
startTime = current_time(1);
|
|
lastOutputTime = 0; /* Reset timer for new file transfer */
|
|
lastPrintedBytes[0] = 0;
|
|
lastPrintedBytes[1] = 0;
|
|
WMEMSET(currentFile, 0, sizeof(currentFile));
|
|
WMEMCPY(currentFile, name, nameSz);
|
|
}
|
|
elapsedTime = currentTime - startTime;
|
|
WSNPRINTF(buf, sizeof(buf), "Processed %8llu\t bytes in %d seconds\r",
|
|
(unsigned long long)longBytes, elapsedTime);
|
|
#ifndef WOLFSSH_NO_SFTP_TIMEOUT
|
|
if (elapsedTime > TIMEOUT_VALUE) {
|
|
WSNPRINTF(buf, sizeof(buf), "\nProcess timed out at %d seconds, "
|
|
"stopping\r", elapsedTime);
|
|
WMEMSET(currentFile, 0, sizeof(currentFile));
|
|
wolfSSH_SFTP_Interrupt(ssh);
|
|
}
|
|
#endif
|
|
#else
|
|
WSNPRINTF(buf, sizeof(buf), "Processed %8llu\t bytes \r",
|
|
(unsigned long long)longBytes);
|
|
(void)currentTime;
|
|
#endif
|
|
WFPUTS(buf, fout);
|
|
(void)name;
|
|
(void)sshIn;
|
|
}
|
|
|
|
|
|
static int NonBlockSSH_connect(void)
|
|
{
|
|
int ret;
|
|
int error;
|
|
SOCKET_T sockfd;
|
|
int select_ret = 0;
|
|
|
|
ret = wolfSSH_SFTP_connect(ssh);
|
|
error = wolfSSH_get_error(ssh);
|
|
sockfd = (SOCKET_T)wolfSSH_get_fd(ssh);
|
|
|
|
while (ret != WS_SUCCESS &&
|
|
(error == WS_WANT_READ || error == WS_WANT_WRITE ||
|
|
error == WS_REKEYING || error == WS_AUTH_PENDING))
|
|
{
|
|
if (error == WS_WANT_READ)
|
|
printf("... client would read block\n");
|
|
else if (error == WS_WANT_WRITE)
|
|
printf("... client would write block\n");
|
|
|
|
select_ret = tcp_select(sockfd, 1);
|
|
if (select_ret == WS_SELECT_RECV_READY ||
|
|
select_ret == WS_SELECT_ERROR_READY ||
|
|
error == WS_WANT_WRITE)
|
|
{
|
|
ret = wolfSSH_SFTP_connect(ssh);
|
|
error = wolfSSH_get_error(ssh);
|
|
|
|
#ifdef WOLFSSH_TEST_BLOCK
|
|
if (select_ret == WS_SELECT_RECV_READY && error == WS_WANT_READ) {
|
|
/* The socket has data to be read but the non blocking test
|
|
* code returned want read. Loop over wolfSSH_SFTP_connect here
|
|
* until we get the data off the socket that select indicated
|
|
* was available. */
|
|
while (error == WS_WANT_READ) {
|
|
ret = wolfSSH_SFTP_connect(ssh);
|
|
error = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else if (select_ret == WS_SELECT_TIMEOUT)
|
|
error = WS_WANT_READ;
|
|
else
|
|
error = WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WS_NO_SIGNAL
|
|
/* for command reget and reput to handle saving offset after interrupt during
|
|
* get and put */
|
|
#include <signal.h>
|
|
static byte interrupt = 0;
|
|
|
|
static void sig_handler(const int sig)
|
|
{
|
|
(void)sig;
|
|
|
|
interrupt = 1;
|
|
wolfSSH_SFTP_Interrupt(ssh); /* NOLINT(bugprone-signal-handler) */
|
|
}
|
|
#endif /* WS_NO_SIGNAL */
|
|
|
|
/* cleans up absolute path */
|
|
static void clean_path(char* path)
|
|
{
|
|
int i;
|
|
long sz;
|
|
byte found;
|
|
|
|
if (path == NULL) {
|
|
return;
|
|
}
|
|
|
|
sz = (long)WSTRLEN(path);
|
|
|
|
/* remove any double '/' chars */
|
|
for (i = 0; i < sz; i++) {
|
|
if (path[i] == '/' && path[i+1] == '/') {
|
|
WMEMMOVE(path + i, path + i + 1, sz - i);
|
|
sz -= 1;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
/* remove any trailing '/' chars */
|
|
sz = (long)WSTRLEN(path);
|
|
for (i = (int)sz - 1; i > 0; i--) {
|
|
if (path[i] == '/') {
|
|
path[i] = '\0';
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* go through path until no cases are found */
|
|
do {
|
|
int prIdx = 0; /* begin of cut */
|
|
int enIdx = 0; /* end of cut */
|
|
sz = (long)WSTRLEN(path);
|
|
|
|
found = 0;
|
|
for (i = 0; i < sz; i++) {
|
|
if (path[i] == '/') {
|
|
int z;
|
|
|
|
/* if next two chars are .. then delete */
|
|
if (path[i+1] == '.' && path[i+2] == '.') {
|
|
enIdx = i + 3;
|
|
|
|
/* start at one char before / and retrace path */
|
|
for (z = i - 1; z > 0; z--) {
|
|
if (path[z] == '/') {
|
|
prIdx = z;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* cut out .. and previous */
|
|
WMEMMOVE(path + prIdx, path + enIdx, sz - enIdx);
|
|
path[sz - (enIdx - prIdx)] = '\0';
|
|
|
|
if (enIdx == sz) {
|
|
path[prIdx] = '\0';
|
|
}
|
|
|
|
/* case of at / */
|
|
if (WSTRLEN(path) == 0) {
|
|
path[0] = '/';
|
|
path[1] = '\0';
|
|
}
|
|
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (found);
|
|
}
|
|
|
|
#define WS_MAX_EXAMPLE_RW 1024
|
|
|
|
static void ShowCommands(void)
|
|
{
|
|
printf("\n\nCommands :\n");
|
|
printf("\tcd <string> change directory\n");
|
|
printf("\tchmod <mode> <path> change mode\n");
|
|
printf("\tcreat <mode> <path> create file with given permissions\n");
|
|
printf("\tget <remote file> <local file> pulls file(s) from server\n");
|
|
printf("\tlcd <path> change local directory\n");
|
|
printf("\tlls list local directory\n");
|
|
printf("\tls list current directory\n");
|
|
printf("\tmkdir <dir name> creates new directory on server\n");
|
|
printf("\tput <local file> <remote file> push file(s) to server\n");
|
|
printf("\tpwd list current path\n");
|
|
printf("\tquit exit\n");
|
|
printf("\trename <old> <new> renames remote file\n");
|
|
printf("\treget <remote file> <local file> resume pulling file\n");
|
|
printf("\treput <remote file> <local file> resume pushing file\n");
|
|
printf("\t<crtl + c> interrupt get/put cmd\n");
|
|
|
|
}
|
|
|
|
static void ShowUsage(void)
|
|
{
|
|
printf("wolfsftp %s linked with wolfSSL %s\n", LIBWOLFSSH_VERSION_STRING,
|
|
LIBWOLFSSL_VERSION_STRING);
|
|
printf(" -? display this help and exit\n");
|
|
printf(" -h <host> host to connect to, default %s\n", wolfSshIp);
|
|
printf(" -p <num> port to connect on, default %d\n", wolfSshPort);
|
|
printf(" -u <username> username to authenticate as (REQUIRED)\n");
|
|
printf(" -P <password> password for username, prompted if omitted\n");
|
|
printf(" -d <path> set the default local path\n");
|
|
printf(" -N use non blocking sockets\n");
|
|
printf(" -e use ECC user authentication\n");
|
|
/*printf(" -E use ECC server authentication\n");*/
|
|
printf(" -l <filename> local filename\n");
|
|
printf(" -r <filename> remote filename\n");
|
|
printf(" -g put local filename as remote filename\n");
|
|
printf(" -G get remote filename as local filename\n");
|
|
printf(" -i <filename> filename for the user's private key\n");
|
|
printf(" -k <list> set the comma separated list of server host key "
|
|
"algos to accept\n");
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
printf(" -W <spec> Windows cert store: \"store:subject[:flags]\"\n");
|
|
printf(" Example: -W \"My:CN=MyCert:CURRENT_USER\"\n");
|
|
printf(" flags: CURRENT_USER (default), LOCAL_MACHINE,\n");
|
|
printf(" USERS, CURRENT_SERVICE, SERVICES,\n");
|
|
printf(" CURRENT_USER_GROUP_POLICY,\n");
|
|
printf(" LOCAL_MACHINE_GROUP_POLICY,\n");
|
|
printf(" LOCAL_MACHINE_ENTERPRISE, each also with a\n");
|
|
printf(" CERT_SYSTEM_STORE_ prefix, or a number.\n");
|
|
printf(" -W supplies both keys; it can not be used with "
|
|
"-i, -j or -J.\n");
|
|
printf(" -W also skips the wolfssh home directory search,\n");
|
|
printf(" so -A/-d/-l resolve against the current "
|
|
"directory\n");
|
|
#endif /* WOLFSSH_WINDOWS_CERT_STORE */
|
|
#ifdef WOLFSSH_CERTS
|
|
printf(" -J <filename> filename for DER certificate to use\n");
|
|
printf(" Certificate example : client -u orange \\\n");
|
|
printf(" -J orange-cert.der -i orange-key.der\n");
|
|
printf(" -A <filename> filename for DER CA certificate to verify host\n");
|
|
printf(" -X Ignore IP checks on peer vs peer certificate\n");
|
|
#endif
|
|
|
|
ShowCommands();
|
|
}
|
|
|
|
|
|
/* returns 0 on success */
|
|
static INLINE int SFTP_FPUTS(func_args* args, const char* msg)
|
|
{
|
|
int ret;
|
|
|
|
if (args && args->sftp_cb)
|
|
ret = args->sftp_cb(msg, NULL, 0);
|
|
else
|
|
ret = WFPUTS(msg, fout);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* returns pointer on success, NULL on failure */
|
|
static INLINE char* SFTP_FGETS(func_args* args, char* msg, int msgSz)
|
|
{
|
|
char* ret = NULL;
|
|
|
|
WMEMSET(msg, 0, msgSz);
|
|
if (args && args->sftp_cb) {
|
|
if (args->sftp_cb(NULL, msg, msgSz) == 0)
|
|
ret = msg;
|
|
}
|
|
else
|
|
ret = WFGETS(msg, msgSz, fin);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse "<mode> <path>" from a SFTP command argument string.
|
|
* pt: points past the command keyword (caller advanced by sizeof).
|
|
* modeBuf: caller buffer of WOLFSSH_MAX_OCTET_LEN bytes; receives the
|
|
* null-terminated octal mode token on success.
|
|
* pathOut: receives a pointer to the resolved remote path on success.
|
|
* allocOut: receives a malloc'd absolute-path buffer when pt was relative
|
|
* (caller must WFREE); NULL when the path was already absolute.
|
|
* remoteDir: current remote working directory.
|
|
* Returns 0 on success,
|
|
* 1 if no mode token found (caller prints error and continues),
|
|
* -1 on malloc failure (caller returns -1).
|
|
*/
|
|
static int sftpParseModeAndPath(char* pt, char* modeBuf, char** pathOut,
|
|
char** allocOut, const char* remoteDir)
|
|
{
|
|
word32 sz, idx;
|
|
char* f;
|
|
|
|
*allocOut = NULL;
|
|
*pathOut = NULL;
|
|
|
|
sz = (word32)WSTRLEN(pt);
|
|
if (sz > 0 && pt[sz - 1] == '\n') {
|
|
pt[sz - 1] = '\0';
|
|
sz--;
|
|
}
|
|
|
|
for (idx = 0; idx < sz && (pt[0] == ' ' || pt[0] == '\t'); idx++, pt++);
|
|
sz = (word32)WSTRLEN(pt);
|
|
|
|
sz = (sz < WOLFSSH_MAX_OCTET_LEN - 1) ? sz : WOLFSSH_MAX_OCTET_LEN - 1;
|
|
WMEMCPY(modeBuf, pt, sz);
|
|
modeBuf[sz] = '\0';
|
|
for (idx = 0; idx < sz; idx++) {
|
|
if (modeBuf[idx] == ' ' || modeBuf[idx] == '\t') {
|
|
modeBuf[idx] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
if (idx == 0 || (idx == sz && pt[sz] != ' ' && pt[sz] != '\t'))
|
|
return 1;
|
|
|
|
pt += (word32)WSTRLEN(modeBuf);
|
|
sz = (word32)WSTRLEN(pt);
|
|
for (idx = 0; idx < sz && (pt[0] == ' ' || pt[0] == '\t'); idx++, pt++);
|
|
|
|
if (pt[0] == '\0')
|
|
return 1;
|
|
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(remoteDir) + (int)WSTRLEN(pt) + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL)
|
|
return -1;
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, remoteDir, maxSz);
|
|
if (WSTRLEN(remoteDir) > 1)
|
|
WSTRNCAT(f, "/", maxSz);
|
|
WSTRNCAT(f, pt, maxSz);
|
|
*allocOut = f;
|
|
*pathOut = f;
|
|
}
|
|
else {
|
|
*pathOut = pt;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* main loop for handling commands */
|
|
static int doCmds(func_args* args)
|
|
{
|
|
byte quit = 0;
|
|
int ret = WS_SUCCESS, err;
|
|
byte resume = 0;
|
|
int i;
|
|
|
|
do {
|
|
char msg[WOLFSSH_MAX_FILENAME * 2];
|
|
char* pt;
|
|
|
|
if (wolfSSH_get_error(ssh) == WS_SOCKET_ERROR_E) {
|
|
if (SFTP_FPUTS(args, "peer disconnected\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
return WS_SOCKET_ERROR_E;
|
|
}
|
|
|
|
if (SFTP_FPUTS(args, "wolfSSH sftp> ") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
WFFLUSH(stdout);
|
|
|
|
WMEMSET(msg, 0, sizeof(msg));
|
|
if (SFTP_FGETS(args, msg, sizeof(msg) - 1) == NULL) {
|
|
err_msg("fgets error");
|
|
return -1;
|
|
}
|
|
msg[WOLFSSH_MAX_FILENAME * 2 - 1] = '\0';
|
|
|
|
/* Anchored to the start of the line so a file name containing
|
|
* "creat" in another command does not match, and dispatched ahead
|
|
* of the unanchored substring matchers below so none of them can
|
|
* steal a creat line whose mode or path contains "get", "cd", etc.
|
|
* The keyword may be followed by a space, tab, or end of line; the
|
|
* bare-keyword form still reaches sftpParseModeAndPath so its
|
|
* empty-argument handling stays covered. */
|
|
pt = msg;
|
|
while (*pt == ' ' || *pt == '\t')
|
|
pt++;
|
|
if (WSTRNCMP(pt, "creat", 5) == 0 &&
|
|
(pt[5] == '\0' || pt[5] == ' ' || pt[5] == '\t' ||
|
|
pt[5] == '\n')) {
|
|
char* f = NULL;
|
|
char* path;
|
|
char mode[WOLFSSH_MAX_OCTET_LEN];
|
|
byte handle[WOLFSSH_MAX_HANDLE];
|
|
word32 handleSz = WOLFSSH_MAX_HANDLE;
|
|
WS_SFTP_FILEATRB atr;
|
|
int parseRet;
|
|
int openRet = WS_FATAL_ERROR;
|
|
unsigned long perVal;
|
|
char* modeEnd;
|
|
|
|
pt += 5;
|
|
while (*pt == ' ' || *pt == '\t')
|
|
pt++;
|
|
parseRet = sftpParseModeAndPath(pt, mode, &path, &f, workingDir);
|
|
if (parseRet == 1) {
|
|
printf("error with getting mode\r\n");
|
|
continue;
|
|
}
|
|
if (parseRet == -1) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
/* build permission attribute from octal mode string;
|
|
* wolfSSH_oct2dec is internal scope so strtoul is used here */
|
|
perVal = strtoul(mode, &modeEnd, 8);
|
|
if (*modeEnd == '\0' && perVal <= 07777) {
|
|
WMEMSET(&atr, 0, sizeof(WS_SFTP_FILEATRB));
|
|
atr.flags = WOLFSSH_FILEATRB_PERM;
|
|
atr.per = (word32)perVal;
|
|
|
|
/* open (create) remote file with the given permissions */
|
|
handleSz = WOLFSSH_MAX_HANDLE;
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
ret = wolfSSH_SFTP_Open(ssh, path,
|
|
WOLFSSH_FXF_WRITE | WOLFSSH_FXF_CREAT |
|
|
WOLFSSH_FXF_TRUNC, &atr, handle, &handleSz);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) && ret != WS_SUCCESS);
|
|
openRet = ret;
|
|
}
|
|
if (openRet == WS_SUCCESS) {
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
ret = wolfSSH_SFTP_Close(ssh, handle, handleSz);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) && ret != WS_SUCCESS);
|
|
if (ret != WS_SUCCESS) {
|
|
if (SFTP_FPUTS(args, "Unable to close file handle\n") < 0) {
|
|
err_msg("fputs error");
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (SFTP_FPUTS(args, "Unable to create file\n") < 0) {
|
|
err_msg("fputs error");
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
if ((pt = WSTRNSTR(msg, "mkdir", sizeof(msg))) != NULL) {
|
|
WS_SFTP_FILEATRB atrb;
|
|
int sz;
|
|
char* f = NULL;
|
|
|
|
pt += sizeof("mkdir");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL)
|
|
return WS_MEMORY_E;
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
WSTRNCAT(f, "/", maxSz);
|
|
WSTRNCAT(f, pt, maxSz);
|
|
|
|
pt = f;
|
|
}
|
|
|
|
do {
|
|
err = WS_SUCCESS;
|
|
if ((ret = wolfSSH_SFTP_MKDIR(ssh, pt, &atrb)) != WS_SUCCESS) {
|
|
err = wolfSSH_get_error(ssh);
|
|
if (ret == WS_PERMISSIONS) {
|
|
if (SFTP_FPUTS(args, "Insufficient permissions\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (err != WS_WANT_READ && err != WS_WANT_WRITE) {
|
|
if (SFTP_FPUTS(args, "Error writing directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE)
|
|
&& ret != WS_SUCCESS);
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
if (WSTRNSTR(msg, "reget", MAX_CMD_SZ) != NULL) {
|
|
resume = 1;
|
|
}
|
|
|
|
if ((pt = WSTRNSTR(msg, "get", MAX_CMD_SZ)) != NULL) {
|
|
int sz;
|
|
char* f = NULL;
|
|
char* to = NULL;
|
|
|
|
pt += sizeof("get");
|
|
|
|
sz = (int)WSTRLEN(pt);
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
|
|
/* search for space delimiter */
|
|
to = pt;
|
|
for (i = 0; i < sz; i++) {
|
|
to++;
|
|
if (pt[i] == ' ') {
|
|
pt[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check if local file path listed */
|
|
if (WSTRLEN(to) <= 0) {
|
|
|
|
to = pt;
|
|
/* if local path not listed follow path till at the tail */
|
|
for (i = 0; i < sz; i++) {
|
|
if (pt[i] == '/') {
|
|
to = pt + i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)(WSTRLEN(workingDir) + sz + 2);
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(f, "/", maxSz);
|
|
}
|
|
WSTRNCAT(f, pt, maxSz);
|
|
|
|
pt = f;
|
|
}
|
|
|
|
{
|
|
char buf[WOLFSSH_MAX_FILENAME * 3];
|
|
if (resume) {
|
|
WSNPRINTF(buf, sizeof(buf), "resuming %s to %s\n", pt, to);
|
|
}
|
|
else {
|
|
WSNPRINTF(buf, sizeof(buf), "fetching %s to %s\n", pt, to);
|
|
}
|
|
if (SFTP_FPUTS(args, buf) < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_Get(ssh, pt, to, resume, &myStatusCb);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
} while (ret == WS_WANT_READ || ret == WS_WANT_WRITE ||
|
|
ret == WS_CHAN_RXD || ret == WS_REKEYING);
|
|
|
|
#ifndef WOLFSSH_NO_TIMESTAMP
|
|
WMEMSET(currentFile, 0, sizeof(currentFile));
|
|
#endif
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
if (wolfSSH_get_error(ssh) == WS_SFTP_NOT_FILE_E) {
|
|
if (SFTP_FPUTS(args, "Not a regular file\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
if (SFTP_FPUTS(args, "Error getting file\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if (SFTP_FPUTS(args, "\n") < 0) { /* new line after status output */
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
resume = 0;
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
|
|
if (WSTRNSTR(msg, "reput", MAX_CMD_SZ) != NULL) {
|
|
resume = 1;
|
|
}
|
|
|
|
if ((pt = WSTRNSTR(msg, "put", MAX_CMD_SZ)) != NULL) {
|
|
int sz;
|
|
char* f = NULL;
|
|
char* to = NULL;
|
|
|
|
pt += sizeof("put");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
|
|
to = pt;
|
|
for (i = 0; i < sz; i++) {
|
|
to++;
|
|
if (pt[i] == ' ') {
|
|
pt[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check if local file path listed */
|
|
if (WSTRLEN(to) <= 0) {
|
|
|
|
to = pt;
|
|
/* if local path not listed follow path till at the tail */
|
|
for (i = 0; i < sz; i++) {
|
|
if (pt[i] == '/') {
|
|
to = pt + i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (to[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(f, "/", maxSz);
|
|
}
|
|
WSTRNCAT(f, to, maxSz);
|
|
|
|
to = f;
|
|
}
|
|
|
|
{
|
|
char buf[WOLFSSH_MAX_FILENAME * 3];
|
|
if (resume) {
|
|
WSNPRINTF(buf, sizeof(buf), "resuming %s to %s\n", pt, to);
|
|
}
|
|
else {
|
|
WSNPRINTF(buf, sizeof(buf), "pushing %s to %s\n", pt, to);
|
|
}
|
|
if (SFTP_FPUTS(args, buf) < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_Put(ssh, pt, to, resume, &myStatusCb);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
} while (ret == WS_WANT_READ || ret == WS_WANT_WRITE ||
|
|
ret == WS_CHAN_RXD || ret == WS_REKEYING);
|
|
|
|
#ifndef WOLFSSH_NO_TIMESTAMP
|
|
WMEMSET(currentFile, 0, sizeof(currentFile));
|
|
#endif
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
if (wolfSSH_get_error(ssh) == WS_SFTP_NOT_FILE_E) {
|
|
if (SFTP_FPUTS(args, "Not a regular file\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
if (SFTP_FPUTS(args, "Error pushing file\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if (SFTP_FPUTS(args, "\n") < 0) { /* new line after status output */
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
resume = 0;
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
/* lcd must be checked before cd so that WSTRNSTR
|
|
* does not match "cd" inside "lcd" */
|
|
#ifndef WOLFSSH_FATFS /* WCHDIR not yet defined for FatFS */
|
|
if ((pt = WSTRNSTR(msg, "lcd", MAX_CMD_SZ)) != NULL) {
|
|
int sz;
|
|
|
|
pt += sizeof("lcd");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') {
|
|
pt[sz - 1] = '\0';
|
|
}
|
|
|
|
if (WCHDIR(ssh->fs, pt) != 0) {
|
|
if (SFTP_FPUTS(args,
|
|
"Error changing local directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
#endif /* !WOLFSSH_FATFS */
|
|
|
|
if ((pt = WSTRNSTR(msg, "cd", MAX_CMD_SZ)) != NULL) {
|
|
WS_SFTP_FILEATRB atrb;
|
|
int sz;
|
|
char* f = NULL;
|
|
|
|
pt += sizeof("cd");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(f, "/", maxSz);
|
|
}
|
|
WSTRNCAT(f, pt, maxSz);
|
|
|
|
pt = f;
|
|
}
|
|
|
|
/* check directory is valid */
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_STAT(ssh, pt, &atrb);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) && ret != WS_SUCCESS);
|
|
if (ret != WS_SUCCESS) {
|
|
if (SFTP_FPUTS(args, "Error changing directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
sz = (int)WSTRLEN(pt);
|
|
WFREE(workingDir, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
workingDir = (char*)WMALLOC(sz + 1, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (workingDir == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
WMEMCPY(workingDir, pt, sz);
|
|
workingDir[sz] = '\0';
|
|
|
|
clean_path(workingDir);
|
|
}
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
if ((pt = WSTRNSTR(msg, "chmod", MAX_CMD_SZ)) != NULL) {
|
|
char* f = NULL;
|
|
char* path;
|
|
char mode[WOLFSSH_MAX_OCTET_LEN];
|
|
int parseRet;
|
|
|
|
pt += sizeof("chmod");
|
|
parseRet = sftpParseModeAndPath(pt, mode, &path, &f, workingDir);
|
|
if (parseRet == 1) {
|
|
printf("error with getting mode\r\n");
|
|
continue;
|
|
}
|
|
if (parseRet == -1) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
/* update permissions */
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_CHMOD(ssh, path, mode);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) && ret != WS_SUCCESS);
|
|
if (ret != WS_SUCCESS) {
|
|
if (SFTP_FPUTS(args, "Unable to change permissions of ") < 0) {
|
|
err_msg("fputs error");
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return -1;
|
|
}
|
|
if (SFTP_FPUTS(args, path) < 0) {
|
|
err_msg("fputs error");
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return -1;
|
|
}
|
|
if (SFTP_FPUTS(args, "\n") < 0) {
|
|
err_msg("fputs error");
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
if ((pt = WSTRNSTR(msg, "rmdir", MAX_CMD_SZ)) != NULL) {
|
|
int sz;
|
|
char* f = NULL;
|
|
|
|
pt += sizeof("rmdir");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(f, "/", maxSz);
|
|
}
|
|
WSTRNCAT(f, pt, maxSz);
|
|
|
|
pt = f;
|
|
}
|
|
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_RMDIR(ssh, pt);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) && ret != WS_SUCCESS);
|
|
if (ret != WS_SUCCESS) {
|
|
if (ret == WS_PERMISSIONS) {
|
|
if (SFTP_FPUTS(args, "Insufficient permissions\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if (SFTP_FPUTS(args, "Error writing directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
|
|
if ((pt = WSTRNSTR(msg, "rm", MAX_CMD_SZ)) != NULL) {
|
|
int sz;
|
|
char* f = NULL;
|
|
|
|
pt += sizeof("rm");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(f, "/", maxSz);
|
|
}
|
|
WSTRNCAT(f, pt, maxSz);
|
|
|
|
pt = f;
|
|
}
|
|
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_Remove(ssh, pt);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) && ret != WS_SUCCESS);
|
|
if (ret != WS_SUCCESS) {
|
|
if (ret == WS_PERMISSIONS) {
|
|
if (SFTP_FPUTS(args, "Insufficient permissions\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if (SFTP_FPUTS(args, "Error writing directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
}
|
|
|
|
if ((pt = WSTRNSTR(msg, "rename", MAX_CMD_SZ)) != NULL) {
|
|
int sz;
|
|
char* f = NULL;
|
|
char* fTo = NULL;
|
|
char* to;
|
|
int toSz;
|
|
|
|
pt += sizeof("rename");
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
|
|
|
|
/* search for space delimiter */
|
|
to = pt;
|
|
for (i = 0; i < sz; i++) {
|
|
to++;
|
|
if (pt[i] == ' ') {
|
|
pt[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
if ((toSz = (int)WSTRLEN(to)) <= 0 || i == sz) {
|
|
printf("bad usage, expected <old> <new> input\n");
|
|
continue;
|
|
}
|
|
sz = (int)WSTRLEN(pt);
|
|
|
|
if (pt[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
|
|
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (f == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
return -1;
|
|
}
|
|
|
|
f[0] = '\0';
|
|
WSTRNCAT(f, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(f, "/", maxSz);
|
|
}
|
|
WSTRNCAT(f, pt, maxSz);
|
|
|
|
pt = f;
|
|
}
|
|
if (to[0] != '/') {
|
|
int maxSz = (int)WSTRLEN(workingDir) + toSz + 2;
|
|
fTo = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (fTo == NULL) {
|
|
err_msg("Error malloc'ing");
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return -1;
|
|
}
|
|
|
|
fTo[0] = '\0';
|
|
WSTRNCAT(fTo, workingDir, maxSz);
|
|
if (WSTRLEN(workingDir) > 1) {
|
|
WSTRNCAT(fTo, "/", maxSz);
|
|
}
|
|
WSTRNCAT(fTo, to, maxSz);
|
|
|
|
to = fTo;
|
|
}
|
|
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
ret = wolfSSH_SFTP_Rename(ssh, pt, to);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
} while (ret == WS_WANT_READ || ret == WS_WANT_WRITE ||
|
|
ret == WS_CHAN_RXD || ret == WS_REKEYING);
|
|
if (ret != WS_SUCCESS) {
|
|
if (SFTP_FPUTS(args, "Error with rename\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
WFREE(fTo, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
continue;
|
|
|
|
}
|
|
|
|
#if !defined(NO_WOLFSSH_DIR) && !defined(WOLFSSH_FATFS)
|
|
/* lls must be checked before ls so that WSTRNSTR
|
|
* does not match "ls" inside "lls" */
|
|
if (WSTRNSTR(msg, "lls", MAX_CMD_SZ) != NULL) {
|
|
char cwd[WOLFSSH_MAX_FILENAME];
|
|
int llsErr = 0;
|
|
|
|
#ifdef WOLFSSH_ZEPHYR
|
|
WSTRNCPY(cwd, CONFIG_WOLFSSH_SFTP_DEFAULT_DIR,
|
|
sizeof(cwd));
|
|
#else
|
|
if (WGETCWD(ssh->fs, cwd, sizeof(cwd)) == NULL) {
|
|
if (SFTP_FPUTS(args,
|
|
"Error getting local directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_WINDOWS_API
|
|
{
|
|
WDIR findHandle;
|
|
char name[MAX_PATH];
|
|
char fileName[MAX_PATH];
|
|
|
|
WSTRNCPY(name, cwd, MAX_PATH);
|
|
WSTRNCAT(name, "\\*", MAX_PATH);
|
|
findHandle = (HANDLE)WS_FindFirstFileA(name,
|
|
fileName, sizeof(fileName), NULL, NULL);
|
|
if (findHandle == INVALID_HANDLE_VALUE) {
|
|
if (SFTP_FPUTS(args,
|
|
"Error opening local directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
do {
|
|
if (SFTP_FPUTS(args, fileName) < 0 ||
|
|
SFTP_FPUTS(args, "\n") < 0) {
|
|
err_msg("fputs error");
|
|
llsErr = 1;
|
|
break;
|
|
}
|
|
} while (WS_FindNextFileA(findHandle,
|
|
fileName, sizeof(fileName)));
|
|
FindClose(findHandle);
|
|
}
|
|
#elif defined(WOLFSSH_ZEPHYR)
|
|
{
|
|
WDIR dir;
|
|
struct fs_dirent dp;
|
|
|
|
if (WOPENDIR(ssh->fs, NULL, &dir, cwd) != 0) {
|
|
if (SFTP_FPUTS(args,
|
|
"Error opening local directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
while (fs_readdir(&dir, &dp) == 0 &&
|
|
dp.name[0] != '\0') {
|
|
if (SFTP_FPUTS(args, dp.name) < 0 ||
|
|
SFTP_FPUTS(args, "\n") < 0) {
|
|
err_msg("fputs error");
|
|
llsErr = 1;
|
|
break;
|
|
}
|
|
}
|
|
WCLOSEDIR(ssh->fs, &dir);
|
|
}
|
|
#else
|
|
{
|
|
WDIR dir;
|
|
struct dirent* dp;
|
|
|
|
if (WOPENDIR(ssh->fs, NULL, &dir, cwd) != 0) {
|
|
if (SFTP_FPUTS(args,
|
|
"Error opening local directory\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
while ((dp = WREADDIR(ssh->fs, &dir)) != NULL) {
|
|
if (SFTP_FPUTS(args, dp->d_name) < 0 ||
|
|
SFTP_FPUTS(args, "\n") < 0) {
|
|
err_msg("fputs error");
|
|
llsErr = 1;
|
|
break;
|
|
}
|
|
}
|
|
WCLOSEDIR(ssh->fs, &dir);
|
|
}
|
|
#endif
|
|
if (llsErr) {
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
#endif /* !NO_WOLFSSH_DIR && !WOLFSSH_FATFS */
|
|
|
|
if (WSTRNSTR(msg, "ls", MAX_CMD_SZ) != NULL) {
|
|
WS_SFTPNAME* tmp;
|
|
WS_SFTPNAME* current;
|
|
|
|
do {
|
|
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
|
|
ret = wolfSSH_get_error(ssh);
|
|
}
|
|
}
|
|
|
|
current = wolfSSH_SFTP_LS(ssh, workingDir);
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_REKEYING) &&
|
|
(current == NULL && err != WS_SUCCESS));
|
|
|
|
if (WSTRNSTR(msg, "-s", MAX_CMD_SZ) != NULL) {
|
|
char tmpStr[WOLFSSH_MAX_FILENAME];
|
|
XMEMSET(tmpStr, 0, WOLFSSH_MAX_FILENAME);
|
|
XSNPRINTF(tmpStr, WOLFSSH_MAX_FILENAME, "size in bytes, file name\n");
|
|
if (SFTP_FPUTS(args, tmpStr) < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
tmp = current;
|
|
while (tmp != NULL) {
|
|
if (WSTRNSTR(msg, "-s", MAX_CMD_SZ) != NULL) {
|
|
char tmpStr[WOLFSSH_MAX_FILENAME];
|
|
XSNPRINTF(tmpStr, WOLFSSH_MAX_FILENAME, "%lld, ",
|
|
(long long)(((long long)tmp->atrb.sz[1] << 32) | tmp->atrb.sz[0]));
|
|
if (SFTP_FPUTS(args, tmpStr) < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (SFTP_FPUTS(args, tmp->fName) < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
if (SFTP_FPUTS(args, "\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
wolfSSH_SFTPNAME_list_free(current);
|
|
continue;
|
|
}
|
|
|
|
/* display current working directory */
|
|
if (WSTRNSTR(msg, "pwd", MAX_CMD_SZ) != NULL) {
|
|
if (SFTP_FPUTS(args, workingDir) < 0 ||
|
|
SFTP_FPUTS(args, "\n") < 0) {
|
|
err_msg("fputs error");
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (WSTRNSTR(msg, "help", MAX_CMD_SZ) != NULL) {
|
|
ShowCommands();
|
|
continue;
|
|
}
|
|
|
|
if (WSTRNSTR(msg, "quit", MAX_CMD_SZ) != NULL) {
|
|
quit = 1;
|
|
continue;
|
|
}
|
|
|
|
if (WSTRNSTR(msg, "exit", MAX_CMD_SZ) != NULL) {
|
|
quit = 1;
|
|
continue;
|
|
}
|
|
|
|
SFTP_FPUTS(args, "Unknown command\n");
|
|
} while (!quit);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Resolve the CWD via RealPath (passing "." to get the current directory)
|
|
* and append the relative remote path into fullpath. WS_REKEYING and
|
|
* WS_WINDOW_FULL are intentionally excluded from the retry condition below;
|
|
* they require a wolfSSH_worker call that the caller's outer loop provides. */
|
|
static int doBuildRemotePath(const char* remote, char* fullpath, word32 fullpathSz,
|
|
WS_SFTPNAME** nameOut)
|
|
{
|
|
int err;
|
|
|
|
if (remote == NULL || fullpath == NULL || fullpathSz == 0 || nameOut == NULL)
|
|
return WS_BAD_ARGUMENT;
|
|
do {
|
|
*nameOut = wolfSSH_SFTP_RealPath(ssh, (char*)".");
|
|
err = wolfSSH_get_error(ssh);
|
|
} while (err == WS_WANT_READ || err == WS_WANT_WRITE);
|
|
if (*nameOut == NULL)
|
|
return (err == WS_SUCCESS) ? WS_FATAL_ERROR : err;
|
|
if (snprintf(fullpath, fullpathSz, "%s/%s", (*nameOut)->fName, remote)
|
|
>= (int)fullpathSz) {
|
|
wolfSSH_SFTPNAME_list_free(*nameOut);
|
|
*nameOut = NULL;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* alternate main loop for the autopilot get/receive */
|
|
static int doAutopilot(int cmd, char* local, char* remote)
|
|
{
|
|
int err = 0;
|
|
int ret = WS_SUCCESS;
|
|
char fullpath[128] = ".";
|
|
WS_SFTPNAME* name = NULL;
|
|
word32 remoteSz;
|
|
byte remoteAbsPath = 0;
|
|
|
|
/* check if is absolute path before making it one */
|
|
if (remote != NULL && WSTRLEN(remote) > 2 && remote[1] == ':' &&
|
|
remote[2] == '\\') {
|
|
remoteAbsPath = 1;
|
|
}
|
|
else if (remote != NULL && WSTRLEN(remote) > 2 && remote[1] == ':' &&
|
|
remote[2] == '/') {
|
|
remoteAbsPath = 1;
|
|
}
|
|
else if (remote != NULL && remote[0] == '/') {
|
|
remoteAbsPath = 1;
|
|
}
|
|
|
|
if (remoteAbsPath) {
|
|
/* use remote absolute path if provided */
|
|
remoteSz = (word32)WSTRLEN(remote);
|
|
if (remoteSz >= sizeof(fullpath))
|
|
return WS_BUFFER_E;
|
|
WMEMSET(fullpath, 0, sizeof(fullpath));
|
|
WMEMCPY(fullpath, remote, remoteSz + 1);
|
|
}
|
|
else {
|
|
err = doBuildRemotePath(remote, fullpath, sizeof(fullpath), &name);
|
|
if (err != WS_SUCCESS && err != WS_REKEYING && err != WS_WINDOW_FULL)
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
do {
|
|
if (err == WS_REKEYING || err == WS_WINDOW_FULL) { /* handle rekeying and window-full state */
|
|
do {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
} while (ret == WS_REKEYING || ret == WS_WINDOW_FULL ||
|
|
wolfSSH_get_error(ssh) == WS_REKEYING);
|
|
|
|
/* wolfSSH_worker returns WS_FATAL_ERROR when the socket
|
|
* would block (DoReceive -> GetInputData -> WS_WANT_READ),
|
|
* so check ssh->error rather than ret for blocking conditions. */
|
|
if (ret != WS_SUCCESS && ret != WS_CHAN_RXD && ret != WS_EOF &&
|
|
wolfSSH_get_error(ssh) != WS_WANT_READ &&
|
|
wolfSSH_get_error(ssh) != WS_WANT_WRITE)
|
|
break;
|
|
|
|
if (!remoteAbsPath && name == NULL) {
|
|
err = doBuildRemotePath(remote, fullpath, sizeof(fullpath), &name);
|
|
if (err != WS_SUCCESS) {
|
|
ret = WS_FATAL_ERROR;
|
|
continue; /* skip wolfSSH_get_error; outer loop retries on err from doBuildRemotePath */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cmd == AUTOPILOT_PUT) {
|
|
ret = wolfSSH_SFTP_Put(ssh, local, fullpath, 0, NULL);
|
|
}
|
|
else if (cmd == AUTOPILOT_GET) {
|
|
ret = wolfSSH_SFTP_Get(ssh, fullpath, local, 0, NULL);
|
|
}
|
|
err = wolfSSH_get_error(ssh);
|
|
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
|
|
err == WS_CHAN_RXD || err == WS_REKEYING ||
|
|
err == WS_WINDOW_FULL) &&
|
|
ret == WS_FATAL_ERROR);
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
if (cmd == AUTOPILOT_PUT) {
|
|
fprintf(stderr, "Unable to copy local file %s to remote file %s\n",
|
|
local, fullpath);
|
|
}
|
|
else if (cmd == AUTOPILOT_GET) {
|
|
fprintf(stderr, "Unable to copy remote file %s to local file %s\n",
|
|
fullpath, local);
|
|
}
|
|
}
|
|
|
|
wolfSSH_SFTPNAME_list_free(name);
|
|
return ret;
|
|
}
|
|
|
|
|
|
THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
|
|
{
|
|
WOLFSSH_CTX* ctx = NULL;
|
|
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
|
|
SOCKADDR_IN_T clientAddr;
|
|
socklen_t clientAddrSz = sizeof(clientAddr);
|
|
int ret;
|
|
int ch;
|
|
int userEcc = 0;
|
|
/* int peerEcc = 0; */
|
|
word16 port = wolfSshPort;
|
|
char* host = (char*)wolfSshIp;
|
|
const char* username = NULL;
|
|
const char* password = NULL;
|
|
const char* defaultSftpPath = NULL;
|
|
const char* privKeyName = NULL;
|
|
byte nonBlock = 0;
|
|
int autopilot = AUTOPILOT_OFF;
|
|
char* apLocal = NULL;
|
|
char* apRemote = NULL;
|
|
char* pubKeyName = NULL;
|
|
char* certName = NULL;
|
|
char* caCert = NULL;
|
|
const char* keyList = NULL;
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
const char* certStoreSpec = NULL; /* Format: "store:subject:flags" */
|
|
#endif /* WOLFSSH_WINDOWS_CERT_STORE */
|
|
SFTPC_HEAP_HINT* heap = NULL;
|
|
|
|
int argc = ((func_args*)args)->argc;
|
|
char** argv = ((func_args*)args)->argv;
|
|
((func_args*)args)->return_code = 0;
|
|
|
|
while ((ch = mygetopt(argc, argv, SFTPC_OPTLIST)) != -1) {
|
|
switch (ch) {
|
|
case 'd':
|
|
defaultSftpPath = myoptarg;
|
|
break;
|
|
|
|
case 'E':
|
|
/* peerEcc = 1; */
|
|
err_sys("wolfSFTP ECC server authentication "
|
|
"not yet supported.");
|
|
break;
|
|
|
|
case 'h':
|
|
host = myoptarg;
|
|
break;
|
|
|
|
case 'p':
|
|
if (myoptarg == NULL)
|
|
err_sys("null argument found");
|
|
port = (word16)atoi(myoptarg);
|
|
#if !defined(NO_MAIN_DRIVER) || defined(USE_WINDOWS_API)
|
|
if (port == 0)
|
|
err_sys("port number cannot be 0");
|
|
#endif
|
|
break;
|
|
|
|
case 'u':
|
|
username = myoptarg;
|
|
break;
|
|
|
|
case 'l':
|
|
apLocal = myoptarg;
|
|
break;
|
|
|
|
case 'r':
|
|
apRemote = myoptarg;
|
|
break;
|
|
|
|
case 'g':
|
|
autopilot = AUTOPILOT_PUT;
|
|
break;
|
|
|
|
case 'G':
|
|
autopilot = AUTOPILOT_GET;
|
|
break;
|
|
|
|
case 'P':
|
|
password = myoptarg;
|
|
break;
|
|
|
|
case 'N':
|
|
nonBlock = 1;
|
|
break;
|
|
|
|
case 'i':
|
|
privKeyName = myoptarg;
|
|
break;
|
|
|
|
case 'j':
|
|
pubKeyName = myoptarg;
|
|
break;
|
|
|
|
case 'k':
|
|
keyList = myoptarg;
|
|
break;
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
case 'J':
|
|
certName = myoptarg;
|
|
break;
|
|
|
|
case 'A':
|
|
caCert = myoptarg;
|
|
break;
|
|
|
|
#if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME)
|
|
case 'X':
|
|
ClientIPOverride(1);
|
|
break;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
case 'W':
|
|
certStoreSpec = myoptarg;
|
|
break;
|
|
#endif /* WOLFSSH_WINDOWS_CERT_STORE */
|
|
|
|
case '?':
|
|
ShowUsage();
|
|
exit(EXIT_SUCCESS);
|
|
|
|
default:
|
|
ShowUsage();
|
|
exit(MY_EX_USAGE);
|
|
}
|
|
}
|
|
myoptind = 0; /* reset for test cases */
|
|
|
|
if (username == NULL)
|
|
err_sys("client requires a username parameter.");
|
|
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
if (certStoreSpec != NULL && (privKeyName != NULL || pubKeyName != NULL ||
|
|
certName != NULL)) {
|
|
err_sys("-W provides both keys, it can not be used with -i, -j "
|
|
"or -J.");
|
|
}
|
|
#endif /* WOLFSSH_WINDOWS_CERT_STORE */
|
|
|
|
if ((pubKeyName == NULL && certName == NULL) && privKeyName != NULL) {
|
|
err_sys("If setting priv key, need pub key.");
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_NO_RSA
|
|
userEcc = 1;
|
|
/* peerEcc = 1; */
|
|
#endif
|
|
|
|
if (autopilot != AUTOPILOT_OFF) {
|
|
if (apLocal == NULL || apRemote == NULL) {
|
|
err_sys("Options -G and -g require both -l and -r.");
|
|
}
|
|
}
|
|
|
|
#ifdef WOLFSSH_TEST_BLOCK
|
|
if (!nonBlock) {
|
|
err_sys("Use -N when testing forced non blocking");
|
|
}
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_STATIC_MEMORY
|
|
ret = wc_LoadStaticMemory_ex(&heap,
|
|
SFTPC_STATIC_LISTSZ, static_sizeList, static_distList,
|
|
static_buffer, sizeof(static_buffer),
|
|
WOLFMEM_GENERAL, 0);
|
|
if (ret != 0) {
|
|
err_sys("Couldn't set up static memory pool.\n");
|
|
}
|
|
#endif /* WOLFSSH_STATIC_MEMORY */
|
|
|
|
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
if (certStoreSpec != NULL) {
|
|
wchar_t* wStoreName = NULL;
|
|
wchar_t* wSubjectName = NULL;
|
|
word32 dwFlags = 0;
|
|
|
|
ret = wolfSSH_ParseCertStoreSpec(certStoreSpec, &wStoreName,
|
|
&wSubjectName, &dwFlags, heap);
|
|
if (ret != WS_SUCCESS) {
|
|
err_sys("Invalid cert store spec. Use: store:subject:flags");
|
|
}
|
|
|
|
/* Create context first */
|
|
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, heap);
|
|
if (ctx == NULL) {
|
|
wolfSSH_FreeCertStoreSpec(wStoreName, wSubjectName, heap);
|
|
err_sys("Couldn't create wolfSSH client context.");
|
|
}
|
|
|
|
/* Set private key from cert store. The names are only needed for
|
|
* this call, so release them here and leave the error paths with
|
|
* nothing to clean up. */
|
|
ret = ClientSetPrivateKeyFromStore(ctx, wStoreName, dwFlags,
|
|
wSubjectName);
|
|
wolfSSH_FreeCertStoreSpec(wStoreName, wSubjectName, heap);
|
|
wStoreName = NULL;
|
|
wSubjectName = NULL;
|
|
if (ret != WS_SUCCESS) {
|
|
wolfSSH_CTX_free(ctx);
|
|
err_sys("Error setting private key from certificate store");
|
|
}
|
|
|
|
/* Set up auth callback globals (public key type, cert DER) so
|
|
* that ClientUserAuth presents the certificate for public key
|
|
* authentication. */
|
|
ret = ClientSetupCertStoreAuth(ctx, heap);
|
|
if (ret != WS_SUCCESS) {
|
|
wolfSSH_CTX_free(ctx);
|
|
err_sys("Error setting up cert store auth");
|
|
}
|
|
} else
|
|
#endif /* WOLFSSH_WINDOWS_CERT_STORE */
|
|
{
|
|
ret = ClientSetPrivateKey(privKeyName, userEcc, heap, NULL);
|
|
if (ret != 0) {
|
|
err_sys("Error setting private key");
|
|
}
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
/* passed in certificate to use */
|
|
if (certName) {
|
|
ret = ClientUseCert(certName, heap);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ret = ClientUsePubKey(pubKeyName, userEcc, heap);
|
|
}
|
|
if (ret != 0) {
|
|
err_sys("Error setting public key");
|
|
}
|
|
|
|
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, heap);
|
|
}
|
|
if (ctx == NULL)
|
|
err_sys("Couldn't create wolfSSH client context.");
|
|
|
|
if (keyList) {
|
|
if (wolfSSH_CTX_SetAlgoListKey(ctx, keyList) != WS_SUCCESS) {
|
|
err_sys("Error setting key list.");
|
|
}
|
|
}
|
|
|
|
if (((func_args*)args)->user_auth == NULL)
|
|
wolfSSH_SetUserAuth(ctx, ClientUserAuth);
|
|
else
|
|
wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth);
|
|
|
|
#if !defined(WS_NO_SIGNAL) && !defined(USE_WINDOWS_API)
|
|
/* handle interrupt with get and put */
|
|
signal(SIGINT, sig_handler);
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
ClientLoadCA(ctx, caCert);
|
|
#else
|
|
(void)caCert;
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
wolfSSH_CTX_SetPublicKeyCheck(ctx, ClientPublicKeyCheck);
|
|
|
|
ssh = wolfSSH_new(ctx);
|
|
if (ssh == NULL)
|
|
err_sys("Couldn't create wolfSSH session.");
|
|
|
|
if (defaultSftpPath != NULL) {
|
|
if (wolfSSH_SFTP_SetDefaultPath(ssh, defaultSftpPath)
|
|
!= WS_SUCCESS) {
|
|
fprintf(stderr, "Couldn't store default sftp path.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (password != NULL)
|
|
wolfSSH_SetUserAuthCtx(ssh, (void*)password);
|
|
|
|
wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)host);
|
|
|
|
ret = wolfSSH_SetUsername(ssh, username);
|
|
if (ret != WS_SUCCESS)
|
|
err_sys("Couldn't set the username.");
|
|
|
|
build_addr(&clientAddr, host, port);
|
|
tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
|
|
|
|
ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
|
|
if (ret != 0)
|
|
err_sys("Couldn't connect to server.");
|
|
|
|
if (nonBlock)
|
|
tcp_set_nonblocking(&sockFd);
|
|
|
|
ret = wolfSSH_set_fd(ssh, (int)sockFd);
|
|
if (ret != WS_SUCCESS)
|
|
err_sys("Couldn't set the session's socket.");
|
|
|
|
if (!nonBlock)
|
|
ret = wolfSSH_SFTP_connect(ssh);
|
|
else
|
|
ret = NonBlockSSH_connect();
|
|
if (ret != WS_SUCCESS) {
|
|
/* the return is a generic failure code; the real cause is kept in
|
|
* the session error */
|
|
int err;
|
|
|
|
err = wolfSSH_get_error(ssh);
|
|
fprintf(stderr, "wolfSSH_SFTP_connect failed: %d, %s\n", err,
|
|
wolfSSH_ErrorToName(err));
|
|
err_sys("Couldn't connect SFTP");
|
|
}
|
|
|
|
{
|
|
/* get current working directory */
|
|
WS_SFTPNAME* n = NULL;
|
|
|
|
do {
|
|
n = wolfSSH_SFTP_RealPath(ssh, (char*)".");
|
|
ret = wolfSSH_get_error(ssh);
|
|
} while (ret == WS_WANT_READ || ret == WS_WANT_WRITE);
|
|
if (n == NULL) {
|
|
err_sys("Unable to get real path for working directory");
|
|
}
|
|
|
|
workingDir = (char*)WMALLOC(n->fSz + 1, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (workingDir == NULL) {
|
|
err_sys("Unable to create working directory");
|
|
}
|
|
WMEMCPY(workingDir, n->fName, n->fSz);
|
|
workingDir[n->fSz] = '\0';
|
|
|
|
/* free after done with names */
|
|
wolfSSH_SFTPNAME_list_free(n);
|
|
n = NULL;
|
|
}
|
|
|
|
if (autopilot == AUTOPILOT_OFF) {
|
|
ret = doCmds((func_args*)args);
|
|
}
|
|
else {
|
|
ret = doAutopilot(autopilot, apLocal, apRemote);
|
|
}
|
|
|
|
WFREE(workingDir, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (ret == WS_SUCCESS) {
|
|
int err;
|
|
ret = wolfSSH_shutdown(ssh);
|
|
|
|
/* peer hung up or channel already closed, stop trying.
|
|
* wolfSSH_shutdown() folds a peer EOF into WS_SUCCESS itself, so
|
|
* there is no WS_EOF to test for here. */
|
|
if (ret == WS_SOCKET_ERROR_E || ret == WS_ERROR ||
|
|
ret == WS_CHANNEL_CLOSED) {
|
|
ret = 0;
|
|
}
|
|
|
|
err = wolfSSH_get_error(ssh);
|
|
if (err != WS_SOCKET_ERROR_E &&
|
|
(err == WS_WANT_READ || err == WS_WANT_WRITE)) {
|
|
int maxAttempt = 10; /* make 10 attempts max before giving up */
|
|
int attempt;
|
|
|
|
for (attempt = 0; attempt < maxAttempt; attempt++) {
|
|
ret = wolfSSH_worker(ssh, NULL);
|
|
err = wolfSSH_get_error(ssh);
|
|
|
|
/* peer successfully closed down gracefully */
|
|
if (ret == WS_CHANNEL_CLOSED || ret == WS_EOF) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
/* peer hung up, stop shutdown */
|
|
if (ret == WS_SOCKET_ERROR_E || ret == WS_ERROR) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
if (err == WS_WANT_READ || err == WS_WANT_WRITE) {
|
|
/* Wanting read or wanting write. Clear ret. */
|
|
ret = 0;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (attempt == maxAttempt) {
|
|
printf("SFTP client gave up on graceful shutdown,"
|
|
"closing the socket\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
WCLOSESOCKET(sockFd);
|
|
wolfSSH_free(ssh);
|
|
|
|
/* ClientFreeBuffers() releases the certificate copy
|
|
* ClientSetupCertStoreAuth() made; it must use the same heap that was
|
|
* passed there. */
|
|
ClientFreeBuffers(pubKeyName, privKeyName, heap);
|
|
wolfSSH_CTX_free(ctx);
|
|
if (ret != WS_SUCCESS) {
|
|
printf("error %d encountered\n", ret);
|
|
((func_args*)args)->return_code = ret;
|
|
}
|
|
|
|
#if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS)
|
|
wc_ecc_fp_free(); /* free per thread cache */
|
|
#endif
|
|
|
|
WOLFSSL_RETURN_FROM_THREAD(0);
|
|
}
|
|
|
|
#else
|
|
|
|
THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
|
|
{
|
|
#ifdef NO_WOLFSSH_CLIENT
|
|
printf("NO_WOLFSSH_CLIENT macro was used. Can not have a client example\n");
|
|
#else
|
|
printf("Not compiled in!\n"
|
|
"Please recompile with WOLFSSH_SFTP or --enable-sftp\n");
|
|
#endif
|
|
(void)args;
|
|
WOLFSSL_RETURN_FROM_THREAD(0);
|
|
}
|
|
#endif /* WOLFSSH_SFTP */
|
|
|
|
#ifndef NO_MAIN_DRIVER
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
#ifdef WOLFSSH_FATFS
|
|
FATFS fs;
|
|
#endif
|
|
func_args args;
|
|
|
|
args.argc = argc;
|
|
args.argv = argv;
|
|
args.return_code = 0;
|
|
args.user_auth = NULL;
|
|
#ifdef WOLFSSH_SFTP
|
|
args.sftp_cb = NULL;
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_FATFS
|
|
if (f_mount(&fs, "0:", 1) != FR_OK) {
|
|
fprintf(stderr, "Failed to mount filesystem\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
WSTARTTCP();
|
|
|
|
#ifdef DEBUG_WOLFSSH
|
|
wolfSSH_Debugging_ON();
|
|
#endif
|
|
|
|
wolfSSH_Init();
|
|
|
|
{
|
|
int useStore = 0;
|
|
#ifdef WOLFSSH_WINDOWS_CERT_STORE
|
|
{
|
|
int ch;
|
|
|
|
/* With -W the keys come from the Windows certificate store
|
|
* and no file-based keys are needed, so skip the root
|
|
* directory search. Parse rather than match on argv, an
|
|
* option value could start with "-W". */
|
|
myoptind = 0;
|
|
while ((ch = mygetopt(argc, argv, SFTPC_OPTLIST)) != -1) {
|
|
if (ch == 'W') {
|
|
useStore = 1;
|
|
break;
|
|
}
|
|
}
|
|
myoptind = 0;
|
|
}
|
|
#endif
|
|
if (!useStore) {
|
|
ChangeToWolfSshRoot();
|
|
}
|
|
}
|
|
sftpclient_test(&args);
|
|
|
|
wolfSSH_Cleanup();
|
|
|
|
#ifdef WOLFSSH_FATFS
|
|
f_mount(NULL, "0:", 1);
|
|
#endif
|
|
|
|
#if defined(WOLFSSH_SFTP) && !defined(NO_WOLFSSH_CLIENT)
|
|
return args.return_code;
|
|
#else
|
|
return -1; /* return error when not compiled in */
|
|
#endif
|
|
}
|
|
|
|
int myoptind = 0;
|
|
char* myoptarg = NULL;
|
|
|
|
#endif /* NO_MAIN_DRIVER */
|