/** * @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."); } onion *o=calloc(1,sizeof(onion)); o->flags=(flags&0x0FF)|O_SSL_AVAILABLE; o->timeout=5000; // 5 seconds of timeout, default. o->poller=onion_poller_new(15); if (!o->poller){ 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 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_AVAILABLE; o->nthreads=8; if (o->flags&O_THREADED) o->flags|=O_THREADS_ENABLED; pthread_mutex_init (&o->mutex, NULL); #endif if (!(o->flags&O_NO_SIGTERM)){ signal(SIGINT,shutdown_server); signal(SIGTERM,shutdown_server); } last_onion=o; return o; }
/** * @short Performs the listening with the given mode * @memberof onion_t * * This is the main loop for the onion server. * * @returns !=0 if there is any error. It returns actualy errno from the network operations. See socket for more information. */ int onion_listen(onion *o){ #ifdef HAVE_PTHREADS if (o->flags&O_DETACH_LISTEN && !(o->flags&O_DETACHED)){ // On first call it sets the variable, and then call again, this time detached. o->flags|=O_DETACHED; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); // It do not need to pthread_join. No leak here. pthread_t listen_thread; pthread_create(&listen_thread, &attr,(void*(*)(void*)) onion_listen, o); pthread_attr_destroy(&attr); return 0; } #endif int sockfd=0; #ifdef HAVE_SYSTEMD if (o->flags&O_SYSTEMD){ int n=sd_listen_fds(0); ONION_DEBUG("Checking if have systemd sockets: %d",n); if (n>0){ // If 0, normal startup. Else use the first LISTEN_FDS. ONION_DEBUG("Using systemd sockets"); if (n>1){ ONION_WARNING("Get more than one systemd socket descriptor. Using only the first."); } sockfd=SD_LISTEN_FDS_START+0; } } #endif if (sockfd==0){ struct addrinfo hints; struct addrinfo *result, *rp; memset(&hints,0, sizeof(struct addrinfo)); hints.ai_canonname=NULL; hints.ai_addr=NULL; hints.ai_next=NULL; hints.ai_socktype=SOCK_STREAM; hints.ai_family=AF_UNSPEC; hints.ai_flags=AI_PASSIVE|AI_NUMERICSERV; if (getaddrinfo(o->hostname, o->port, &hints, &result) !=0 ){ ONION_ERROR("Error getting local address and port: %s", strerror(errno)); return errno; } int optval=1; for(rp=result;rp!=NULL;rp=rp->ai_next){ sockfd=socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol); if(SOCK_CLOEXEC == 0) { // Good compiler know how to cut this out int flags=fcntl(sockfd, F_GETFD); if (flags==-1){ ONION_ERROR("Retrieving flags from listen socket"); } flags|=FD_CLOEXEC; if (fcntl(sockfd, F_SETFD, flags)==-1){ ONION_ERROR("Setting O_CLOEXEC to listen socket"); } } if (sockfd<0) // not valid continue; if (setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) ) < 0){ ONION_ERROR("Could not set socket options: %s",strerror(errno)); } if (bind(sockfd, rp->ai_addr, rp->ai_addrlen) == 0) break; // Success close(sockfd); } if (rp==NULL){ ONION_ERROR("Could not find any suitable address to bind to."); return errno; } #ifdef __DEBUG__ char address[64]; getnameinfo(rp->ai_addr, rp->ai_addrlen, address, 32, &address[32], 32, NI_NUMERICHOST | NI_NUMERICSERV); ONION_DEBUG("Listening to %s:%s",address,&address[32]); #endif freeaddrinfo(result); listen(sockfd,5); // queue of only 5. } o->listenfd=sockfd; // Drops priviledges as it has binded. if (o->username){ struct passwd *pw; pw=getpwnam(o->username); int error; if (!pw){ ONION_ERROR("Cant find user to drop priviledges: %s", o->username); return errno; } else{ error=initgroups(o->username, pw->pw_gid); error|=setgid(pw->pw_gid); error|=setuid(pw->pw_uid); } if (error){ ONION_ERROR("Cant set the uid/gid for user %s", o->username); return errno; } } if (o->flags&O_POLL){ #ifdef HAVE_PTHREADS o->poller=onion_poller_new(o->max_threads+1); #else o->poller=onion_poller_new(8); #endif onion_poller_add(o->poller, onion_poller_slot_new(o->listenfd, (void*)onion_accept_request, o)); // O_POLL && O_THREADED == O_POOL. Create several threads to poll. #ifdef HAVE_PTHREADS if (o->flags&O_THREADED){ ONION_WARNING("Pool mode is experimental. %d threads.", o->max_threads); pthread_t *thread=(pthread_t*)malloc(sizeof(pthread_t)*(o->max_threads-1)); int i; for(i=0;i<o->max_threads-1;i++){ pthread_create(&thread[i], NULL, onion_poller_adaptor, o); } onion_poller_poll(o->poller); ONION_DEBUG("Stopped poll"); for(i=0;i<o->max_threads-1;i++){ //pthread_cancel(thread[i]); // Cancel is WRONG! It left sometimes mutex without unlock, wich made deadlocks. For example. pthread_join(thread[i], NULL); } free(thread); } else #endif { ONION_WARNING("Poller mode is experimental."); onion_poller_poll(o->poller); } } else if (o->flags&O_ONE){ if ((o->flags&O_ONE_LOOP) == O_ONE_LOOP){ while(o->listenfd>0){ // Loop while listening onion_accept_request(o); } } else{ ONION_DEBUG("Listening just one connection"); onion_accept_request(o); } } #ifdef HAVE_PTHREADS else if (o->flags&O_THREADS_ENABLED){ pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); // It do not need to pthread_join. No leak here. while(1){ struct sockaddr_storage cli_addr; socklen_t cli_len; int clientfd=onion_accept(o, &cli_addr, &cli_len); if (clientfd<0) return errno; // If more than allowed processes, it waits here blocking socket, as it should be. // __DEBUG__ #if 0 int nt; sem_getvalue(&o->thread_count, &nt); ONION_DEBUG("%d threads working, %d max threads", o->max_threads-nt, o->max_threads); #endif sem_wait(&o->thread_count); // Has to be malloc'd. If not it wil be overwritten on next petition. The threads frees it onion_request_thread_data *data=malloc(sizeof(onion_request_thread_data)); data->o=o; data->clientfd=clientfd; data->client_addr=cli_addr; data->client_len=cli_len; pthread_create(&data->thread_handle, &attr, onion_request_thread, data); } pthread_attr_destroy(&attr); } #endif close(o->listenfd); return 0; }