Example #1
0
/// Gets the poller to do some modifications as change shutdown
onion_poller_slot *onion_poller_get(onion_poller *poller, int fd){
	ONION_ERROR("Not implemented! Use epoll poller.");
	return NULL;
}
Example #2
0
/**
 * @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;
}
Example #3
0
/// Removes a fd from the poller
int onion_poller_remove(onion_poller *poller, int fd){
	ONION_ERROR("FIXME!! not removing fd %d", fd);
	return -1;
}
Example #4
0
/**
 * @short Performs the listening with the given mode
 * @memberof onion_t
 *
 * This is the main loop for the onion server.
 * 
 * It initiates the listening on all the selected ports and addresses.
 *
 * @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_DETACHED) && (o->flags&O_DETACH_LISTEN)){ // Must detach and return
		o->flags|=O_DETACHED;
		int errcode=pthread_create(&o->listen_thread,NULL, (void*)onion_listen, o);
		if (errcode!=0)
			return errcode;
		return 0;
	}
#endif
	
	if (!o->listen_points){
		onion_add_listen_point(o,NULL,NULL,onion_http_new());
		ONION_DEBUG("Created default HTTP listen port");
	}

	/// Start listening
	size_t successful_listened_points=0;
	onion_listen_point **lp=o->listen_points;
	while (*lp){
		int listen_result=onion_listen_point_listen(*lp);
		if (!listen_result) {
			successful_listened_points++;
		}
		lp++;
	}
	if (!successful_listened_points){
		ONION_ERROR("There are no available listen points");
		return 1;
	}

	
	if (o->flags&O_ONE){
		onion_listen_point **listen_points=o->listen_points;
		if (listen_points[1]!=NULL){
			ONION_WARNING("Trying to use non-poll and non-thread mode with several listen points. Only the first will be listened");
		}
		onion_listen_point *op=listen_points[0];
		do{
			onion_request *req=onion_request_new(op);
			if (!req)
				continue;
			ONION_DEBUG("Accepted request %p", req);
			onion_request_set_no_keep_alive(req);
			int ret;
			do{
				ret=req->connection.listen_point->read_ready(req);
			}while(ret>=0);
			ONION_DEBUG("End of request %p", req);
			onion_request_free(req);
			//req->connection.listen_point->close(req);
		}while(((o->flags&O_ONE_LOOP) == O_ONE_LOOP) && op->listenfd>0);
	}
	else{
		onion_listen_point **listen_points=o->listen_points;
		while (*listen_points){
			onion_listen_point *p=*listen_points;
			ONION_DEBUG("Adding listen point fd %d to poller", p->listenfd);
			onion_poller_slot *slot=onion_poller_slot_new(p->listenfd, (void*)onion_listen_point_accept, p);
			onion_poller_slot_set_type(slot, O_POLL_ALL);
			onion_poller_add(o->poller, slot);
			listen_points++;
		}

#ifdef HAVE_PTHREADS
		ONION_DEBUG("Start polling / listening %p, %p, %p", o->listen_points, *o->listen_points, *(o->listen_points+1));
		if (o->flags&O_THREADED){
			o->threads=malloc(sizeof(pthread_t)*(o->nthreads-1));
			int i;
			for (i=0;i<o->nthreads-1;i++){
				pthread_create(&o->threads[i],NULL,(void*)onion_poller_poll, o->poller);
			}
			
			// Here is where it waits.. but eventually it will exit at onion_listen_stop
			onion_poller_poll(o->poller);
			ONION_DEBUG("Closing onion_listen");
			
			for (i=0;i<o->nthreads-1;i++){
				pthread_join(o->threads[i],NULL);
			}
		}
		else
#endif
			onion_poller_poll(o->poller);

		listen_points=o->listen_points;
		while (*listen_points){
			onion_listen_point *p=*listen_points;
			if (p->listenfd>0){
				ONION_DEBUG("Removing %d from poller", p->listenfd);
				onion_poller_remove(o->poller, p->listenfd);
			}
			listen_points++;
		}
	}
	return 0;
}
Example #5
0
File: poller.c Project: Nov11/onion
/**
 * @short Do the event polling.
 * @memberof onion_poller_t
 *
 * It loops over polling. To exit polling call onion_poller_stop().
 *
 * If no fd to poll, returns.
 */
