Example #1
0
/// Reads a string until a '\n|\r\n' is found. Returns an onion_token.
int token_read_LINE(onion_token *token, onion_buffer *data){
	if (data->pos>=data->size)
		return OCS_NEED_MORE_DATA;
	
	char c=data->data[data->pos++];
	int ignore_to_end=0;
	while (c!='\n'){
		if (!ignore_to_end && (token->pos>=(sizeof(token->str)-1))){
			ONION_WARNING("Token too long to parse it. Ignoring remaining. "); 
#ifdef __DEBUG__
				char tmp[16];
				strncpy(tmp, token->str, 16);
				tmp[15]='\0';
				ONION_DEBUG("Long token starts with: %s...",tmp);
#endif
			ignore_to_end=1;
		}
		if (!ignore_to_end)
			token->str[token->pos++]=c;
		if (data->pos>=data->size)
			return OCS_NEED_MORE_DATA;
		
		c=data->data[data->pos++];
	}
	if (token->str[token->pos-1]=='\r')
		token->str[token->pos-1]='\0';
	else
		token->str[token->pos]='\0';
	//token->pos=0;
	
	//ONION_DEBUG0("Found LINE token %s",token->str);
	return LINE;
}
Example #2
0
int onion_assets_file_free(onion_assets_file *f){
	FILE *file=fopen(f->filename,"wt");
	if (!file){
		ONION_WARNING("Could not open %s asset file for updating.", f->filename);
		return -1;
	}
	
	int i;
	for (i=0;i<f->lines_count;i++){
		ONION_DEBUG("Write: %s", f->lines[i]);
		ssize_t length=strlen(f->lines[i]);
		ssize_t wlength=fwrite(f->lines[i], 1, length, file);
		if (wlength!=length){
			ONION_ERROR("Could not write all data. Aborting");
			abort();
		}
		wlength=fwrite("\n",1, 1, file);
		if (wlength!=1){
			ONION_ERROR("Could not write all data. Aborting");
			abort();
		}
		free(f->lines[i]);
	}
	free(f->lines);
	
	if (f->has_endif)
		fprintf(file, "#endif\n");
	
	assert(fclose(file)==0);
	free(f->filename);
	free(f);
	return 0;
}
Example #3
0
/**
 * @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));
	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){
		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;
}
Example #4
0
/// @memberof onion_poller_t
void onion_poller_free(onion_poller *p){
	ONION_DEBUG("Free onion poller: %d waiting", p->n);
	p->stop=1;
	close(p->fd);
	// Wait until all pollers exit.

	if (pthread_mutex_trylock(&p->mutex)>0){
		ONION_WARNING("When cleaning the poller object, some poller is still active; not freeing memory");
	}
	else{
		onion_poller_slot *next=p->head;
		while (next){
			onion_poller_slot *tnext=next->next;
			if (next->shutdown)
				next->shutdown(next->shutdown_data);
			next=tnext;
		}
		pthread_mutex_unlock(&p->mutex);

		if (p->eventfd>=0)
			close(p->eventfd);
		if (p->timerfd>=0)
			close(p->timerfd);

		onion_poller_static_deinit();

		onion_low_free(p);
	}
	ONION_DEBUG0("Done");
}
Example #5
0
/// Plexes the request depending on arguments.
int oterm_get_data(oterm_data *data, onion_request *req, onion_response *res){
  const char *username=onion_request_get_session(req,"username");
  if (!username){
    ONION_WARNING("Trying to enter authenticated area without username.");
    return OCS_FORBIDDEN;
  }
	oterm_session *o=(oterm_session*)onion_dict_get(data->sessions, onion_request_get_session(req,"username"));
	if (!o){
		o=oterm_session_new();
		onion_dict_lock_write(data->sessions);
		onion_dict_add(data->sessions,onion_request_get_session(req,"username"),o, 0);
		onion_dict_unlock(data->sessions);
	}
  const char *path=onion_request_get_path(req);

  ONION_DEBUG("Ask path %s (%p)", path, data);
  
  if (strcmp(path,"new")==0){
    if (onion_request_get_post(req, "command")){
      free(data->exec_command);
      data->exec_command=strdup(onion_request_get_post(req, "command"));
    }
    oterm_new(data, o, onion_request_get_session(req, "username"), onion_request_get_session(req, "nopam") ? 0 : 1 );
    return onion_shortcut_response("ok", 200, req, res);
  }
  if (strcmp(path,"status")==0)
    return oterm_status(o,req, res);

  return OCS_NOT_PROCESSED;
}
Example #6
0
/**
 * @short Initializes the global random number generator
 * 
 * Initializes the global random number generator with some random seed.
 *
 * onion_random_free() must be called later to free up used memory.
 *
 * It is safe to call onion_random_init() more than once, but union_random_free() must be called the same amount of times.
 */
