diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 681260f..50ee3d4 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -5907,21 +5907,23 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, state->idx += UINT32_SZ; state->state = STATE_SEND_READ_SEND_REQ; + FALL_THROUGH; case STATE_SEND_READ_SEND_REQ: WLOG(WS_LOG_SFTP, "SFTP SEND_READ STATE: SEND_REQ"); /* send header and type specific data */ ret = wolfSSH_stream_send(ssh, state->data, state->idx); - if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE) { + if (ret < 0) { + if (ssh->error != WS_WANT_READ && + ssh->error != WS_WANT_WRITE) { + state->state = STATE_SEND_READ_CLEANUP; + continue; + } return ret; } WFREE(state->data, ssh->ctx->heap, DYNTYPE_BUFFER); state->data = NULL; - if (ret < 0) { - return ret; - } - state->state = STATE_SEND_READ_GET_HEADER; FALL_THROUGH; @@ -5929,8 +5931,14 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, WLOG(WS_LOG_SFTP, "SFTP SEND_READ STATE: GET_HEADER"); /* Get response */ if ((ret = SFTP_GetHeader(ssh, &state->reqId, &state->type)) - <= 0) + <= 0) { + if (ssh->error != WS_WANT_READ && + ssh->error != WS_WANT_WRITE) { + state->state = STATE_SEND_READ_CLEANUP; + continue; + } return WS_FATAL_ERROR; + } state->sz = ret; state->state = STATE_SEND_READ_CHECK_REQ_ID; @@ -5941,7 +5949,9 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, /* check request ID */ if (state->reqId != ssh->reqId) { WLOG(WS_LOG_SFTP, "Bad request ID received"); - return WS_FATAL_ERROR; + ret = WS_FATAL_ERROR; + state->state = STATE_SEND_READ_CLEANUP; + continue; } else ssh->reqId++; @@ -5952,7 +5962,8 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, state->state = STATE_SEND_READ_FTP_STATUS; else { WLOG(WS_LOG_SFTP, "Unexpected packet type"); - return WS_FATAL_ERROR; + ret = WS_FATAL_ERROR; + state->state = STATE_SEND_READ_CLEANUP; } continue; @@ -5961,12 +5972,19 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, /* get size of string and place it into out buffer */ ret = wolfSSH_stream_read(ssh, szFlat, UINT32_SZ); if (ret < 0) { + if (ssh->error != WS_WANT_READ && + ssh->error != WS_WANT_WRITE) { + state->state = STATE_SEND_READ_CLEANUP; + continue; + } return ret; } ato32(szFlat, &state->sz); if (state->sz > outSz) { WLOG(WS_LOG_SFTP, "Server sent more data then expected"); - return WS_FATAL_ERROR; + ret = WS_FATAL_ERROR; + state->state = STATE_SEND_READ_CLEANUP; + continue; } state->state = STATE_SEND_READ_REMAINDER; @@ -6004,6 +6022,11 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz, ssh->ctx->heap, DYNTYPE_BUFFER); ret = wolfSSH_stream_read(ssh, data, state->sz); if (ret < 0) { + if (ssh->error != WS_WANT_READ && + ssh->error != WS_WANT_WRITE) { + state->state = STATE_SEND_READ_CLEANUP; + continue; + } return WS_FATAL_ERROR; } ret = wolfSSH_SFTP_DoStatus(ssh, state->reqId, data, diff --git a/tests/api.c b/tests/api.c index ddd0bf0..8bfe152 100644 --- a/tests/api.c +++ b/tests/api.c @@ -25,6 +25,21 @@ #ifdef WOLFSSH_SCP #include #endif +#ifdef WOLFSSH_SFTP + #define WOLFSSH_TEST_LOCKING + #define WOLFSSH_TEST_THREADING + + #define WOLFSSH_TEST_SERVER + #define WOLFSSH_TEST_ECHOSERVER + #include + + #include "examples/echoserver/echoserver.h" + +#endif + +/* for echoserver test cases */ +int myoptind = 0; +char* myoptarg = NULL; #define Fail(description, result) do { \ @@ -231,6 +246,93 @@ static int ConvertHexToBin(const char* h1, byte** b1, word32* b1Sz, return 0; } +#ifdef WOLFSSH_SFTP +byte userPassword[256]; +static int sftpUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) +{ + int ret = WOLFSSH_USERAUTH_INVALID_AUTHTYPE; + + if (authType == WOLFSSH_USERAUTH_PASSWORD) { + const char* defaultPassword = (const char*)ctx; + word32 passwordSz; + + ret = WOLFSSH_USERAUTH_SUCCESS; + if (defaultPassword != NULL) { + passwordSz = (word32)strlen(defaultPassword); + memcpy(userPassword, defaultPassword, passwordSz); + } + else { + printf("Expecting password set for test cases\n"); + return ret; + } + + if (ret == WOLFSSH_USERAUTH_SUCCESS) { + authData->sf.password.password = userPassword; + authData->sf.password.passwordSz = passwordSz; + } + } + return ret; +} + +/* preforms connection to port, sets WOLFSSH_CTX and WOLFSSH on success + * caller needs to free ctx and ssh when done + */ +static void sftp_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port) +{ + SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID; + SOCKADDR_IN_T clientAddr; + socklen_t clientAddrSz = sizeof(clientAddr); + int ret; + char* host = (char*)wolfSshIp; + const char* username = "jill"; + const char* password = "upthehill"; + + if (ctx == NULL || ssh == NULL) { + return; + } + + *ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); + if (*ctx == NULL) { + return; + } + + wolfSSH_SetUserAuth(*ctx, sftpUserAuth); + *ssh = wolfSSH_new(*ctx); + if (*ssh == NULL) { + wolfSSH_CTX_free(*ctx); + *ctx = NULL; + return; + } + + build_addr(&clientAddr, host, port); + tcp_socket(&sockFd); + ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz); + if (ret != 0){ + wolfSSH_free(*ssh); + wolfSSH_CTX_free(*ctx); + *ctx = NULL; + *ssh = NULL; + return; + } + + wolfSSH_SetUserAuthCtx(*ssh, (void*)password); + ret = wolfSSH_SetUsername(*ssh, username); + if (ret == WS_SUCCESS) + ret = wolfSSH_set_fd(*ssh, (int)sockFd); + + if (ret == WS_SUCCESS) + ret = wolfSSH_SFTP_connect(*ssh); + + if (ret != WS_SUCCESS){ + wolfSSH_free(*ssh); + wolfSSH_CTX_free(*ctx); + *ctx = NULL; + *ssh = NULL; + return; + } +} +#endif /* WOLFSSH_SFTP */ + enum WS_TestEndpointTypes { TEST_GOOD_ENDPOINT_SERVER = WOLFSSH_ENDPOINT_SERVER, @@ -529,6 +631,97 @@ static void test_wolfSSH_SCP_CB(void) #endif /* WOLFSSH_NO_CLIENT */ } +static void test_wolfSSH_SFTP_SendReadPacket(void) +{ +#ifdef WOLFSSH_SFTP + func_args ser; + tcp_ready ready; + int argsCount; + + const char* args[10]; + WOLFSSH_CTX* ctx = NULL; + WOLFSSH* ssh = NULL; + + THREAD_TYPE serThread; + + WMEMSET(&ser, 0, sizeof(func_args)); + + argsCount = 0; + args[argsCount++] = "."; + args[argsCount++] = "-1"; +#ifndef USE_WINDOWS_API + args[argsCount++] = "-p"; + args[argsCount++] = "0"; +#endif + ser.argv = (char**)args; + ser.argc = argsCount; + ser.signal = &ready; + InitTcpReady(ser.signal); + ThreadStart(echoserver_test, (void*)&ser, &serThread); + WaitTcpReady(&ser); + + sftp_client_connect(&ctx, &ssh, ready.port); + AssertNotNull(ctx); + AssertNotNull(ssh); + + { + WS_SFTPNAME* tmp; + WS_SFTPNAME* current; + byte handle[WOLFSSH_MAX_HANDLE]; + word32 handleSz = WOLFSSH_MAX_HANDLE; + const char* currentDir = "."; + byte* out = NULL; + int outSz = 18; + const word32 ofst[2] = {0}; + + current = wolfSSH_SFTP_LS(ssh, (char*)currentDir); + tmp = current; + while (tmp != NULL) { + if ((tmp->atrb.sz[0] > 0) && + (tmp->atrb.flags & WOLFSSH_FILEATRB_PERM) && + !(tmp->atrb.per & 0x4000)) { + break; + } + tmp = tmp->next; + } + + if (tmp != NULL) { + out = (byte*)malloc(tmp->atrb.sz[0]); + AssertIntEQ(wolfSSH_SFTP_Open(ssh, tmp->fName, WOLFSSH_FXF_READ, + NULL, handle, &handleSz), WS_SUCCESS); + + /* read 18 bytes */ + if (tmp->atrb.sz[0] >= 18) { + outSz = 18; + AssertIntEQ(wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz, + ofst, out, outSz), outSz); + } + + /* partial read */ + outSz = tmp->atrb.sz[0] / 2; + AssertIntEQ(wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz, ofst, + out, outSz), outSz); + + /* read all */ + outSz = tmp->atrb.sz[0]; + AssertIntEQ(wolfSSH_SFTP_SendReadPacket(ssh, handle, handleSz, ofst, + out, outSz), outSz); + + free(out); + wolfSSH_SFTP_Close(ssh, handle, handleSz); + wolfSSH_SFTPNAME_list_free(current); + } + } + + AssertIntEQ(wolfSSH_shutdown(ssh), WS_SUCCESS); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); + ThreadJoin(serThread); +#endif +} + + + #ifdef USE_WINDOWS_API static byte color_test[] = { 0x1B, 0x5B, 0x34, 0x6D, 0x75, 0x6E, 0x64, 0x65, @@ -636,6 +829,10 @@ int main(void) /* SCP tests */ test_wolfSSH_SCP_CB(); + /* SFTP tests */ + test_wolfSSH_SFTP_SendReadPacket(); + + AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS); return 0; diff --git a/tests/include.am b/tests/include.am index cdef8b9..986f31f 100644 --- a/tests/include.am +++ b/tests/include.am @@ -18,7 +18,8 @@ endif tests_unit_test_LDADD = src/libwolfssh.la tests_unit_test_DEPENDENCIES = src/libwolfssh.la -tests_api_test_SOURCES = tests/api.c +tests_api_test_SOURCES = tests/api.c \ + examples/echoserver/echoserver.c tests_api_test_CPPFLAGS = -DNO_MAIN_DRIVER if BUILD_SCP tests_api_test_CPPFLAGS += -DWOLFSSH_SCP diff --git a/tests/testsuite.c b/tests/testsuite.c index eaca68a..25c2fc3 100644 --- a/tests/testsuite.c +++ b/tests/testsuite.c @@ -164,18 +164,3 @@ int TestsuiteTest(int argc, char** argv) #endif /* !NO_WOLFSSH_SERVER && !NO_WOLFSSH_CLIENT */ -void WaitTcpReady(func_args* args) -{ -#if defined(_POSIX_THREADS) && !defined(__MINGW32__) - pthread_mutex_lock(&args->signal->mutex); - - if (!args->signal->ready) - pthread_cond_wait(&args->signal->cond, &args->signal->mutex); - args->signal->ready = 0; /* reset */ - - pthread_mutex_unlock(&args->signal->mutex); -#else - (void)args; -#endif -} - diff --git a/wolfssh/test.h b/wolfssh/test.h index 97853d0..b7c94b8 100644 --- a/wolfssh/test.h +++ b/wolfssh/test.h @@ -741,7 +741,21 @@ static INLINE void FreeTcpReady(tcp_ready* ready) } -void WaitTcpReady(func_args*); +static INLINE void WaitTcpReady(func_args* args) +{ +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_lock(&args->signal->mutex); + + if (!args->signal->ready) + pthread_cond_wait(&args->signal->cond, &args->signal->mutex); + args->signal->ready = 0; /* reset */ + + pthread_mutex_unlock(&args->signal->mutex); +#else + (void)args; +#endif +} + #endif /* WOLFSSH_TEST_LOCKING */