void onion_poller_poll(onion_poller *p){
	struct epoll_event event[MAX_EVENTS];
	ONION_DEBUG("Start polling");
	p->stop=0;
#ifdef HAVE_PTHREADS
	pthread_mutex_lock(&p->mutex);
	p->npollers++;
	ONION_DEBUG0("Npollers %d. %d listenings %p", p->npollers, p->n, p->head);
	pthread_mutex_unlock(&p->mutex);
#endif
	int maxtime;
	time_t ctime;
	int timeout;
	while (!p->stop && p->head){
		ctime=time(NULL);
		pthread_mutex_lock(&p->mutex);
		maxtime=onion_poller_get_next_timeout(p);
		pthread_mutex_unlock(&p->mutex);

		timeout=maxtime-ctime;
		if (timeout>3600)
			timeout=3600000;
		else
			timeout*=1000;
		ONION_DEBUG0("Wait for %d ms", timeout);
		int nfds = epoll_wait(p->fd, event, MAX_EVENTS, timeout);
		int ctime_end=time(NULL);
		ONION_DEBUG0("Current time is %d, limit is %d, timeout is %d. Waited for %d seconds", ctime, maxtime, timeout, ctime_end-ctime);
    ctime=ctime_end;

		pthread_mutex_lock(&p->mutex);
		{ // Somebody timedout?
			onion_poller_slot *next=p->head;
			while (next){
				onion_poller_slot *cur=next;
				next=next->next;
				if (cur->timeout_limit <= ctime){
					ONION_DEBUG0("Timeout on %d, was %d (ctime %d)", cur->fd, cur->timeout_limit, ctime);
          int i;
          for (i=0;i<nfds;i++){
            onion_poller_slot *el=(onion_poller_slot*)event[i].data.ptr;
            if (cur==el){ // If removed just one with event, make it ignore the event later.
              ONION_DEBUG0("Ignoring event as it timeouted: %d", cur->fd);
              event[i].data.ptr=NULL;
            }
          }
          onion_poller_remove(p, cur->fd);
				}
			}
		}
		pthread_mutex_unlock(&p->mutex);

		if (nfds<0){ // This is normally closed p->fd
			//ONION_DEBUG("Some error happened"); // Also spurious wakeups... gdb is to blame sometimes or any other.
			if(p->fd<0 || !p->head){
				ONION_DEBUG("Finishing the epoll as finished: %s", strerror(errno));
#ifdef HAVE_PTHREADS
				pthread_mutex_lock(&p->mutex);
				p->npollers--;
				pthread_mutex_unlock(&p->mutex);
#endif
				return;
			}
		}
		int i;
		for (i=0;i<nfds;i++){
			onion_poller_slot *el=(onion_poller_slot*)event[i].data.ptr;
      if (!el)
        continue;
			// Call the callback
			//ONION_DEBUG("Calling callback for fd %d (%X %X)", el->fd, event[i].events);
			int n=-1;
			if (event[i].events&EPOLLRDHUP){
				n=-1;
			}
			else{ // I also take care of the timeout, no timeout when on the handler, it should handle it itself.
				el->timeout_limit=INT_MAX;

#ifdef __DEBUG0__
        char **bs=backtrace_symbols((void * const *)&el->f, 1);
        ONION_DEBUG0("Calling handler: %s (%d)",bs[0], el->fd);
        onion_low_free(bs); /* This cannot be onion_low_free since from
		     backtrace_symbols. */
#endif
	/* Sometimes, el->f happens to be null. We want to remove this
	   polling in that weird case. */
				if (el->f)
				  n= el->f(el->data);
				else
				  n= -1;

				ctime=time(NULL);
				if (el->timeout>0)
					el->timeout_limit=ctime+el->timeout;
			}
			if (n<0){
				onion_poller_remove(p, el->fd);
			}
			else{
				ONION_DEBUG0("Re setting poller %d", el->fd);
				event[i].events=el->type;
				if (p->fd>=0){
					int e=epoll_ctl(p->fd, EPOLL_CTL_MOD, el->fd, &event[i]);
					if (e<0){
						ONION_ERROR("Error resetting poller, %s", strerror(errno));
					}
				}
			}
		}
	}
	ONION_DEBUG("Finished polling fds");
#ifdef HAVE_PTHREADS
	pthread_mutex_lock(&p->mutex);
	p->npollers--;
	ONION_DEBUG0("Npollers %d", p->npollers);
	pthread_mutex_unlock(&p->mutex);
#endif
}
Example #6
0
/**
 * Hard parser as I must set into the file as I read, until i found the boundary token (or start), try to parse, and if fail, 
 * write to the file.
 * 
 * The boundary may be in two or N parts.
 */
static onion_connection_status parse_POST_multipart_file(onion_request *req, onion_buffer *data){
	char tmp[1024]; /// Read 1024 bytes fo the read on this call, and write. If read less write as read. 
	int tmppos=0;
	onion_token *token=req->parser_data;
	onion_multipart_buffer *multipart=(onion_multipart_buffer*)token->extra;
	const char *p=data->data+data->pos;
	for (;data->pos<data->size;data->pos++){
		//ONION_DEBUG("*p %d boundary %d (%s)",*p,multipart->boundary[multipart->pos],multipart->boundary);
		if (multipart->pos==0){
			if (*p=='\n') // \r is optional.
				multipart->startpos=multipart->pos=1;
			else
				multipart->startpos=0;
		}
		if (*p==multipart->boundary[multipart->pos]){
			multipart->pos++;
			if (multipart->pos==multipart->size){
				multipart->startpos=multipart->pos=0;
				data->pos++; // Not sure why this is needed. FIXME.

				int w=write(multipart->fd,tmp,tmppos); 
				if (w!=tmppos){
					ONION_ERROR("Error writing multipart data to file. Check permissions on temp directory, and availabe disk.");
					close(multipart->fd);
					return OCS_INTERNAL_ERROR;
				}

				close(multipart->fd);
				req->parser=parse_POST_multipart_next;
				return OCS_NEED_MORE_DATA;
			}
		}
		else{
			if (multipart->file_total_size>req->connection.listen_point->server->max_file_size){
				ONION_ERROR("Files on this post too big. Aborting.");
				return OCS_INTERNAL_ERROR;
			}
			if (multipart->pos!=0){
				multipart->file_total_size+=multipart->pos;
				int r=multipart->pos-multipart->startpos;
				//ONION_DEBUG0("Write %d bytes",r);
				int w=write(multipart->fd,tmp,tmppos); 
				if (w!=tmppos){
					ONION_ERROR("Error writing multipart data to file. Check permissions on temp directory, and availabe disk.");
					close(multipart->fd);
					return OCS_INTERNAL_ERROR;
				}
				tmppos=0;
				
				w=write(multipart->fd, multipart->boundary+multipart->startpos, r);
				if (w!=r){
					ONION_ERROR("Error writing multipart data to file. Check permissions on temp directory, and availabe disk.");
					close(multipart->fd);
					return OCS_INTERNAL_ERROR;
				}
				multipart->startpos=multipart->pos=0;
				data->pos--; // Ignore read charater, try again. May be start of boundary.
				continue;
			}
			//ONION_DEBUG0("Write 1 byte");
			tmp[tmppos++]=*p;
			if (tmppos==sizeof(tmp)){
				int w=write(multipart->fd,tmp,tmppos); 
				if (w!=tmppos){
					ONION_ERROR("Error writing multipart data to file. Check permissions on temp directory, and availabe disk.");
					close(multipart->fd);
					return OCS_INTERNAL_ERROR;
				}
				tmppos=0;
			}
		}
		multipart->file_total_size++;
		p++;
	}
	int w=write(multipart->fd,tmp,tmppos); 
	if (w!=tmppos){
		ONION_ERROR("Error writing multipart data to file. Check permissions on temp directory, and availabe disk.");
		close(multipart->fd);
		return OCS_INTERNAL_ERROR;
	}
	return OCS_NEED_MORE_DATA;
}
Example #7
0
/**
 * @short Prepares the POST
 */
