--- /dev/null
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <poll.h>
+#include <signal.h>
+
+#include <s2n.h>
+
+#include "http.h"
+#include "cert.h"
+
+const char *WEBSITE =
+"HTTP/1.0 200 OK\r\n"
+"Server: u461\r\n"
+"Content-Type: text/html\r\n"
+"\r\n"
+"<html><body><h1>HELLO ITS A WEBSITE (delivered encrypted using amazon s2n TLS)</h1></body></html>\r\n";
+
+#define SERVER_HEADER "Server: u461\r\n"
+#define HTTP_ERROR( X ) \
+ "HTTP/1.0 " X "\r\n" SERVER_HEADER "Connection: close\r\n\r\n"
+
+const char *k_response_saturated = HTTP_ERROR( "503 Service Unavailable" );
+const char *k_response_parsefail = HTTP_ERROR( "500 Internal Server Error" );
+const char *k_response_toobig = HTTP_ERROR( "413 Payload Too Large" );
+const char *k_response_temp = HTTP_ERROR( "501 Not Implemented" );
+const char *k_response_timeout = HTTP_ERROR( "408 Request Timeout" );
+
+#define MAX_CLIENTS 10
+#define REQUEST_MAX_SIZE 1024
+
+struct client
+{
+ int connfd;
+
+ enum state
+ {
+ k_state_none,
+ k_state_opening,
+ k_state_secured,
+ k_state_closing
+ }
+ state;
+
+ struct http_request request;
+ struct s2n_connection *s2n;
+}
+_clients[ MAX_CLIENTS ];
+int _ticks_without_action = 0;
+int _run_server = 1;
+
+static void int_handle( int sig )
+{
+ _run_server = 0;
+ printf( "Ok stopping..\n" );
+}
+
+static void _write_s2n_error( FILE *fp, const char *prefix )
+{
+ fprintf( fp, "%s | s2n Error: %s. %s\n", prefix,
+ s2n_strerror(s2n_errno, NULL),
+ s2n_strerror_debug(s2n_errno, NULL));
+}
+
+#define STRINGIZE(x) STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+#define write_s2n_error( FP ) \
+ _write_s2n_error( FP, __FILE__ ":L" STRINGIZE(__LINE__) );
+
+static int open_listen_socket( int port )
+{
+ int listenfd;
+ struct sockaddr_in serv_addr = {0};
+ listenfd = socket( AF_INET, SOCK_STREAM, 0 );
+
+ if( listenfd == -1 )
+ {
+ fprintf( stderr, "socket() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ if( fcntl( listenfd, F_SETFL, O_NONBLOCK ) == -1 )
+ {
+ fprintf( stderr, "fcntl() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
+ serv_addr.sin_port = htons( port );
+
+ if( bind( listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) ) == -1 )
+ {
+ fprintf( stderr, "bind() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ if( listen( listenfd, 10 ) == -1 )
+ {
+ fprintf( stderr, "listen() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ return listenfd;
+}
+
+int _accept_any_new_client( int listenfd, struct s2n_config *conf )
+{
+ int fd = accept( listenfd, (struct sockaddr*)NULL, NULL );
+ if( fd == -1 )
+ {
+ if( !( errno == EAGAIN || errno == EWOULDBLOCK ) )
+ {
+ fprintf( stderr, "accept() warning: %s\n", strerror(errno) );
+ }
+
+ return 0;
+ }
+
+ if( fcntl( fd, F_SETFL, O_NONBLOCK ) == -1 )
+ {
+ fprintf( stderr, "fcntl() error: %s\n", strerror(errno) );
+ close( fd );
+ return 0;
+ }
+
+
+ struct client *client = NULL;
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *c = &_clients[ i ];
+ if( c->state == k_state_none )
+ {
+ client = c;
+ break;
+ }
+ }
+
+ if( client == NULL )
+ {
+ fprintf( stderr, "Server saturated, cannot accept new client.\n" );
+ //write( fd, k_response_saturated, strlen(k_response_saturated) );
+ close( fd );
+ return 0;
+ }
+
+ memset( client, 0, sizeof(struct client) );
+ client->connfd = fd;
+
+ printf( "Accepted new client (%d)\n", fd );
+
+ client->s2n = s2n_connection_new( S2N_SERVER );
+
+ if( client->s2n == NULL )
+ {
+ write_s2n_error( stderr );
+ goto _cleanup_sock;
+ }
+
+ if( s2n_connection_set_config( client->s2n, conf ) == -1 )
+ {
+ write_s2n_error( stderr );
+ goto _cleanup_s2n;
+ }
+
+ if( s2n_connection_set_blinding( client->s2n,
+ S2N_SELF_SERVICE_BLINDING ) == -1 )
+ {
+ write_s2n_error( stderr );
+ goto _cleanup_s2n;
+ }
+
+ if( s2n_connection_set_fd( client->s2n, client->connfd ) == -1 )
+ {
+ write_s2n_error( stderr );
+ goto _cleanup_s2n;
+ }
+
+ client->state = k_state_opening;
+ return 1;
+
+_cleanup_s2n:
+ s2n_connection_free( client->s2n );
+
+_cleanup_sock:
+ close( client->connfd );
+ return 0;
+}
+
+/* returns number of currently handshaking */
+int _process_handshakes(void)
+{
+ int verifying = 0;
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *c = &_clients[ i ];
+ if( c->state == k_state_opening )
+ {
+ s2n_blocked_status blocked;
+ if( s2n_negotiate( c->s2n, &blocked ) == 0 )
+ {
+ printf( "handshake completed\n" );
+ c->state = k_state_secured;
+ }
+ else
+ {
+ if( s2n_error_get_type( s2n_errno ) != S2N_ERR_T_BLOCKED )
+ {
+ write_s2n_error( stderr );
+ s2n_connection_free( c->s2n );
+ close( c->connfd );
+ c->state = k_state_none;
+ }
+ else verifying ++;
+ }
+ }
+ }
+
+ return verifying;
+}
+
+/* returns number of shutting down */
+int _process_shutdowns(void)
+{
+ int closing = 0;
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *c = &_clients[ i ];
+ if( c->state == k_state_closing )
+ {
+ s2n_blocked_status blocked;
+ if( s2n_shutdown( c->s2n, &blocked ) == 0 )
+ {
+ printf( "sucessfuly closed\n" );
+ s2n_connection_free( c->s2n );
+ close( c->connfd );
+ c->state = k_state_none;
+ }
+ else
+ {
+ if( s2n_error_get_type( s2n_errno ) != S2N_ERR_T_BLOCKED )
+ {
+ write_s2n_error( stderr );
+ s2n_connection_free( c->s2n );
+ close( c->connfd );
+ c->state = k_state_none;
+ }
+ else closing ++;
+ }
+ }
+ }
+
+ if( closing )
+ printf( "Still closing: %d\n", closing );
+ return closing;
+}
+
+int s2nsend_busy( struct s2n_connection *conn, const void *buf, int len )
+{
+ int written = 0;
+ while( written < len )
+ {
+ s2n_blocked_status blocked;
+ int w = s2n_send( conn, buf + written, len - written, &blocked);
+ if( w >= 0 )
+ {
+ written += w;
+ }
+ else if( s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED )
+ {
+ }
+ else
+ {
+ write_s2n_error( stderr );
+ return -1;
+ }
+
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 10000000; /* 0.01s (100 tick)*/
+ nanosleep( &spec, NULL );
+ }
+ return 1;
+}
+
+/* returns -1 if error, 0 if no action/disconnect, 1 if recieved data/sent */
+int client_handle_requests( struct client *client )
+{
+ char recv_buf[ 512 ];
+ //int len = recv( client->connfd, recv_buf, sizeof(recv_buf), 0 );
+
+ s2n_blocked_status blocked;
+ int len = s2n_recv( client->s2n, recv_buf, sizeof(recv_buf), &blocked );
+
+ if( len > 0 )
+ {
+ //printf( "RAW BUF ------\n%.*s\n-----------\n", len, recv_buf );
+
+ int remaining = len;
+ while( remaining )
+ {
+ /* New data has been read off the socket */
+ int amt = http_request_parse( &client->request,
+ recv_buf + (len-remaining),
+ remaining );
+ if( amt == -1 )
+ {
+ fprintf( stderr, "[#%d][http parser] There was %s while %s.\n"
+ " line: %d, column: %d\n",
+ client->connfd,
+ ehttp_parse_error[ client->request.error ],
+ ehttp_parse_state[ client->request.state ],
+ client->request.line, client->request.col );
+
+ if( s2nsend_busy( client->s2n, k_response_parsefail,
+ strlen( k_response_parsefail )) == -1 )
+ {
+ s2n_connection_free( client->s2n );
+ close( client->connfd );
+ client->state = k_state_none;
+ }
+ else
+ {
+ client->state = k_state_closing;
+ }
+
+ return -1;
+ }
+ else
+ {
+ remaining -= amt;
+
+ if( client->request.state == k_parse_state_complete )
+ {
+ http_fprintf_request_info( stdout, &client->request );
+
+ if( !strncmp( client->request.resource, "/",
+ client->request.resource_len ) )
+ {
+ printf( "Gave website :D\n" );
+ if( s2nsend_busy( client->s2n, WEBSITE, strlen(WEBSITE) )
+ == -1 )
+ {
+ s2n_connection_free( client->s2n );
+ close( client->connfd );
+ client->state = k_state_none;
+ return -1;
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Responding #%d with 501\n", client->connfd );
+ if( s2nsend_busy( client->s2n,
+ k_response_temp, strlen(k_response_temp) )
+ == -1 )
+ {
+ s2n_connection_free( client->s2n );
+ close( client->connfd );
+ client->state = k_state_none;
+ return -1;
+ }
+ }
+
+ printf( "Setting state to closing!\n" );
+ /* reset parser */
+ memset( &client->request, 0, sizeof(struct http_request) );
+ client->state = k_state_closing;
+ }
+ }
+ }
+ return 1;
+ }
+ else if( len == 0 )
+ {
+ printf( "Client #%d disconnected\n", client->connfd );
+ client->state = k_state_closing;
+ return 0;
+ }
+ else
+ {
+ if( s2n_error_get_type( s2n_errno ) == S2N_ERR_T_BLOCKED )
+ {
+ return 0;
+ }
+ else
+ {
+ write_s2n_error( stderr );
+ s2n_connection_free( client->s2n );
+ close( client->connfd );
+ client->state = k_state_none;
+ return -1;
+ }
+ }
+}
+
+int _process_requests(void)
+{
+ int closing = 0;
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *c = &_clients[ i ];
+ if( c->state == k_state_secured )
+ client_handle_requests( c );
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ signal( SIGINT, int_handle );
+ signal( SIGPIPE, SIG_IGN );
+
+ printf( "Configuring s2n\n" );
+ s2n_init();
+ struct s2n_config *s2n_conf = s2n_config_new();
+ if( s2n_conf == NULL )
+ {
+ write_s2n_error( stderr );
+ return 1;
+ }
+
+ struct s2n_cert_chain_and_key *s2n_cert = s2n_cert_chain_and_key_new();
+ if( s2n_cert == NULL )
+ {
+ write_s2n_error( stderr );
+ return 1;
+ }
+
+ if( s2n_cert_chain_and_key_load_pem( s2n_cert,
+ SERVER_CHAIN_CERT_PEM,
+ SERVER_PRIV_PEM ) == -1 )
+ {
+ write_s2n_error( stderr );
+ s2n_cert_chain_and_key_free( s2n_cert );
+ s2n_config_free( s2n_conf );
+ return 1;
+ }
+
+ if( s2n_config_add_cert_chain_and_key_to_store( s2n_conf, s2n_cert ) == -1 )
+ {
+ write_s2n_error( stderr );
+ s2n_cert_chain_and_key_free( s2n_cert );
+ s2n_config_free( s2n_conf );
+ return 1;
+ }
+ printf( "s2n configured\n" );
+
+ int listenfd = open_listen_socket( 1521 );
+
+ if( listenfd == -1 )
+ {
+ fprintf( stderr, "Failed opening listen socket\n" );
+ s2n_cert_chain_and_key_free( s2n_cert );
+ s2n_config_free( s2n_conf );
+ return 1;
+ }
+
+ int ticks_without_action = 0;
+
+ while( _run_server )
+ {
+ _accept_any_new_client( listenfd, s2n_conf );
+ _process_handshakes();
+ _process_shutdowns();
+ _process_requests();
+
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 50000000; /* 0.05s (20 tick)*/
+ nanosleep( &spec, NULL );
+ }
+
+ printf( "Closing active connections\n" );
+ while(1)
+ {
+ if( _process_shutdowns() )
+ {
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 100000000; /* 0.10s (10 tick)*/
+ nanosleep( &spec, NULL );
+ }
+ else break;
+ }
+ printf( "Done\n" );
+
+ s2n_cert_chain_and_key_free( s2n_cert );
+ s2n_config_free( s2n_conf );
+ close( listenfd );
+}
--- /dev/null
+#include <signal.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "http.h"
+
+const char *WEBSITE =
+"HTTP/1.0 200 OK\r\n"
+"Server: u461\r\n"
+"Content-Type: text/html\r\n"
+"\r\n"
+"<html><body><h1>HELLO ITS A SECURE WEBSITE</h1></body></html>\r\n";
+
+#define SERVER_HEADER "Server: u461\r\n"
+#define HTTP_ERROR( X ) \
+ "HTTP/1.0 " X "\r\n" SERVER_HEADER "Connection: close\r\n\r\n"
+
+const char *k_response_saturated = HTTP_ERROR( "503 Service Unavailable" );
+const char *k_response_parsefail = HTTP_ERROR( "500 Internal Server Error" );
+const char *k_response_toobig = HTTP_ERROR( "413 Payload Too Large" );
+const char *k_response_temp = HTTP_ERROR( "501 Not Implemented" );
+const char *k_response_timeout = HTTP_ERROR( "408 Request Timeout" );
+
+int _run_server = 1;
+static const char ssl_cache_id[] = "u461_net http/tls session cache";
+
+static void int_handle( int sig )
+{
+ printf( "Stopping server..\n" );
+ _run_server = 0;
+}
+
+SSL_CTX *create_ssl_context(void)
+{
+ SSL_CTX *ctx = SSL_CTX_new( TLS_server_method() );
+
+ if( !ctx )
+ {
+ fprintf( stderr, "SSL_CTX_new error: " );
+ ERR_print_errors_fp( stderr );
+ return NULL;
+ }
+
+ if( !SSL_CTX_set_min_proto_version( ctx, TLS1_2_VERSION ) )
+ {
+ SSL_CTX_free( ctx );
+ fprintf( stderr, "SSL_CTX_set_min_proto_version error: " );
+ ERR_print_errors_fp( stderr );
+ return NULL;
+ }
+
+ long opts = SSL_OP_IGNORE_UNEXPECTED_EOF |
+ SSL_OP_NO_RENEGOTIATION |
+ SSL_OP_CIPHER_SERVER_PREFERENCE;
+
+ SSL_CTX_set_options( ctx, opts );
+
+ if( SSL_CTX_use_certificate_chain_file( ctx, "chain.pem" ) <= 0 )
+ {
+ SSL_CTX_free( ctx );
+ fprintf( stderr, "SSL_CTX_use_certificate_chain_file error: " );
+ ERR_print_errors_fp( stderr );
+ return NULL;
+ }
+
+ if( SSL_CTX_use_PrivateKey_file( ctx, "priv.pem", SSL_FILETYPE_PEM ) <= 0 )
+ {
+ SSL_CTX_free( ctx );
+ fprintf( stderr, "SSL_CTX_use_PrivateKey_file error: " );
+ ERR_print_errors_fp(stderr);
+ return NULL;
+ }
+
+ SSL_CTX_set_session_id_context( ctx, (void *)ssl_cache_id,
+ sizeof(ssl_cache_id) );
+ SSL_CTX_set_session_cache_mode( ctx, SSL_SESS_CACHE_SERVER );
+ SSL_CTX_sess_set_cache_size( ctx, 1024 );
+ SSL_CTX_set_timeout( ctx, 3600 );
+ SSL_CTX_set_verify( ctx, SSL_VERIFY_NONE, NULL );
+
+ return ctx;
+}
+
+#define MAX_CLIENTS 10
+
+struct client
+{
+ int connfd, closing;
+ SSL *ssl;
+ struct http_request request;
+}
+_clients[ MAX_CLIENTS ];
+
+int open_listen_socket( int port )
+{
+ int listenfd;
+ struct sockaddr_in serv_addr = {0};
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ _clients[i].connfd = -1;
+ }
+
+ listenfd = socket( AF_INET, SOCK_STREAM, 0 );
+
+ if( listenfd == -1 )
+ {
+ fprintf( stderr, "socket() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ if( fcntl( listenfd, F_SETFL, O_NONBLOCK ) == -1 )
+ {
+ fprintf( stderr, "fcntl() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
+ serv_addr.sin_port = htons( port );
+
+ if( bind( listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) ) == -1 )
+ {
+ fprintf( stderr, "bind() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ if( listen( listenfd, 10 ) == -1 )
+ {
+ fprintf( stderr, "listen() error: %s\n", strerror(errno) );
+ return -1;
+ }
+
+ return listenfd;
+}
+
+int _accept_any_new_client( SSL_CTX *tls_ctx, int listenfd )
+{
+ int fd = accept( listenfd, (struct sockaddr*)NULL, NULL );
+ if( fd == -1 )
+ {
+ if( !( errno == EAGAIN || errno == EWOULDBLOCK ) )
+ {
+ fprintf( stderr, "accept() warning: %s\n", strerror(errno) );
+ }
+ }
+ else
+ {
+ struct client *client = NULL;
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *c = &_clients[ i ];
+ if( c->connfd == -1 )
+ {
+ client = c;
+ break;
+ }
+ }
+
+ if( client )
+ {
+ memset( client, 0, sizeof(struct client) );
+ client->connfd = fd;
+ printf( "Creating SSL connection for %d\n", client->connfd );
+
+ client->ssl = SSL_new( tls_ctx );
+
+ if( client->ssl == NULL )
+ {
+ fprintf( stderr, "Failed to SSL_new.\n" );
+ close( client->connfd );
+ client->connfd = -1;
+ }
+ else
+ {
+ if( SSL_set_fd( client->ssl, client->connfd ) )
+ {
+ if( SSL_accept( client->ssl ) <= 0 )
+ {
+ fprintf( stderr, "SSL_accept() failure: " );
+ ERR_print_errors_fp( stderr );
+ fprintf( stderr, "\n" );
+ SSL_free( client->ssl );
+ close( fd );
+ client->connfd = -1;
+ }
+ else
+ {
+ printf( "Accepted new client (%d)\n", fd );
+ return 1;
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Failed to SSL_set_fd.\n" );
+ SSL_free( client->ssl );
+ close( client->connfd );
+ client->connfd = -1;
+ }
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Server saturated, cannot accept new client.\n" );
+ close( fd );
+ }
+ }
+
+ return 0;
+}
+
+/* returns -1 if error, 0 if no action/disconnect, 1 if recieved data/sent */
+int client_handle_requests( struct client *client )
+{
+ char recv_buf[ 512 ];
+ int len = SSL_read( client->ssl, recv_buf, sizeof(recv_buf) );
+
+ if( len > 0 )
+ {
+ //printf( "RAW BUF ------\n%.*s\n-----------\n", len, recv_buf );
+
+ int remaining = len;
+ while( remaining )
+ {
+ /* New data has been read off the socket */
+ int amt = http_request_parse( &client->request,
+ recv_buf + (len-remaining),
+ remaining );
+ if( amt == -1 )
+ {
+ fprintf( stderr, "[#%d][http parser] There was %s while %s.\n"
+ " line: %d, column: %d\n",
+ client->connfd,
+ ehttp_parse_error[ client->request.error ],
+ ehttp_parse_state[ client->request.state ],
+ client->request.line, client->request.col );
+
+ SSL_write( client->ssl, k_response_parsefail,
+ strlen(k_response_parsefail) );
+
+ SSL_shutdown( client->ssl );
+ client->closing = 1;
+ return -1;
+ }
+ else
+ {
+ remaining -= amt;
+
+ if( client->request.state == k_parse_state_complete )
+ {
+ http_fprintf_request_info( stdout, &client->request );
+
+ if( !strncmp( client->request.resource, "/",
+ client->request.resource_len ) )
+ {
+ printf( "Gave website :D\n" );
+ SSL_write( client->ssl, WEBSITE, strlen(WEBSITE) );
+ }
+ else
+ {
+ fprintf( stderr, "Responding #%d with 501\n", client->connfd );
+ SSL_write( client->ssl,
+ k_response_temp, strlen(k_response_temp) );
+ }
+
+ //client->total_request_bytes = 0;
+
+ /* reset parser */
+ memset( &client->request, 0, sizeof(struct http_request) );
+
+ SSL_shutdown( client->ssl );
+ client->closing = 1;
+ return 1;
+ }
+ else
+ {
+ }
+ }
+ }
+
+ return 1;
+ }
+ else
+ {
+ int reason = SSL_get_error( client->ssl, len );
+
+ if( reason == SSL_ERROR_WANT_READ )
+ {
+ return 0;
+ }
+ else
+ {
+ fprintf( stderr, "Error with client %d. SSL error code: %d\n",
+ client->connfd, reason );
+ SSL_free( client->ssl );
+ close( client->connfd );
+ client->connfd = -1;
+ return -1;
+ }
+ }
+}
+
+int _handle_ssl_shutdowns()
+{
+ int num_closing = 0;
+
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *client = &_clients[ i ];
+ if( client->connfd == -1 ) continue;
+
+ if( client->closing )
+ {
+ if( SSL_shutdown( client->ssl ) )
+ {
+ SSL_free( client->ssl );
+ close( client->connfd );
+ client->connfd = -1;
+ client->closing = 0;
+ }
+ else
+ {
+ num_closing ++;
+ }
+ }
+ }
+
+ return num_closing;
+}
+
+int main( int argc, char *argv[] )
+{
+ signal( SIGINT, int_handle );
+ signal( SIGPIPE, SIG_IGN );
+
+ SSL_CTX *ctx = create_ssl_context();
+
+ if( !ctx )
+ {
+ fprintf( stderr, "Failed to create SSL context.\n" );
+ return 1;
+ }
+
+ int listenfd = open_listen_socket( 1521 );
+ if( listenfd == -1 )
+ {
+ fprintf( stderr, "Failed to open listen socket.\n" );
+ return 1;
+ }
+
+ while( _run_server )
+ {
+ _handle_ssl_shutdowns();
+ _accept_any_new_client( ctx, listenfd );
+
+ /* Handle incoming data */
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *client = &_clients[ i ];
+ if( client->connfd == -1 ) continue;
+
+ // if( client_timeout_step( client ) ) continue;
+
+ client_handle_requests( client );
+ }
+
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 50000000; /* 0.05s (20 tick)*/
+ nanosleep( &spec, NULL );
+ }
+
+ /* shutdown sequence */
+ printf( "Closing connections...\n" );
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *client = &_clients[ i ];
+ if( client->connfd == -1 ) continue;
+ client->closing = 1;
+ }
+
+ while( _handle_ssl_shutdowns() )
+ {
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 50000000; /* 0.05s (20 tick)*/
+ nanosleep( &spec, NULL );
+ }
+
+ printf( "Done...\n" );
+
+ close( listenfd );
+
+ SSL_CTX_free( ctx );
+ return 0;
+}