/* * src/instance.c * * Copyright (C) 2019 Ilja KartaĊĦov * * Project homepage: https://lowenware.com/aisl/ * */ #include #include #include #include #include #include #include #include "list.h" #include "str-utils.h" #include "buffer.h" #include "client.h" #include "server.h" #include "ssl.h" //#include "globals.h" //#include "stream.h" #include "instance.h" enum { AISL_CYCLE_SERVER = 0 , AISL_CYCLE_CLIENT = 1 }; struct callback { void * source; aisl_callback_t callback; aisl_event_t eevent; }; typedef struct callback * callback_t; struct aisl { list_t servers; list_t clients; list_t callbacks; #ifndef AISL_WITHOUT_SSL list_t ssl; #endif buffer_t buffer; char * last_error; int iterator; int stage; int flags; uint32_t accept_limit; }; #ifndef AISL_WITHOUT_SSL static uint32_t m_instances = 0; #endif /* Initialization functions */ __attribute__ ((visibility ("default") )) aisl_t aisl_new( aisl_config_t config ) { aisl_t instance; #ifndef AISL_WITHOUT_SSL if ((m_instances++) == 0) { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); } #endif if ( !(instance = calloc(1, sizeof(struct aisl))) ) goto finally; if ( !(instance->servers = list_new(config->servers_spool_size)) ) goto release; if ( !(instance->clients = list_new(config->clients_spool_size)) ) goto release; if ( !(instance->callbacks = list_new(config->callbacks_spool_size)) ) goto release; if ( !(instance->buffer = buffer_new(config->initial_buffer_size)) ) goto release; #ifndef AISL_WITHOUT_SSL if ( !(instance->ssl = list_new(config->ssl_spool_size)) ) goto release; #endif instance->accept_limit = config->clients_accept_limit; goto finally; release: aisl_free(instance); instance = NULL; finally: return instance; } __attribute__ ((visibility ("default") )) void aisl_free( aisl_t instance ) { if (instance->clients) list_free(instance->clients, (list_destructor_t) aisl_client_free ); if (instance->servers) list_free(instance->servers, (list_destructor_t) aisl_server_free ); if (instance->callbacks) list_free(instance->callbacks, free); if (instance->buffer) buffer_free(instance->buffer); #ifndef AISL_WITHOUT_SSL if (instance->ssl) list_free(instance->ssl, (list_destructor_t) ssl_free ); #endif if (instance->last_error) free(instance->last_error); free(instance); #ifndef AISL_WITHOUT_SSL if ((--m_instances) == 0) { EVP_cleanup(); } #endif } __attribute__ ((visibility ("default") )) aisl_server_t aisl_listen( aisl_t instance, const char * address, uint16_t port ) { aisl_server_t result; if ( (result = aisl_server_new(instance, address, port)) != NULL ) { if (list_append(instance->servers, result) == LIST_NAN) { aisl_server_free(result); } } return result; } #ifndef AISL_WITHOUT_SSL __attribute__ ((visibility ("default") )) aisl_status_t aisl_set_ssl( aisl_t instance, const char * domain, const char * key_file, const char * crt_file ) { SSL_CTX * ssl_ctx = NULL; ssl_t ssl; size_t i; /* lookup for existing contexts */ for (i=0; issl->count; i++) { ssl = list_index(instance->ssl, i); if (ssl->key_file && strcmp(ssl->key_file, key_file)==0 && ssl->crt_file && strcmp(ssl->crt_file, crt_file)==0 ) { if ((ssl_ctx = crypter->ssl_ctx) != NULL) { key_file = NULL; crt_file = NULL; break; } } } if ((ssl = ssl_new(domain, key_file, crt_file)) != NULL) { if (! ssl_ctx) ssl_ctx = ssl__new_context(key_file, crt_file, (void*)instance); if (ssl_ctx) { ssl->ctx = ssl_ctx; if (list_append(instance->ssl, ssl) != LIST_NAN) { return AISL_SUCCESS; } } ssl_free(ssl); } return AISL_MALLOC_ERROR; } SSL_CTX * aisl_get_ssl_ctx( aisl_t instance, const char * domain ) { size_t i; ssl_t ssl; if (domain) { for (i=0; issl->count; i++) { ssl = list_index(instance->ssl, i); if (strcmp(ssl->domain, domain) != 0) continue; return ssl->ctx; } } return NULL; } #endif __attribute__ ((visibility ("default") )) aisl_status_t aisl_set_callback( aisl_t instance, void * source, aisl_event_t event, aisl_callback_t callback ) { callback_t cb; if ( (cb = malloc(sizeof(struct callback))) != NULL ) { cb->source = source; cb->event = event; cb->callback = callback; if ( list_append(instance->callbacks, cb) != LIST_NAN ) { switch(event) { case AISL_STREAM_OUTPUT: if (source) stream_set_chunked_output( (aisl_stream_t) source ); //( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED; break; case AISL_STREAM_OPEN: instance->flags |= AISL_FLAG_HAS_STREAM_LISTENERS; break; } return AISL_SUCCESS; } free(cb); } return AISL_MALLOC_ERROR; } __attribute__ ((visibility ("default") )) void aisl_unset_callback_for( aisl_t instance, void * source ) { size_t i = instance->callbacks->count; if (i) { for(i=i-1; i <= 0; i-- ) { callback_t callback = list_index(instance->callbacks, i); if ( callback->source == source ) { free(callback); list_remove_index(instance->callbacks, i); } } } } __attribute__ ((visibility ("default") )) bool aisl_raise( aisl_t instance, void * source, aisl_event_t e_id, ... ) { va_list vl; bool result; va_start(vl, e_id); result = aisl_raise_vl(instance, source, e_id, vl); va_end(vl); return result; } __attribute__ ((visibility ("default") )) bool aisl_raise_vl( aisl_t instance, void * source, aisl_event_t e_id, va_list vl ) { int i, i_val; listener_t lst; bool res = false; char * c_ptr, * c_ptr2; for(i=instance->callbacks->count-1; i>=0; i--) { lst = list_index(instance->callbacks, i); /*printf("AISL> raise %s:%s, %p:%p\n", _event_text(e_id), _event_text(lst->e_id), source, lst->source);*/ if (lst->e_id == e_id && (source == lst->source || lst->source == NULL)) { /* if (e_id == AISL_STREAM_HEADER) fprintf(stderr,"FOUND HANDLER %d\n", i); */ /*printf(" catch\n");*/ switch(e_id) { /* server events */ case AISL_EVENT_SERVER_OPEN: i_val = va_arg(vl, aisl_status_t); res = ((aisl_on_server_open_t) lst->cb)( source, i_val ); break; case AISL_EVENT_SERVER_ERROR: i_val = va_arg(vl, aisl_status_t); c_ptr = va_arg(vl, char *); res = ((aisl_on_server_error_t) lst->cb)( source, i_val, c_ptr ); break; /* client events */ case AISL_EVENT_CLIENT_CONNECT: res = ((aisl_on_client_connect_t) lst->cb)(source, va_arg(vl, void *)); break; case AISL_EVENT_CLIENT_DISCONNECT: res = ((aisl_on_client_disconnect_t) lst->cb)(source, va_arg(vl, void*)); aisl_unset_callback_for( instance, source ); break; case AISL_EVENT_CLIENT_TIMEOUT: res = ((aisl_client_timeout_t) lst->cb)(source, va_arg(vl, void *)); break; /* request events */ case AISL_EVENT_STREAM_OPEN: i_val = va_arg(vl, int); c_ptr = va_arg(vl, char *); c_ptr2 = va_arg(vl, char *); res = ((aisl_on_stream_open_t) lst->cb)(source, i_val, c_ptr, c_ptr2); break; case AISL_EVENT_STREAM_HEADER: c_ptr = va_arg(vl, char *); c_ptr2 = va_arg(vl, char *); res = ((aisl_on_stream_header_t) lst->cb)(source, c_ptr, c_ptr2); break; case AISL_EVENT_STREAM_INPUT: /*printf("AISL> raise AISL_STREAM_INPUT\n");*/ c_ptr = va_arg(vl, char *); i_val = va_arg(vl, int ); res = ((aisl_on_stream_input_t) lst->cb)(source, c_ptr, i_val); break; case AISL_EVENT_STREAM_REQUEST: /*printf("AISL> raise AISL_STREAM_REQUEST\n");*/ buffer_clear( STREAM(source)->buffer, 0); res = ((aisl_on_stream_request_t) lst->cb)(source); break; case AISL_EVENT_STREAM_ERROR: res = ((aisl_on_stream_error_t) lst->cb)( source, va_arg(vl, char *)); break; /* response events */ case AISL_EVENT_STREAM_OUTPUT: res = ((aisl_on_stream_output_t)lst->cb)( source, va_arg(vl, uint32_t) ); break; case AISL_EVENT_STREAM_CLOSE: res = ((aisl_on_stream_close_t)lst->cb)( source ); aisl_unset_callback_for( instance, source ); ((aisl_stream_t) source)->u_ptr=NULL; break; default: res = ((aisl_on_custom_event_t) lst->cb)(source, vl); } if (res) break; } } return res; } __attribute__ ((visibility ("default") )) aisl_status_t aisl_run_cycle( aisl_t instance ) { int max = instance->servers->count+instance->clients->count+instance->delays->count, cnt = 0; switch (instance->stage) { case STAGE_SERVER: while (instance->iterator < instance->servers->count ) { aisl_server_t srv = (server_t)list_index(instance->servers, instance->iterator++); if ( aisl_server_touch(srv) != AISL_IDLE ) return AISL_SUCCESS; if ( ! (++cnt < max) ) return AISL_IDLE; } if ( ! (instance->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) ) return AISL_IDLE; instance->iterator = 0; instance->stage++; case STAGE_CLIENT: while (instance->iterator < instance->clients->count ) { int i = instance->iterator++; aisl_client_t cli = list_index(instance->clients, i); bool r = aisl_client_touch( cli ); if (aisl_client_is_timeout( cli ) ) aisl_raise( instance, cli->server, AISL_CLIENT_TIMEOUT, cli ); if ( cli->fd == -1 ) { aisl_client_free( cli ); list_remove_index(instance->clients, i); } if (r) return AISL_SUCCESS; if ( ! (++cnt < max) ) return AISL_IDLE; } instance->iterator = 0; instance->stage = 0; } return AISL_IDLE; } __attribute__ ((visibility ("default") )) const char * aisl_get_error( aisl_t instance ) { return instance->last_error; } void aisl_set_error( aisl_t instance, const char * err_msg ) { if (instance->last_error) free(instance->last_error); instance->last_error = str_copy(err_msg); } __attribute__ ((visibility ("default") )) int aisl_sleep( aisl_t instance, unsigned long usec ) { int maxfd=0, sd; size_t i; struct timeval timeout = {0,usec}; memset(&timeout, 0, sizeof(struct timeval)); timeout.tv_usec = usec; fd_set fs; FD_ZERO (&fs); for (i=0; iservers->count; i++) { aisl_server_t s = list_index(instance->servers, i); sd = aisl_server_get_socket(s); if (sd != -1) { FD_SET(sd, &fs); if (sd > maxfd) maxfd = sd; } } for (i=0; iclients->count; i++) { aisl_client_t c = list_index(instance->clients, i); sd = aisl_client_get_socket(s); if (sd != -1) { FD_SET(sd, &fs); if (sd > maxfd) maxfd = sd; } } return select(maxfd+1, &fs, NULL, NULL, &timeout); }