void onion_random_init() {
  onion_random_refcount_mutex_lock();
  if (onion_random_refcount == 0) {
    int fd = open("/dev/random", O_RDONLY);
    if (fd < 0) {
      ONION_WARNING
          ("Unsecure random number generation; could not open /dev/random to feed the seed");
      // Just in case nobody elses do it... If somebody else do it, then no problem.
      srand(time(NULL));
    } else {
      unsigned int sr;
      ssize_t n;
      n = read(fd, &sr, sizeof(sr));
      if (n != sizeof(sr)) {
        ONION_ERROR
            ("Error reading seed value from /dev/random after file descriptor opened");
        exit(1);
      } else {
        close(fd);
        srand(sr);
      }
    }
  }
  onion_random_refcount++;
  onion_random_refcount_mutex_unlock();
}
Example #7
0
/**
 * @short Do the real authorization. Checks if access allowed
 */
int authorize(const char *pamname, const char *username, const char *password){
	int ok;
	pam_handle_t *pamh=NULL;
	
	const char *password_local=password; //strdup(password);
	struct pam_conv conv = {
    authPAM_passwd,
    (void*)password_local
	};
	
	ok=pam_start(pamname, username, &conv, &pamh);
	
	if (ok==PAM_SUCCESS)
		ok = pam_authenticate(pamh, 0);    /* is user really user? */
	if (ok==PAM_SUCCESS)
		ok = pam_acct_mgmt(pamh, 0);       /* permitted access? */
	
	if (pam_end(pamh, ok)!=PAM_SUCCESS){
		ONION_ERROR("Error releasing PAM structures");
	}
	if (ok==PAM_SUCCESS){
		ONION_DEBUG("Authenticated user %s OK", username);
		return 1;
	}
	ONION_WARNING("NOT authenticated user '%s', code %d", username, ok);
	return 0;
}
Example #8
0
/**
 * @short Writes all buffered output waiting for sending.
 * @ingroup response
 *
 * If header has not been sent yet (delayed), it uses a temporary buffer to send it now. This
 * way header can use the buffer_size information to send the proper content-length, even when it
 * wasnt properly set by programmer. Whith this information its possib to keep alive the connection
 * on more cases.
 */
