diff --git a/tests/api.c b/tests/api.c index 3b2088142..7f4dd4ba8 100644 --- a/tests/api.c +++ b/tests/api.c @@ -666,8 +666,13 @@ static THREAD_RETURN WOLFSSL_THREAD test_server_bio(void* args) wc_BioSetFd(bio, clientfd, BIO_NOCLOSE); +read_again: idx = wc_BioRead(bio, input, sizeof(input)-1); if (idx <= 0) { + if (wc_BioShouldRetry(bio)) { + printf("Retry read\n"); + goto read_again; + } printf("wc_BioWrite failed\n"); goto done; } @@ -704,7 +709,6 @@ done: #endif } - static void test_client_bio(void* args) { WOLFCRYPT_BIO* bio = 0; @@ -756,8 +760,13 @@ static void test_client_bio(void* args) goto done2; } +read_again: input = wc_BioRead(bio, reply, sizeof(reply)-1); if (input <= 0) { + if (wc_BioShouldRetry(bio)) { + printf("Retry read\n"); + goto read_again; + } printf("wc_BioRead failed"); goto done2; } @@ -774,10 +783,206 @@ done2: #ifdef WOLFSSL_TIRTOS fdCloseSession(Task_self()); #endif - + return; } +#ifndef TEST_IPV6 +static THREAD_RETURN WOLFSSL_THREAD test_server_full_bio(void* args) +{ + WOLFCRYPT_BIO *abio = 0, *cbio = 0; + char buf[256]; + char msg[] = "I hear you fa shizzle!"; + int r; + +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + + ((func_args*)args)->return_code = TEST_FAIL; + + abio = wc_BioNew(wc_Bio_s_accept()); + if (abio == NULL) { + printf("wc_Bio_s_accept failed\n"); + goto done; + } + + snprintf(buf, sizeof(buf), "%s:%d", wolfSSLIP, wolfSSLPort); + if (wc_BioSetAcceptPort(abio, buf) <= 0) { + printf("wc_BioSetAcceptPort failed\n"); + goto done; + } + XMEMSET(buf, 0, sizeof(buf)); + + /* force SO_REUSEADDR */ + wc_BioSetBindMode(abio, BIO_BIND_REUSEADDR); + + /* force NO_SIGPIPE and TCP_NODELAY */ + wc_BioSetSocketOptions(abio, BIO_OPT_TCP_NO_DELAY|BIO_OPT_IGN_SIGPIPE); + + /* First call to wc_BioAccept() sets up accept BIO */ + if (wc_BioDoAccept(abio) <= 0) { + printf("Error setting up accept\n"); + goto done; + } + + /* activate Thread */ + tcp_set_ready(args, wolfSSLPort, 0); + + /* Wait for incoming connection */ + if (wc_BioDoAccept(abio) <= 0) { + printf("Error accepting connection\n"); + goto done; + } + + /* Retrieve BIO for connection */ + cbio = wc_BioPop(abio); + wc_BioPuts(cbio, msg); + + /* Close accept BIO to refuse further connections */ + wc_BioFree(abio); + abio = 0; + + /* Read msg, send ack */ + do { + XMEMSET(buf, 0, sizeof(buf)); + r = wc_BioRead(cbio, buf, sizeof(buf)); + if (r <= 0) { + if (wc_BioShouldRetry(cbio)) { + printf("Retry read\n"); + continue; + } + printf("wc_BioRead failed\n"); + break; + } + if (r >= 3 && !XSTRNCMP("end", buf, 3)) { + printf("BioFullSrv, Client close connection\n"); + break; + } + + buf[r] = 0; + printf("BioFullSrv, Client sent: %s\n", buf); + wc_BioPuts(cbio, "Server ACK"); + } while (1); + +done: + if (abio != 0) + wc_BioFree(abio); + if (cbio != 0) + wc_BioFree(cbio); + + ((func_args*)args)->return_code = TEST_SUCCESS; + +#ifdef WOLFSSL_TIRTOS + fdCloseSession(Task_self()); +#endif + +#ifndef WOLFSSL_TIRTOS + return 0; +#endif +} + +static void test_client_full_bio(void* args) +{ + WOLFCRYPT_BIO* bio = 0; + char msg[64] = "Hello wolfssl!"; + char reply[1024]; +#ifdef TEST_IPV6 + SOCKET_T sockfd = 0; +#else + char ip[] = {127, 0, 0, 1}; +#endif + int input, port; + int msgSz = (int)strlen(msg); + + printf("client\n"); +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + + ((func_args*)args)->return_code = TEST_FAIL; + +#ifdef TEST_IPV6 + bio = wc_BioNew(wc_Bio_s_socket()); +#else + bio = wc_BioNew(wc_Bio_s_connect()); +#endif + if (bio == NULL) { + printf("wc_BioNew failed\n"); + goto done2; + } + + port = ((func_args*)args)->signal->port; + +#ifdef TEST_IPV6 + tcp_connect(&sockfd, wolfSSLIP, port, 0, NULL); + wc_BioSetFd(bio, sockfd, BIO_NOCLOSE); +#else + wc_BioSetConnIp(bio, ip); + wc_BioSetConnIntPort(bio, &port); + + printf("client do connect\n"); + /* start connection */ + input = (int)wc_BioDoConnect(bio); + if (input <= 0) { + printf("wc_BioDoConnect failed : %d\n", input); + goto done2; + } + printf("done\n"); +#endif + +read_again: + input = wc_BioRead(bio, reply, sizeof(reply)-1); + if (input <= 0) { + if (wc_BioShouldRetry(bio)) { + printf("Retry read\n"); + goto read_again; + } + printf("wc_BioRead failed"); + goto done2; + } + reply[input] = 0; + printf("BioFullCli, Server sent: %s\n", reply); + + if (wc_BioWrite(bio, msg, msgSz) != msgSz) { + printf("wc_BioWrite failed"); + goto done2; + } + +read_again2: + input = wc_BioRead(bio, reply, sizeof(reply)-1); + if (input <= 0) { + if (wc_BioShouldRetry(bio)) { + printf("Retry read\n"); + goto read_again2; + } + printf("wc_BioRead failed"); + goto done2; + } + + reply[input] = 0; + printf("BioFullCli, Server response: %s\n", reply); + + /* close */ + if (wc_BioWrite(bio, "end", 3) != 3) { + printf("wc_BioWrite failed"); + goto done2; + } + +done2: + if (bio != 0) + wc_BioFreeAll(bio); + + ((func_args*)args)->return_code = TEST_SUCCESS; + +#ifdef WOLFSSL_TIRTOS + fdCloseSession(Task_self()); +#endif +} +#endif /* TEST_IPV6 */ + + + /* BIO SSL test */ static THREAD_RETURN WOLFSSL_THREAD test_server_bio_ssl(void* args) { @@ -863,8 +1068,13 @@ static THREAD_RETURN WOLFSSL_THREAD test_server_bio_ssl(void* args) goto done; } +read_again: idx = wc_BioRead(ssl_bio, input, sizeof(input)-1); if (idx <= 0) { + if (wc_BioShouldRetry(ssl_bio)) { + printf("Retry read\n"); + goto read_again; + } printf("wc_BioWrite failed\n"); goto done; } @@ -977,8 +1187,13 @@ static void test_client_bio_ssl(void* args) goto done2; } +read_again: input = wc_BioRead(ssl_bio, reply, sizeof(reply)-1); if (input <= 0) { + if (wc_BioShouldRetry(ssl_bio)) { + printf("Retry read\n"); + goto read_again; + } printf("wc_BioRead failed"); goto done2; } @@ -1294,6 +1509,64 @@ static void test_wolfSSL_read_write_bio(void) #endif } +static void test_wolfSSL_read_write_bio_full(void) +{ +#ifdef TEST_IPV6 + /* nothing to do */ +#else +#ifdef HAVE_IO_TESTS_DEPENDENCIES + /* The unit testing for read and write shall happen simutaneously, since + * one can't do anything with one without the other. (Except for a failure + * test case.) This function will call all the others that will set up, + * execute, and report their test findings. + * + * Set up the success case first. This function will become the template + * for the other tests. This should eventually be renamed + * + * The success case isn't interesting, how can this fail? + * - Do not give the client context a CA certificate. The connect should + * fail. Do not need server for this? + * - Using NULL for the ssl object on server. Do not need client for this. + * - Using NULL for the ssl object on client. Do not need server for this. + * - Good ssl objects for client and server. Client write() without server + * read(). + * - Good ssl objects for client and server. Server write() without client + * read(). + * - Forgetting the password callback? + */ + tcp_ready ready; + func_args client_args; + func_args server_args; + THREAD_TYPE serverThread; + +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + + StartTCP(); + InitTcpReady(&ready); + + server_args.signal = &ready; + client_args.signal = &ready; + + start_thread(test_server_full_bio, &server_args, &serverThread); + wait_tcp_ready(&server_args); + test_client_full_bio(&client_args); + join_thread(serverThread); + + AssertTrue(client_args.return_code); + AssertTrue(server_args.return_code); + + FreeTcpReady(&ready); + +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + +#endif /* HAVE_IO_TESTS_DEPENDENCIES */ +#endif /* TEST_IPV6 */ +} + static void test_wolfSSL_read_write_bio_ssl(void) { #ifdef HAVE_IO_TESTS_DEPENDENCIES @@ -1345,7 +1618,7 @@ static void test_wolfSSL_read_write_bio_ssl(void) fdOpenSession(Task_self()); #endif -#endif +#endif /* HAVE_IO_TESTS_DEPENDENCIES */ } #endif /* OPENSSL_EXTRA */ @@ -2089,6 +2362,7 @@ void ApiTest(void) test_wolfSSL_read_write(); #if defined(OPENSSL_EXTRA) + test_wolfSSL_read_write_bio_full(); test_wolfSSL_read_write_bio(); test_wolfSSL_read_write_bio_ssl(); #endif diff --git a/wolfcrypt/src/bio.c b/wolfcrypt/src/bio.c index b12069999..35e12aab1 100644 --- a/wolfcrypt/src/bio.c +++ b/wolfcrypt/src/bio.c @@ -66,6 +66,35 @@ #include #include +static int wc_BioIntToStr(int i, char *str, int strSz){ + char const digit[] = "0123456789"; + int shift, count = 0; + + if (i < 0) + return -1; + + shift = i; + + do { + ++str; + shift = shift/10; + count++; + } while(shift); + + /* check size */ + if (strSz <= count) + return -1; + + *str = '\0'; + + do { + *--str = digit[i%10]; + i = i/10; + } while(i); + + return count; +} + WOLFCRYPT_BIO *wc_BioNew(WOLFCRYPT_BIO_METHOD *method) { WOLFCRYPT_BIO *bio; @@ -3008,7 +3037,7 @@ err: int wc_BioAccept(int sock, char **addr) { - int dsock = WOLFSSL_SOCKET_INVALID; + int dsock = WOLFSSL_SOCKET_INVALID, idx, ret; unsigned long l; struct { @@ -3031,6 +3060,7 @@ int wc_BioAccept(int sock, char **addr) if (sizeof(sa.len.i) != sizeof(sa.len.s) && !sa.len.i) { if (sa.len.s > sizeof(sa.from)) { WOLFSSL_ERROR(MEMORY_E); + dsock = WOLFSSL_SOCKET_INVALID; goto end; } @@ -3057,11 +3087,45 @@ int wc_BioAccept(int sock, char **addr) l = ntohl(sa.from.sa_in.sin_addr.s_addr); - XSNPRINTF(*addr, 24, "%d.%d.%d.%d:%d", - (unsigned char)(l >> 24L) & 0xff, - (unsigned char)(l >> 16L) & 0xff, - (unsigned char)(l >> 8L) & 0xff, - (unsigned char)(l) & 0xff, ntohs(sa.from.sa_in.sin_port)); + ret = wc_BioIntToStr((unsigned char)(l >> 24L) & 0xff, *addr, 24); + if (ret <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + dsock = WOLFSSL_SOCKET_INVALID; + goto end; + } + idx = ret; + *(*addr+(idx++)) = '.'; + ret = wc_BioIntToStr((unsigned char)(l >> 16L) & 0xff, *addr+idx, 24-idx); + if (ret <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + dsock = WOLFSSL_SOCKET_INVALID; + goto end; + } + idx += ret; + *(*addr+(idx++)) = '.'; + ret = wc_BioIntToStr((unsigned char)(l >> 8L) & 0xff, *addr+idx, 24-idx); + if (ret <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + dsock = WOLFSSL_SOCKET_INVALID; + goto end; + } + idx += ret; + *(*addr+(idx++)) = '.'; + ret = wc_BioIntToStr((unsigned char)(l) & 0xff, *addr+idx, 24-idx); + if (ret <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + dsock = WOLFSSL_SOCKET_INVALID; + goto end; + } + idx += ret; + *(*addr+(idx++)) = ':'; + ret = wc_BioIntToStr(ntohs(sa.from.sa_in.sin_port), *addr+idx, 24-idx); + if (ret <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + dsock = WOLFSSL_SOCKET_INVALID; + goto end; + } + end: return dsock; } @@ -3304,7 +3368,7 @@ again: } /* TCP NO DELAY */ - if (baccept->options & 1) { + if (baccept->options & BIO_OPT_TCP_NO_DELAY) { if (!wc_BioSetTcpNdelay(s, 1)) { #ifdef USE_WINDOWS_API closesocket(s); @@ -3317,7 +3381,7 @@ again: } /* IGNORE SIGPIPE */ - if (baccept->options & 2) { + if (baccept->options & BIO_OPT_IGN_SIGPIPE) { if (!wc_BioSetTcpNsigpipe(s, 1)) { #ifdef USE_WINDOWS_API closesocket(s); @@ -4078,9 +4142,38 @@ static long wc_BioConn_ctrl(WOLFCRYPT_BIO *bio, int cmd, long num, void *ptr) else if (num == 2) { char buf[16]; unsigned char *p = ptr; + int idx, res; - XSNPRINTF(buf, sizeof(buf), "%d.%d.%d.%d", - p[0], p[1], p[2], p[3]); + res = wc_BioIntToStr(p[0], buf, sizeof(buf)); + if (res <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + ret = -1; + break; + } + idx = res; + buf[idx++] = '.'; + res = wc_BioIntToStr(p[1], buf+idx, sizeof(buf)-idx); + if (res <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + ret = -1; + break; + } + idx += res; + buf[idx++] = '.'; + res = wc_BioIntToStr(p[2], buf+idx, sizeof(buf)-idx); + if (res <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + ret = -1; + break; + } + idx += res; + buf[idx++] = '.'; + res = wc_BioIntToStr(p[3], buf+idx, sizeof(buf)-idx); + if (res <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + ret = -1; + break; + } if (conn->pHostname != NULL) XFREE(conn->pHostname, 0, DYNAMIC_TYPE_OPENSSL); @@ -4097,8 +4190,15 @@ static long wc_BioConn_ctrl(WOLFCRYPT_BIO *bio, int cmd, long num, void *ptr) } else if (num == 3) { char buf[6]; + int res; + + res = wc_BioIntToStr(*(int *)ptr, buf, sizeof(buf)); + if (res <= 0) { + WOLFSSL_ERROR(BAD_FUNC_ARG); + ret = -1; + break; + } - XSNPRINTF(buf, sizeof(buf), "%d", *(int *)ptr); if (conn->pPort != NULL) XFREE(conn->pPort, 0, DYNAMIC_TYPE_OPENSSL); diff --git a/wolfssl/test.h b/wolfssl/test.h index 43ddae1db..dc4b2ac46 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -828,6 +828,54 @@ static INLINE void udp_accept(SOCKET_T* sockfd, SOCKET_T* clientfd, *clientfd = *sockfd; } +static INLINE void tcp_set_ready(func_args* args, word16 port, int ready_file) +{ +#if defined(_POSIX_THREADS) && defined(NO_MAIN_DRIVER) && !defined(__MINGW32__) + /* signal ready to tcp_accept */ + { + tcp_ready* ready = args->signal; + if (ready) { + pthread_mutex_lock(&ready->mutex); + ready->ready = 1; + ready->port = port; + pthread_cond_signal(&ready->cond); + pthread_mutex_unlock(&ready->mutex); + } + } +#elif defined (WOLFSSL_TIRTOS) + /* Need mutex? */ + tcp_ready* ready = args->signal; + ready->ready = 1; + ready->port = port; +#endif + + if (ready_file) { +#ifndef NO_FILESYSTEM + FILE* srf = NULL; + tcp_ready* ready = args ? args->signal : NULL; + + if (ready) { + srf = fopen(ready->srfName, "w"); + + if (srf) { + /* let's write port sever is listening on to ready file + external monitor can then do ephemeral ports by passing + -p 0 to server on supported platforms with -R ready_file + client can then wait for existence of ready_file and see + which port the server is listening on. */ + fprintf(srf, "%d\n", (int)port); + fclose(srf); + } + } +#endif + } + + (void)port; + (void)ready_file; + (void)args; +} + + static INLINE void tcp_accept(SOCKET_T* sockfd, SOCKET_T* clientfd, func_args* args, word16 port, int useAnyAddr, int udp, int ready_file, int do_listen) @@ -843,45 +891,7 @@ static INLINE void tcp_accept(SOCKET_T* sockfd, SOCKET_T* clientfd, if(do_listen) { tcp_listen(sockfd, &port, useAnyAddr, udp); - #if defined(_POSIX_THREADS) && defined(NO_MAIN_DRIVER) && !defined(__MINGW32__) - /* signal ready to tcp_accept */ - { - tcp_ready* ready = args->signal; - if (ready) { - pthread_mutex_lock(&ready->mutex); - ready->ready = 1; - ready->port = port; - pthread_cond_signal(&ready->cond); - pthread_mutex_unlock(&ready->mutex); - } - } - #elif defined (WOLFSSL_TIRTOS) - /* Need mutex? */ - tcp_ready* ready = args->signal; - ready->ready = 1; - ready->port = port; - #endif - - if (ready_file) { - #ifndef NO_FILESYSTEM - FILE* srf = NULL; - tcp_ready* ready = args ? args->signal : NULL; - - if (ready) { - srf = fopen(ready->srfName, "w"); - - if (srf) { - /* let's write port sever is listening on to ready file - external monitor can then do ephemeral ports by passing - -p 0 to server on supported platforms with -R ready_file - client can then wait for existence of ready_file and see - which port the server is listening on. */ - fprintf(srf, "%d\n", (int)port); - fclose(srf); - } - } - #endif - } + tcp_set_ready(args, port, ready_file); } *clientfd = accept(*sockfd, (struct sockaddr*)&client, @@ -891,7 +901,6 @@ static INLINE void tcp_accept(SOCKET_T* sockfd, SOCKET_T* clientfd, } } - static INLINE void tcp_set_nonblocking(SOCKET_T* sockfd) { #ifdef USE_WINDOWS_API diff --git a/wolfssl/wolfcrypt/bio.h b/wolfssl/wolfcrypt/bio.h index 7465a2012..6f543742a 100644 --- a/wolfssl/wolfcrypt/bio.h +++ b/wolfssl/wolfcrypt/bio.h @@ -277,6 +277,12 @@ enum WS_BIO_BIND { BIO_BIND_REUSEADDR = 2, }; +enum WC_BIO_OPT { + BIO_OPT_NONE = 0, + BIO_OPT_TCP_NO_DELAY = 1, + BIO_OPT_IGN_SIGPIPE = 2, +}; + WOLFSSL_API WOLFCRYPT_BIO *wc_BioNew(WOLFCRYPT_BIO_METHOD *method); WOLFSSL_API int wc_BioFree(WOLFCRYPT_BIO *bio); WOLFSSL_API void wc_BioFreeAll(WOLFCRYPT_BIO *bio); diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 3e91a3d98..13911d9f8 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -212,10 +212,8 @@ #define XSTRNCAT(s1,s2,n) strncat((s1),(s2),(n)) #ifndef USE_WINDOWS_API #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) - #define XSNPRINTF snprintf #else #define XSTRNCASECMP(s1,s2,n) _strnicmp((s1),(s2),(n)) - #define XSNPRINTF _snprintf #endif #if defined(WOLFSSL_CERT_EXT) || defined(HAVE_ALPN)