505 lines
8.3 KiB
C
505 lines
8.3 KiB
C
|
|
// webserver.c
|
||
|
|
|
||
|
|
|
||
|
|
#include "./fileSystem.h"
|
||
|
|
|
||
|
|
#include "./text.h"
|
||
|
|
|
||
|
|
#include "./mimeTypes.h"
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#include <arpa/inet.h>
|
||
|
|
|
||
|
|
#include <errno.h>
|
||
|
|
|
||
|
|
#include <stdio.h>
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
#include <sys/socket.h>
|
||
|
|
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#include <request.h>
|
||
|
|
|
||
|
|
#include <headers.h>
|
||
|
|
|
||
|
|
|
||
|
|
#include <openssl/err.h>
|
||
|
|
|
||
|
|
#include <openssl/ssl.h>
|
||
|
|
|
||
|
|
|
||
|
|
#include <sys/resource.h>
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
class http{
|
||
|
|
|
||
|
|
int socket;
|
||
|
|
|
||
|
|
struct sockaddr_in * hostAddress = malloc( sizeof( struct sockaddr_in ) );
|
||
|
|
|
||
|
|
int hostAddresslength = sizeof( struct sockaddr_in );
|
||
|
|
|
||
|
|
struct fileSystem * filesystem = new fileSystem();
|
||
|
|
|
||
|
|
struct mimeTypes * mimetypes = new mimeTypes();
|
||
|
|
|
||
|
|
struct headerManager * headers = new headerManager();
|
||
|
|
|
||
|
|
int useSSL = -1;
|
||
|
|
|
||
|
|
|
||
|
|
SSL_CTX * sslContext;
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
void ( * requestCallback )( struct request * requestInstance, struct text * response );
|
||
|
|
|
||
|
|
|
||
|
|
void createServer( void ( * requestCallback )( request * req, text * response ) ) {
|
||
|
|
|
||
|
|
this->requestCallback = requestCallback;
|
||
|
|
|
||
|
|
|
||
|
|
if( this->useSSL == 1 ) {
|
||
|
|
|
||
|
|
this->initializeOpenSSL();
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("after initializeOpenSSL\n\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
initializeOpenSSL() {
|
||
|
|
|
||
|
|
// OpenSSL 1.1.0 or above initializes by itself
|
||
|
|
//SSL_load_error_strings();
|
||
|
|
//ssl_load_ciphers();
|
||
|
|
//OpenSSL_add_all_algorithms();
|
||
|
|
|
||
|
|
/* Lets get nice error messages */
|
||
|
|
SSL_load_error_strings();
|
||
|
|
|
||
|
|
OpenSSL_add_ssl_algorithms();
|
||
|
|
|
||
|
|
SSLeay_add_ssl_algorithms();
|
||
|
|
|
||
|
|
return;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
int listen( int port ) {
|
||
|
|
|
||
|
|
this->socket = socket( AF_INET, SOCK_STREAM, 0 );
|
||
|
|
|
||
|
|
int iSetOption = 1;
|
||
|
|
|
||
|
|
setsockopt( this->socket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & iSetOption, sizeof( iSetOption ) );
|
||
|
|
|
||
|
|
if ( this->socket == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (socket)");
|
||
|
|
|
||
|
|
exit( 0 );
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
printf("socket created successfully\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// Create the address to bind the socket to
|
||
|
|
this->hostAddress->sin_family = AF_INET;
|
||
|
|
|
||
|
|
this->hostAddress->sin_port = htons( port );
|
||
|
|
|
||
|
|
this->hostAddress->sin_addr.s_addr = htonl( INADDR_ANY );
|
||
|
|
|
||
|
|
|
||
|
|
// Bind the socket to the address
|
||
|
|
if ( bind( this->socket, ( struct sockaddr * ) this->hostAddress, this->hostAddresslength ) != 0 ) {
|
||
|
|
|
||
|
|
perror("webserver (bind)");
|
||
|
|
|
||
|
|
exit( 0 );
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
printf("socket successfully bound to address\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// Listen for incoming connections
|
||
|
|
if ( listen( this->socket, SOMAXCONN ) != 0 ) {
|
||
|
|
|
||
|
|
perror("webserver (listen)");
|
||
|
|
|
||
|
|
exit( 0 );
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
printf("server listening for connections\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
for (;;) {
|
||
|
|
|
||
|
|
this->acceptConnection( );
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//this->DestroySSL();
|
||
|
|
|
||
|
|
printf("exit");
|
||
|
|
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
char * getExtension( char * path ) {
|
||
|
|
|
||
|
|
array * parts = path->split(".");
|
||
|
|
|
||
|
|
int count = parts->length();
|
||
|
|
|
||
|
|
return parts->get( count - 1 );
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
logRequest( request * requestInstance ) {
|
||
|
|
|
||
|
|
printf("server address: [%s:%u]\n", requestInstance->address, requestInstance->port );
|
||
|
|
|
||
|
|
printf("mimeType: %-30s \n", requestInstance->mimeType);
|
||
|
|
|
||
|
|
printf("header: %-30s \n", requestInstance->extension );
|
||
|
|
|
||
|
|
printf("method: %-30s \n", requestInstance->method );
|
||
|
|
|
||
|
|
printf("version: %-30s \n\n", requestInstance->version );
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
acceptConnection( ) {
|
||
|
|
|
||
|
|
fileSystem * filesystem = this->filesystem;
|
||
|
|
|
||
|
|
mimeTypes * mimetypes = this->mimetypes;
|
||
|
|
|
||
|
|
text * response = new text("");
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
char buffer[ 4096 ];
|
||
|
|
|
||
|
|
// Create client address
|
||
|
|
struct sockaddr_in client_addr;
|
||
|
|
|
||
|
|
int client_addrlen = sizeof(client_addr);
|
||
|
|
|
||
|
|
// Accept incoming connections
|
||
|
|
int socketConnection = accept( this->socket,
|
||
|
|
(struct sockaddr *)this->hostAddress,
|
||
|
|
( socklen_t * ) & this->hostAddresslength );
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
|
||
|
|
struct rusage r_usage;
|
||
|
|
|
||
|
|
getrusage(RUSAGE_SELF,&r_usage);
|
||
|
|
// Print the maximum resident set size used (in kilobytes).
|
||
|
|
|
||
|
|
printf("Memory usage: %ld kilobytes\n\n",r_usage.ru_maxrss);
|
||
|
|
|
||
|
|
free( r_usage );
|
||
|
|
*/
|
||
|
|
|
||
|
|
SSL * ssl = NULL;
|
||
|
|
|
||
|
|
if( this->useSSL == 1 ) {
|
||
|
|
|
||
|
|
const SSL_METHOD * method = SSLv23_server_method();
|
||
|
|
|
||
|
|
this->sslContext = SSL_CTX_new( method );
|
||
|
|
|
||
|
|
if ( this->sslContext == NULL ) {
|
||
|
|
|
||
|
|
printf("Error loading SSL_CTX_NEW '%s'\n\n", stderr);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
SSL_CTX_set_options( this->sslContext, SSL_OP_SINGLE_DH_USE );
|
||
|
|
|
||
|
|
int use_cert = SSL_CTX_use_certificate_file( this->sslContext, "/etc/letsencrypt/live/unifyjs.org/fullchain.pem" , SSL_FILETYPE_PEM );
|
||
|
|
|
||
|
|
int use_prv = SSL_CTX_use_PrivateKey_file( this->sslContext, "/etc/letsencrypt/live/unifyjs.org/privkey.pem", SSL_FILETYPE_PEM );
|
||
|
|
|
||
|
|
if( use_cert != 1 ) {
|
||
|
|
|
||
|
|
printf( "error: SSL_CTX_use_certificate_file\n\n" );
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
if( use_prv != 1 ) {
|
||
|
|
|
||
|
|
printf( "error: SSL_CTX_use_PrivateKey_file\n\n" );
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( !SSL_CTX_check_private_key(this->sslContext) ) {
|
||
|
|
|
||
|
|
printf("Private key does not match the certificate public key\n");
|
||
|
|
|
||
|
|
// initiate page describing error.
|
||
|
|
}
|
||
|
|
|
||
|
|
ssl = SSL_new( this->sslContext );
|
||
|
|
|
||
|
|
SSL_set_fd( ssl, socketConnection );
|
||
|
|
|
||
|
|
//Here is the SSL Accept portion. Now all reads and writes must use SSL
|
||
|
|
int ssl_err = SSL_accept( ssl ); // SSL routines:SSL_UNDEFINED_FUNCTION:called a function you should not call
|
||
|
|
|
||
|
|
//printf ("SSL connection using %s\n", SSL_get_cipher( ssl ) );
|
||
|
|
|
||
|
|
//ERR_print_errors_fp( stderr );
|
||
|
|
|
||
|
|
if( ssl_err == 0 ){
|
||
|
|
|
||
|
|
printf("SSL_accept returned zero\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
int n;
|
||
|
|
|
||
|
|
if( ssl_err < 0 ) {
|
||
|
|
|
||
|
|
int err;
|
||
|
|
|
||
|
|
if( ( err = SSL_get_error(ssl,n ) ) == SSL_ERROR_WANT_READ) {
|
||
|
|
|
||
|
|
printf("SSL_accept wants more data\n");
|
||
|
|
|
||
|
|
return ;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
exit(7);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
|
||
|
|
printf ("SSL connection using %s\n", SSL_get_cipher (this->sslContext));
|
||
|
|
|
||
|
|
#define CHK_NULL(x) if ((x)==NULL) exit (1)
|
||
|
|
|
||
|
|
X509* client_cert;
|
||
|
|
|
||
|
|
char* str;
|
||
|
|
|
||
|
|
// Get client's certificate (note: beware of dynamic allocation) - opt
|
||
|
|
|
||
|
|
client_cert = SSL_get_peer_certificate (this->sslContext);
|
||
|
|
|
||
|
|
if ( client_cert != NULL ) {
|
||
|
|
|
||
|
|
printf ("Client certificate:\n");
|
||
|
|
|
||
|
|
str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
|
||
|
|
|
||
|
|
CHK_NULL(str);
|
||
|
|
|
||
|
|
printf ("\t subject: %s\n", str);
|
||
|
|
|
||
|
|
OPENSSL_free (str);
|
||
|
|
|
||
|
|
str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
|
||
|
|
|
||
|
|
CHK_NULL(str);
|
||
|
|
|
||
|
|
printf ("\t issuer: %s\n", str);
|
||
|
|
|
||
|
|
OPENSSL_free (str);
|
||
|
|
|
||
|
|
X509_free (client_cert);
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
printf ("Client does not have certificate.\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
if ( socketConnection == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (accept)");
|
||
|
|
|
||
|
|
return;
|
||
|
|
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
|
||
|
|
//printf("connection accepted2\n\n");
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
} else {
|
||
|
|
printf("connection accepted\n\n");
|
||
|
|
|
||
|
|
// Get client address
|
||
|
|
int sockn = getsockname( socketConnection,
|
||
|
|
( struct sockaddr * ) &client_addr,
|
||
|
|
( socklen_t * ) &client_addrlen);
|
||
|
|
|
||
|
|
|
||
|
|
if ( sockn == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (getsockname)");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
int valread;
|
||
|
|
|
||
|
|
if( this->useSSL == 1 ) {
|
||
|
|
|
||
|
|
valread = SSL_read( ssl, buffer, 4096 - 1 );
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
printf("read without ssl\n\n");
|
||
|
|
|
||
|
|
int valread = read( socketConnection, buffer, 4096 -1 );
|
||
|
|
|
||
|
|
if ( valread == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (read)");
|
||
|
|
|
||
|
|
//return;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
if ( valread == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (read)");
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
request * requestInstance = new request();
|
||
|
|
|
||
|
|
|
||
|
|
sscanf( buffer, "%s %s %s", requestInstance->method, requestInstance->url, requestInstance->version );
|
||
|
|
|
||
|
|
|
||
|
|
requestInstance->address = inet_ntoa( client_addr.sin_addr );
|
||
|
|
|
||
|
|
requestInstance->port = ntohs( client_addr.sin_port );
|
||
|
|
|
||
|
|
requestInstance->extension = this->getExtension( requestInstance->url );
|
||
|
|
|
||
|
|
requestInstance->mimeType = mimetypes->getByExtension( requestInstance->extension );
|
||
|
|
|
||
|
|
this->requestCallback( requestInstance, response );
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
//printf("response: %s",response->value);
|
||
|
|
|
||
|
|
int writeResponse;
|
||
|
|
|
||
|
|
if( this->useSSL == 1 ) {
|
||
|
|
|
||
|
|
writeResponse = SSL_write( ssl, response->value, response->length );
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
// Write to the socket
|
||
|
|
int writeResponse = write( socketConnection, response->value, response->length );
|
||
|
|
|
||
|
|
printf("close connection");
|
||
|
|
|
||
|
|
if ( writeResponse == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (write)");
|
||
|
|
|
||
|
|
return;
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( writeResponse == -1 ) {
|
||
|
|
|
||
|
|
perror("webserver (write)");
|
||
|
|
|
||
|
|
return;
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
//printf("written a response\n");
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//free( buffer );
|
||
|
|
|
||
|
|
//SSL_free( writeResponse );
|
||
|
|
|
||
|
|
//response->free();
|
||
|
|
|
||
|
|
//requestInstance->free();
|
||
|
|
|
||
|
|
close( socketConnection );
|
||
|
|
|
||
|
|
//SSL_free( response->value );
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
// SSL_CTX_free( this->sslContext );
|
||
|
|
// close( this->socket );
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void dump_buffer(void *buffer, int buffer_size)
|
||
|
|
{
|
||
|
|
int i;
|
||
|
|
|
||
|
|
for(i = 0;i < buffer_size;++i){
|
||
|
|
|
||
|
|
printf( "%c", ( (char *) buffer )[i]);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void abort() {
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
//kill -9 $(lsof -t -i tcp:8080)
|
||
|
|
|