Nilorea Library
C utilities for networking, threading, graphics
All Data Structures Files Functions Variables Typedefs Enumerations Macros Modules Pages
ex_network_ssl.c
1
8#include "nilorea/n_list.h"
9#include "nilorea/n_str.h"
10#include "nilorea/n_log.h"
11#include "nilorea/n_network.h"
13#include "nilorea/n_signals.h"
14
15char *port = NULL ;
16char *addr = NULL ;
17char *key = NULL ;
18char *cert = NULL ;
19char *root_dir = NULL ;
20LIST *routes = NULL ;
21int ip_version = NETWORK_IPALL ;
22int max_http_request_size = 16384 ;
23bool done = 0 ;
24
25NETWORK *server = NULL,
26 *netw = NULL ;
28void usage(void)
29{
30 fprintf( stderr,
31 " -p 'port' : set the https server port\n"
32 " -k 'key file' : SSL key file path\n"
33 " -c 'cert file' : SSL certificate file path\n"
34 " -a 'address name/ip' : optional, specify where to bind interface\n"
35 " -i 'ipmode' : optional, force 'ipv4' or 'ipv6', default supports both\n"
36 " -s 'size' : optional, maximum http request size (default: %d)\n"
37 " -d 'html root' : optional, specify a different http root dir (default: ./DATAS/)\n"
38 " -v : version\n"
39 " -h : help\n"
40 " -V 'log level' : optional, set the log level (default: LOG_ERR)\n" , max_http_request_size );
41}
42
43void 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 )
44{
45 int getoptret = 0,
46 log_level = LOG_ERR; /* default log level */
47
48 if( argc == 1 )
49 {
50 fprintf( stderr, "No arguments given, help:\n" );
51 usage();
52 exit( 1 );
53 }
54 while( ( getoptret = getopt( argc, argv, "hvs:V:p:i:a:r:k:c:s:d:" ) ) != EOF)
55 {
56 switch( getoptret )
57 {
58 case 'i' :
59 if( !strcmp( "v4", optarg ) )
60 {
61 (*ip_version) = NETWORK_IPV4 ;
62 n_log( LOG_NOTICE, "IPV4 selected" );
63 }
64 else if( !strcmp( "v6", optarg ) )
65 {
66 (*ip_version) = NETWORK_IPV6 ;
67 n_log( LOG_NOTICE, "IPV6 selected" );
68 }
69 else
70 {
71 n_log( LOG_NOTICE, "IPV4/6 selected" );
72 }
73 break;
74 case 'v' :
75 fprintf( stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__ );
76 exit( 1 );
77 case 'V' :
78 if( !strncmp( "LOG_NULL", optarg, 8 ) )
79 {
80 log_level = LOG_NULL ;
81 }
82 else
83 {
84 if( !strncmp( "LOG_NOTICE", optarg, 10 ) )
85 {
86 log_level = LOG_NOTICE;
87 }
88 else
89 {
90 if( !strncmp( "LOG_INFO", optarg, 8 ) )
91 {
92 log_level = LOG_INFO;
93 }
94 else
95 {
96 if( !strncmp( "LOG_ERR", optarg, 7 ) )
97 {
98 log_level = LOG_ERR;
99 }
100 else
101 {
102 if( !strncmp( "LOG_DEBUG", optarg, 9 ) )
103 {
104 log_level = LOG_DEBUG;
105 }
106 else
107 {
108 fprintf( stderr, "%s n'est pas un niveau de log valide.\n", optarg );
109 exit( -1 );
110 }
111 }
112 }
113 }
114 }
115 break;
116 case 'p' :
117 (*port) = strdup( optarg );
118 break ;
119 case 'r':
120 list_push( routes , strdup( optarg ) , &free );
121 break;
122 case 'a' :
123 (*addr) = strdup( optarg );
124 break;
125 case 'k' :
126 (*key) = strdup( optarg );
127 break;
128 case 'c' :
129 (*cert) = strdup( optarg );
130 break;
131 case 's' :
132 (*max_http_request_size) = atoi( optarg );
133 break;
134 case 'd' :
135 (*root_dir) = strdup( optarg );
136 break;
137 default :
138 case '?' :
139 {
140 if( optopt == 'd' )
141 {
142 fprintf( stderr, "\n Missing html root directory\n" );
143 }
144 if( optopt == 's' )
145 {
146 fprintf( stderr, "\n Missing max http size string\n" );
147 }
148 if( optopt == 'k' )
149 {
150 fprintf( stderr, "\n Missing key file string\n" );
151 }
152 if( optopt == 'c' )
153 {
154 fprintf( stderr, "\n Missing certificate file string\n" );
155 }
156
157 if( optopt == 'r' )
158 {
159 fprintf( stderr, "\n Missing route string\n" );
160 }
161 if( optopt == 'a' )
162 {
163 fprintf( stderr, "\n Missing binding host/addr string\n" );
164 }
165 if( optopt == 'i' )
166 {
167 fprintf( stderr, "\n Missing ip version (v4 or v6) string \n" );
168 }
169 else if( optopt == 'V' )
170 {
171 fprintf( stderr, "\n Missing log level string\n" );
172 }
173 else if( optopt == 'p' )
174 {
175 fprintf( stderr, "\n Missing port\n" );
176 }
177 else if( optopt != 's' )
178 {
179 fprintf( stderr, "\n Unknow missing option %c\n", optopt );
180 }
181 usage();
182 exit( 1 );
183 }
184 case 'h' :
185 {
186 usage();
187 exit( 1 );
188 }
189 }
190 }
191 set_log_level( log_level );
192} /* void process_args( ... ) */
193
194
195
196/* Exit handling */
197void action_on_sig( int recvd_signal )
198{
199 static int nb_sigterm = 0 ;
200
201 switch( recvd_signal )
202 {
203 /* We should not use these signals as they make the debugging going funky */
204#ifndef WIN32
205 case( SIGABRT ):
206 n_log( LOG_ERR, "Caught SIGABRT !" );
207 break;
208 case( SIGINT ):
209 n_log( LOG_ERR, "Caught SIGINT !" );
210 break;
211 case( SIGBUS ):
212 n_log( LOG_ERR, "Caught SIGBUS !" );
213 break;
214 case( SIGFPE ):
215 n_log( LOG_ERR, "Caught SIGFPE !" );
216 break;
217 case( SIGSEGV ):
218 n_log( LOG_ERR, "Caught SIGSEGV !" );
219 break;
220 case( SIGSYS ):
221 n_log( LOG_ERR, "Caught SIGSYS !" );
222 break;
223 case( SIGTERM ):
224 nb_sigterm ++ ;
225 if( nb_sigterm >= 2 )
226 {
227 n_log( LOG_ERR, "Caught too much SIGTERM, trying _exit() !!" );
228 _exit( -1 );
229 }
230 n_log( LOG_ERR, "Caught %d SIGTERM, exiting now !!" , nb_sigterm );
231 exit( -1 );
232 case( SIGUSR1 ):
233 done = TRUE ;
234 n_log( LOG_ERR, "Caught SIGUSR1 !" );
235 break;
236 case( SIGUSR2 ):
237 done = TRUE ;
238 n_log( LOG_ERR, "Caught SIGUSR1 !" );
239 break;
240 case( SIGHUP ):
241 n_log( LOG_NOTICE, "Caught SIGHUP !" );
242 break;
243#endif
244 default:
245 n_log( LOG_ERR, "Caught unknow signal %d", recvd_signal );
246 break ;
247 }
248} /* action_on_sig() */
249
250
251// Function to handle different URLs and return appropriate responses
252void handle_request(NETWORK *netw , LIST *routes) {
253 __n_assert( netw , return );
254 __n_assert( routes , return );
255
256 bool found = 0 ;
257 char **split_results = NULL ;
258 char *http_url = NULL ;
259 N_STR *dynamic_request_answer = NULL ;
260
261 // Read request
262 char *http_buffer = NULL ;
263 Alloca( http_buffer , max_http_request_size + 1 );
264 __n_assert( http_buffer , netw_close( &netw ) ; return );
265
266 SSL_read(netw -> ssl, http_buffer, max_http_request_size );
267 //n_log( LOG_DEBUG , "http_request: %s" , http_buffer );
268
269 // Extract URL from the request
270 char url[4096]="";
271 netw_get_url_from_http_request(http_buffer, url, sizeof(url));
272 n_log( LOG_DEBUG , "url: %s" , url );
273
274 // Handle the request based on the URL
275 N_STR *origin = new_nstr( 32 );
276 nstrprintf( origin , "%s:"SOCKET_SIZE_FORMAT , _str( netw -> link . ip ) , netw -> link . sock );
277
278 NETWORK_HTTP_INFO http_request = netw_extract_http_info( http_buffer );
279 N_STR *http_body = NULL ;
280
281 split_results = split( url , "?" , 0 );
282 if( !split_results || !split_results[ 0 ] )
283 {
284 http_body = char_to_nstr("<html><body><h1>Bad Request</h1></body></html>");
285 if( netw_build_http_response( &dynamic_request_answer, 400, "ex_network_ssl server", netw_guess_http_content_type( url ), "", http_body ) == FALSE )
286 {
287 n_log( LOG_ERR , "couldn't build a Bad Request answer for %s" , url );
288 }
289 n_log( LOG_ERR , "%s: %s %s 400" , _nstr( origin ) , http_request . type , url );
290 }
291 else
292 {
293 http_url = split_results[ 0 ];
294 n_log( LOG_INFO , "%s: %s %s request..." , _nstr( origin ) , http_request . type , url );
295 if( strcmp( "OPTIONS" , http_request . type ) == 0 )
296 {
297 if( netw_build_http_response( &dynamic_request_answer, 200, "ex_network_ssl server", netw_guess_http_content_type( url ), "Allow: OPTIONS, GET, POST\r\n", NULL ) == FALSE )
298 {
299 n_log( LOG_ERR , "couldn't build an OPTION answer for %s" , url );
300 }
301 n_log( LOG_INFO , "%s: %s %s 200" , _nstr( origin ) , http_request . type , url );
302 }
303 else if( strcmp( "GET" , http_request . type ) == 0 )
304 {
305 char system_url[ 4096 ] = "" ;
306 // example assume a root dir at DATAS
307 if( !root_dir )
308 {
309 snprintf(system_url, sizeof(system_url), "./DATAS%s" , http_url );
310 }
311 else
312 {
313 snprintf(system_url, sizeof(system_url), "%s%s" , root_dir ,http_url );
314 }
315
316 n_log( LOG_DEBUG , "%s: searching for file %s..." , _nstr( origin ) , system_url );
317
318 if( file_exist( system_url ) )
319 {
320 n_log( LOG_DEBUG , "%s: file %s found !" , _nstr( origin ) , system_url );
321 http_body = file_to_nstr( system_url );
322 if( !http_body )
323 {
324 http_body = char_to_nstr("<html><body><h1>Internal Server Error</h1></body></html>");
325 if( netw_build_http_response( &dynamic_request_answer, 500, "ex_network_ssl server", netw_guess_http_content_type( url ), "", http_body ) == FALSE )
326 {
327 n_log( LOG_ERR , "couldn't build an Internal Server Error answer for %s" , url );
328 }
329 n_log( LOG_ERR , "%s: %s %s 500" , _nstr( origin ) , http_request . type , url );
330 }
331 else
332 {
333 if( netw_build_http_response( &dynamic_request_answer, 200, "ex_network_ssl server", netw_guess_http_content_type( url ), "", http_body ) == FALSE )
334 {
335 n_log( LOG_ERR , "couldn't build an http answer for %s" , url );
336 }
337 n_log( LOG_INFO , "%s: %s %s 200" , _nstr( origin ) , http_request . type , url );
338 }
339 }
340 else
341 {
342 http_body = char_to_nstr("<html><body><h1>404 Not Found</h1></body></html>");
343 if( netw_build_http_response( &dynamic_request_answer, 404, "ex_network_ssl server", netw_guess_http_content_type( url ), "", http_body ) == FALSE )
344 {
345 n_log( LOG_ERR , "couldn't build a NOT FOUND answer for %s" , url );
346 }
347 n_log( LOG_ERR , "%s: %s %s 404" , _nstr( origin ) , http_request . type , url );
348 }
349 }
350 else if( strcmp( "POST" , http_request . type ) == 0 )
351 {
352 // Parse virtual route
353 found = 0 ;
354 list_foreach( node , routes )
355 {
356 if( strcmp( node -> ptr , http_url ) == 0 )
357 {
358 // Handle 200 OK from virtual route
359 HASH_TABLE *post_data = netw_parse_post_data( http_request . body );
360 if( post_data )
361 {
362 HT_FOREACH( node , post_data ,
363 {
364 n_log( LOG_DEBUG , "%s: POST DATA: %s=%s" , _nstr( origin ) , node -> key , (char *)node -> data . ptr );
365 });
366 destroy_ht( &post_data );
367 }
368 http_body = char_to_nstr("{\"status\":\"ok\"}");
369 if( netw_build_http_response( &dynamic_request_answer, 200, "ex_network_ssl server", "application/json" , "", http_body ) == FALSE )
370 {
371 n_log( LOG_ERR , "couldn't build a route 200 answer for %s" , url );
372 }
373 found = 1 ;
374 n_log( LOG_INFO , "%s: %s virtual:%s 200" , _nstr( origin ) , http_request . type , url );
375 break ;
376 }
377 }
378 if( !found )
379 {
380 http_body = char_to_nstr("<html><body><h1>404 Not Found</h1></body></html>");
381 if( netw_build_http_response( &dynamic_request_answer, 404, "ex_network_ssl server", netw_guess_http_content_type( url ), "", http_body ) == FALSE )
382 {
383 n_log( LOG_ERR , "couldn't build a NOT FOUND answer for %s" , url );
384 }
385 n_log( LOG_ERR , "%s: %s %s 404" , _nstr( origin ) , http_request . type , url );
386 }
387 }
388 else
389 {
390 http_body = char_to_nstr("<html><body><h1>Bad Request</h1></body></html>");
391 if( netw_build_http_response( &dynamic_request_answer, 400, "ex_network_ssl server", netw_guess_http_content_type( url ), "", http_body ) == FALSE )
392 {
393 n_log( LOG_ERR , "couldn't build a Bad Request answer for %s" , url );
394 }
395 n_log( LOG_ERR , "%s: %s %s 400" , _nstr( origin ) , http_request . type , url );
396 }
397 free_split_result( &split_results );
398 }
399 if( dynamic_request_answer )
400 {
401 SSL_write( netw -> ssl, _nstr( dynamic_request_answer ), dynamic_request_answer -> written );
402 free_nstr( &dynamic_request_answer );
403 }
404 else
405 {
406 n_log( LOG_ERR , "couldn't build an answer for %s: %s %s" , _nstr( origin ) , http_request . type , url );
407 }
408 netw_info_destroy( http_request );
409 free_nstr( &origin );
410 free_nstr( &http_body );
411} /* handle_request */
412
413
422
423void *ssl_network_thread( void *params )
424{
425 __n_assert( params, return NULL );
427 handle_request(ssl_params -> netw , ssl_params -> routes);
428 netw_close( &ssl_params -> netw );
429 Free( ssl_params );
430 return NULL ;
431}
432
433int main( int argc , char *argv[] ) {
434
435 int exit_code = 0 ;
436
437 routes = new_generic_list( -1 );
438 __n_assert( routes , n_log( LOG_ERR , "could not allocate list !"); exit( 1 ) );
439
440 /* processing args and set log_level */
441 process_args( argc, argv, &addr, &port, &key, &cert, routes, &ip_version , &max_http_request_size , &root_dir );
442
443 if( !port )
444 {
445 n_log( LOG_ERR, "No port given. Exiting." );
446 exit_code = 1; goto clean_and_exit ;
447 }
448 if( !key )
449 {
450 n_log( LOG_ERR, "No key given. Exiting." );
451 exit_code = 1; goto clean_and_exit ;
452 }
453 if( !cert )
454 {
455 n_log( LOG_ERR, "No certificate given. Exiting." );
456 exit_code = 1; goto clean_and_exit ;
457 }
458 if( routes -> nb_items == 0 )
459 {
460 n_log( LOG_ERR, "No route given. Exiting." );
461 exit_code = 1; goto clean_and_exit ;
462 }
463
464 #ifndef __windows__
465 errno = 0 ;
466 sigignore( SIGPIPE );
467 /* initializing signal catching */
468 struct sigaction signal_catcher ;
469
470 /* quit on sig */
471 signal_catcher . sa_handler = action_on_sig ;
472 sigemptyset( &signal_catcher . sa_mask );
473 signal_catcher . sa_flags = SA_SIGINFO;
474
475 sigaction( SIGTERM, &signal_catcher, NULL );
476 sigaction( SIGUSR1, &signal_catcher, NULL );
477 #endif
478
479
480 int nb_active_threads = get_nb_cpu_cores();
481 int nb_waiting_threads = 10 * nb_active_threads ;
482 n_log( LOG_INFO, "Creating a new thread pool of %d active and %d waiting threads" , nb_active_threads , nb_waiting_threads );
483 THREAD_POOL *thread_pool = new_thread_pool( nb_active_threads , nb_waiting_threads );
484
485 n_log( LOG_INFO, "Creating listening network for %s:%s %d", _str( addr ), _str( port ), ip_version );
486 /* create listening network */
487 if( netw_make_listening( &server , addr, port, SOMAXCONN , ip_version ) == FALSE )
488 {
489 n_log( LOG_ERR, "Fatal error with network initialization" );
490 exit( -1 );
491 }
492
493 netw_set_crypto( server , key , cert );
494 while( !done )
495 {
496 n_log( LOG_DEBUG, "Blocking on accept..." );
497 /* get any accepted client on a network */
498 int return_code = 0 ;
499 if( !(netw = netw_accept_from_ex( server , 0 , 0 , 0 , &return_code ) ) )
500 {
501 if( return_code == EINTR )
502 {
503 n_log( LOG_INFO, "accept exited after catching a signal");
504 goto clean_and_exit ;
505 }
506 else
507 {
508 n_log( LOG_ERR, "error on accept, %s !" , strerror( return_code ) );
509 }
510 }
511 else
512 {
513 NETWORK_SSL_THREAD_PARAMS *netw_ssl_params = NULL ;
514 Malloc( netw_ssl_params , NETWORK_SSL_THREAD_PARAMS , 1 );
515 netw_ssl_params -> netw = netw ;
516 netw_ssl_params -> routes = routes ;
517 if( add_threaded_process( thread_pool, &ssl_network_thread, (void *)netw_ssl_params, DIRECT_PROC) == FALSE )
518 {
519 n_log( LOG_ERR, "Error adding client management to thread pool" );
520 }
521 }
522 }
523clean_and_exit:
524 wait_for_threaded_pool( thread_pool, 1000 );
525 destroy_threaded_pool( &thread_pool, 1000 );
526 netw_close( &server );
527 netw_unload();
528 list_destroy( &routes );
529 exit( exit_code );
530}
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:191
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:284
#define _str(__PTR)
define true
Definition n_common.h:180
#define Alloca(__ptr, __size)
Malloca Handler to get errors and set to 0.
Definition n_common.h:206
int file_exist(const char *filename)
test if file exist and if it's readable
Definition n_common.c:94
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:264
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
Definition n_common.h:186
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition n_hash.c:2449
#define HT_FOREACH(__ITEM_, __HASH_,...)
ForEach macro helper.
Definition n_hash.h:194
structure of a hash table
Definition n_hash.h:108
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:244
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper.
Definition n_list.h:70
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:605
LIST * new_generic_list(int max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:20
Structure of a generic LIST container.
Definition n_list.h:45
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:74
#define LOG_DEBUG
debug-level messages
Definition n_log.h:66
#define LOG_ERR
error conditions
Definition n_log.h:58
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:97
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:62
#define LOG_NULL
no log output
Definition n_log.h:27
#define LOG_INFO
informational
Definition n_log.h:64
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:222
#define nstrprintf(__nstr_var,...)
Macro to quickly allocate and sprintf to N_STR
Definition n_str.h:97
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
Definition n_str.c:273
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
Definition n_str.c:215
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.
Definition n_str.c:1032
N_STR * file_to_nstr(char *filename)
Load a whole file into a N_STR.
Definition n_str.c:332
int free_split_result(char ***tab)
Free a split result allocated array.
Definition n_str.c:1131
A box including a string and his lenght.
Definition n_str.h:173
NETWORK * netw_accept_from_ex(NETWORK *from, int send_list_limit, int recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
Definition n_network.c:1923
#define NETWORK_IPV6
Flag to force IPV6
Definition n_network.h:31
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition n_network.c:1797
#define NETWORK_IPV4
Flag to force IPV4
Definition n_network.h:29
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
Definition n_network.c:3852
#define SOCKET_SIZE_FORMAT
socket associated printf style
Definition n_network.h:54
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
Definition n_network.h:27
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.
Definition n_network.c:3662
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:1588
NETWORK_HTTP_INFO netw_extract_http_info(char *request)
extract a lot of informations, mostly as pointers, and populate a NETWORK_HTTP_INFO structure
Definition n_network.c:3592
int netw_info_destroy(NETWORK_HTTP_INFO http_request)
destroy a NETWORK_HTTP_INFO loaded informations
Definition n_network.c:3648
HASH_TABLE * netw_parse_post_data(const char *post_data)
Function to parse POST data.
Definition n_network.c:3710
const char * netw_guess_http_content_type(const char *url)
function to guess the content type based on URL extension
Definition n_network.c:3755
Structure of a NETWORK.
Definition n_network.h:233
structure for splitting HTTP requests
Definition n_network.h:331
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.
Generic log system.
Network Engine.
Signals general handling with stack printing, from https://gist.github.com/jvranish/4441299.
N_STR and string function declaration.
Thread pool declaration.
structure of a NETWORK_SSL_THREAD_PARAMS
LIST * routes
virtual routes for the server
NETWORK * netw
network to use for the receiving thread