static onion_connection_status prepare_POST(onion_request *req){
	// ok post
	onion_token *token=req->parser_data;
	const char *content_type=onion_dict_get(req->headers, "Content-Type");
	const char *content_size=onion_dict_get(req->headers, "Content-Length");
	
	if (!content_size){
		ONION_ERROR("I need the content size header to support POST data");
		return OCS_INTERNAL_ERROR;
	}
	size_t cl=atol(content_size);
	if (cl==0)
		return onion_request_process(req);
	
	//ONION_DEBUG("Content type %s",content_type);
	if (!content_type || (strstr(content_type, "application/x-www-form-urlencoded"))){
		if (cl>req->connection.listen_point->server->max_post_size){
			ONION_ERROR("Asked to send much POST data. Limit %d. Failing.",req->connection.listen_point->server->max_post_size);
			return OCS_INTERNAL_ERROR;
		}
		assert(token->extra==NULL);
		token->extra=onion_low_scalar_malloc(cl+1); // Cl + \0
		token->extra_size=cl;
		req->free_list=onion_ptr_list_add(req->free_list, token->extra); // Free when the request is freed.
		
		req->parser=parse_POST_urlencode;
		return OCS_NEED_MORE_DATA;
	}
	
	// multipart.
	
	const char *mp_token=strstr(content_type, "boundary=");
	if (!mp_token){
		ONION_ERROR("No boundary set at content-type");
		return OCS_INTERNAL_ERROR;
	}
	mp_token+=9;
	if (cl>req->connection.listen_point->server->max_post_size) // I hope the missing part is files, else error later.
		cl=req->connection.listen_point->server->max_post_size;
	
	int mp_token_size=strlen(mp_token);
	token->extra_size=cl; // Max size of the multipart->data
	onion_multipart_buffer *multipart=onion_low_malloc(token->extra_size+sizeof(onion_multipart_buffer)+mp_token_size+2);
	assert(token->extra==NULL);
	token->extra=(char*)multipart;
	
	multipart->boundary=(char*)multipart+sizeof(onion_multipart_buffer)+1;
	multipart->size=mp_token_size+4;
	multipart->pos=2; // First boundary already have [\r]\n readen
	multipart->post_total_size=cl;
	multipart->file_total_size=0;
	multipart->boundary[0]='\r';
	multipart->boundary[1]='\n';
	multipart->boundary[2]='-';
	multipart->boundary[3]='-';
	strcpy(&multipart->boundary[4],mp_token);
	multipart->data=(char*)multipart+sizeof(onion_multipart_buffer)+multipart->size+1;
	
	//ONION_DEBUG("Multipart POST boundary '%s'",multipart->boundary);
	
	req->parser=parse_POST_multipart_start;
	
	return OCS_NEED_MORE_DATA;
}
Example #8
0
/**
 * @short Solves a variable into code.
 * 
 * It uses the type to check it its a literal string, a dcit string or a dict.
 */