int onion_response_flush(onion_response * res) {
  res->sent_bytes += res->buffer_pos;
  res->sent_bytes_total += res->buffer_pos;
  if (res->buffer_pos == 0)     // Not used.
    return 0;
  if (!(res->flags & OR_HEADER_SENT)) { // Automatic header write
    ONION_DEBUG0
        ("Doing fast header hack: store current buffer, send current headers. Resend buffer.");
    char tmpb[sizeof(res->buffer)];
    int tmpp = res->buffer_pos;
    memcpy(tmpb, res->buffer, res->buffer_pos);
    res->buffer_pos = 0;

    onion_response_write_headers(res);
    onion_response_write(res, tmpb, tmpp);
    return 0;
  }
  if (res->flags & OR_SKIP_CONTENT)     // HEAD request
    return 0;
  ONION_DEBUG0("Flush %d bytes", res->buffer_pos);

  onion_request *req = res->request;
  ssize_t(*write) (onion_request *, const char *data, size_t len);
  write = req->connection.listen_point->write;

  ssize_t w;
  off_t pos = 0;
  //ONION_DEBUG0("Write %d bytes",res->buffer_pos);
  if (res->flags & OR_CHUNKED) {
    char tmp[16];
    snprintf(tmp, sizeof(tmp), "%X\r\n", (unsigned int)res->buffer_pos);
    if ((w = write(req, tmp, strlen(tmp))) <= 0) {
      ONION_WARNING("Error writing chunk encoding length. Aborting write.");
      return OCS_CLOSE_CONNECTION;
    }
    ONION_DEBUG0("Write %d-%d bytes", res->buffer_pos, w);
  }
  while ((w =
          write(req, &res->buffer[pos], res->buffer_pos)) != res->buffer_pos) {
    if (w <= 0 || res->buffer_pos < 0) {
      ONION_ERROR("Error writing %d bytes. Maybe closed connection. Code %d. ",
                  res->buffer_pos, w);
      perror("");
      res->buffer_pos = 0;
      return OCS_CLOSE_CONNECTION;
    }
    pos += w;
    ONION_DEBUG0("Write %d-%d bytes", res->buffer_pos, w);
    res->buffer_pos -= w;
  }
  if (res->flags & OR_CHUNKED) {
    write(req, "\r\n", 2);
  }
  res->buffer_pos = 0;
  return 0;
}
Example #9
0
/**
 * @short Sets the header length. Normally it should be through set_header, but as its very common and needs some procesing here is a shortcut
 * @memberof onion_response_t
 */
void onion_response_set_length(onion_response *res, size_t len){
	if (len!=res->sent_bytes && res->flags&OR_HEADER_SENT){
		ONION_WARNING("Trying to set length after headers sent. Undefined onion behaviour.");
		return;
	}
	char tmp[16];
	sprintf(tmp,"%lu",(unsigned long)len);
	onion_response_set_header(res, "Content-Length", tmp);
	res->length=len;
	res->flags|=OR_LENGTH_SET;
}
Example #10
0
/**
 * @short If no root handler is set, creates an url handler and returns it.
 * @memberof onion_t
 * 
 * It can also check if the current root handler is a url handler, and if it is, returns it. Else returns NULL.
 */
onion_url *onion_root_url(onion *server){
	if (server->root_handler){
		if (server->root_handler->priv_data_free==(void*)onion_url_free_data) // Only available check
			return (onion_url*)server->root_handler;
		ONION_WARNING("Could not get root url handler, as there is another non url handler at root.");
		return NULL;
	}
	ONION_DEBUG("New root url handler");
	onion_url *url=onion_url_new();
	server->root_handler=(onion_handler*)url;
	return url;
}
Example #11
0
/**
 * @short Returns the session dict.
 * @memberof onion_request_t
 *
 * If it does not exists it creates it. If there is a cookie with a proper name it is used,
 * even for creation.
 *
 * Sessions HAVE TO be gotten before sending any header, or user may face double sessionid, ghost sessions and some other
 * artifacts, as the cookie is not set if session is not used. If this condition happen (send headers and then ask for session) a
 * WARNING is written.
 *
 * Session is not automatically retrieved as it is a slow operation and not used normally, only on "active" handlers.
 *
 * Returned dictionary can be freely managed (added new keys...) and this is the session data.
 *
 * @return session dictionary for current request.
 */
onion_dict *onion_request_get_session_dict(onion_request *req) {
    if (!req->session) {
        if (req->flags & OR_HEADER_SENT) {
            ONION_WARNING("Asking for session AFTER sending headers. This may result in double sessionids, and wrong session behaviour. Please modify your handlers to ask for session BEFORE sending any data.");
        }
        onion_request_guess_session_id(req);
        if (!req->session) { // Maybe old session is not to be used anymore
            req->session_id=onion_sessions_create(req->connection.listen_point->server->sessions);
            req->session=onion_sessions_get(req->connection.listen_point->server->sessions, req->session_id);
        }
    }
    return req->session;
}
Example #12
0
/**
 * @short Moves a resource
 */
