diff --git a/tls/server-tls-nonblocking.c b/tls/server-tls-nonblocking.c index 6dc17121..4a27028b 100644 --- a/tls/server-tls-nonblocking.c +++ b/tls/server-tls-nonblocking.c @@ -30,6 +30,7 @@ #include #include #include +#include /* wolfSSL */ #include @@ -37,62 +38,139 @@ #include #define DEFAULT_PORT 11111 -#define SELECT_WAIT_SEC 1 +#define SELECT_WAIT_mSEC 1000 #define CERT_FILE "../certs/server-cert.pem" #define KEY_FILE "../certs/server-key.pem" -enum { - TEST_SELECT_FAIL, - TEST_TIMEOUT, - TEST_RECV_READY, - TEST_SEND_READY, - TEST_ERROR_READY -}; +typedef enum { + SV_IDLE, /* Free to use */ + SV_BEGIN, /* ready to begin */ + SV_ACCEPT, + SV_SSL_ACCEPT, + SV_SSL_READ, + SV_SSL_WRITE, + SV_SSL_SHUTDOWN +} SV_STATUS; + +typedef enum { + NB_CONTINUE, + NB_COMPLETE, + NB_FAITAL, +} NV_RETURN; + +typedef struct { + /* core context */ + SV_STATUS stat; + int sock; + + /* application context */ + int sockfd; + int connd; + WOLFSSL_CTX *ctx; + WOLFSSL *ssl; +} SV_CTX; + +#define MAX_CONTEXT 10 +static SV_CTX sv_tbl[MAX_CONTEXT]; + +static void sv_init() +{ + int i; + for(i = 0; i < MAX_CONTEXT; i++) { + sv_tbl[i].stat = SV_IDLE; + } +} + +static void sv_sock(SV_CTX *current, int sock) +{ + current->sock = sock; +} -static int tcp_select(SOCKET_T socketfd, int to_sec, int rx) +static SV_CTX * sv_fork(SV_CTX *current, int sock) { - fd_set fds, errfds; - fd_set* recvfds = NULL; - fd_set* sendfds = NULL; - SOCKET_T nfds = socketfd + 1; - struct timeval timeout; - int result; - - FD_ZERO(&fds); - FD_SET(socketfd, &fds); - FD_ZERO(&errfds); - FD_SET(socketfd, &errfds); - - if (rx) - recvfds = &fds; - else - sendfds = &fds; - - result = select(nfds, recvfds, sendfds, &errfds, &timeout); - - if (result == 0) - return TEST_TIMEOUT; - else if (result > 0) { - if (FD_ISSET(socketfd, &fds)) { - if (rx) - return TEST_RECV_READY; - else - return TEST_SEND_READY; + int i; + for (i = 0; i < MAX_CONTEXT; i++) { + if (sv_tbl[i].stat == SV_IDLE) { + sv_tbl[i] = *current; + sv_tbl[i].sock = sock; + #ifdef SV_VERBOUSE + printf("sv_fork: %lx, sock = %d\n", (unsigned long)&sv_tbl[i], sock); + #endif + return &sv_tbl[i]; } - else if (FD_ISSET(socketfd, &errfds)) - return TEST_ERROR_READY; } + #ifdef SV_VERBOUSE + printf("sv_fork: NONE\n"); + #endif + return NULL; +} - return TEST_SELECT_FAIL; +static void sv_new() +{ + int i; + for (i = 0; i < MAX_CONTEXT; i++) { + if (sv_tbl[i].stat == SV_IDLE) { + sv_tbl[i].stat = SV_BEGIN; + sv_tbl[i].sock = -1; + #ifdef SV_VERBOUSE + printf("sv_new: %d, %lx\n", i, (unsigned long)&sv_tbl[i]); + #endif + return; + } + } + #ifdef SV_VERBOUSE + printf("sv_new: NONE\n"); + #endif + return; } +static void sv_free(SV_CTX *sv_ctx) +{ + #ifdef SV_VERBOUSE + printf("sv_free: %lx\n", (unsigned long)sv_ctx); + #endif + sv_ctx->stat = SV_IDLE; +} + +#define WAIT_mSEC 1000 + +static int scan = 0; + +static SV_CTX *sv_poll() { + int i; + int nfd = 0; + struct pollfd fds[MAX_CONTEXT]; + SV_CTX *next = NULL; -int main() + /* Scan context table */ + for (i = 0; i < MAX_CONTEXT; i++) { + if (sv_tbl[i].stat >= SV_BEGIN && sv_tbl[i].sock != SOCKET_INVALID) { + fds[nfd].events = POLLIN | POLLOUT; + fds[nfd].fd = sv_tbl[i].sock; + nfd++; + } + } + + poll(fds, nfd, WAIT_mSEC); + + for (i = 0; i < MAX_CONTEXT; i++) { + if(sv_tbl[scan].stat != SV_IDLE) { + next = &sv_tbl[scan]; + scan = (scan + 1) % MAX_CONTEXT; + break; + } + scan = (scan + 1) % MAX_CONTEXT; + } + return next; +} + + +int sv_main(SV_CTX *sv_ctx) { - int ret, err; - int sockfd = SOCKET_INVALID; - int connd = SOCKET_INVALID; + int ret = NB_FAITAL; + int err; + struct sockaddr_in servAddr; struct sockaddr_in clientAddr; socklen_t size = sizeof(clientAddr); @@ -101,17 +179,23 @@ int main() int shutdown = 0; const char* reply = "I hear ya fa shizzle!\n"; - /* declare wolfSSL objects */ - WOLFSSL_CTX* ctx = NULL; - WOLFSSL* ssl = NULL; - + if(sv_ctx == NULL) + return NB_CONTINUE; +switch(sv_ctx->stat) { +case SV_BEGIN: - /* Initialize wolfSSL */ - wolfSSL_Init(); - //wolfSSL_Debugging_ON(); - + /* Initialize application context. Note they are with sv_ctx */ + #define sockfd sv_ctx->sockfd + #define connd sv_ctx->connd + #define ctx sv_ctx->ctx + #define ssl sv_ctx->ssl + sockfd = SOCKET_INVALID; + connd = SOCKET_INVALID; + ctx = NULL; + ssl = NULL; + /* Create a socket that uses an internet IPv4 address, * Sets the socket to be stream based (TCP), * 0 means choose the default protocol. */ @@ -121,6 +205,8 @@ int main() goto exit; } + sv_sock(sv_ctx, sockfd); /* attach socket to the server context */ + /* Set the socket options to use nonblocking I/O */ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { fprintf(stderr, "ERROR: failed to set socket options\n"); @@ -183,23 +269,35 @@ int main() /* Continue to accept clients until shutdown is issued */ while (!shutdown) { printf("Waiting for a connection...\n"); + sv_ctx->stat = SV_ACCEPT; + FALL_THROUGH; +case SV_ACCEPT: /* Accept client connections */ - while ((connd = accept(sockfd, (struct sockaddr*)&clientAddr, &size)) + if((connd = accept(sockfd, (struct sockaddr*)&clientAddr, &size)) == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - /* non-blocking, wait for read activity on socket */ - tcp_select(sockfd, SELECT_WAIT_SEC, 1); - continue; - } - else if (errno == EINPROGRESS || errno == EALREADY) { - break; + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == EALREADY) { + return NB_CONTINUE; } fprintf(stderr, "ERROR: failed to accept the connection\n\n"); ret = -1; goto exit; } + if((sv_ctx = sv_fork(sv_ctx, connd)) == NULL) { + fprintf(stderr, "ERROR: failed to folk the context\n"); + ret = -1; + goto exit; + } + + /* Set the socket options to use nonblocking I/O */ + if (fcntl(connd, F_SETFL, O_NONBLOCK) == -1) { + fprintf(stderr, "ERROR: failed to set socket options\n"); + ret = -1; + goto exit; + } + /* Create a WOLFSSL object */ if ((ssl = wolfSSL_new(ctx)) == NULL) { fprintf(stderr, "ERROR: failed to create WOLFSSL object\n"); @@ -215,31 +313,35 @@ int main() /* Establish TLS connection */ printf("wolfSSL_accepting\n"); - - do { - ret = wolfSSL_accept(ssl); - err = wolfSSL_get_error(ssl, ret); - if (err == WOLFSSL_ERROR_WANT_READ) - tcp_select(sockfd, SELECT_WAIT_SEC, 1); - } while (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE); - if (ret != WOLFSSL_SUCCESS) { + + sv_ctx->stat = SV_SSL_ACCEPT; + FALL_THROUGH; + +case SV_SSL_ACCEPT: + ret = wolfSSL_accept(ssl); + err = wolfSSL_get_error(ssl, ret); + if (ret != WOLFSSL_SUCCESS && + (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE)) + return NB_CONTINUE; + else if (ret != WOLFSSL_SUCCESS) { fprintf(stderr, "wolfSSL_accept error %d (%d)\n", err, ret); goto exit; } printf("client connected successfully\n"); + sv_ctx->stat = SV_SSL_READ; + FALL_THROUGH; + +case SV_SSL_READ: /* read the client data into our buff array */ memset(buff, 0, sizeof(buff)); - do { - ret = wolfSSL_read(ssl, buff, sizeof(buff)-1); - err = wolfSSL_get_error(ssl, ret); - - if (err == WOLFSSL_ERROR_WANT_READ) - tcp_select(sockfd, SELECT_WAIT_SEC, 1); - } - while (err == WOLFSSL_ERROR_WANT_READ); - if (ret < 0) { + + ret = wolfSSL_read(ssl, buff, sizeof(buff)-1); + err = wolfSSL_get_error(ssl, ret); + if (ret < 0 && err == WOLFSSL_ERROR_WANT_READ) + return NB_CONTINUE; + else if (ret < 0) { fprintf(stderr, "ERROR %d: failed to read\n", ret); goto exit; } @@ -252,43 +354,55 @@ int main() printf("Shutdown command issued!\n"); shutdown = 1; } + sv_ctx->stat = SV_SSL_WRITE; + FALL_THROUGH; +case SV_SSL_WRITE: /* Write our reply into buff */ memset(buff, 0, sizeof(buff)); memcpy(buff, reply, strlen(reply)); len = strnlen(buff, sizeof(buff)); /* Reply back to the client */ - do { - ret = wolfSSL_write(ssl, reply, len); - err = wolfSSL_get_error(ssl, ret); - sleep(1); - } - while (err == WOLFSSL_ERROR_WANT_WRITE); - if (ret < 0) { + ret = wolfSSL_write(ssl, reply, len); + err = wolfSSL_get_error(ssl, ret); + if (ret < 0 && err == WOLFSSL_ERROR_WANT_WRITE) + return NB_CONTINUE; + else if (ret < 0) { fprintf(stderr, "ERROR %d: failed to write\n", ret); goto exit; } + sv_ctx->stat = SV_SSL_SHUTDOWN; + FALL_THROUGH; + +case SV_SSL_SHUTDOWN: /* send close notify */ - do { - ret = wolfSSL_shutdown(ssl); - err = wolfSSL_get_error(ssl, 0); - if (err == WOLFSSL_ERROR_WANT_READ) - tcp_select(sockfd, SELECT_WAIT_SEC, 1); - } while (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE); + ret = wolfSSL_shutdown(ssl); + err = wolfSSL_get_error(ssl, 0); + if (ret != WOLFSSL_SUCCESS && err == WOLFSSL_ERROR_WANT_READ) + return NB_CONTINUE; /* Cleanup after this connection */ wolfSSL_free(ssl); /* Free the wolfSSL object */ close(connd); /* Close the connection to the client */ + /* Parent context keeps sockfd */ connd = SOCKET_INVALID; + + sv_ctx->stat = SV_IDLE; + FALL_THROUGH; + +case SV_IDLE: + return NB_CONTINUE; + } printf("Shutdown complete\n"); +} /* End of switch(sv_ctx->stat) */ - ret = 0; +ret = NB_COMPLETE; exit: /* Cleanup and return */ @@ -301,6 +415,36 @@ int main() if (ctx) wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */ wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */ - + return ret; } + + +int main(int argc, char ** argv) +{ + SV_CTX *sv_ctx; + + /* Initialize wolfSSL */ + wolfSSL_Init(); + // wolfSSL_Debugging_ON(); + + + sv_init(); + sv_new(); /* get a new context so that sv_poll can pick it up */ + + /* Super Loop */ + while(1) { + switch (sv_main(sv_ctx = sv_poll())) { + case NB_CONTINUE: + break; + case NB_COMPLETE: + sv_free(sv_ctx); + sv_new(); + break; + case NB_FAITAL: + sv_free(sv_ctx); + break; + } + } + return 0; +}