void variable_solve(parser_status *st, const char *data, const char *tmpname, vartype_e type){
	if (type==LITERAL){
		char *s=onion_c_quote_new(data);
		function_add_code(st,
"    %s=%s;\n", tmpname, s);
		free(s);
		return;
	}
	if (! (type==STRING || type==DICT) ){
		ONION_ERROR("Invalid type for variable solve");
		exit(1);
	}
	
	
	list *parts=list_new(onion_block_free);
	onion_block *lastblock;
	list_add(parts, lastblock=onion_block_new());
	
	
	int i=0;
	int l=strlen(data);
	const char *d=data;
	for (i=0;i<l;i++){
		if (d[i]=='.')
			list_add(parts, lastblock=onion_block_new());
		else if (d[i]==' ')
			continue;
		else
			onion_block_add_char(lastblock, d[i]);
	}

	if (list_count(parts)==1){
		char *s=onion_c_quote_new(onion_block_data(lastblock));
		if (type==STRING)
			function_add_code(st, 
	"    %s=onion_dict_get(context, %s);\n", tmpname, s);
		else if (type==DICT)
			function_add_code(st, 
	"    %s=onion_dict_get_dict(context, %s);\n", tmpname, s);
		free(s);
	}
	else{
		if (type==STRING)
			function_add_code(st,"    %s=onion_dict_rget(context", tmpname);
		else if (type==DICT)
			function_add_code(st,"    %s=onion_dict_rget_dict(context", tmpname);
		else{
			ONION_ERROR("Invalid type for variable solve");
			exit(1);
		}
		list_item *it=parts->head;
		while (it){
			lastblock=it->data;
			char *s=onion_c_quote_new(onion_block_data(lastblock));
			function_add_code(st,", %s", s);
			free(s);
			it=it->next;
		}
		function_add_code(st,", NULL);\n");
	}
	list_free(parts);
}
Example #9
0
/// There is a bug when posting large files. Introduced when change write 1 by 1, to write by blocks on the FILE parser
void t04_post_largefile(){
	INIT_LOCAL();
	
	int postfd=open(BIG_FILE, O_RDONLY);
	off_t filesize=lseek(postfd, 0, SEEK_END);
	lseek(postfd, 0, SEEK_SET);
	
	buffer *b=buffer_new(1024);
	expected_post post={};;
	post.filename=BIG_FILE_BASE;
	post.test_ok=0; // Not ok as not called processor yet
	post.tmpfilename=NULL;
	post.size=filesize;
	
	onion_server *server=onion_server_new();
	onion_server_set_write(server, (void*)&buffer_append);
	onion_server_set_root_handler(server, onion_handler_new((void*)&post_check,&post,NULL));
	
	onion_request *req=onion_request_new(server,b,"test");

#define POST_HEADER "POST / HTTP/1.1\nContent-Type: multipart/form-data; boundary=end\nContent-Length: %d\n\n--end\nContent-Disposition: text/plain; name=\"file\"; filename=\"" BIG_FILE_BASE "\"\n\n"
	char tmp[1024];
	ONION_DEBUG("Post size is about %d",filesize+73);
	snprintf(tmp, sizeof(tmp), POST_HEADER, (int)filesize+73);
	ONION_DEBUG("%s",tmp);
	onion_request_write(req,tmp,strlen(tmp));
	
	int r=read(postfd, tmp, sizeof(tmp));
	while ( r>0 ){
		onion_request_write(req, tmp, r);
		r=read(postfd, tmp, sizeof(tmp));
	}
	
	onion_request_write(req,"\n--end--",8);
	FAIL_IF_NOT_EQUAL_INT(post.test_ok,1);
#undef POST_HEADER

	post.test_ok=0; // Not ok as not called processor yet

	lseek(postfd, 0, SEEK_SET);

	int difffd=open(post.tmpfilename, O_RDONLY);
	FAIL_IF_NOT_EQUAL_INT(difffd,-1); // Orig file is removed at handler returns. But i have a copy
	difffd=open(post.tmplink, O_RDONLY);
	FAIL_IF_EQUAL_INT(difffd,-1);
	ONION_DEBUG("tmp filename %s",post.tmpfilename);
	int r1=1, r2=1;
	char c1=0, c2=0;
	int p=0;
	while ( r1 && r2 && c1==c2){
		r1=read(difffd, &c1, 1);
		r2=read(postfd, &c2, 1);
		//ONION_DEBUG("%d %d",c1,c2);
		FAIL_IF_NOT_EQUAL_INT(c1,c2);
		p++;
	}
	if ( r1 || r2 ){
		ONION_ERROR("At %d",p);
		FAIL_IF_NOT_EQUAL_INT(r1,0);
		FAIL_IF_NOT_EQUAL_INT(r2,0);
		FAIL_IF_NOT_EQUAL_INT(c1,c2);
		FAIL("Files are different");
	}
	else
		ONION_DEBUG("Files are ok");
	
	close(difffd);
	close(postfd);

	onion_request_clean(req);

	onion_request_free(req);

	if (post.tmpfilename){
		struct stat st;
		FAIL_IF_EQUAL(stat(post.tmpfilename,&st), 0); // Should not exist
	}
	
	onion_server_free(server);
	buffer_free(b);
	if (post.tmpfilename)
		free(post.tmpfilename);
	if (post.tmplink)
		free(post.tmplink);
	
	END_LOCAL();
}
Example #10
0
/**
 * @short Main parsing loop.
 */
void parse_template(parser_status *status){
	int c;
	while ( (c=fgetc(status->in)) != EOF){
		status->c=c;
		if (c=='\n')
			status->line++;
		switch(status->mode){
			case TEXT:
				if (c=='{')
					set_mode(status, BEGIN);
				else
					add_char(status, c);
				break;
			case BEGIN:
				if (c=='{')
					set_mode(status, VARIABLE);
				else if (c=='%')
					set_mode(status, TAG);
				else{
					set_mode(status, TEXT);
					add_char(status, '{');
					add_char(status, c);
				}
				break;
			case VARIABLE:
				if (c=='}')
					set_mode(status, END_VARIABLE);
				else
					add_char(status, c);
				break;
			case TAG:
				if (c=='%')
					set_mode(status, END_TAG);
				else
					add_char(status, c);
				break;
			case END_VARIABLE:
				if (c=='}')
					set_mode(status, TEXT);
				else{
					set_mode(status, VARIABLE);
					add_char(status, '}');
					add_char(status, c);
				}
				break;
			case END_TAG:
				if (c=='}')
					set_mode(status, TEXT);
				else{
					set_mode(status, END_TAG);
					add_char(status, '%');
					add_char(status, c);
				}
				break;
			default:
				ONION_ERROR("Unknown mode %d",status->mode);
				status->status=1;
				return;
		}
	}
	set_mode(status, END);
}
Example #11
0
File: tags.c Project: Paxa/onion
/**
 * Current block is a tag, slice it and call the proper handler.
 */