onion_connection_status onion_webdav_move(const char *filename, onion_webdav *wd, onion_request *req, onion_response *res){
	const char *dest=onion_request_get_header(req,"Destination");
	if (!dest)
		return OCS_INTERNAL_ERROR;
	const char *dest_orig=dest;
	// Skip the http... part. Just 3 /.
	int i;
	for (i=0;i<3;i+=(*dest++=='/'))
		if (*dest==0)
			return OCS_INTERNAL_ERROR;
	dest--;
	
	const char *fullpath=onion_request_get_fullpath(req);
	const char *partialpath=onion_request_get_path(req);
	// Not the fixed URL part for this handler.
	int fpl=strlen(fullpath); // Full path length
	int ppl=strlen(onion_request_get_path(req)); // Partial, the fullpath[fpl-ppl] is the end point of the handler path
	if (strncmp(fullpath, dest, fpl-ppl)!=0){
		char tmp[512];
		int l=fpl-ppl < sizeof(tmp)-1 ? fpl-ppl : sizeof(tmp)-1;
		strncpy(tmp, fullpath, l);
		tmp[l]=0;
		ONION_WARNING("Move to out of this webdav share! (%s is out of %s)", dest, tmp);
		return onion_shortcut_response("Moving out of shared share", HTTP_FORBIDDEN, req, res);
	}
	dest=&dest[fpl-ppl];


	char orig[512];
	snprintf(orig, sizeof(orig), "%s/%s", wd->path, partialpath);
	
	if (wd->check_permissions(wd->path, orig, req)!=0){
		return onion_shortcut_response("Forbidden", HTTP_FORBIDDEN, req, res);
	}

	const char *fdest=filename;

	ONION_INFO("Move %s to %s (webdav)", fullpath, dest_orig);
	
	int ok=onion_shortcut_rename(orig, fdest);

	if (ok==0){
		ONION_DEBUG("Created %s succesfully", fdest);
		return onion_shortcut_response("201 Created", 201, req, res);
	}
	else{
		ONION_ERROR("Could not rename %s to %s (%s)", orig, fdest, strerror(errno));
		return onion_shortcut_response("Could not create resource", HTTP_FORBIDDEN, req, res);
	}
}
Example #13
0
/// Input data to the process
int oterm_in(process *p, onion_request *req, onion_response *res){
	oterm_check_running(p);

	const char *data;
	data=onion_request_get_post(req,"type");
	ssize_t w;
	if (data){
		//fprintf(stderr,"%s:%d write %ld bytes\n",__FILE__,__LINE__,strlen(data));
		size_t r=strlen(data);
		w=write(p->fd, data, r);
		if (w!=r){
			ONION_WARNING("Error writing data to process. Not all data written. (%d).",w);
			return onion_shortcut_response("Error", HTTP_INTERNAL_ERROR, req, res);
		}
	}
	return onion_shortcut_response("OK", HTTP_OK, req, res);
}
Example #14
0
/**
 * @short Removes a file descriptor, and all related callbacks from the listening queue
 * @memberof onion_poller_t
 * @ingroup poller
 */
