From db09f672f24ce9a2256a2be482f6805f43279b64 Mon Sep 17 00:00:00 2001 From: Jacob Barthelmeh Date: Thu, 27 Dec 2018 16:15:15 -0700 Subject: [PATCH] add -N flag to SFTP client --- src/wolfsftp.c | 90 ++++++++++++++------- wolfsftp/client/sftpclient.c | 148 +++++++++++++++++++++++++++++------ wolfssh/internal.h | 1 + wolfssh/wolfsftp.h | 1 + 4 files changed, 184 insertions(+), 56 deletions(-) diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 80c88ee..eb0fa32 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -298,7 +298,7 @@ int wolfSSH_SFTP_accept(WOLFSSH* ssh) if ((ssh->error = SFTP_ServerRecvInit(ssh)) != WS_SUCCESS) { return WS_FATAL_ERROR; } - ssh->connectState = SFTP_RECV; + ssh->sftpState = SFTP_RECV; FALL_THROUGH; /* no break */ @@ -306,7 +306,7 @@ int wolfSSH_SFTP_accept(WOLFSSH* ssh) if ((ssh->error = SFTP_ServerSendInit(ssh)) != WS_SUCCESS) { return WS_FATAL_ERROR; } - ssh->connectState = SFTP_DONE; + ssh->sftpState = SFTP_DONE; WLOG(WS_LOG_SFTP, "SFTP connection established"); break; @@ -2735,33 +2735,59 @@ static int SFTP_ClientRecvInit(WOLFSSH* ssh) { 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; - } + switch (ssh->sftpState) { + case SFTP_RECV: + if ((len = wolfSSH_stream_read(ssh, buf, sizeof(buf))) + != sizeof(buf)) { + /* @TODO partial read on small packet */ + return len; + } - ato32(buf, &sz); - if (sz < MSG_ID_SZ + UINT32_SZ) { - return WS_BUFFER_E; - } + 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; - } + /* 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); + ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version); + sz = sz - MSG_ID_SZ - UINT32_SZ; + ssh->sftpExtSz = sz; + ssh->sftpState = SFTP_EXT; + FALL_THROUGH; + /* no break */ + + case SFTP_EXT: + /* silently ignore extensions if not supported */ + if (ssh->sftpExtSz > 0) { + byte* data = (byte*)WMALLOC(ssh->sftpExtSz, ssh->ctx->heap, + DYNTYPE_BUFFER); + if (data == NULL) return WS_MEMORY_E; + if ((len = wolfSSH_stream_read(ssh, data, ssh->sftpExtSz)) + <= 0) { + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + return len; + } + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + + /* case where expecting more */ + if (len < ssh->sftpExtSz) { + ssh->sftpExtSz -= len; + ssh->error = WS_WANT_READ; + return WS_FATAL_ERROR; + } + } + break; + + default: + WLOG(WS_LOG_SFTP, "Unexpected SFTP connect state"); + 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++; @@ -2791,16 +2817,19 @@ static int SFTP_ClientSendInit(WOLFSSH* ssh) { /* Completes SFTP connection to server - * returns WS_SFTP_COMPLETE on success + * returns WS_SUCCESS on success */ int wolfSSH_SFTP_connect(WOLFSSH* ssh) { - int ret = WS_SFTP_COMPLETE; + int ret = WS_SUCCESS; if (ssh == NULL) { return WS_BAD_ARGUMENT; } + if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE) + ssh->error = WS_SUCCESS; + /* check connect is done, if not call wolfSSH connect */ if (ssh->connectState < CONNECT_SERVER_CHANNEL_REQUEST_DONE) { byte name[] = "sftp"; @@ -2822,15 +2851,16 @@ int wolfSSH_SFTP_connect(WOLFSSH* ssh) if ((ssh->error = SFTP_ClientSendInit(ssh)) != WS_SUCCESS) { return WS_FATAL_ERROR; } - ssh->connectState = SFTP_RECV; + ssh->sftpState = SFTP_RECV; FALL_THROUGH; /* no break */ case SFTP_RECV: + case SFTP_EXT: if ((ssh->error = SFTP_ClientRecvInit(ssh)) != WS_SUCCESS) { return WS_FATAL_ERROR; } - ssh->connectState = SFTP_DONE; + ssh->sftpState = SFTP_DONE; WLOG(WS_LOG_SFTP, "SFTP connection established"); break; @@ -2891,7 +2921,7 @@ int SendPacketType(WOLFSSH* ssh, byte type, byte* buf, word32 bufSz) return WS_BAD_ARGUMENT; } - if (ssh->connectState != SFTP_DONE) { + if (ssh->sftpState != SFTP_DONE) { WLOG(WS_LOG_SFTP, "SFTP connection not complete, trying to finish"); ret = wolfSSH_SFTP_negotiate(ssh); } diff --git a/wolfsftp/client/sftpclient.c b/wolfsftp/client/sftpclient.c index 59e9be6..55a0284 100644 --- a/wolfsftp/client/sftpclient.c +++ b/wolfsftp/client/sftpclient.c @@ -34,6 +34,7 @@ int doCmds(void); +/* static so that signal handler can access and interrupt get/put */ static WOLFSSH* ssh = NULL; static char* workingDir; WFILE* fin; @@ -49,6 +50,43 @@ static void myStatusCb(WOLFSSH* sshIn, long bytes, char* 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)) + { + 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) + { + ret = wolfSSH_SFTP_connect(ssh); + error = wolfSSH_get_error(ssh); + } + 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 */ @@ -230,6 +268,7 @@ static void ShowUsage(void) printf(" -p port to connect on, default %d\n", wolfSshPort); printf(" -u username to authenticate as (REQUIRED)\n"); printf(" -P password for username, prompted if omitted\n"); + printf(" -N use non blocking sockets\n"); ShowCommands(); } @@ -282,12 +321,13 @@ static int wsUserAuth(byte authType, int doCmds() { byte quit = 0; - int ret; + int ret, err; byte resume = 0; int i; fin = stdin ; fout = stdout ; + while (!quit) { char msg[WOLFSSH_MAX_FILENAME * 2]; char* pt; @@ -321,16 +361,21 @@ int doCmds() pt = f; } - if ((ret = wolfSSH_SFTP_MKDIR(ssh, pt, &atrb)) != WS_SUCCESS) { - if (ret == WS_PERMISSIONS) { - if (WFPUTS("Insufficient permissions\n", fout) < 0) - err_sys("fputs error"); + do { + err = WS_SUCCESS; + if ((ret = wolfSSH_SFTP_MKDIR(ssh, pt, &atrb)) != WS_SUCCESS) { + err = wolfSSH_get_error(ssh); + if (ret == WS_PERMISSIONS) { + if (WFPUTS("Insufficient permissions\n", fout) < 0) + err_sys("fputs error"); + } + else if (err != WS_WANT_READ && err != WS_WANT_WRITE) { + if (WFPUTS("Error writing directory\n", fout) < 0) + err_sys("fputs error"); + } } - else { - if (WFPUTS("Error writing directory\n", fout) < 0) - err_sys("fputs error"); - } - } + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); XFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER); continue; } @@ -486,14 +531,18 @@ int doCmds() } - if (wolfSSH_SFTP_Put(ssh, pt, to, resume, &myStatusCb) - != WS_SUCCESS) { + do { + ret = wolfSSH_SFTP_Put(ssh, pt, to, resume, &myStatusCb); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); + if (ret != WS_SUCCESS) { if (WFPUTS("Error pushing file\n", fout) < 0) - err_sys("fputs error"); + err_sys("fputs error"); } else { if (WFPUTS("\n", fout) < 0) /* new line after status output */ - err_sys("fputs error"); + err_sys("fputs error"); } resume = 0; XFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -527,11 +576,17 @@ int doCmds() } /* check directory is valid */ - if ((ret = wolfSSH_SFTP_STAT(ssh, pt, &atrb)) != WS_SUCCESS) { + do { + ret = wolfSSH_SFTP_STAT(ssh, pt, &atrb); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); + if (ret != WS_SUCCESS) { if (WFPUTS("Error changing directory\n", fout) < 0) - err_sys("fputs error"); + err_sys("fputs error"); } - else { + + if (ret == WS_SUCCESS) { sz = (int)WSTRLEN(pt); XFREE(workingDir, NULL, DYNAMIC_TYPE_TMP_BUFFER); workingDir = (char*)XMALLOC(sz + 1, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -594,8 +649,14 @@ int doCmds() } /* update permissions */ - if (wolfSSH_SFTP_CHMOD(ssh, pt, mode) != WS_SUCCESS) { - printf("unable to change path permissions\n"); + do { + ret = wolfSSH_SFTP_CHMOD(ssh, pt, mode); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); + if (ret != WS_SUCCESS) { + if (WFPUTS("Unable to change path permissions\n", fout) < 0) + err_sys("fputs error"); } XFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -627,7 +688,12 @@ int doCmds() pt = f; } - if ((ret = wolfSSH_SFTP_RMDIR(ssh, pt)) != WS_SUCCESS) { + do { + ret = wolfSSH_SFTP_RMDIR(ssh, pt); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); + if (ret != WS_SUCCESS) { if (ret == WS_PERMISSIONS) { if (WFPUTS("Insufficient permissions\n", fout) < 0) err_sys("fputs error"); @@ -664,7 +730,12 @@ int doCmds() pt = f; } - if ((ret = wolfSSH_SFTP_Remove(ssh, pt)) != WS_SUCCESS) { + do { + ret = wolfSSH_SFTP_Remove(ssh, pt); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); + if (ret != WS_SUCCESS) { if (ret == WS_PERMISSIONS) { if (WFPUTS("Insufficient permissions\n", fout) < 0) err_sys("fputs error"); @@ -735,7 +806,12 @@ int doCmds() to = fTo; } - if ((ret = wolfSSH_SFTP_Rename(ssh, pt, to)) != WS_SUCCESS) { + do { + ret = wolfSSH_SFTP_Rename(ssh, pt, to); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && ret != WS_SUCCESS); + if (ret != WS_SUCCESS) { if (WFPUTS("Error with rename\n", fout) < 0) err_sys("fputs error"); } @@ -747,10 +823,19 @@ int doCmds() if ((pt = WSTRNSTR(msg, "ls", sizeof(msg))) != NULL) { WS_SFTPNAME* tmp; - WS_SFTPNAME* current = wolfSSH_SFTP_LS(ssh, workingDir); + WS_SFTPNAME* current; + + do { + current = wolfSSH_SFTP_LS(ssh, workingDir); + err = wolfSSH_get_error(ssh); + } while ((err == WS_WANT_READ || err == WS_WANT_WRITE) + && current == NULL && err != WS_SUCCESS); tmp = current; while (tmp != NULL) { - printf("%s\n", tmp->fName); + if (WFPUTS(tmp->fName, fout) < 0) + err_sys("fputs error"); + if (WFPUTS("\n", fout) < 0) + err_sys("fputs error"); tmp = tmp->next; } wolfSSH_SFTPNAME_list_free(current); @@ -799,12 +884,13 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args) char* host = (char*)wolfSshIp; const char* username = NULL; const char* password = NULL; + byte nonBlock = 0; int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; ((func_args*)args)->return_code = 0; - while ((ch = mygetopt(argc, argv, "?h:p:u:P:")) != -1) { + while ((ch = mygetopt(argc, argv, "?h:p:u:P:N")) != -1) { switch (ch) { case 'h': host = myoptarg; @@ -826,6 +912,10 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args) password = myoptarg; break; + case 'N': + nonBlock = 1; + break; + case '?': ShowUsage(); exit(EXIT_SUCCESS); @@ -871,11 +961,17 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args) 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."); - ret = wolfSSH_SFTP_connect(ssh); + if (!nonBlock) + ret = wolfSSH_SFTP_connect(ssh); + else + ret = NonBlockSSH_connect(); if (ret != WS_SUCCESS) err_sys("Couldn't connect SFTP"); diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 0d125b2..46e763d 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -385,6 +385,7 @@ struct WOLFSSH { word32 reqId; byte sftpState; byte sftpInt; + byte sftpExtSz; /* size of extension buffer (buffer not currently used) */ SFTP_OFST sftpOfst[WOLFSSH_MAX_SFTPOFST]; #ifdef WOLFSSH_STOREHANDLE WS_HANDLE_LIST* handleList; diff --git a/wolfssh/wolfsftp.h b/wolfssh/wolfsftp.h index b81448e..f4fb12e 100644 --- a/wolfssh/wolfsftp.h +++ b/wolfssh/wolfsftp.h @@ -78,6 +78,7 @@ enum WS_SFTPStatus { enum WS_SFTPConnectStates { SFTP_BEGIN = 20, SFTP_RECV, + SFTP_EXT, SFTP_DONE, };