void tag_write(parser_status *st, onion_block *b){
	//ONION_DEBUG("Write tag %s",b->data);
	
	list *command=list_new((void*)tag_token_free);
	
	char mode=0; // 0 skip spaces, 1 in single var, 2 in quotes
	
	// Split into elements for the list
	int i, li=0;
	const char *data=onion_block_data(b);
	int size=onion_block_size(b);
	for (i=0;i<size;i++){
		char c=data[i];
		switch(mode){
			case 0:
				if (!isspace(c)){
					if (c=='"'){
						mode=2;
						li=i+1;
					}
					else{
						mode=1;
						li=i;
					}
				}
				break;
			case 1:
				if (isspace(c)){
					mode=0;
					list_add(command, tag_token_new(&data[li], i-li, T_VAR));
				}
				break;
			case 2:
				if (c=='"'){
					mode=0;
					list_add(command, tag_token_new(&data[li], i-li, T_STRING));
				}
				break;
		}
	}
	if (mode==1)
		list_add(command, tag_token_new(&data[li], i-li, T_VAR));
	
	if (!command->head){
		ONION_ERROR("%s:%d Incomplete command", st->infilename, st->line);
		st->status=1;
		return;
	}
	
	// Call the function.
	tag_token *commandtoken=command->head->data;
	const char *commandname=commandtoken->data;

	void (*f)(parser_status *st, list *args);
	f=(void*)onion_dict_get(tags, commandname);
	if (f)
		f(st, command);
	else{
		ONION_ERROR("%s:%d Unknown command '%s'. ", st->infilename, st->line, commandname);
		st->status=1;
	}
	
	list_free(command);
}
Example #12
0
/**
 * @short Do the event polling.
 * @memberof onion_poller_t
 * @ingroup poller
 *
 * It loops over polling. To exit polling call onion_poller_stop().
 *
 * If no fd to poll, returns.
 */
void onion_poller_poll(onion_poller *p){
	struct epoll_event event[onion_poller_max_events];
	ONION_DEBUG("Start polling");
#ifdef HAVE_PTHREADS
	pthread_mutex_lock(&p->mutex);
	p->npollers++;
	p->stop=0;
	ONION_DEBUG0("Npollers %d. %d listenings %p", p->npollers, p->n, p->head);
	pthread_mutex_unlock(&p->mutex);
#else
	p->stop=0;
#endif
#ifdef HAVE_PTHREADS
	pthread_mutex_lock(&p->mutex);
	char stop = !p->stop && p->head;
	pthread_mutex_unlock(&p->mutex);
#else
	char stop = !p->stop && p->head;
#endif
	while (stop){
		int nfds = epoll_wait(p->fd, event, onion_poller_max_events, -1);

		if (nfds<0){ // This is normally closed p->fd
			//ONION_DEBUG("Some error happened"); // Also spurious wakeups... gdb is to blame sometimes or any other.
			if(p->fd<0 || !p->head){
				ONION_DEBUG("Finishing the epoll as finished: %s", strerror(errno));
#ifdef HAVE_PTHREADS
				pthread_mutex_lock(&p->mutex);
				p->npollers--;
				pthread_mutex_unlock(&p->mutex);
#endif
				return;
			}
		}
		int i;
		for (i=0;i<nfds;i++){
			onion_poller_slot *el=(onion_poller_slot*)event[i].data.ptr;
      if (!el)
        continue;
			// Call the callback
			//ONION_DEBUG("Calling callback for fd %d (%X %X)", el->fd, event[i].events);
			int n=-1;
			if (event[i].events&(EPOLLRDHUP | EPOLLHUP)){
				n=-1;
			}
			else{ // I also take care of the timeout, no timeout when on the handler, it should handle it itself.
				el->timeout_limit=INT_MAX;

#ifdef __DEBUG0__
        char **bs=backtrace_symbols((void * const *)&el->f, 1);
        ONION_DEBUG0("Calling handler: %s (%d)",bs[0], el->fd);
        onion_low_free(bs); /* This cannot be onion_low_free since from
		     backtrace_symbols. */
#endif
				n = el->f(el->data);

				if (el->timeout>0){
					el->timeout_limit=onion_time()+el->timeout;
					onion_poller_timer_check(p, el->timeout_limit);
				}
			}
			if (n<0){
				onion_poller_remove(p, el->fd);
			}
			else{
				ONION_DEBUG0("Re setting poller %d", el->fd);
				event[i].events=el->type;
				if (p->fd>=0){
					int e=epoll_ctl(p->fd, EPOLL_CTL_MOD, el->fd, &event[i]);
					if (e<0){
						ONION_ERROR("Error resetting poller, %s", strerror(errno));
					}
				}
			}
		}
#ifdef HAVE_PTHREADS
		pthread_mutex_lock(&p->mutex);
		stop = !p->stop && p->head;
		pthread_mutex_unlock(&p->mutex);
#else
		stop = !p->stop && p->head;
#endif
	}
	ONION_DEBUG("Finished polling fds");
#ifdef HAVE_PTHREADS
	pthread_mutex_lock(&p->mutex);
	p->npollers--;
	ONION_DEBUG0("Npollers %d", p->npollers);
	pthread_mutex_unlock(&p->mutex);
#endif
}
Example #13
0
int main(int argc, char **argv){
	char *port="8080";
	char *serverip="::";
	const char *command="/bin/bash";
	const char *certificatefile="/etc/pki/tls/certs/pound.pem";
	const char *keyfile="/etc/pki/tls/certs/pound.key";
	int error;
	int i;
	int ssl=1;
#ifdef HAVE_PAM
	int use_pam=1;
#endif
	
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"--help")==0){
			show_help();
			exit(0);
		}
		else if(strcmp(argv[i],"-p")==0 || strcmp(argv[i],"--port")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the port number.");
				show_help();
				exit(1);
			}
			port=argv[++i];
			fprintf(stderr, "Using port %s\n",port);
		}
		else if(strcmp(argv[i],"-i")==0 || strcmp(argv[i],"--ip")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the ip address or hostname.");
				show_help();
				exit(1);
			}
			serverip=argv[++i];
			fprintf(stderr, "Using ip %s\n",serverip);
		}
		else if(strcmp(argv[i],"-c")==0 || strcmp(argv[i],"--cert")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the certificate filename");
				show_help();
				exit(1);
			}
			certificatefile=argv[++i];
			ONION_INFO("Using certificate %s",certificatefile);
		}
		else if(strcmp(argv[i],"-k")==0 || strcmp(argv[i],"--key")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the certificate key filename.");
				show_help();
				exit(1);
			}
			keyfile=argv[++i];
			ONION_INFO("Using certificate key %s",keyfile);
		}
		else if(strcmp(argv[i],"-x")==0 || strcmp(argv[i],"--exec")==0){
			if (i+1>argc){
				ONION_ERROR("Need the command to execute.");
				show_help();
				exit(1);
			}
			command=argv[++i];
			ONION_INFO("New terminal execute the command %s",command);
		}
		else if(strcmp(argv[i],"--no-ssl")==0){
			ssl=0;
			ONION_INFO("Disabling SSL!");
		}
