Network module example, interacting with SSL.
char *port = NULL ;
char *addr = NULL ;
char *key = NULL ;
char *cert = NULL ;
char *root_dir = NULL ;
int max_http_request_size = 16384 ;
bool done = 0 ;
*netw = NULL ;
void usage(void)
{
fprintf( stderr,
" -p 'port' : set the https server port\n"
" -k 'key file' : SSL key file path\n"
" -c 'cert file' : SSL certificate file path\n"
" -a 'address name/ip' : optional, specify where to bind interface\n"
" -i 'ipmode' : optional, force 'ipv4' or 'ipv6', default supports both\n"
" -s 'size' : optional, maximum http request size (default: %d)\n"
" -d 'html root' : optional, specify a different http root dir (default: ./DATAS/)\n"
" -v : version\n"
" -h : help\n"
" -V 'log level' : optional, set the log level (default: LOG_ERR)\n" , max_http_request_size );
}
void process_args(
int argc,
char **argv,
char **addr,
char **port,
char **key,
char **cert,
LIST *routes ,
int *ip_version ,
int *max_http_request_size ,
char **root_dir )
{
int getoptret = 0,
if( argc == 1 )
{
fprintf( stderr, "No arguments given, help:\n" );
usage();
exit( 1 );
}
while( ( getoptret = getopt( argc, argv, "hvs:V:p:i:a:r:k:c:s:d:" ) ) != EOF)
{
switch( getoptret )
{
case 'i' :
if( !strcmp( "v4", optarg ) )
{
}
else if( !strcmp( "v6", optarg ) )
{
}
else
{
}
break;
case 'v' :
fprintf( stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__ );
exit( 1 );
case 'V' :
if( !strncmp( "LOG_NULL", optarg, 8 ) )
{
}
else
{
if( !strncmp( "LOG_NOTICE", optarg, 10 ) )
{
}
else
{
if( !strncmp( "LOG_INFO", optarg, 8 ) )
{
}
else
{
if( !strncmp( "LOG_ERR", optarg, 7 ) )
{
}
else
{
if( !strncmp( "LOG_DEBUG", optarg, 9 ) )
{
}
else
{
fprintf( stderr, "%s n'est pas un niveau de log valide.\n", optarg );
exit( -1 );
}
}
}
}
}
break;
case 'p' :
(*port) = strdup( optarg );
break ;
case 'r':
list_push( routes , strdup( optarg ) , &free );
break;
case 'a' :
(*addr) = strdup( optarg );
break;
case 'k' :
(*key) = strdup( optarg );
break;
case 'c' :
(*cert) = strdup( optarg );
break;
case 's' :
(*max_http_request_size) = atoi( optarg );
break;
case 'd' :
(*root_dir) = strdup( optarg );
break;
default :
case '?' :
{
if( optopt == 'd' )
{
fprintf( stderr, "\n Missing html root directory\n" );
}
if( optopt == 's' )
{
fprintf( stderr, "\n Missing max http size string\n" );
}
if( optopt == 'k' )
{
fprintf( stderr, "\n Missing key file string\n" );
}
if( optopt == 'c' )
{
fprintf( stderr, "\n Missing certificate file string\n" );
}
if( optopt == 'r' )
{
fprintf( stderr, "\n Missing route string\n" );
}
if( optopt == 'a' )
{
fprintf( stderr, "\n Missing binding host/addr string\n" );
}
if( optopt == 'i' )
{
fprintf( stderr, "\n Missing ip version (v4 or v6) string \n" );
}
else if( optopt == 'V' )
{
fprintf( stderr, "\n Missing log level string\n" );
}
else if( optopt == 'p' )
{
fprintf( stderr, "\n Missing port\n" );
}
else if( optopt != 's' )
{
fprintf( stderr, "\n Unknow missing option %c\n", optopt );
}
usage();
exit( 1 );
}
case 'h' :
{
usage();
exit( 1 );
}
}
}
}
void action_on_sig( int recvd_signal )
{
static int nb_sigterm = 0 ;
switch( recvd_signal )
{
#ifndef WIN32
case( SIGABRT ):
break;
case( SIGINT ):
break;
case( SIGBUS ):
break;
case( SIGFPE ):
break;
case( SIGSEGV ):
break;
case( SIGSYS ):
break;
case( SIGTERM ):
nb_sigterm ++ ;
if( nb_sigterm >= 2 )
{
n_log(
LOG_ERR,
"Caught too much SIGTERM, trying _exit() !!" );
_exit( -1 );
}
n_log(
LOG_ERR,
"Caught %d SIGTERM, exiting now !!" , nb_sigterm );
exit( -1 );
case( SIGUSR1 ):
done = TRUE ;
break;
case( SIGUSR2 ):
done = TRUE ;
break;
case( SIGHUP ):
break;
#endif
default:
break ;
}
}
bool found = 0 ;
char **split_results = NULL ;
char *http_url = NULL ;
N_STR *dynamic_request_answer = NULL ;
char *http_buffer = NULL ;
Alloca( http_buffer , max_http_request_size + 1 );
SSL_read(netw -> ssl, http_buffer, max_http_request_size );
char url[4096]="";
N_STR *http_body = NULL ;
split_results =
split( url ,
"?" , 0 );
if( !split_results || !split_results[ 0 ] )
{
http_body =
char_to_nstr(
"<html><body><h1>Bad Request</h1></body></html>");
{
n_log(
LOG_ERR ,
"couldn't build a Bad Request answer for %s" , url );
}
}
else
{
http_url = split_results[ 0 ];
if( strcmp( "OPTIONS" , http_request . type ) == 0 )
{
{
n_log(
LOG_ERR ,
"couldn't build an OPTION answer for %s" , url );
}
}
else if( strcmp( "GET" , http_request . type ) == 0 )
{
char system_url[ 4096 ] = "" ;
if( !root_dir )
{
snprintf(system_url, sizeof(system_url), "./DATAS%s" , http_url );
}
else
{
snprintf(system_url, sizeof(system_url), "%s%s" , root_dir ,http_url );
}
{
if( !http_body )
{
http_body =
char_to_nstr(
"<html><body><h1>Internal Server Error</h1></body></html>");
{
n_log(
LOG_ERR ,
"couldn't build an Internal Server Error answer for %s" , url );
}
}
else
{
{
n_log(
LOG_ERR ,
"couldn't build an http answer for %s" , url );
}
}
}
else
{
http_body =
char_to_nstr(
"<html><body><h1>404 Not Found</h1></body></html>");
{
n_log(
LOG_ERR ,
"couldn't build a NOT FOUND answer for %s" , url );
}
}
}
else if( strcmp( "POST" , http_request . type ) == 0 )
{
found = 0 ;
{
if( strcmp( node -> ptr , http_url ) == 0 )
{
if( post_data )
{
{
n_log(
LOG_DEBUG ,
"%s: POST DATA: %s=%s" ,
_nstr( origin ) , node -> key , (
char *)node -> data . ptr );
});
}
if(
netw_build_http_response( &dynamic_request_answer, 200,
"ex_network_ssl server",
"application/json" ,
"", http_body ) == FALSE )
{
n_log(
LOG_ERR ,
"couldn't build a route 200 answer for %s" , url );
}
found = 1 ;
n_log(
LOG_INFO ,
"%s: %s virtual:%s 200" ,
_nstr( origin ) , http_request . type , url );
break ;
}
}
if( !found )
{
http_body =
char_to_nstr(
"<html><body><h1>404 Not Found</h1></body></html>");
{
n_log(
LOG_ERR ,
"couldn't build a NOT FOUND answer for %s" , url );
}
}
}
else
{
http_body =
char_to_nstr(
"<html><body><h1>Bad Request</h1></body></html>");
{
n_log(
LOG_ERR ,
"couldn't build a Bad Request answer for %s" , url );
}
}
}
if( dynamic_request_answer )
{
SSL_write( netw -> ssl,
_nstr( dynamic_request_answer ), dynamic_request_answer -> written );
}
else
{
n_log(
LOG_ERR ,
"couldn't build an answer for %s: %s %s" ,
_nstr( origin ) , http_request . type , url );
}
}
{
void *ssl_network_thread( void *params )
{
handle_request(ssl_params -> netw , ssl_params -> routes);
return NULL ;
}
int main( int argc , char *argv[] ) {
int exit_code = 0 ;
process_args( argc, argv, &addr, &port, &key, &cert, routes, &ip_version , &max_http_request_size , &root_dir );
if( !port )
{
exit_code = 1; goto clean_and_exit ;
}
if( !key )
{
exit_code = 1; goto clean_and_exit ;
}
if( !cert )
{
exit_code = 1; goto clean_and_exit ;
}
if( routes -> nb_items == 0 )
{
exit_code = 1; goto clean_and_exit ;
}
#ifndef __windows__
errno = 0 ;
sigignore( SIGPIPE );
struct sigaction signal_catcher ;
signal_catcher . sa_handler = action_on_sig ;
sigemptyset( &signal_catcher . sa_mask );
signal_catcher . sa_flags = SA_SIGINFO;
sigaction( SIGTERM, &signal_catcher, NULL );
sigaction( SIGUSR1, &signal_catcher, NULL );
#endif
int nb_waiting_threads = 10 * nb_active_threads ;
n_log(
LOG_INFO,
"Creating a new thread pool of %d active and %d waiting threads" , nb_active_threads , nb_waiting_threads );
{
n_log(
LOG_ERR,
"Fatal error with network initialization" );
exit( -1 );
}
netw_set_crypto( server , key , cert );
while( !done )
{
int return_code = 0 ;
{
if( return_code == EINTR )
{
goto clean_and_exit ;
}
else
{
n_log(
LOG_ERR,
"error on accept, %s !" , strerror( return_code ) );
}
}
else
{
netw_ssl_params -> netw = netw ;
netw_ssl_params -> routes = routes ;
{
n_log(
LOG_ERR,
"Error adding client management to thread pool" );
}
}
}
clean_and_exit:
netw_unload();
exit( exit_code );
}
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define _str(__PTR)
define true
#define Alloca(__ptr, __size)
Malloca Handler to get errors and set to 0.
int file_exist(const char *filename)
test if file exist and if it's readable
#define Free(__ptr)
Free Handler to get errors.
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
#define HT_FOREACH(__ITEM_, __HASH_,...)
ForEach macro helper.
structure of a hash table
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(int max_items)
Initialiaze a generic list container to max_items pointers.
Structure of a generic LIST container.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
#define LOG_NOTICE
normal but significant condition
#define LOG_NULL
no log output
#define LOG_INFO
informational
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
#define nstrprintf(__nstr_var,...)
Macro to quickly allocate and sprintf to N_STR
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
char ** split(const char *str, const char *delim, int empty)
split the strings into a an array of char *pointer , ended by a NULL one.
N_STR * file_to_nstr(char *filename)
Load a whole file into a N_STR.
int free_split_result(char ***tab)
Free a split result allocated array.
A box including a string and his lenght.
NETWORK * netw_accept_from_ex(NETWORK *from, int send_list_limit, int recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
#define NETWORK_IPV6
Flag to force IPV6
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
#define NETWORK_IPV4
Flag to force IPV4
int netw_build_http_response(N_STR **http_response, int status_code, const char *server_name, const char *content_type, char *additional_headers, N_STR *body)
function to dynamically generate an HTTP response
#define SOCKET_SIZE_FORMAT
socket associated printf style
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
void netw_get_url_from_http_request(const char *request, char *url, size_t size)
Helper function to extract the URL from the HTTP request line.
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
NETWORK_HTTP_INFO netw_extract_http_info(char *request)
extract a lot of informations, mostly as pointers, and populate a NETWORK_HTTP_INFO structure
int netw_info_destroy(NETWORK_HTTP_INFO http_request)
destroy a NETWORK_HTTP_INFO loaded informations
HASH_TABLE * netw_parse_post_data(const char *post_data)
Function to parse POST data.
const char * netw_guess_http_content_type(const char *url)
function to guess the content type based on URL extension
structure for splitting HTTP requests
int get_nb_cpu_cores()
get number of core of current system
int destroy_threaded_pool(THREAD_POOL **pool, int delay)
delete a thread_pool, exit the threads and free the structs
int add_threaded_process(THREAD_POOL *thread_pool, void *(*func_ptr)(void *param), void *param, int mode)
add a function and params to a thread pool
#define DIRECT_PROC
processing mode for added func, direct start
int wait_for_threaded_pool(THREAD_POOL *thread_pool, int delay)
Wait for all the launched process in the thread pool to terminate.
THREAD_POOL * new_thread_pool(int nbmaxthr, int nb_max_waiting)
Create a new pool of nbmaxthr threads.
Structure of a trhead pool.
List structures and definitions.
Signals general handling with stack printing, from https://gist.github.com/jvranish/4441299.
N_STR and string function declaration.
structure of a NETWORK_SSL_THREAD_PARAMS
LIST * routes
virtual routes for the server
NETWORK * netw
network to use for the receiving thread