int onion_poller_remove(onion_poller *poller, int fd){
	if (epoll_ctl(poller->fd, EPOLL_CTL_DEL, fd, NULL) < 0){
		if (errno!=ENOENT && errno!=EBADF){
			ONION_ERROR("Error remove descriptor to listen to. %s (%d)", strerror(errno), errno);
		}
	}

	pthread_mutex_lock(&poller->mutex);
	ONION_DEBUG0("Trying to remove fd %d (%d)", fd, poller->n);
	onion_poller_slot *el=poller->head;
	if (el && el->fd==fd){
		ONION_DEBUG0("Removed from head %p", el);

		poller->head=el->next;
		pthread_mutex_unlock(&poller->mutex);

		onion_poller_slot_free(el);
		return 0;
	}
	while (el->next){
		if (el->next->fd==fd){
			ONION_DEBUG0("Removed from tail %p",el);
			onion_poller_slot *t=el->next;
			el->next=t->next;

      if (poller->head->next==NULL){ // This means only eventfd is here.
        ONION_DEBUG0("Removed last, stopping poll");
        onion_poller_stop(poller);
      }

			pthread_mutex_unlock(&poller->mutex);

			onion_poller_slot_free(t);
			return 0;
		}
		el=el->next;
	}
	pthread_mutex_unlock(&poller->mutex);
	ONION_WARNING("Trying to remove unknown fd from poller %d", fd);
	return 0;
}
Example #15
0
File: onion.c Project: 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;
}
Example #16
0
/**
 * @short Executes each script file passed as argument.
 * 
 * Optionally a -r sets the new lines to \r\n. It takes care of not changing content types.
 */
int main(int argc, char **argv){
	server=onion_server_new();
	onion_server_set_root_handler(server,onion_handler_new((void*)allinfo_handler,NULL,NULL));
	onion_server_set_write(server,(void*)buffer_append);
	
	int i;
	int do_r=0;
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"-r")==0){
			ONION_WARNING("Setting the end of lines to \\r\\n");
			do_r=1;
		}
		else{
			ONION_INFO("Launching test %s",argv[i]);
			prerecorded(argv[i], do_r);
		}
	}
	
	onion_server_free(server);
	END();
}
Example #17
0
onion_assets_file *onion_assets_file_new(const char *filename){
	onion_assets_file *ret=malloc(sizeof(onion_assets_file));
	ret->file=fopen(filename, "rt");
	ret->lines_count=0;
	ret->lines_capacity=16;
	ret->lines=malloc(sizeof(const char *)*ret->lines_capacity);
	ret->has_endif=0;
	if (!ret->file){
		ret->file=fopen(filename,"wt");
		if (!ret->file){
			ONION_WARNING("Could not open %s asset file for updating.", filename);
			return NULL;
		}
		onion_assets_file_add_line(ret,"/* Autogenerated by onion assets */");
		onion_assets_file_add_line(ret,"#ifndef __ONION_ASSETS_H__");
		onion_assets_file_add_line(ret,"#define __ONION_ASSETS_H__");
		onion_assets_file_add_line(ret,"#endif");
	}
	else{
		char buffer[4096];
		int r;
		int o=0;
		do{
			r=fread(&buffer[o],1, sizeof(buffer)-o, ret->file);
			int i;
			o=0;
			for (i=0;i<r;i++){
				if (buffer[i]=='\n'){
					buffer[i]=0;
					onion_assets_file_add_line(ret, &buffer[o]);
					o=i+1;
				}
			}
			assert(o!=0 && r>0); // "Line is longer than buffer size or EOF without EOL.");
		}while(r<0);
		fclose(ret->file);
		ret->file=fopen(filename,"wt");
	}
	return ret;
}
Example #18
0
int main(int argc, char **argv){
	o=onion_new(O_THREADED);
	signal(SIGINT, free_onion);

	onion_set_root_handler(o, onion_handler_export_local_new("."));
	onion_add_listen_point(o, "localhost", "8080", onion_http_new());
	onion_add_listen_point(o, "localhost", "8081", onion_http_new());
#ifdef HAVE_GNUTLS
	onion_add_listen_point(o, "localhost", "4443", onion_https_new(O_SSL_CERTIFICATE_KEY, "cert.pem", "cert.key"));
#else
	ONION_WARNING("HTTPS support is not enabled. Recompile with gnutls");
#endif
	
	
	
	/**
	onion_set_port(o, "localhost", "6121", onion_protocol_spdy());
	*/
	onion_listen(o);
	onion_free(o);
	return 0;
}
Example #19
0
/**
 * @short Sets a new cookie into the response.
 * @ingroup response
 *
 * @param res Response object
 * @param cookiename Name for the cookie
 * @param cookievalue Value for the cookis
 * @param validity_t Seconds this cookie is valid (added to current datetime). -1 to do not expire, 0 to expire inmediatly.
 * @param path Cookie valid only for this path
 * @param Domain Cookie valid only for this domain (www.example.com, or *.example.com).
 * @param flags Flags from onion_cookie_flags_t, for example OC_SECURE or OC_HTTP_ONLY
 *
 * @returns boolean indicating succesfully added the cookie or not.
 *
 * If validity is 0, cookie is set to expire right now.
 *
 * If the cookie is too long (all data > 4096), it is not added. A warning is
 * emmited and returns false.
 */
