/* server-tls-epoll-perf.c * * Copyright (C) 2006-2020 wolfSSL Inc. * * This file is part of wolfSSL. (formerly known as CyaSSL) * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA *============================================================================= * * This is an example of a TCP Server that uses non-blocking input and output to * handle a large number of connections. Reports performance figures. */ #include #include #include #include /* Default port to listen on. */ #define DEFAULT_PORT 11111 /* The number of EPOLL events to accept and process at one time. */ #define EPOLL_NUM_EVENTS 10 /* The number of concurrent connections to support. */ #define SSL_NUM_CONN 15 /* The number of bytes to read from client. */ #define NUM_READ_BYTES 16384 /* The number of bytes to write to client. */ #define NUM_WRITE_BYTES 16384 /* The maximum number of bytes to send in a run. */ #define MAX_BYTES -1 /* The maximum number of connections to perform in this run. */ #define MAX_CONNECTIONS 100 /* The maximum length of the queue of pending connections. */ #define NUM_CLIENTS 100 /* The number of wolfSSL events to accept and process at one time. */ #define MAX_WOLF_EVENTS 10 /* The command line options. */ #define OPTIONS "?p:v:al:c:k:A:n:N:R:W:B:" /* The default server certificate. */ #define SVR_CERT "../certs/server-cert.pem" /* The default server private key. */ #define SVR_KEY "../certs/server-key.pem" /* The default certificate/CA file for the client. */ #define CLI_CERT "../certs/client-cert.pem" /* The states of the SSL connection. */ typedef enum SSLState { ACCEPT, READ, WRITE, CLOSED } SSLState; /* Type for the SSL connection data. */ typedef struct SSLConn SSLConn; /* Data for each active connection. */ struct SSLConn { /* The socket listening on, reading from and writing to. */ int sockfd; /* The wolfSSL object to perform TLS communications. */ WOLFSSL* ssl; /* The current state of the SSL/TLS connection. */ SSLState state; /* Previous SSL connection data object. */ SSLConn* prev; /* Next SSL connection data object. */ SSLConn* next; }; /* The information about SSL/TLS connections. */ typedef struct SSLConn_CTX { /* An array of active connections. */ SSLConn* sslConn; /* Free list. */ SSLConn* freeSSLConn; /* Maximum number of active connections. */ int numConns; /* Count of currently active connections. */ int cnt; /* Accepting new connections. */ int accepting; /* Size of the client data buffer. */ int bufferLen; /* Number of bytes to write. */ int replyLen; /* Number of connections handled. */ int numConnections; /* Number of resumed connections handled. */ int numResumed; /* Maximum number of connections to perform. */ int maxConnections; /* Total number of bytes read. */ int totalReadBytes; /* Total number of bytes written. */ int totalWriteBytes; /* Maximum number of bytes to read/write. */ int maxBytes; /* Total time handling accept. */ double acceptTime; /* Total time handling accept - resumed connections. */ double resumeTime; #ifdef WOLFSSL_ASYNC_CRYPT /* Total time handling asynchronous operations. */ double asyncTime; #endif /* Total time handling reading. */ double readTime; /* Total time handling writing. */ double writeTime; /* Total time handling connections. */ double totalTime; } SSLConn_CTX; static void SSLConn_Free(SSLConn_CTX* ctx); static void SSLConn_Close(SSLConn_CTX* ctx, SSLConn* sslConn); static void SSLConn_FreeSSLConn(SSLConn_CTX* ctx); /* The index of the command line option. */ int myoptind = 0; /* The current command line option. */ char* myoptarg = NULL; #ifdef WOLFSSL_ASYNC_CRYPT /* Global device identifier. */ static int devId = INVALID_DEVID; #endif /* The data to reply with. */ static char reply[NUM_WRITE_BYTES]; /* Get the wolfSSL server method function for the specified version. * * version Protocol version to use. * returns The server method function or NULL when version not supported. */ static wolfSSL_method_func SSL_GetMethod(int version, int allowDowngrade) { wolfSSL_method_func method = NULL; switch (version) { #ifndef NO_OLD_TLS #ifdef WOLFSSL_ALLOW_SSLV3 case 0: method = wolfSSLv3_server_method_ex; break; #endif #ifndef NO_TLS #ifdef WOLFSSL_ALLOW_TLSV10 case 1: method = wolfTLSv1_server_method_ex; break; #endif #ifndef NO_OLD_TLS case 2: method = wolfTLSv1_1_server_method_ex; break; #endif #endif #endif #ifndef NO_TLS case 3: method = allowDowngrade ? wolfSSLv23_server_method_ex : wolfTLSv1_2_server_method_ex; break; #endif } return method; } /* Write data to a client. * * ssl The wolfSSL object. * reply The data to send to the client. * replyLen The length of the data to send to the client. * totalBytes The total number of bytes sent to clients. * writeTime The amount of time spent writing data to client. * returns 0 on failure, 1 on success, 2 on want read and 3 on want write. */ static int SSL_Write(WOLFSSL* ssl, char* reply, int replyLen, int* totalBytes, double* writeTime) { int rwret = 0; int error; double start; start = current_time(1); rwret = wolfSSL_write(ssl, reply, replyLen); *writeTime += current_time(0) - start; if (rwret == 0) { fprintf(stderr, "The client has closed the connection - write!\n"); return 0; } if (rwret > 0) *totalBytes += rwret; if (rwret == replyLen) return 1; error = wolfSSL_get_error(ssl, 0); if (error == SSL_ERROR_WANT_READ) return 2; if (error == SSL_ERROR_WANT_WRITE) return 3; if (error == WC_PENDING_E) return 4; if (error == 0) return 1; /* Cannot do anything about other errors. */ fprintf(stderr, "wolfSSL_write error = %d\n", error); return 0; } /* Reads data from a client. * * ssl The wolfSSL object. * buffer The buffer to place client data into. * len The length of the buffer. * totalBytes The total number of bytes read from clients. * readTime The amount of time spent reading data from client. * returns 0 on failure, 1 on success, 2 on want read and 3 on want write. */ static int SSL_Read(WOLFSSL* ssl, char* buffer, int len, int* totalBytes, double* readTime) { int rwret = 0; int error; double start; start = current_time(1); rwret = wolfSSL_read(ssl, buffer, len); *readTime += current_time(0) - start; if (rwret == 0) { return 0; } if (rwret > 0) *totalBytes += rwret; error = wolfSSL_get_error(ssl, 0); if (error == SSL_ERROR_WANT_READ) return 2; if (error == SSL_ERROR_WANT_WRITE) return 3; if (error == WC_PENDING_E) return 4; if (error == 0) return 1; /* Cannot do anything about other errors. */ fprintf(stderr, "wolfSSL_read error = %d\n", error); return 0; } /* Accept/negotiate a secure connection. * * ssl The wolfSSL object. * acceptTime The amount of time spent accepting a client. * resumeTime The amount of time spent resuming a connection with a client. * returns 0 on failure, 1 on success, 2 on want read and 3 on want write. */ static int SSL_Accept(WOLFSSL* ssl, double* acceptTime, double* resumeTime) { int ret; int error; double start; /* Accept the connection. */ start = current_time(1); ret = wolfSSL_accept(ssl); if (!wolfSSL_session_reused(ssl)) *acceptTime += current_time(0) - start; else *resumeTime += current_time(0) - start; if (ret == 0) { fprintf(stderr, "The client has closed the connection - accept!\n"); return 0; } if (ret == SSL_SUCCESS) return 1; error = wolfSSL_get_error(ssl, 0); if (error == SSL_ERROR_WANT_READ) return 2; if (error == SSL_ERROR_WANT_WRITE) return 3; if (error == WC_PENDING_E) return 4; /* Cannot do anything about other errors. */ fprintf(stderr, "wolfSSL_accept error = %d (%p)\n", error, ssl); return 0; } /* Create a new SSL/TLS connection data object. * * max The maximum number of concurrent connections. * bufferLen The number of data bytes to read from client. * replyLen The number of data bytes to write to client. * maxConns The number of connections to process this run. * -1 indicates no maximum. * maxBytes The number of bytes to send this run. * -1 indicates no maximum. * returns an allocated and initialized connection data object or NULL on error. */ static SSLConn_CTX* SSLConn_New(int numConns, int bufferLen, int replyLen, int maxConns, int maxBytes) { SSLConn_CTX* ctx; ctx = (SSLConn_CTX*)malloc(sizeof(*ctx)); if (ctx == NULL) return NULL; memset(ctx, 0, sizeof(*ctx)); ctx->numConns = numConns; ctx->bufferLen = bufferLen; ctx->replyLen = replyLen; ctx->maxConnections = maxConns; ctx->maxBytes = maxBytes; ctx->sslConn = NULL; return ctx; } /* Free the SSL/TLS connection data. * * ctx The connection data. */ static void SSLConn_Free(SSLConn_CTX* ctx) { if (ctx == NULL) return; while (ctx->sslConn != NULL) SSLConn_Close(ctx, ctx->sslConn); SSLConn_FreeSSLConn(ctx); free(ctx); } /* Close an active connection. * * ctx The SSL/TLS connection data. * sslConn The SSL connection data object. */ static void SSLConn_Close(SSLConn_CTX* ctx, SSLConn* sslConn) { if (sslConn->state == CLOSED) return; /* Display cipher suite all connection will use. */ if (ctx->numConnections == 0) { WOLFSSL_CIPHER* cipher; cipher = wolfSSL_get_current_cipher(sslConn->ssl); printf("SSL cipher suite is %s\n", wolfSSL_CIPHER_get_name(cipher)); } if (wolfSSL_session_reused(sslConn->ssl)) ctx->numResumed++; ctx->numConnections++; sslConn->state = CLOSED; /* Take it out of the double-linked list. */ if (ctx->sslConn == sslConn) ctx->sslConn = sslConn->next; if (sslConn->next != NULL) sslConn->next->prev = sslConn->prev; if (sslConn->prev != NULL) sslConn->prev->next = sslConn->next; /* Put object at head of free list */ sslConn->next = ctx->freeSSLConn; sslConn->prev = NULL; ctx->freeSSLConn = sslConn; ctx->cnt--; } /* Free the SSL/TLS connections that are closed. * * ctx The connection data. */ static void SSLConn_FreeSSLConn(SSLConn_CTX* ctx) { SSLConn* sslConn = ctx->freeSSLConn; ctx->freeSSLConn = NULL; while (sslConn != NULL) { SSLConn* next = sslConn->next; wolfSSL_free(sslConn->ssl); close(sslConn->sockfd); free(sslConn); sslConn = next; } } /* Checks whether this run is done i.e. maximum number of connections or bytes * have been server. * * ctx The SSL/TLS connection data. * returns 1 if the run is done or 0 otherwise. */ static int SSLConn_Done(SSLConn_CTX* ctx) { if (ctx->maxConnections > 0) return (ctx->numConnections >= ctx->maxConnections); return (ctx->totalWriteBytes >= ctx->maxBytes) && (ctx->totalReadBytes >= ctx->maxBytes); } /* Accepts a new connection. * * ctx The SSL/TLS connection data. * sslCtx The SSL/TLS context. * sockfd The socket file descriptor to accept on. * sslConn The newly create SSL connection data object. * returns EXIT_SUCCESS if a new connection was accepted or EXIT_FAILURE * otherwise. */ static int SSLConn_Accept(SSLConn_CTX* ctx, WOLFSSL_CTX* sslCtx, socklen_t sockfd, SSLConn** sslConn) { struct sockaddr_in clientAddr = {0}; socklen_t size = sizeof(clientAddr); SSLConn* conn; if (ctx->cnt == ctx->numConns) { fprintf(stderr, "ERROR: Too many connections!\n"); return EXIT_FAILURE; } conn = malloc(sizeof(*conn)); if (conn == NULL) return EXIT_FAILURE; /* Accept the client connection. */ conn->sockfd = accept(sockfd, (struct sockaddr *)&clientAddr, &size); if (conn->sockfd == -1) { free(conn); fprintf(stderr, "ERROR: failed to accept\n"); return EXIT_FAILURE; } /* Set the new socket to be non-blocking. */ fcntl(conn->sockfd, F_SETFL, O_NONBLOCK); /* Setup SSL/TLS connection. */ if ((conn->ssl = wolfSSL_new(sslCtx)) == NULL) { free(conn); fprintf(stderr, "wolfSSL_new error.\n"); return EXIT_FAILURE; } /* Set the socket to communicate over into the wolfSSL object. */ wolfSSL_set_fd(conn->ssl, conn->sockfd); conn->state = ACCEPT; conn->next = ctx->sslConn; conn->prev = NULL; if (ctx->sslConn != NULL) ctx->sslConn->prev = conn; ctx->sslConn = conn; ctx->cnt++; *sslConn = conn; return EXIT_SUCCESS; } /* Read/write from/to client at the specified socket. * * ctx The SSL/TLS connection data. * sslConn The SSL connection data object. * returns EXIT_FAILURE on failure and EXIT_SUCCESS otherwise. */ static int SSLConn_ReadWrite(SSLConn_CTX* ctx, SSLConn* sslConn) { int ret; int len; switch (sslConn->state) { case ACCEPT: /* Perform TLS handshake. */ ret = SSL_Accept(sslConn->ssl, &ctx->acceptTime, &ctx->resumeTime); if (ret == 0) { printf("ERROR: Accept failed\n"); SSLConn_Close(ctx, sslConn); return EXIT_FAILURE; } if (ret == 1) { sslConn->state = READ; } break; case READ: { char buffer[NUM_READ_BYTES]; len = ctx->bufferLen; if (ctx->maxBytes > 0) { len = min(len, ctx->maxBytes - ctx->totalReadBytes); } if (len == 0) break; /* Read application data. */ ret = SSL_Read(sslConn->ssl, buffer, len, &ctx->totalReadBytes, &ctx->readTime); if (ret == 0) { SSLConn_Close(ctx, sslConn); return EXIT_FAILURE; } if (ret != 1) break; sslConn->state = WRITE; } case WRITE: len = ctx->replyLen; if (ctx->maxBytes > 0) { len = min(len, ctx->maxBytes - ctx->totalWriteBytes); } if (len == 0) break; /* Write application data. */ ret = SSL_Write(sslConn->ssl, reply, len, &ctx->totalWriteBytes, &ctx->writeTime); if (ret == 0) { printf("ERROR: Write failed\n"); SSLConn_Close(ctx, sslConn); return EXIT_FAILURE; } if (ret == 1) sslConn->state = READ; break; case CLOSED: break; } return EXIT_SUCCESS; } /* Print the connection statistics. * * ctx The SSL/TLS connection data. */ static void SSLConn_PrintStats(SSLConn_CTX* ctx) { fprintf(stderr, "wolfSSL Server Benchmark %d bytes\n" "\tNum Conns : %9d\n" "\tTotal : %9.3f ms\n" "\tTotal Avg : %9.3f ms\n" "\tt/s : %9.3f\n" "\tAccept : %9.3f ms\n" "\tAccept Avg : %9.3f ms\n", ctx->replyLen, ctx->numConnections - ctx->numResumed, ctx->totalTime * 1000, ctx->totalTime * 1000 / ctx->numConnections, ctx->numConnections / ctx->totalTime, ctx->acceptTime * 1000, ctx->acceptTime * 1000 / (ctx->numConnections - ctx->numResumed)); if (ctx->numResumed > 0) { fprintf(stderr, "\tResumed Conns : %9d\n" "\tResume : %9.3f ms\n" "\tResume Avg : %9.3f ms\n", ctx->numResumed, ctx->resumeTime * 1000, ctx->resumeTime * 1000 / ctx->numResumed); } #ifdef WOLFSSL_ASYNC_CRYPT fprintf(stderr, "\tAsync : %9.3f ms\n" "\tAsync Avg : %9.3f ms\n", ctx->asyncTime * 1000, ctx->asyncTime * 1000 / ctx->numConnections); #endif fprintf(stderr, "\tTotal Read bytes : %9d bytes\n" "\tTotal Write bytes : %9d bytes\n" "\tRead : %9.3f ms (%9.3f MBps)\n" "\tWrite : %9.3f ms (%9.3f MBps)\n", ctx->totalReadBytes, ctx->totalWriteBytes, ctx->readTime * 1000, ctx->totalReadBytes / ctx->readTime / 1024 / 1024, ctx->writeTime * 1000, ctx->totalWriteBytes / ctx->writeTime / 1024 / 1024 ); } /* Initialize the wolfSSL library and create a wolfSSL context. * * version The protocol version. * cert The server's certificate. * key The server's private key matching the certificate. * verifyCert The certificate for client authentication. * cipherList The list of negotiable ciphers. * wolfsslCtx The new wolfSSL context object. * returns EXIT_SUCCESS when a wolfSSL context object is created and * EXIT_FAILURE otherwise. */ static int WolfSSLCtx_Init(int version, int allowDowngrade, char* cert, char* key, char* verifyCert, char* cipherList, WOLFSSL_CTX** wolfsslCtx) { WOLFSSL_CTX* ctx; wolfSSL_method_func method = NULL; method = SSL_GetMethod(version, allowDowngrade); if (method == NULL) return(EXIT_FAILURE); /* Create and initialize WOLFSSL_CTX structure */ if ((ctx = wolfSSL_CTX_new(method(NULL))) == NULL) { fprintf(stderr, "wolfSSL_CTX_new error.\n"); return(EXIT_FAILURE); } #ifdef WOLFSSL_ASYNC_CRYPT if (wolfAsync_DevOpen(&devId) != 0) { fprintf(stderr, "Async device open failed\nRunning without async\n"); } wolfSSL_CTX_UseAsync(ctx, devId); #endif /* Load server certificate into WOLFSSL_CTX */ if (wolfSSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != SSL_SUCCESS) { fprintf(stderr, "Error loading %s, please check the file.\n", cert); wolfSSL_CTX_free(ctx); return(EXIT_FAILURE); } /* Load server key into WOLFSSL_CTX */ if (wolfSSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != SSL_SUCCESS) { fprintf(stderr, "Error loading %s, please check the file.\n", key); wolfSSL_CTX_free(ctx); return(EXIT_FAILURE); } /* Setup client authentication. */ wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, 0); if (wolfSSL_CTX_load_verify_locations(ctx, verifyCert, 0) != SSL_SUCCESS) { fprintf(stderr, "Error loading %s, please check the file.\n", verifyCert); wolfSSL_CTX_free(ctx); return(EXIT_FAILURE); } if (cipherList != NULL) { if (wolfSSL_CTX_set_cipher_list(ctx, cipherList) != SSL_SUCCESS) { fprintf(stderr, "Server can't set cipher list.\n"); wolfSSL_CTX_free(ctx); return(EXIT_FAILURE); } } #ifndef NO_DH SetDHCtx(ctx); #endif *wolfsslCtx = ctx; return EXIT_SUCCESS; } /* Cleanup the wolfSSL context and wolfSSL library. * * ctx The wolfSSL context object. */ static void WolfSSLCtx_Final(WOLFSSL_CTX* ctx) { wolfSSL_CTX_free(ctx); #ifdef WOLFSSL_ASYNC_CRYPT wolfAsync_DevClose(&devId); #endif } /* Create a random reply. * * reply The buffer to put the random data into. * replyLen The amount of data to generate. */ static void RandomReply(char* reply, int replyLen) { int ret; WC_RNG rng; ret = wc_InitRng(&rng); if (ret != 0) { fprintf(stderr, "Error: initialize random\n"); exit(EXIT_FAILURE); } ret = wc_RNG_GenerateBlock(&rng, (byte*)reply, replyLen); wc_FreeRng(&rng); if (ret != 0) { fprintf(stderr, "Error: initialize random\n"); exit(EXIT_FAILURE); } } /* Create a socket to listen on and wait for first client. * * port The port to listen on. * numClients The number of clients for listen to support. * socketfd The socket file descriptor to accept on. * returns EXIT_SUCCESS on success and EXIT_FAILURE otherwise. */ static int CreateSocketListen(int port, int numClients, socklen_t* socketfd) { int ret; socklen_t sockfd; struct sockaddr_in serverAddr = {0}; int on = 1; socklen_t len = sizeof(on); /* Set the server's address. */ memset((char *)&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); /* Create a non-blocking socket to listen on for new connections. */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == (socklen_t)-1) { fprintf(stderr, "ERROR: failed to create the socket\n"); return(EXIT_FAILURE); } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, len) < 0) fprintf(stderr, "setsockopt SO_REUSEADDR failed\n"); if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &on, len) < 0) fprintf(stderr, "setsockopt TCP_NODELAY failed\n"); if (bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { fprintf(stderr, "ERROR: failed to bind\n"); return(EXIT_FAILURE); } printf("Waiting for a connection...\n"); /* Listen for a client to connect. */ ret = listen(sockfd, numClients); if (ret == -1) { fprintf(stderr, "ERROR: failed to listen\n"); return(EXIT_FAILURE); } *socketfd = sockfd; return EXIT_SUCCESS; } /* Display the usage for the program. */ static void Usage(void) { printf("perf " LIBWOLFSSL_VERSION_STRING " (NOTE: All files relative to wolfSSL home dir)\n"); printf("-? Help, print this usage\n"); printf("-p Port to listen on, not 0, default %d\n", wolfSSLPort); printf("-v SSL version [0-3], SSLv3(0) - TLS1.2(3)), default %d\n", SERVER_DEFAULT_VERSION); printf("-a Allow TLS version downgrade\n"); printf("-l Cipher suite list (: delimited)\n"); printf("-c Certificate file, default %s\n", SVR_CERT); printf("-k Key file, default %s\n", SVR_KEY); printf("-A Certificate Authority file, default %s\n", CLI_CERT); printf("-n Benchmark connections\n"); printf("-N concurrent connections\n"); printf("-R bytes read from client\n"); printf("-W bytes written to client\n"); printf("-B Benchmark written bytes\n"); } /* Main entry point for the program. * * argc The count of command line arguments. * argv The command line arguments. * returns 0 on success and 1 otherwise. */ int main(int argc, char* argv[]) { int ret = 0; socklen_t socketfd = -1; int efd; struct epoll_event event; struct epoll_event event_conn; struct epoll_event* events = NULL; int ch; WOLFSSL_CTX* ctx = NULL; SSLConn_CTX* sslConnCtx; word16 port = wolfSSLPort; char* cipherList = NULL; char* ourCert = SVR_CERT; char* ourKey = SVR_KEY; char* verifyCert = CLI_CERT; int version = SERVER_DEFAULT_VERSION; int allowDowngrade= 0; int numConns = SSL_NUM_CONN; int numBytesRead = NUM_READ_BYTES; int numBytesWrite = NUM_WRITE_BYTES; int maxBytes = MAX_BYTES; int maxConns = MAX_CONNECTIONS; int numClients = NUM_CLIENTS; #ifdef WOLFSSL_ASYNC_CRYPT WOLF_EVENT* wolfEvents[MAX_WOLF_EVENTS]; #endif /* Parse the command line arguments. */ while ((ch = mygetopt(argc, argv, OPTIONS)) != -1) { switch (ch) { /* Help with command line options. */ case '?': Usage(); exit(EXIT_SUCCESS); /* Port number to listen on. */ case 'p': port = (word16)atoi(myoptarg); break; /* Version of SSL/TLS to use. */ case 'v': version = atoi(myoptarg); if (version < 0 || version > 3) { Usage(); exit(MY_EX_USAGE); } break; case 'a': allowDowngrade = 1; break; /* List of cipher suites to use. */ case 'l': cipherList = myoptarg; break; /* File name of server certificate for authentication. */ case 'c': ourCert = myoptarg; break; /* File name of server private key for authentication. */ case 'k': ourKey = myoptarg; break; /* File name of client certificate/CA for peer verification. */ case 'A': verifyCert = myoptarg; break; /* Number of connections to make. */ case 'n': maxConns = atoi(myoptarg); if (maxConns < 0 || maxConns > 1000000) { Usage(); exit(MY_EX_USAGE); } maxBytes = 0; break; /* Number of conncurrent connections to use. */ case 'N': numConns = atoi(myoptarg); if (numConns < 0 || numConns > 1000000) { Usage(); exit(MY_EX_USAGE); } break; /* Number of bytes to read each call. */ case 'R': numBytesRead = atoi(myoptarg); if (numBytesRead <= 0) { Usage(); exit(MY_EX_USAGE); } break; /* Number of bytes to write each call. */ case 'W': numBytesWrite = atoi(myoptarg); if (numBytesWrite <= 0) { Usage(); exit(MY_EX_USAGE); } break; /* Maximum number of read and write bytes (separate counts). */ case 'B': maxBytes = atoi(myoptarg); if (maxBytes <= 0) { Usage(); exit(MY_EX_USAGE); } maxConns = 0; break; /* Unrecognized command line argument. */ default: Usage(); exit(MY_EX_USAGE); } } /* Allocate space for EPOLL events to be stored. */ events = (struct epoll_event*)malloc(EPOLL_NUM_EVENTS * sizeof(*events)); if (events == NULL) exit(EXIT_FAILURE); #ifdef DEBUG_WOLFSSL wolfSSL_Debugging_ON(); #endif /* Initialize wolfSSL */ wolfSSL_Init(); /* Initialize wolfSSL and create a context object. */ if (WolfSSLCtx_Init(version, allowDowngrade, ourCert, ourKey, verifyCert, cipherList, &ctx) == -1) exit(EXIT_FAILURE); RandomReply(reply, sizeof(reply)); /* Create SSL/TLS connection data object. */ sslConnCtx = SSLConn_New(numConns, numBytesRead, numBytesWrite, maxConns, maxBytes); if (sslConnCtx == NULL) exit(EXIT_FAILURE); /* Create a socket and listen for a client. */ if (CreateSocketListen(port, numClients, &socketfd) == EXIT_FAILURE) exit(EXIT_FAILURE); /* Create an EPOLL file descriptor. */ efd = epoll_create1(0); if (efd == -1) { fprintf(stderr, "ERROR: failed to create epoll\n"); exit(EXIT_FAILURE); } /* Add the event for communications on listening socket. */ memset(&event, 0, sizeof(event)); event.events = EPOLLIN; event.data.ptr = NULL; ret = epoll_ctl(efd, EPOLL_CTL_ADD, socketfd, &event); if (ret == -1) { fprintf(stderr, "ERROR: failed to add event to epoll\n"); exit(EXIT_FAILURE); } sslConnCtx->accepting = 1; /* Keep handling clients until done. */ while (!SSLConn_Done(sslConnCtx)) { int n; int i; #ifdef WOLFSSL_ASYNC_CRYPT do { double start = current_time(1); ret = wolfSSL_CTX_AsyncPoll(ctx, wolfEvents, MAX_WOLF_EVENTS, WOLF_POLL_FLAG_CHECK_HW, &n); sslConnCtx->asyncTime += current_time(0) - start; for (i = 0; i < n; i++) { SSLConn* sslConn = sslConnCtx->sslConn; while (sslConn != NULL) { if (sslConn->ssl != wolfEvents[i]->context) { sslConn = sslConn->next; continue; } SSLConn_ReadWrite(sslConnCtx, sslConn); break; } } } while (n > 0); #endif #ifdef WOLFSSL_ASYNC_CRYPT /* Look for events. */ n = epoll_wait(efd, events, EPOLL_NUM_EVENTS, 0); #else /* Wait for events. */ n = epoll_wait(efd, events, EPOLL_NUM_EVENTS, -1); #endif /* Process all returned events. */ for (i = 0; i < n; i++) { /* Error event on socket. */ if (!(events[i].events & EPOLLIN)) { if (events[i].data.ptr == NULL) { /* Not a client, therefore the listening connection. */ close(socketfd); socketfd = -1; } else { /* Client connection. */ SSLConn_Close(sslConnCtx, events[i].data.ptr); ret = epoll_ctl(efd, EPOLL_CTL_ADD, socketfd, &event); } } else if (events[i].data.ptr == NULL) { SSLConn* sslConn; /* Accept a new client on the listener. */ ret = SSLConn_Accept(sslConnCtx, ctx, socketfd, &sslConn); if (ret == EXIT_SUCCESS) { /* Set EPOLL to check for events on the new socket. */ memset(&event_conn, 0, sizeof(event_conn)); event_conn.events = EPOLLIN | EPOLLET; event_conn.data.ptr = sslConn; ret = epoll_ctl(efd, EPOLL_CTL_ADD, sslConn->sockfd, &event_conn); if (ret == -1) { fprintf(stderr, "ERROR: failed add event to epoll\n"); exit(EXIT_FAILURE); } } if (sslConnCtx->cnt == sslConnCtx->numConns) { /* Don't accept any more TCP connections. */ ret = epoll_ctl(efd, EPOLL_CTL_DEL, socketfd, &event); if (ret == -1) { fprintf(stderr, "ERROR: failed delete epoll event\n"); exit(EXIT_FAILURE); } sslConnCtx->accepting = 0; } } else { if (sslConnCtx->totalTime == 0) sslConnCtx->totalTime = current_time(1); SSLConn_ReadWrite(sslConnCtx, events[i].data.ptr); } } SSLConn_FreeSSLConn(sslConnCtx); /* Accept more connections again up to the maximum concurrent. */ if (!sslConnCtx->accepting && sslConnCtx->cnt < sslConnCtx->numConns) { ret = epoll_ctl(efd, EPOLL_CTL_ADD, socketfd, &event); if (ret == -1) { fprintf(stderr, "ERROR: failed add event to epoll\n"); exit(EXIT_FAILURE); } sslConnCtx->accepting = 1; } } sslConnCtx->totalTime = current_time(0) - sslConnCtx->totalTime; if (socketfd != -1) close(socketfd); free(events); SSLConn_PrintStats(sslConnCtx); SSLConn_Free(sslConnCtx); WolfSSLCtx_Final(ctx); wolfSSL_Cleanup(); exit(EXIT_SUCCESS); }