/** * @short Creates the onion structure to fill with the server data, and later do the onion_listen() * @memberof onion_t * * Creates an onion structure that can be used to set the server, port, SSL and similar parameters. It works over * the onion structure, which is the main structure to control the listening of new connections throught TCP/IP. * * A normal usage would be like this: * * @code * * onion *o=onion_new(O_THREADED); * onion_set_root_handler(o, onion_handler_directory(".")); * onion_listen(o); * * @endcode * * @param flags Or'ed flags to use at the listening daemon. Normally one of O_ONE, O_ONE_LOOP or O_THREADED. * * @returns The onion structure. * * @see onion_mode_e onion_t */ onion *onion_new(int flags){ ONION_DEBUG0("Some internal sizes: onion size: %d, request size %d, response size %d",sizeof(onion),sizeof(onion_request),sizeof(onion_response)); if(SOCK_CLOEXEC == 0){ ONION_WARNING("There is no support for SOCK_CLOEXEC compiled in libonion. This may be a SECURITY PROBLEM as connections may leak into executed programs."); } if (!(flags&O_NO_SIGPIPE)){ ONION_DEBUG("Ignoring SIGPIPE"); signal(SIGPIPE, SIG_IGN); } onion *o=onion_low_calloc(1,sizeof(onion)); if (!o){ return NULL; } o->flags=(flags&0x0FF)|O_SSL_AVAILABLE; o->timeout=5000; // 5 seconds of timeout, default. o->poller=onion_poller_new(15); if (!o->poller){ onion_low_free(o); return NULL; } o->sessions=onion_sessions_new(); o->internal_error_handler=onion_handler_new((onion_handler_handler)onion_default_error, NULL, NULL); o->max_post_size=1024*1024; // 1MB o->max_file_size=1024*1024*1024; // 1GB #ifdef HAVE_PTHREADS o->flags|=O_THREADS_AVALIABLE; o->nthreads=8; if (o->flags&O_THREADED) o->flags|=O_THREADS_ENABLED; #endif return o; }
/** * @short Creates an onion handler with that private datas. * @memberof onion_handler_t * */ onion_handler *onion_handler_new(onion_handler_handler handler, void *priv_data, onion_handler_private_data_free priv_data_free){ onion_handler *phandler=onion_low_calloc(1, sizeof(onion_handler)); phandler->handler=handler; phandler->priv_data=priv_data; phandler->priv_data_free=priv_data_free; return phandler; }
/** * @short Creates a request object * @memberof onion_request_t * * @param op Listen point this request is listening to, to be able to read and write data */ onion_request *onion_request_new(onion_listen_point *op) { onion_request *req; req=onion_low_calloc(1, sizeof(onion_request)); req->connection.listen_point=op; req->connection.fd=-1; //req->connection=con; req->headers=onion_dict_new(); onion_dict_set_flags(req->headers, OD_ICASE); ONION_DEBUG0("Create request %p", req); if (op) { if (op->request_init) { if (op->request_init(req)<0) { ONION_DEBUG("Invalid request, closing"); onion_request_free(req); return NULL; } } else onion_listen_point_request_init_from_socket(req); } return req; }
/** * @short Creates a new slot for the poller, for input data to be ready. * @memberof onion_poller_slot_t * @ingroup poller * * @param fd File descriptor to watch * @param f Function to call when data is ready. If function returns <0, the slot will be removed. * @param data Data to pass to the function. * * @returns A new poller slot, ready to be added (onion_poller_add) or modified (onion_poller_slot_set_shutdown, onion_poller_slot_set_timeout). */ onion_poller_slot *onion_poller_slot_new(int fd, int (*f)(void*), void *data){ static onion_poller_slot empty_slot; static onion_poller_slot *slots; static rlim_t max_slots; if (!max_slots) { struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim)) { ONION_ERROR("getrlimit: %s", strerror(errno)); return NULL; } max_slots = rlim.rlim_cur; if (max_slots > MAX_SLOTS) max_slots = MAX_SLOTS; slots = (onion_poller_slot *)onion_low_calloc(max_slots, sizeof(onion_poller_slot)); } if (fd<0||fd>=max_slots){ ONION_ERROR("Trying to add an invalid file descriptor to the poller. Please check."); return NULL; } onion_poller_slot *el=&slots[fd]; *el=empty_slot; el->fd=fd; el->f=f; el->data=data; el->timeout=-1; el->timeout_limit=INT_MAX; el->type=EPOLLIN | EPOLLHUP | EPOLLONESHOT | EPOLLHUP; return el; }
/// Create a new poller onion_poller *onion_poller_new(int aprox_n){ evthread_use_pthreads(); onion_poller *ret=onion_low_calloc(1,sizeof(onion_poller)); ret->base=event_base_new(); sem_init(&ret->sem, 0, 1); return ret; }
/// Create a new slot for the poller onion_poller_slot *onion_poller_slot_new(int fd, int (*f)(void*), void *data){ onion_poller_slot *ret=onion_low_calloc(1, sizeof(onion_poller_slot)); ret->fd=fd; ret->f=f; ret->data=data; ret->type=EV_READ | EV_WRITE; return ret; }
/** * @memberof onion_dict_t * Initializes the basic tree with all the structure in place, but empty. */ onion_dict *onion_dict_new(){ onion_dict *dict=onion_low_calloc(1, sizeof(onion_dict)); #ifdef HAVE_PTHREADS pthread_rwlock_init(&dict->lock, NULL); pthread_mutex_init(&dict->refmutex, NULL); #endif dict->refcount=1; dict->cmp=strcmp; ONION_DEBUG0("New %p, refcount %d",dict, dict->refcount); return dict; }
/** * @short Creates a new slot for the poller, for input data to be ready. * @memberof onion_poller_slot_t * * @param fd File descriptor to watch * @param f Function to call when data is ready. If function returns <0, the slot will be removed. * @param data Data to pass to the function. * * @returns A new poller slot, ready to be added (onion_poller_add) or modified (onion_poller_slot_set_shutdown, onion_poller_slot_set_timeout). */ onion_poller_slot *onion_poller_slot_new(int fd, int (*f)(void*), void *data){ if (fd<0){ ONION_ERROR("Trying to add an invalid file descriptor to the poller. Please check."); return NULL; } onion_poller_slot *el=(onion_poller_slot*)onion_low_calloc(1, sizeof(onion_poller_slot)); el->fd=fd; el->f=f; el->data=data; el->timeout=-1; el->timeout_limit=INT_MAX; el->type=EPOLLIN | EPOLLHUP | EPOLLONESHOT; return el; }
/// Initializes static data. Init at onion_poller_new, deinit at _free. static void onion_poller_static_init(){ int16_t prevcount = __sync_fetch_and_add(&onion_poller_static.refcount, 1); if (prevcount!=0) // Only init first time return; memset(&onion_poller_static.empty_slot, 0, sizeof(onion_poller_static.empty_slot)); struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim)) { ONION_ERROR("getrlimit: %s", strerror(errno)); return; } onion_poller_static.max_slots = rlim.rlim_cur; if (onion_poller_static.max_slots > MAX_SLOTS) onion_poller_static.max_slots = MAX_SLOTS; onion_poller_static.slots = (onion_poller_slot *)onion_low_calloc(onion_poller_static.max_slots, sizeof(onion_poller_slot)); }
/** * @short Returns a poller object that helps polling on sockets and files * @memberof onion_poller_t * @ingroup poller * * This poller is implemented through epoll, but other implementations are possible * */ onion_poller *onion_poller_new(int n){ onion_poller *p=onion_low_calloc(1, sizeof(onion_poller)); p->fd=epoll_create1(EPOLL_CLOEXEC); if (p->fd < 0){ ONION_ERROR("Error creating the poller. %s", strerror(errno)); onion_low_free(p); return NULL; } p->eventfd=eventfd(0,EFD_CLOEXEC | EFD_NONBLOCK); #if EFD_CLOEXEC == 0 fcntl(p->eventfd,F_SETFD,FD_CLOEXEC); #endif p->head=NULL; p->n=0; p->stop=0; #ifdef HAVE_PTHREADS ONION_DEBUG("Init thread stuff for poll. Eventfd at %d", p->eventfd); p->npollers=0; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&p->mutex, &attr); pthread_mutexattr_destroy(&attr); #endif onion_poller_static_init(); onion_poller_slot *ev=onion_poller_slot_new(p->eventfd,onion_poller_stop_helper,p); onion_poller_add(p,ev); p->timerfd=timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); ev=onion_poller_slot_new(p->timerfd,&onion_poller_timer,p); onion_poller_add(p,ev); onion_poller_timer(p); // Force first timeout return p; }
/** * @short Write some data into the request, and passes it line by line to onion_request_fill * * It features a state machine, from req->parse_state. * * Depending on the state input is redirected to a diferent parser, one for headers, POST url encoded data... * * @return Returns the number of bytes writen, or <=0 if connection should close, according to onion_connection_status * @see onion_connection_status */ onion_connection_status onion_request_write(onion_request *req, const char *data, size_t size){ if (!req->parser_data){ req->parser_data=onion_low_calloc(1, sizeof(onion_token)); req->parser=parse_headers_GET; } onion_connection_status (*parse)(onion_request *req, onion_buffer *data); parse=req->parser; if (parse){ onion_buffer odata={ data, size, 0}; while (odata.size>odata.pos){ int r=parse(req, &odata); if (r!=OCS_NEED_MORE_DATA){ return r; } parse=req->parser; } return OCS_NEED_MORE_DATA; } return OCS_INTERNAL_ERROR; }
/// Create a new poller onion_poller *onion_poller_new(int aprox_n){ onion_poller *ret=onion_low_calloc(1,sizeof(onion_poller)); ret->loop=ev_default_loop(0); ret->sem=sem_open("/poller", O_CREAT, 0, 1); return ret; }
/** * @short Creates an empty listen point. * @memberof onion_listen_point_t * * Called by real listen points to ease the creation. * * @returns An alloc'ed onion_listen_point pointer */ onion_listen_point *onion_listen_point_new(){ onion_listen_point *ret=onion_low_calloc(1,sizeof(onion_listen_point)); return ret; }
/** * @short Creates a new listen point with HTTPS powers. * @memberof onion_https_t * * Creates the HTTPS listen point. * * Might be called with (O_SSL_NONE,NULL), and set up the certificate later with onion_https_set_certificate. * * @param type Type of certificate to setup * @param filename File from where to get the data * @param ... More types and filenames until O_SSL_NONE. * @returns An onion_listen_point with the desired data, ready to start listening. */ onion_listen_point *onion_https_new(){ onion_listen_point *op=onion_listen_point_new(); op->request_init=onion_https_request_init; op->free_user_data=onion_https_free_user_data; op->listen_stop=onion_https_listen_stop; op->read=onion_https_read; op->write=onion_https_write; op->close=onion_https_close; op->read_ready=onion_http_read_ready; op->secure = true; op->user_data=onion_low_calloc(1,sizeof(onion_https)); onion_https *https=(onion_https*)op->user_data; #ifdef HAVE_PTHREADS gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); #endif //if (!(o->flags&O_USE_DEV_RANDOM)){ gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); //} gnutls_global_init (); gnutls_certificate_allocate_credentials (&https->x509_cred); // set cert here?? //onion_https_set_certificate(op,O_SSL_CERTIFICATE_KEY, "mycert.pem","mycert.pem"); int e; int bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LOW); e=gnutls_dh_params_init (&https->dh_params); if (e<0){ ONION_ERROR("Error initializing HTTPS: %s", gnutls_strerror(e)); gnutls_certificate_free_credentials (https->x509_cred); op->free_user_data=NULL; onion_listen_point_free(op); onion_low_free(https); return NULL; } e=gnutls_dh_params_generate2 (https->dh_params, bits); if (e<0){ ONION_ERROR("Error initializing HTTPS: %s", gnutls_strerror(e)); gnutls_certificate_free_credentials (https->x509_cred); op->free_user_data=NULL; onion_listen_point_free(op); onion_low_free(https); return NULL; } e=gnutls_priority_init (&https->priority_cache, "PERFORMANCE:%SAFE_RENEGOTIATION:-VERS-TLS1.0", NULL); if (e<0){ ONION_ERROR("Error initializing HTTPS: %s", gnutls_strerror(e)); gnutls_certificate_free_credentials (https->x509_cred); gnutls_dh_params_deinit(https->dh_params); op->free_user_data=NULL; onion_listen_point_free(op); onion_low_free(https); return NULL; } gnutls_certificate_set_dh_params (https->x509_cred, https->dh_params); gnutls_priority_init (&https->priority_cache, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL); // PERFORMANCE:%SAFE_RENEGOTIATION:-VERS-TLS1.0:%COMPAT" ONION_DEBUG("HTTPS connection ready"); return op; }