bool onion_response_add_cookie(onion_response * res, const char *cookiename,
                               const char *cookievalue, time_t validity_t,
                               const char *path, const char *domain,
                               int flags) {
  char data[4096];
  int pos;
  pos = snprintf(data, sizeof(data), "%s=%s", cookiename, cookievalue);
  if (validity_t == 0)
    pos +=
        snprintf(data + pos, sizeof(data) - pos,
                 "; expires=Thu, 01 Jan 1970 00:00:00 GMT");
  else if (validity_t > 0) {
    struct tm *tmp;
    time_t t = time(NULL) + validity_t;
    tmp = localtime(&t);
    pos +=
        strftime(data + pos, sizeof(data) - pos,
                 "; expires=%a, %d %b %Y %H:%M:%S %Z", tmp);
  }
  if (path)
    pos += snprintf(data + pos, sizeof(data) - pos, "; path=%s", path);
  if (domain)
    pos += snprintf(data + pos, sizeof(data) - pos, "; domain=%s", domain);
  if (flags & OC_HTTP_ONLY)
    pos += snprintf(data + pos, sizeof(data) - pos, "; HttpOnly");
  if (flags & OC_SECURE)
    pos += snprintf(data + pos, sizeof(data) - pos, "; Secure");

  if (pos >= sizeof(data)) {
    ONION_WARNING("Cookie too long to be constructed. Not added to response.");
    return false;
  }

  onion_response_set_header(res, "Set-Cookie", data);
  ONION_DEBUG("Set cookie %s", data);

  return true;
}
Example #20
0
int onion_handler_export_local_handler(onion_handler_export_local_data *d, onion_request *request, onion_response *response){
	char tmp[PATH_MAX];
	char realp[PATH_MAX];
	
	if (d->is_file)
		strncpy(tmp, d->localpath, PATH_MAX);
	else
		snprintf(tmp,PATH_MAX, "%s/%s",d->localpath,onion_request_get_path(request));

	ONION_DEBUG0("Get %s (base %s)",tmp, d->localpath);

	// First check if it exists and so on. If it does not exist, no trying to escape message
	struct stat reals;
	int ok=stat(tmp,&reals);
	if (ok<0) // Cant open for even stat
	{
		ONION_DEBUG0("Not found %s.", tmp);
		return 0;
	}
		
	const char *ret=realpath(tmp, realp);
	if (!ret || strncmp(realp, d->localpath, strlen(d->localpath))!=0){ // out of secured dir.
		ONION_WARNING("Trying to escape from secured dir (secured dir %s, trying %s).", d->localpath, realp);
		return 0;
	}

	if (S_ISDIR(reals.st_mode)){
		//ONION_DEBUG("DIR");
		return onion_handler_export_local_directory(d, realp, onion_request_get_path(request), request, response);
	}
	else if (S_ISREG(reals.st_mode)){
		//ONION_DEBUG("FILE");
		return onion_shortcut_response_file(realp, request, response);
	}
	ONION_DEBUG0("Dont know how to handle");
	return OCS_NOT_PROCESSED;
}
Example #21
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 #22
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
  {
    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 #23
0
// Shows a warning on the logs
static void warning(png_struct *p, const char *msg){
	ONION_WARNING("%s", msg);
} 
Example #24
0
/**
 * @short Reads the /etc/mime.types file
 * 
 * 
 */
static void onion_mime_fill(){
	onion_mime_set(NULL);
	onion_mime_dict=onion_dict_new();
	//ONION_DEBUG("Filling mime types");
	
	FILE *fd=fopen("/etc/mime.types", "rt");
	if (!fd){
		ONION_WARNING("Could not read MIME types (etc/mime.types), returned mime types may be incorrect. Adding minimal set.");
		onion_dict_add(onion_mime_dict, "html", "text/html",0);
		onion_dict_add(onion_mime_dict, "htm", "text/html",0);
		onion_dict_add(onion_mime_dict, "js", "application/javascript",0);
		onion_dict_add(onion_mime_dict, "css", "text/css",0);
		onion_dict_add(onion_mime_dict, "png", "image/png",0);
		onion_dict_add(onion_mime_dict, "jpg", "image/jpeg",0);
		return;
	}
	char mimetype[128];
	char extension[8];
	int mode=0; // 0 mimetype, 1 extension
	int i=0;
	int c;
	
	while ( (c=getc(fd)) >= 0){
		if (c=='#'){
			while ( (c=getc(fd)) >= 0 && c!='\n');
		}
		if (c=='\n'){
			if (mode==1 && i!=0){
				extension[i]=0;
				onion_dict_add(onion_mime_dict, extension, mimetype, OD_DUP_ALL);
				//ONION_DEBUG("Add mimetype '%s' (%s).", extension, mimetype);
			}
			mode=0;
			i=0;
		}
		else{
			if (is_space(c)){
				if (mode==0){
					mimetype[i]='\0';
					mode=1;
					i=0;
				}
				else if (i!=0){
					extension[i]='\0';
					i=0;
					onion_dict_add(onion_mime_dict, extension, mimetype, OD_DUP_ALL);
					//ONION_DEBUG("Add mimetype '%s' (%s)", extension, mimetype);
				}
			}
			else{
				if (mode==0){
					if (i>=sizeof(mimetype)-1){
						while ( (c=getc(fd)) >= 0 && c!='\n');
					}
					else
						mimetype[i++]=c;
				}
				else{
					if (i>=sizeof(extension)-1){
						while ( (c=getc(fd)) >= 0 && c!='\n');
						extension[i]='\0';
						i=0;
						mode=0;
						onion_dict_add(onion_mime_dict, extension, mimetype,  OD_DUP_ALL);
						//ONION_DEBUG("Add mimetype '%s' (%s)..", extension, mimetype);
					}
					else
						extension[i++]=c;
				}
			}
		}
	}
	fclose(fd);
	
	ONION_DEBUG("I know %d mime types", onion_dict_count(onion_mime_dict));
}
Example #25
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.
 */
onion_connection_status 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;
  }
	onion_response_write_headers(res);
	if ((onion_request_get_flags(request)&OR_HEAD) == OR_HEAD){ // Just head.
		length=0;
	}
	
	if (length){
#ifdef USE_SENDFILE
		if (request->connection.listen_point->write==(void*)onion_http_write){ // Lets have a house party! I can use sendfile!
			onion_response_write(res,NULL,0);
			ONION_DEBUG("Using sendfile");
			int r=sendfile(request->connection.fd, 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;
}
Example #26
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;
		pthread_create(&o->listen_thread,NULL, (void*)onion_listen, o);
		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 #27
0
/**
 * @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;
}
Example #28
0
// Not implemented for libev
void onion_poller_set_queue_size_per_thread(onion_poller *poller, size_t count){
	ONION_WARNING("onion_poller_queue_size_per_thread only used with epoll polling, not libev.");
}