#ifdef HAVE_PAM
		else if(strcmp(argv[i],"--no-pam")==0){
			use_pam=0;
			ONION_INFO("Disabling PAM!");
		}
#endif
	}
  o=onion_new(O_POOL|O_SYSTEMD);
  
	
	// I prepare the url handler, with static, uuid and term. Also added the empty rule that redirects to static/index.html
	onion_url *url=onion_url_new();
  onion_handler *term_handler=oterm_handler(o,command);
#ifdef HAVE_PAM
  if (use_pam){
    onion_url_add_handler(url, "^term/", onion_handler_auth_pam("Onion Terminal", "login", term_handler));
  }
  else
#endif
  {
    onion_url_add_with_data(url, "^term/", oterm_nopam, term_handler, NULL);
  }
  onion_url_add_with_data(url, "^uuid/", oterm_uuid, onion_handler_get_private_data(term_handler), NULL);
  
#ifdef __DEBUG__
	if (getenv("OTERM_DEBUG"))
		onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
	else
#endif
  {
#ifndef JQUERY_JS
    onion_url_add(url, "^static/jquery.js", opack_jquery_js);
#else
    onion_url_add_handler(url, "^static/jquery.js", onion_handler_export_local_new(JQUERY_JS));
#endif
    onion_url_add(url, "^static/", opack_static);
	}
  onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);

  srand(time(NULL));
	onion_set_root_handler(o, onion_url_to_handler(url));

	if (!(onion_flags(o)&O_SSL_AVAILABLE)){
		ONION_WARNING("SSL support is not available. Oterm is in unsecure mode!");
	}
	else if (ssl){ // Not necesary the else, as onion_use_certificate would just return an error. But then it will exit.
		error=onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, certificatefile, keyfile);
		if (error){
			ONION_ERROR("Cant set certificate and key files (%s, %s)",certificatefile, keyfile);
			show_help();
			exit(1);
		}
	}
	
	onion_set_port(o, port);
	onion_set_hostname(o, serverip);
  onion_set_timeout(o,5000);
	
	signal(SIGINT, free_onion);
	signal(SIGPIPE, SIG_IGN);
	fprintf(stderr, "Listening at %s\n",port);
	error=onion_listen(o);
	if (error){
		ONION_ERROR("Cant create the server: %s", strerror(errno));
	}
	
	onion_free(o);
	
	return 0;
}
Example #14
0
/**
 * @short Starts the listening phase for this listen point for sockets.
 * @memberof onion_listen_point_t
 * 
 * Default listen implementation that listens on sockets. Opens sockets and setup everything properly.
 * 
 * @param op The listen point
 * @returns 0 if ok, !=0 some error; it will be the errno value.
 */
int onion_listen_point_listen(onion_listen_point *op){
	if (op->listen){
			op->listen(op);
			return 0;
	}
#ifdef HAVE_SYSTEMD
	if (op->server->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.");
			}
			op->listenfd=SD_LISTEN_FDS_START+0;
			return 0;
		}
	}
