From d2c2989a54aac8858eb07ce33a54ad260cfe4b6d Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Mon, 15 Jun 2026 16:42:40 +0900 Subject: [PATCH] Run threaded api-test SFTP/SCP tests on Windows --- .github/workflows/windows-check.yml | 13 ++++--- examples/echoserver/echoserver.c | 23 ++++++++++- examples/sftpclient/sftpclient.c | 25 ++++++++---- src/wolfscp.c | 37 +++++++++++++++--- tests/api.c | 60 +++++++++++++---------------- tests/auth.c | 1 + tests/unit.c | 41 ++++++++++++++++++++ wolfssh/internal.h | 3 ++ wolfssh/port.h | 8 ++++ wolfssh/test.h | 32 ++++++++++++++- 10 files changed, 190 insertions(+), 53 deletions(-) diff --git a/.github/workflows/windows-check.yml b/.github/workflows/windows-check.yml index c9fe6f32c..aed2420da 100644 --- a/.github/workflows/windows-check.yml +++ b/.github/workflows/windows-check.yml @@ -139,12 +139,13 @@ jobs: run: nuget restore ${{env.WOLFSSL_SOLUTION_FILE_PATH}} # These env paths already include the wolfssh/ and wolfssl/ checkout - # prefixes, so they must run from the workspace root (as the build job does). - - name: updated user_settings.h for sshd and x509 - run: cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}} - - - name: replace wolfSSL user_settings.h with wolfSSH user_settings.h - run: get-content ${{env.USER_SETTINGS_H_NEW}} | %{$_ -replace "if 0","if 1"} + # prefixes, so this runs from the default working directory, the workspace + # root. + - name: Enable wolfSSH options (sshd, sftp, x509) in user_settings.h + shell: bash + run: | + sed -i 's/#if 0/#if 1/g' ${{env.USER_SETTINGS_H_NEW}} + cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}} # WholeProgramOptimization=false disables /GL so the v142-independent objects # link without a cross-version code-generation requirement (C1047). diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 8ce9c1528..7832aac04 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2249,6 +2249,11 @@ static int LoadPasswdList(StrList* strList, PwMapList* mapList) int count = 0; while (strList) { + if (WSTRLEN(strList->str) >= sizeof names - 1) { + fprintf(stderr, "Ignoring over-long entry: %.32s\n", strList->str); + strList = strList->next; + continue; + } WSTRNCPY(names, strList->str, sizeof names - 1); passwd = WSTRCHR(names, ':'); if (passwd != NULL) { @@ -2278,6 +2283,11 @@ static int LoadKeyboardList(StrList* strList, PwMapList* mapList, int count = 0; while (strList) { + if (WSTRLEN(strList->str) >= sizeof names - 1) { + fprintf(stderr, "Ignoring over-long entry: %.32s\n", strList->str); + strList = strList->next; + continue; + } WSTRNCPY(names, strList->str, sizeof names - 1); passwd = WSTRCHR(names, ':'); if (passwd != NULL) { @@ -2314,6 +2324,11 @@ static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList) buf = NULL; bufSz = 0; + if (WSTRLEN(strList->str) >= sizeof names - 1) { + fprintf(stderr, "Ignoring over-long entry: %.32s\n", strList->str); + strList = strList->next; + continue; + } WSTRNCPY(names, strList->str, sizeof names - 1); fileName = WSTRCHR(names, ':'); if (fileName != NULL) { @@ -2834,6 +2849,12 @@ static INLINE void SignalTcpReady(tcp_ready* ready, word16 port) ready->port = port; pthread_cond_signal(&ready->cond); pthread_mutex_unlock(&ready->mutex); +#elif defined(USE_WINDOWS_API) && defined(NO_MAIN_DRIVER) && \ + !defined(SINGLE_THREADED) + ready->ready = 1; + ready->port = port; + /* SetEvent is a full barrier; this one-shot signal needs no lock */ + SetEvent(ready->readyEvent); #else WOLFSSH_UNUSED(ready); WOLFSSH_UNUSED(port); @@ -2979,7 +3000,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) } else { port = (word16)atoi(myoptarg); - #if !defined(NO_MAIN_DRIVER) || defined(USE_WINDOWS_API) + #if !defined(NO_MAIN_DRIVER) if (port == 0) { ES_ERROR("port number cannot be 0"); } diff --git a/examples/sftpclient/sftpclient.c b/examples/sftpclient/sftpclient.c index b7e7c332d..9e0f74449 100644 --- a/examples/sftpclient/sftpclient.c +++ b/examples/sftpclient/sftpclient.c @@ -156,6 +156,7 @@ static void myStatusCb(WOLFSSH* sshIn, word32* bytes, char* name) 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]; @@ -176,13 +177,19 @@ static void myStatusCb(WOLFSSH* sshIn, word32* bytes, char* name) } lastPrintedBytes[0] = bytes[0]; lastPrintedBytes[1] = bytes[1]; - if (WSTRNCMP(currentFile, name, WSTRLEN(name)) != 0) { + /* 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, WOLFSSH_MAX_FILENAME); - WSTRNCPY(currentFile, name, WOLFSSH_MAX_FILENAME); + WMEMSET(currentFile, 0, sizeof(currentFile)); + WMEMCPY(currentFile, name, nameSz); } elapsedTime = currentTime - startTime; WSNPRINTF(buf, sizeof(buf), "Processed %8llu\t bytes in %d seconds\r", @@ -191,7 +198,7 @@ static void myStatusCb(WOLFSSH* sshIn, word32* bytes, char* name) if (elapsedTime > TIMEOUT_VALUE) { WSNPRINTF(buf, sizeof(buf), "\nProcess timed out at %d seconds, " "stopping\r", elapsedTime); - WMEMSET(currentFile, 0, WOLFSSH_MAX_FILENAME); + WMEMSET(currentFile, 0, sizeof(currentFile)); wolfSSH_SFTP_Interrupt(ssh); } #endif @@ -667,7 +674,7 @@ static int doCmds(func_args* args) ret == WS_CHAN_RXD || ret == WS_REKEYING); #ifndef WOLFSSH_NO_TIMESTAMP - WMEMSET(currentFile, 0, WOLFSSH_MAX_FILENAME); + WMEMSET(currentFile, 0, sizeof(currentFile)); #endif if (ret != WS_SUCCESS) { @@ -778,7 +785,7 @@ static int doCmds(func_args* args) ret == WS_CHAN_RXD || ret == WS_REKEYING); #ifndef WOLFSSH_NO_TIMESTAMP - WMEMSET(currentFile, 0, WOLFSSH_MAX_FILENAME); + WMEMSET(currentFile, 0, sizeof(currentFile)); #endif if (ret != WS_SUCCESS) { @@ -1445,6 +1452,7 @@ static int doAutopilot(int cmd, char* local, char* remote) 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 */ @@ -1462,8 +1470,11 @@ static int doAutopilot(int cmd, char* local, char* remote) 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)); - WSTRNCPY(fullpath, remote, sizeof(fullpath) - 1); + WMEMCPY(fullpath, remote, remoteSz + 1); } else { err = doBuildRemotePath(remote, fullpath, sizeof(fullpath), &name); diff --git a/src/wolfscp.c b/src/wolfscp.c index 81b024aa7..1eb1b83da 100644 --- a/src/wolfscp.c +++ b/src/wolfscp.c @@ -1952,12 +1952,19 @@ int wolfSSH_SCP_connect(WOLFSSH* ssh, byte* cmd) } +/* Shared format so the size probe and the write cannot drift. */ +#define SCP_CMD_FMT "scp -%c %s" + static char* MakeScpCmd(const char* name, char dir, void* heap) { char* cmd; int sz; - sz = WSNPRINTF(NULL, 0, "scp -%c %s", dir, name) + 1; +#ifdef USE_WINDOWS_API + sz = WSCPRINTF(SCP_CMD_FMT, dir, name) + 1; +#else + sz = WSNPRINTF(NULL, 0, SCP_CMD_FMT, dir, name) + 1; +#endif if (sz <= 0) { return NULL; } @@ -1965,7 +1972,7 @@ static char* MakeScpCmd(const char* name, char dir, void* heap) if (cmd == NULL) { return NULL; } - sz = WSNPRINTF(cmd, sz, "scp -%c %s", dir, name); + sz = WSNPRINTF(cmd, sz, SCP_CMD_FMT, dir, name); if (sz <= 0) { WFREE(cmd, heap, DYNTYPE_STRING); return NULL; @@ -2712,10 +2719,15 @@ static ScpDir* ScpNewDir(void *fs, const char* path, void* heap) int ScpPushDir(void *fs, ScpSendCtx* ctx, const char* path, void* heap) { ScpDir* entry; + word32 pathSz; if (ctx == NULL || path == NULL) return WS_BAD_ARGUMENT; + pathSz = (word32)WSTRLEN(path); + if (pathSz >= sizeof(ctx->dirName)) + return WS_BUFFER_E; + entry = ScpNewDir(fs, path, heap); if (entry == NULL) { return WS_FATAL_ERROR; @@ -2729,13 +2741,28 @@ int ScpPushDir(void *fs, ScpSendCtx* ctx, const char* path, void* heap) ctx->currentDir = entry; } - /* append directory name to ctx->dirName */ - WSTRNCPY(ctx->dirName, path, DEFAULT_SCP_FILE_NAME_SZ-1); - ctx->dirName[DEFAULT_SCP_FILE_NAME_SZ-1] = '\0'; + /* append directory name to ctx->dirName, terminator included; the guard + * above bounds pathSz so the copy always fits */ + WMEMCPY(ctx->dirName, path, pathSz + 1); return WS_SUCCESS; } +#ifdef WOLFSSH_TEST_INTERNAL +int wolfSSH_TestScpPushDir(const char* path) +{ + ScpSendCtx ctx; + int ret; + + WMEMSET(&ctx, 0, sizeof(ctx)); + ret = ScpPushDir(NULL, &ctx, path, NULL); + if (ret == WS_SUCCESS) + ScpSendCtxFreeDirs(NULL, &ctx, NULL); + + return ret; +} +#endif /* WOLFSSH_TEST_INTERNAL */ + /* Remove top ScpDir from directory stack, remove dir from ctx->dirName */ int ScpPopDir(void *fs, ScpSendCtx* ctx, void* heap) { diff --git a/tests/api.c b/tests/api.c index bda465eef..1b785086a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1687,6 +1687,20 @@ static void test_wolfSSH_agent_signrequest_success(void) #if defined(WOLFSSH_SFTP) && !defined(NO_WOLFSSH_CLIENT) && \ !defined(SINGLE_THREADED) +/* A peer gone at shutdown is not a failure: the send path returns the reset, + * the recv path returns generic WS_ERROR with the code in wolfSSH_get_error. + * WS_SOCKET_ERROR_E is generic, so a real shutdown failure is tolerated too. */ +static int AbsorbBenignReset(WOLFSSH* ssh, int ret) +{ + if (ret == WS_SOCKET_ERROR_E || + (ret == WS_ERROR && + wolfSSH_get_error(ssh) == WS_SOCKET_ERROR_E)) { + ret = WS_SUCCESS; + } + + return ret; +} + byte userPassword[256]; static int sftpUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) @@ -1820,10 +1834,8 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) 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; @@ -2004,11 +2016,7 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) wolfSSH_worker(ssh, NULL); } - argsCount = wolfSSH_shutdown(ssh); - if (argsCount == WS_SOCKET_ERROR_E) { - /* If the socket is closed on shutdown, peer is gone, this is OK. */ - argsCount = WS_SUCCESS; - } + argsCount = AbsorbBenignReset(ssh, wolfSSH_shutdown(ssh)); #if DEFAULT_HIGHWATER_MARK < 8000 if (argsCount == WS_REKEYING) { @@ -2030,6 +2038,7 @@ static void test_wolfSSH_SFTP_SendReadPacket(void) k_sleep(Z_TIMEOUT_TICKS(100)); #endif ThreadJoin(serThread); + FreeTcpReady(&ready); } @@ -2090,10 +2099,8 @@ static void test_wolfSSH_SFTP_PartialSend(void) 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; @@ -2279,10 +2286,7 @@ static void test_wolfSSH_SFTP_PartialSend(void) wolfSSH_worker(ssh, NULL); } - argsCount = wolfSSH_shutdown(ssh); - if (argsCount == WS_SOCKET_ERROR_E) { - argsCount = WS_SUCCESS; - } + argsCount = AbsorbBenignReset(ssh, wolfSSH_shutdown(ssh)); #if DEFAULT_HIGHWATER_MARK < 8000 if (argsCount == WS_REKEYING) { argsCount = WS_SUCCESS; @@ -2299,6 +2303,7 @@ static void test_wolfSSH_SFTP_PartialSend(void) k_sleep(Z_TIMEOUT_TICKS(100)); #endif ThreadJoin(serThread); + FreeTcpReady(&ready); #endif /* WOLFSSH_TEST_INTERNAL */ } @@ -2330,10 +2335,8 @@ static void sftp_rekey_test(int nonBlock) 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; @@ -2417,9 +2420,7 @@ static void sftp_rekey_test(int nonBlock) * before the WS_REKEYING fixup below could otherwise mask it. The loop cap * is one above the assert threshold to leave last-iteration headroom. */ AssertIntLE(tries, SFTP_REKEY_MAX_TRIES); - if (argsCount == WS_SOCKET_ERROR_E) { - argsCount = WS_SUCCESS; - } + argsCount = AbsorbBenignReset(ssh, argsCount); #if DEFAULT_HIGHWATER_MARK < 8000 if (argsCount == WS_REKEYING) { /* in cases where highwater mark is really small a re-key could happen */ @@ -2437,6 +2438,7 @@ static void sftp_rekey_test(int nonBlock) k_sleep(Z_TIMEOUT_TICKS(100)); #endif ThreadJoin(serThread); + FreeTcpReady(&ready); } static void test_wolfSSH_SFTP_ReKey(void) @@ -2615,10 +2617,8 @@ static void test_wolfSSH_SFTP_Confinement(void) 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; @@ -2791,10 +2791,7 @@ static void test_wolfSSH_SFTP_Confinement(void) while (wolfSSH_get_error(ssh) == WS_REKEYING) wolfSSH_worker(ssh, NULL); - ret = wolfSSH_shutdown(ssh); - if (ret == WS_SOCKET_ERROR_E) { - ret = WS_SUCCESS; - } + ret = AbsorbBenignReset(ssh, wolfSSH_shutdown(ssh)); #if DEFAULT_HIGHWATER_MARK < 8000 if (ret == WS_REKEYING) { ret = WS_SUCCESS; @@ -2809,6 +2806,7 @@ static void test_wolfSSH_SFTP_Confinement(void) k_sleep(Z_TIMEOUT_TICKS(100)); #endif ThreadJoin(serThread); + FreeTcpReady(&ready); /* remove staged targets; escMkdir/escDest only exist if confinement * leaked, and inJailDir only if the positive-case RMDIR did not run, so @@ -3184,10 +3182,8 @@ static void scp_rekey_test(int nonBlock, int toServer) 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; @@ -3282,6 +3278,7 @@ static void scp_rekey_test(int nonBlock, int toServer) wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); ThreadJoin(serThread); + FreeTcpReady(&ready); /* verify the transferred file matches the source once the server is done */ AssertIntEQ(scpFilesMatch(verifyName, fileData, SCP_REKEY_FILE_SZ), 0); @@ -4035,10 +4032,8 @@ static void test_wolfSSH_KeyboardInteractive(void) args[argsCount++] = "-1"; args[argsCount++] = "-i"; args[argsCount++] = "test:test"; -#ifndef USE_WINDOWS_API args[argsCount++] = "-p"; args[argsCount++] = "0"; -#endif ser.argv = (char**)args; ser.argc = argsCount; ser.signal = &ready; @@ -4051,11 +4046,7 @@ static void test_wolfSSH_KeyboardInteractive(void) AssertNotNull(ssh); - argsCount = wolfSSH_shutdown(ssh); - if (argsCount == WS_SOCKET_ERROR_E) { - /* If the socket is closed on shutdown, peer is gone, this is OK. */ - argsCount = WS_SUCCESS; - } + argsCount = AbsorbBenignReset(ssh, wolfSSH_shutdown(ssh)); #if DEFAULT_HIGHWATER_MARK < 8000 if (argsCount == WS_REKEYING) { @@ -4077,6 +4068,7 @@ static void test_wolfSSH_KeyboardInteractive(void) k_sleep(Z_TIMEOUT_TICKS(100)); #endif ThreadJoin(serThread); + FreeTcpReady(&ready); } #else /* WOLFSSH_SFTP && !NO_WOLFSSH_CLIENT && !SINGLE_THREADED */ @@ -4095,6 +4087,8 @@ int wolfSSH_ApiTest(int argc, char** argv) #ifdef WOLFSSH_TEST_BLOCK return 77; #else + WSTARTTCP(); + AssertIntEQ(wolfSSH_Init(), WS_SUCCESS); #if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,2) diff --git a/tests/auth.c b/tests/auth.c index 83b1dd4c6..6961eb6c2 100644 --- a/tests/auth.c +++ b/tests/auth.c @@ -1836,6 +1836,7 @@ static void test_client(void) if (!unbalanced) { AssertIntEQ(serverArgs.return_code, WS_SUCCESS); } + FreeTcpReady(&ready); } static void test_basic_KeyboardInteractive(void) diff --git a/tests/unit.c b/tests/unit.c index f89da0cee..ab42f436b 100644 --- a/tests/unit.c +++ b/tests/unit.c @@ -8705,6 +8705,39 @@ static int test_ScpExtractFileName(void) } #endif /* WOLFSSH_TEST_INTERNAL && WOLFSSH_SCP && !WOLFSSH_SCP_USER_CALLBACKS */ +#if defined(WOLFSSH_TEST_INTERNAL) && defined(WOLFSSH_SCP) && \ + !defined(WOLFSSH_SCP_USER_CALLBACKS) && !defined(NO_FILESYSTEM) +/* ScpPushDir bounds the request path against ctx->dirName before copying it. + * The accept side stops short of WS_SUCCESS because no directory that long + * exists to open, so it asserts the guard did not fire rather than success. */ +static int test_ScpPushDir(void) +{ + char path[DEFAULT_SCP_FILE_NAME_SZ + 1]; + int ret; + + /* one byte too long for ctx->dirName: rejected before anything is opened */ + WMEMSET(path, 'a', DEFAULT_SCP_FILE_NAME_SZ); + path[DEFAULT_SCP_FILE_NAME_SZ] = '\0'; + ret = wolfSSH_TestScpPushDir(path); + if (ret != WS_BUFFER_E) + return -913; + + /* exact fit clears the length guard and fails at the open instead */ + path[DEFAULT_SCP_FILE_NAME_SZ - 1] = '\0'; + ret = wolfSSH_TestScpPushDir(path); + if (ret == WS_BUFFER_E) + return -914; + + /* NULL path is rejected */ + ret = wolfSSH_TestScpPushDir(NULL); + if (ret != WS_BAD_ARGUMENT) + return -915; + + return 0; +} +#endif /* WOLFSSH_TEST_INTERNAL && WOLFSSH_SCP && + * !WOLFSSH_SCP_USER_CALLBACKS && !NO_FILESYSTEM */ + #endif /* WOLFSSH_TEST_INTERNAL */ /* Error Code And Message Test */ @@ -9315,6 +9348,14 @@ int wolfSSH_UnitTest(int argc, char** argv) testResult = testResult || unitResult; #endif +#if defined(WOLFSSH_TEST_INTERNAL) && defined(WOLFSSH_SCP) && \ + !defined(WOLFSSH_SCP_USER_CALLBACKS) && !defined(NO_FILESYSTEM) + unitResult = test_ScpPushDir(); + printf("ScpPushDir: %s\n", + (unitResult == 0 ? "SUCCESS" : "FAILED")); + testResult = testResult || unitResult; +#endif + #ifdef WOLFSSH_TEST_CAPTURING_ALLOCATOR unitResult = test_SshResourceFree_zeroesSecrets(); printf("SshResourceFree_zeroesSecrets: %s\n", diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 49679d91f..2ead7d88e 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -1533,6 +1533,9 @@ enum WS_MessageIdLimits { word32 bufSz, word32* inOutIdx); WOLFSSH_API int wolfSSH_TestScpGetTimestamp(WOLFSSH* ssh, byte* buf, word32 bufSz, word32* inOutIdx); +#if !defined(WOLFSSH_SCP_USER_CALLBACKS) && !defined(NO_FILESYSTEM) + WOLFSSH_API int wolfSSH_TestScpPushDir(const char* path); +#endif #endif /* WOLFSSH_SCP */ #ifndef WOLFSSH_NO_DH WOLFSSH_API int wolfSSH_TestKeyAgreeDh_client(WOLFSSH* ssh, byte hashId, diff --git a/wolfssh/port.h b/wolfssh/port.h index af1efe1cc..cb1c08b39 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -642,6 +642,14 @@ extern "C" { #endif #endif /* WSTRING_USER */ +/* Measures a formatted string's length; _snprintf_s rejects the NULL/0 probe + * form that WSNPRINTF maps to on Windows. Defined outside WSTRING_USER so + * custom string ports need not supply it, and left overridable. */ +#if defined(USE_WINDOWS_API) && !defined(WSCPRINTF) + #include + #define WSCPRINTF(f,...) _scprintf((f),##__VA_ARGS__) +#endif + /* get local time for debug print out */ #if defined(USE_WINDOWS_API) || defined(__MINGW32__) #define WTIME time diff --git a/wolfssh/test.h b/wolfssh/test.h index 641f20413..bfadb703d 100644 --- a/wolfssh/test.h +++ b/wolfssh/test.h @@ -687,7 +687,7 @@ static INLINE void tcp_listen(WS_SOCKET_T* sockfd, word16* port, int useAnyAddr) err_sys("tcp listen failed"); #endif - #if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_TIRTOS) && !defined(WOLFSSL_NUCLEUS) + #if !defined(WOLFSSL_TIRTOS) && !defined(WOLFSSL_NUCLEUS) if (*port == 0) { socklen_t len = sizeof(addr); if (getsockname(*sockfd, (struct sockaddr*)&addr, &len) == 0) { @@ -837,6 +837,8 @@ typedef struct tcp_ready { #if defined(_POSIX_THREADS) && !defined(__MINGW32__) pthread_mutex_t mutex; pthread_cond_t cond; +#elif defined(USE_WINDOWS_API) + HANDLE readyEvent; /* manual-reset event, set when ready */ #endif } tcp_ready; @@ -860,6 +862,14 @@ typedef struct func_args { #ifdef WOLFSSH_TEST_LOCKING +#if defined(USE_WINDOWS_API) && defined(NO_MAIN_DRIVER) && \ + !defined(SINGLE_THREADED) +/* Upper bound on the wait for the server to become ready. The signal fires + * right after the listen() so the real wait is sub-second; this generous + * bound only keeps a dead server from hanging the run. */ +#define TCP_READY_TIMEOUT_MS 30000 +#endif + static INLINE void InitTcpReady(tcp_ready* ready) { ready->ready = 0; @@ -869,6 +879,12 @@ static INLINE void InitTcpReady(tcp_ready* ready) !defined(__MINGW32__) && !defined(SINGLE_THREADED) pthread_mutex_init(&ready->mutex, NULL); pthread_cond_init(&ready->cond, NULL); +#elif defined(USE_WINDOWS_API) && defined(NO_MAIN_DRIVER) && \ + !defined(SINGLE_THREADED) + ready->readyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ready->readyEvent == NULL) { + err_sys("Create tcp ready event failed"); + } #endif } @@ -879,6 +895,12 @@ static INLINE void FreeTcpReady(tcp_ready* ready) !defined(__MINGW32__) && !defined(SINGLE_THREADED) pthread_mutex_destroy(&ready->mutex); pthread_cond_destroy(&ready->cond); +#elif defined(USE_WINDOWS_API) && defined(NO_MAIN_DRIVER) && \ + !defined(SINGLE_THREADED) + if (ready->readyEvent != NULL) { + CloseHandle(ready->readyEvent); + ready->readyEvent = NULL; + } #else WOLFSSH_UNUSED(ready); #endif @@ -902,6 +924,14 @@ static INLINE void WaitTcpReady(tcp_ready* ready) * seems to help. This is not ideal. (XXX) */ k_sleep(Z_TIMEOUT_TICKS(300)); #endif /* WOLFSSH_ZEPHYR */ +#elif defined(USE_WINDOWS_API) && defined(NO_MAIN_DRIVER) && \ + !defined(SINGLE_THREADED) + /* timeout or unset predicate means the server never signalled; abort + * so the failure points at startup, not a later bogus connect */ + if (WaitForSingleObject(ready->readyEvent, TCP_READY_TIMEOUT_MS) + != WAIT_OBJECT_0 || !ready->ready) { + err_sys("wait for tcp ready failed"); + } #else WOLFSSH_UNUSED(ready); #endif