oppenssl and s2n versions
authorhgn <hgodden00@gmail.com>
Mon, 2 Sep 2024 15:16:21 +0000 (16:16 +0100)
committerhgn <hgodden00@gmail.com>
Mon, 2 Sep 2024 15:16:21 +0000 (16:16 +0100)
.gitignore
builds2n.sh [new file with mode: 0755]
buildtls.sh [new file with mode: 0755]
servers2n.c [new file with mode: 0644]
servertls.c [new file with mode: 0644]

index 36f971e324f3e948a5289e81d36eb4dbbacb887e..ec3e2b6f8310627376d0895b32e9eb243db9a302 100644 (file)
@@ -1 +1,3 @@
 bin/*
+*.pem
+cert.h
diff --git a/builds2n.sh b/builds2n.sh
new file mode 100755 (executable)
index 0000000..a77344f
--- /dev/null
@@ -0,0 +1,3 @@
+gcc -Wall -Wno-unused-function -ggdb3 -O0 -fsanitize=address servers2n.c \
+   -ls2n \
+   -o bin/servers2n && bin/servers2n ; echo exit: $?
diff --git a/buildtls.sh b/buildtls.sh
new file mode 100755 (executable)
index 0000000..3fef495
--- /dev/null
@@ -0,0 +1,3 @@
+clang -Wall -g -O0 -fsanitize=address servertls.c \
+   -lcrypto -lssl \
+   -o bin/servertls && bin/servertls ; echo exit: $?
diff --git a/servers2n.c b/servers2n.c
new file mode 100644 (file)
index 0000000..e93260e
--- /dev/null
@@ -0,0 +1,498 @@
+#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 );
+}
diff --git a/servertls.c b/servertls.c
new file mode 100644 (file)
index 0000000..e9e5613
--- /dev/null
@@ -0,0 +1,407 @@
+#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;
+}