#endif

	
	struct addrinfo hints;
	struct addrinfo *result, *rp;
	int sockfd;

	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;
	
	ONION_DEBUG("Trying to listen at %s:%s", op->hostname, op->port ? op->port : "8080");

	if (getaddrinfo(op->hostname, op->port ? op->port : "8080", &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 (sockfd<0) // not valid
			continue;
		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 (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
		else {
			ONION_ERROR("Could not bind to socket: %s",strerror(errno));
		}
		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, fd %d",address,&address[32],sockfd);
#endif
	freeaddrinfo(result);
	listen(sockfd,5); // queue of only 5.
	
	op->listenfd=sockfd;
	return 0;
}
Example #15
0
/**
 * @short Write the properties of a path.
 * 
 * Given a path, and optionally a file at that path, writes the XML of its properties.
 * 
 * If no file given, data is for that path, else it is for the path+filename.
 * 
 * @param writer XML writer to write the data to
 * @param realpath The real path on the file server, used to check permissions and read data TODO
 * @param urlpath The base URL of the element, needed in the response. Composed with filename if filename!=NULL.
 * @param filename NULL if that element itself, else the subelement (file in path).
 * @param props Bitmask of the properties the user asked for.
 * 
 * @return 0 is ok, 1 if could not stat file.
 */
int onion_webdav_write_props(xmlTextWriterPtr writer, const char *basepath, 
														 const char *realpath, const char *urlpath, const char *filename, 
														 int props){
	ONION_DEBUG("Info for path '%s', urlpath '%s', file '%s', basepath '%s'", realpath, urlpath, filename, basepath);
	// Stat the thing itself
	char tmp[PATH_MAX];
	if (filename)
		snprintf(tmp, sizeof(tmp), "%s/%s", realpath, filename);
	else{
		if (strlen(realpath)>=sizeof(tmp)-1){
			ONION_ERROR("File path too long");
			return 1;
		}
		strncpy(tmp, realpath, sizeof(tmp)-1);
	}
	struct stat st;
	if (stat(tmp, &st)<0){
		ONION_ERROR("Error on %s: %s", tmp, strerror(errno));
		return 1;
	}
	while (*urlpath=='/') // No / at the begining.
		urlpath++;

	if (filename){
		if (urlpath[0]==0)
			snprintf(tmp, sizeof(tmp), "%s/%s", basepath, filename);
		else
			snprintf(tmp, sizeof(tmp), "%s/%s/%s", basepath, urlpath, filename);
	}
	else{
		if (urlpath[0]==0)
			snprintf(tmp, sizeof(tmp), "%s/", basepath);
		else{
			snprintf(tmp, sizeof(tmp), "%s/%s", basepath, urlpath);
		}
	}
	if (S_ISDIR(st.st_mode) && urlpath[0]!=0)
		strncat(tmp,"/", sizeof(tmp) - 1);
	ONION_DEBUG0("Props for %s", tmp);
	
	xmlTextWriterStartElement(writer, BAD_CAST "D:response");
	xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:lp1" ,BAD_CAST "DAV:");
	xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:g0" ,BAD_CAST "DAV:");
	xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:a" ,BAD_CAST "http://apache.org/dav/props/");
	
		xmlTextWriterWriteElement(writer, BAD_CAST "D:href", BAD_CAST  tmp); 
		
		/// OK
		xmlTextWriterStartElement(writer, BAD_CAST "D:propstat");
			xmlTextWriterStartElement(writer, BAD_CAST "D:prop");
				if (props&WD_RESOURCE_TYPE){
					xmlTextWriterStartElement(writer, BAD_CAST "lp1:resourcetype");
						if (S_ISDIR(st.st_mode)){ // no marker for other resources
							xmlTextWriterStartElement(writer, BAD_CAST "D:collection");
							xmlTextWriterEndElement(writer);
						}
					xmlTextWriterEndElement(writer);
				}
				if (props&WD_LAST_MODIFIED){
					char time[32];
					onion_shortcut_date_string(st.st_mtime, time);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getlastmodified", BAD_CAST time); 
				}
				if (props&WD_CREATION_DATE){
					char time[32];
					onion_shortcut_date_string_iso(st.st_mtime, time);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:creationdate", BAD_CAST time );
				}
				if (props&WD_CONTENT_LENGTH && !S_ISDIR(st.st_mode)){
					snprintf(tmp, sizeof(tmp), "%d", (int)st.st_size);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getcontentlength", BAD_CAST tmp);
				}
				if (props&WD_CONTENT_TYPE && S_ISDIR(st.st_mode)){
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getcontenttype", BAD_CAST "httpd/unix-directory");
				}
				if (props&WD_ETAG){
					char etag[32];
					onion_shortcut_etag(&st, etag);
					xmlTextWriterWriteElement(writer, BAD_CAST "lp1:getetag", BAD_CAST etag);
				}
				if (props&WD_EXECUTABLE && !S_ISDIR(st.st_mode)){
					if (st.st_mode&0111)
						xmlTextWriterWriteElement(writer, BAD_CAST "a:executable", BAD_CAST "true");
					else
						xmlTextWriterWriteElement(writer, BAD_CAST "a:executable", BAD_CAST "false");
				}

			xmlTextWriterEndElement(writer);
			xmlTextWriterWriteElement(writer, BAD_CAST "D:status", BAD_CAST  "HTTP/1.1 200 OK"); 
		xmlTextWriterEndElement(writer); // /propstat

		/// NOT FOUND
		xmlTextWriterStartElement(writer, BAD_CAST "D:propstat");
			xmlTextWriterStartElement(writer, BAD_CAST "D:prop");
				if (props&WD_CONTENT_LENGTH && S_ISDIR(st.st_mode)){
					snprintf(tmp, sizeof(tmp), "%d", (int)st.st_size);
					xmlTextWriterWriteElement(writer, BAD_CAST "g0:getcontentlength", BAD_CAST "");
				}
				if (props&WD_CONTENT_TYPE && !S_ISDIR(st.st_mode)){
					xmlTextWriterWriteElement(writer, BAD_CAST "g0:getcontenttype", BAD_CAST "");
				}
				if (props&WD_DISPLAY_NAME){
					xmlTextWriterWriteElement(writer, BAD_CAST "g0:displayname", BAD_CAST "");
				}
			xmlTextWriterEndElement(writer);
			xmlTextWriterWriteElement(writer, BAD_CAST "D:status", BAD_CAST  "HTTP/1.1 404 Not Found"); 
		xmlTextWriterEndElement(writer); // /propstat
		
	xmlTextWriterEndElement(writer);
	
	return 0;
}
Example #16
0
/**
 * @short This shortcut returns the given file contents. 
 * 
 * It sets all the compilant headers (TODO), cache and so on.
 * 
 * This is the recomended way to send static files; it even can use sendfile Linux call 
 * if suitable (TODO).
 * 
 * It does no security checks, so caller must be security aware.
 */
int onion_shortcut_response_file(const char *filename, onion_request *request, onion_response *res){
	int fd=open(filename,O_RDONLY|O_CLOEXEC);
	
	if (fd<0)
		return OCS_NOT_PROCESSED;

	if(O_CLOEXEC == 0) { // Good compiler know how to cut this out
		int flags=fcntl(fd, F_GETFD);
		if (flags==-1){
			ONION_ERROR("Retrieving flags from file descriptor");
		}
		flags|=FD_CLOEXEC;
		if (fcntl(fd, F_SETFD, flags)==-1){
			ONION_ERROR("Setting O_CLOEXEC to file descriptor");
		}
	}
	
	struct stat st;
	if (stat(filename, &st)!=0){
		ONION_WARNING("File does not exist: %s",filename);
		close(fd);
		return OCS_NOT_PROCESSED;
	}
	
	if (S_ISDIR(st.st_mode)){
		close(fd);
		return OCS_NOT_PROCESSED;
	}
	
	size_t length=st.st_size;
	
	char etag[64];
	onion_shortcut_etag(&st, etag);
		
	const char *range=onion_request_get_header(request, "Range");
	if (range){
		strncat(etag,range,sizeof(etag)-1);
	}
	onion_response_set_header(res, "Etag", etag);
	
	if (range && strncmp(range,"bytes=",6)==0){
		onion_response_set_code(res, HTTP_PARTIAL_CONTENT);
		//ONION_DEBUG("Need just a range: %s",range);
		char tmp[1024];
		strncpy(tmp, range+6, 1024);
		char *start=tmp;
		char *end=tmp;
		while (*end!='-' && *end) end++;
		if (*end=='-'){
			*end='\0';
			end++;
			
			//ONION_DEBUG("Start %s, end %s",start,end);
			size_t ends, starts;
			if (*end)
				ends=atol(end);
			else
				ends=length;
			starts=atol(start);
			length=ends-starts+1;
			lseek(fd, starts, SEEK_SET);
			snprintf(tmp,sizeof(tmp),"bytes %d-%d/%d",(unsigned int)starts, (unsigned int)ends, (unsigned int)st.st_size);
			//onion_response_set_header(res, "Accept-Ranges","bytes");
			onion_response_set_header(res, "Content-Range",tmp);
		}
	}
	
	onion_response_set_length(res, length);
	onion_response_set_header(res, "Content-Type", onion_mime_get(filename) );
	ONION_DEBUG("Mime type is %s",onion_mime_get(filename));

  ONION_DEBUG0("Etag %s", etag);
  const char *prev_etag=onion_request_get_header(request, "If-None-Match");
  if (prev_etag && (strcmp(prev_etag, etag)==0)){
    ONION_DEBUG0("Not modified");
    onion_response_set_length(res, 0);
    onion_response_set_code(res, HTTP_NOT_MODIFIED);
    onion_response_write_headers(res);
    close(fd);
    return OCS_PROCESSED;
  }

	if ((onion_request_get_flags(request)&OR_HEAD) == OR_HEAD){ // Just head.
		length=0;
	}
	
	if (length){
#ifdef USE_SENDFILE
		if (request->server->write==(void*)onion_write_to_socket){ // Lets have a house party! I can use sendfile!
			onion_response_write(res,NULL,0);
			ONION_DEBUG("Using sendfile");
			int r=sendfile((long int)request->socket, fd, NULL, length);
			ONION_DEBUG("Wrote %d, should be %d (%s)", r, length, r==length ? "ok" : "nok");
			if (r!=length || r<0){
				ONION_ERROR("Could not send all file (%s)", strerror(errno));
				close(fd);
				return OCS_INTERNAL_ERROR;
			}
			res->sent_bytes+=length;
			res->sent_bytes_total+=length;
		}
		else
#endif
		{ // Ok, no I cant, do it as always.
			int r=0,w;
			size_t tr=0;
			char tmp[4046];
			if (length>sizeof(tmp)){
				size_t max=length-sizeof(tmp);
				while( tr<max ){
					r=read(fd,tmp,sizeof(tmp));
					tr+=r;
					if (r<0)
						break;
					w=onion_response_write(res, tmp, r);
					if (w!=r){
						ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r);
						break;
					}
				}
			}
			if (sizeof(tmp) >= (length-tr)){
				r=read(fd, tmp, length-tr);
				w=onion_response_write(res, tmp, r);
				if (w!=r){
					ONION_ERROR("Wrote less than read: write %d, read %d. Quite probably closed connection.",w,r);
				}
			}
		}
	}
	close(fd);
	return OCS_PROCESSED;
}