示例#1
0
文件: onion.c 项目: KokaKiwi/onion
/**
 * @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;
}
示例#2
0
文件: onion.c 项目: Nov11/onion
/**
 * @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;
}
示例#3
0
文件: onion.c 项目: arunsirrpi/onion
/**
 * @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;
}