54 " -p 'port' : set the https server port\n"
55 " -k 'key file' : SSL key file path\n"
56 " -c 'cert file' : SSL certificate file path\n"
57 " -A 'CA file' : optional, CA certificate file for chain verification\n"
58 " -e : optional, enable SSL peer certificate verification\n"
59 " -a 'address name/ip' : optional, specify where to bind interface\n"
60 " -i 'ipmode' : optional, force 'ipv4' or 'ipv6', default supports both\n"
61 " -s 'size' : optional, maximum http request size (default: %d)\n"
62 " -n 'count' : optional, exit after count connections (0 = unlimited, default)\n"
63 " -d 'html root' : optional, specify a different http root dir (default: ./DATAS/)\n"
66 " -V 'log level' : optional, set the log level (default: LOG_ERR)\n",
70void process_args(
int argc_nb,
char** argv_ptr,
char** addr_ptr,
char** port_ptr,
char** key_ptr,
char** cert_ptr,
char** ca_file_ptr,
int* ssl_verify_ptr,
LIST* routes_ptr,
int* ip_version_ptr,
int* max_http_request_size_ptr,
char** root_dir_ptr) {
75 fprintf(stderr,
"No arguments given, help:\n");
79 while ((
getoptret = getopt(argc_nb, argv_ptr,
"hven:s:V:p:i:a:r:k:c:s:d:A:")) != EOF) {
85 if (!strcmp(
"v4", optarg)) {
88 }
else if (!strcmp(
"v6", optarg)) {
96 fprintf(stderr,
"Date de compilation : %s a %s.\n", __DATE__, __TIME__);
99 if (!strncmp(
"LOG_NULL", optarg, 8)) {
102 if (!strncmp(
"LOG_NOTICE", optarg, 10)) {
105 if (!strncmp(
"LOG_INFO", optarg, 8)) {
108 if (!strncmp(
"LOG_ERR", optarg, 7)) {
111 if (!strncmp(
"LOG_DEBUG", optarg, 9)) {
114 fprintf(stderr,
"%s n'est pas un niveau de log valide.\n", optarg);
123 (*ssl_verify_ptr) = 1;
126 (*port_ptr) = strdup(optarg);
129 list_push(routes_ptr, strdup(optarg), &free);
132 (*addr_ptr) = strdup(optarg);
135 (*key_ptr) = strdup(optarg);
138 (*cert_ptr) = strdup(optarg);
141 (*ca_file_ptr) = strdup(optarg);
144 (*max_http_request_size_ptr) = atoi(optarg);
147 (*root_dir_ptr) = strdup(optarg);
152 fprintf(stderr,
"\n Missing html root directory\n");
155 fprintf(stderr,
"\n Missing max http size string\n");
158 fprintf(stderr,
"\n Missing key file string\n");
161 fprintf(stderr,
"\n Missing certificate file string\n");
164 fprintf(stderr,
"\n Missing CA file string\n");
167 fprintf(stderr,
"\n Missing route string\n");
170 fprintf(stderr,
"\n Missing binding host/addr string\n");
173 fprintf(stderr,
"\n Missing ip version (v4 or v6) string \n");
174 }
else if (optopt ==
'V') {
175 fprintf(stderr,
"\n Missing log level string\n");
176 }
else if (optopt ==
'p') {
177 fprintf(stderr,
"\n Missing port\n");
178 }
else if (optopt !=
's') {
179 fprintf(stderr,
"\n Unknow missing option %c\n", optopt);
197 static int nb_sigterm = 0;
198 switch (recvd_signal) {
220 if (nb_sigterm >= 2) {
221 n_log(
LOG_ERR,
"Caught too much SIGTERM, trying _exit() !!");
224 n_log(
LOG_ERR,
"Caught %d SIGTERM, exiting now !!", nb_sigterm);
238 n_log(
LOG_ERR,
"Caught unknow signal %d", recvd_signal);
250 char** split_results = NULL;
251 char* http_url = NULL;
252 N_STR* dynamic_request_answer = NULL;
255 char* http_buffer = NULL;
263 if (ssl_read_ret <= 0) {
264 int ssl_error = SSL_get_error(netw_ptr->
ssl, ssl_read_ret);
265 if (ssl_error == SSL_ERROR_ZERO_RETURN) {
266 n_log(
LOG_INFO,
"SSL_read: peer closed the connection cleanly");
268 n_log(
LOG_ERR,
"SSL_read failed with SSL error %d", ssl_error);
285 N_STR* http_body = NULL;
287 split_results =
split(url,
"?", 0);
288 if (!split_results || !split_results[0]) {
289 http_body =
char_to_nstr(
"<html><body><h1>Bad Request</h1></body></html>");
291 n_log(
LOG_ERR,
"couldn't build a Bad Request answer for %s", url);
295 http_url = split_results[0];
297 if (strcmp(
"OPTIONS", http_request.
type) == 0) {
299 n_log(
LOG_ERR,
"couldn't build an OPTION answer for %s", url);
302 }
else if (strcmp(
"GET", http_request.
type) == 0) {
303 char system_url[4096] =
"";
306 snprintf(system_url,
sizeof(system_url),
"./DATAS%s", http_url);
308 snprintf(system_url,
sizeof(system_url),
"%s%s",
root_dir, http_url);
314 if (stat(system_url, &st) == 0 && S_ISREG(st.st_mode)) {
318 http_body =
char_to_nstr(
"<html><body><h1>Internal Server Error</h1></body></html>");
320 n_log(
LOG_ERR,
"couldn't build an Internal Server Error answer for %s", url);
325 n_log(
LOG_ERR,
"couldn't build an http answer for %s", url);
329 }
else if (stat(system_url, &st) == 0 && S_ISDIR(st.st_mode)) {
331 char index_path[4096 + 16] =
"";
332 size_t url_len = strlen(http_url);
333 const char* slash = (url_len > 0 && http_url[url_len - 1] ==
'/') ?
"" :
"/";
334 snprintf(index_path,
sizeof(index_path),
"%s%sindex.html", system_url, slash);
336 if (stat(index_path, &idx_st) == 0 && S_ISREG(idx_st.st_mode)) {
337 char location_header[4096] =
"";
338 snprintf(location_header,
sizeof(location_header),
"Location: %s%sindex.html\r\n", http_url, slash);
339 if (
netw_build_http_response(&dynamic_request_answer, 301,
"ex_network_ssl server",
"text/html", location_header, NULL) == FALSE) {
340 n_log(
LOG_ERR,
"couldn't build a redirect answer for %s", url);
342 n_log(
LOG_INFO,
"%s: %s %s 301 -> %s%sindex.html",
_nstr(origin), http_request.
type, url, http_url, slash);
344 http_body =
char_to_nstr(
"<html><body><h1>404 Not Found</h1></body></html>");
346 n_log(
LOG_ERR,
"couldn't build a NOT FOUND answer for %s", url);
351 http_body =
char_to_nstr(
"<html><body><h1>404 Not Found</h1></body></html>");
353 n_log(
LOG_ERR,
"couldn't build a NOT FOUND answer for %s", url);
357 }
else if (strcmp(
"POST", http_request.
type) == 0) {
361 if (strcmp(node->ptr, http_url) == 0) {
367 n_log(
LOG_DEBUG,
"%s: POST DATA: %s=%s",
_nstr(origin), hnode->key, (
char*)hnode->data.ptr);
372 if (
netw_build_http_response(&dynamic_request_answer, 200,
"ex_network_ssl server",
"application/json",
"", http_body) == FALSE) {
373 n_log(
LOG_ERR,
"couldn't build a route 200 answer for %s", url);
381 http_body =
char_to_nstr(
"<html><body><h1>404 Not Found</h1></body></html>");
383 n_log(
LOG_ERR,
"couldn't build a NOT FOUND answer for %s", url);
388 http_body =
char_to_nstr(
"<html><body><h1>Bad Request</h1></body></html>");
390 n_log(
LOG_ERR,
"couldn't build a Bad Request answer for %s", url);
396 if (dynamic_request_answer) {
397 if (dynamic_request_answer->
written > UINT32_MAX) {
398 n_log(
LOG_ERR,
"response too large to send for %s: %s %s (size: %zu)",
_nstr(origin), http_request.
type, url, dynamic_request_answer->
written);
428int main(
int argc,
char* argv[]) {
435 process_args(argc, argv, &
addr, &
port, &
key, &
cert, &
ca_file, &
ssl_verify,
routes, &
ip_version, &
max_http_request_size, &
root_dir);
462 signal(SIGPIPE, SIG_IGN);
464 struct sigaction signal_catcher;
468 sigemptyset(&signal_catcher.sa_mask);
469 signal_catcher.sa_flags = 0;
471 sigaction(SIGTERM, &signal_catcher, NULL);
472 sigaction(SIGUSR1, &signal_catcher, NULL);
476 int nb_active_threads = (cores > 0) ? (
int)cores : 1;
477 int nb_waiting_threads = 10 * nb_active_threads;
478 n_log(
LOG_INFO,
"Creating a new thread pool of %d active and %d waiting threads", nb_active_threads, nb_waiting_threads);
484 n_log(
LOG_ERR,
"Fatal error with network initialization");
495 n_log(
LOG_INFO,
"SSL peer certificate verification enabled");
498 int accepted_count = 0;
505 if (return_code == EINTR) {
509 n_log(
LOG_ERR,
"error on accept, NULL netw returned !");
518 n_log(
LOG_ERR,
"Error adding client management to thread pool");
void process_args(int argc, char **argv)
THREAD_POOL * thread_pool
NETWORK * netw
Network for server mode, accepting incomming.
int max_http_request_size
void * ssl_network_thread(void *params)
void action_on_sig(int recvd_signal)
void handle_request(NETWORK *netw_ptr, LIST *routes_ptr)
LIST * routes
virtual routes for the server
NETWORK * netw
network to use for the receiving thread
structure of a NETWORK_SSL_THREAD_PARAMS
#define FreeNoLog(__ptr)
Free Handler without log.
#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.
#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, safe for node removal during iteration.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
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
size_t written
size of the written data inside the string
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
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
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
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.
char * ip
ip of the connected socket
N_SOCKET link
networking socket
char * type
Type of request.
char * body
Pointer to the body data.
SOCKET sock
a normal socket
ssize_t send_ssl_data(void *netw, char *buf, uint32_t n)
send data onto the socket
int netw_ssl_set_verify(NETWORK *netw, int enable)
enable or disable SSL peer certificate verification
int 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_set_crypto(NETWORK *netw, char *key, char *certificate)
activate SSL encryption on selected network, using key and certificate
#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
NETWORK * netw_accept_from_ex(NETWORK *from, size_t send_list_limit, size_t recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
int netw_set_crypto_chain(NETWORK *netw, char *key, char *certificate, char *ca_file)
activate SSL encryption using key/certificate files and a CA file for chain verification
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
THREAD_POOL * new_thread_pool(size_t nbmaxthr, size_t nb_max_waiting)
Create a new pool of nbmaxthr threads.
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
int wait_for_threaded_pool(THREAD_POOL *thread_pool)
Wait for the thread pool to become idle (no active threads, empty waiting list), blocking without pol...
int destroy_threaded_pool(THREAD_POOL **pool, unsigned int delay)
delete a thread_pool, exit the threads and free the structs
long int get_nb_cpu_cores()
get number of core of current system
#define DIRECT_PROC
processing mode for added func, direct start, not queued
Structure of a thread pool.
List structures and definitions.
Signals general handling with stack printing, from https://gist.github.com/jvranish/4441299.
N_STR and string function declaration.