Beispiel #1
0
/**
 * @short Tryes to handle the petition with that handler.
 * @memberof onion_handler_t
 *
 * It needs the handler to handle, the request and the response.
 *
 * It checks this parser, and siblings.
 * 
 * @returns If can not, returns OCS_NOT_PROCESSED (0), else the onion_connection_status. (normally OCS_PROCESSED)
 */
onion_connection_status onion_handler_handle(onion_handler *handler, onion_request *request, onion_response *response){
	onion_connection_status res;
	while (handler){
		if (handler->handler){
#ifdef __DEBUG0__
			char **bs=backtrace_symbols((void * const *)&handler->handler, 1);
			ONION_DEBUG0("Calling handler: %s",bs[0]);
			/* backtrace_symbols is explicitly documented
			   to malloc. We need to call the system free
			   routine, not our onion_low_free ! */
			onion_low_free(bs); /* Can't be onion_low_free.... */
#endif
			res=handler->handler(handler->priv_data, request, response);
			ONION_DEBUG0("Result: %d",res);
			if (res){
				// write pending data.
				if (!(response->flags&OR_HEADER_SENT) && response->buffer_pos<sizeof(response->buffer))
					onion_response_set_length(response, response->buffer_pos);
				onion_response_flush(response);
				if (res==OCS_WEBSOCKET){
					if (request->websocket)
						return onion_websocket_call(request->websocket);
					else{
						ONION_ERROR("Handler did set the OCS_WEBSOCKET, but did not initialize the websocket on this request.");
						return OCS_INTERNAL_ERROR;
					}
				}
				return res;
			}
		}
		handler=handler->next;
	}
	return OCS_NOT_PROCESSED;
}
Beispiel #2
0
static onion_dict *onion_sessions_mem_get(onion_sessions *sessions, const char *session_id){
	ONION_DEBUG0("Accessing session '%s'",session_id);
	onion_dict *sess=onion_dict_get_dict(sessions->data, session_id);
	if (!sess){
		ONION_DEBUG0("Unknown session '%s'.", session_id);
		return NULL;
	}
	return onion_dict_dup(sess);
}
Beispiel #3
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;
}
Beispiel #4
0
static onion_connection_status parse_POST_multipart_content_type(onion_request *req, onion_buffer *data){
	onion_token *token=req->parser_data;
	int res=token_read_until(token, data,';');

	if (res<=1000)
		return res;

	//ONION_DEBUG("Got content type %s",token->str);

	onion_multipart_buffer *multipart=(onion_multipart_buffer*)token->extra;
	const char *name;
	name=strstr(token->str, "filename=");
	if (name){
		int l=strlen(token->str)-9;
		if (l>multipart->post_total_size){
			ONION_ERROR("Post buffer exhausted. content-Length wrong passed.");
			return OCS_INTERNAL_ERROR;
		}
		multipart->filename=multipart->data;
		memcpy(multipart->filename, name+9, l);
		multipart->filename[l]=0;
		multipart->data=multipart->data+l+1;
		if (*multipart->filename=='"' && multipart->filename[l-2]=='"'){
			multipart->filename[l-2]='\0';
			multipart->filename++;
		}
		ONION_DEBUG0("Set filename '%s'",multipart->filename);
	}
	else{
		name=strstr(token->str, "name=");
		if (name){
			int l=strlen(token->str)-5;
			if (l>multipart->post_total_size){
				ONION_ERROR("Post buffer exhausted. Content-Length had wrong size.");
				return OCS_INTERNAL_ERROR;
			}
			multipart->name=multipart->data;
			memcpy(multipart->name, name+5, l);
			multipart->name[l]=0;
			if (*multipart->name=='"' && multipart->name[l-2]=='"'){
				l-=2;
				multipart->name[l]='\0';
				multipart->name++;
			}
			multipart->data=multipart->name+l+1;
			ONION_DEBUG0("Set field name '%s'",multipart->name);
		}
	}


	if (res==STRING_NEW_LINE){
		req->parser=parse_POST_multipart_headers_key;
		return OCS_NEED_MORE_DATA;
	}
	return OCS_NEED_MORE_DATA;
}
Beispiel #5
0
static onion_dict* onion_sessions_redis_get(onion_sessions* sessions, const char *session_id)
{
    onion_session_redis *p = sessions->data;
    ONION_DEBUG0("Load session %s", session_id);
    onion_dict *ret = NULL;

#ifdef HAVE_PTHREADS
    pthread_mutex_lock(&p->mutex);
#endif

    // When commands are sent via redisCommand, they are interpolated by the library
    // so it will avoid any type of command injection. No need to worry about sending
    // the session_id directly to redis.
    redisReply* reply = redisCommand(p->context, "HEXISTS SESSIONS %b", session_id, strlen(session_id));

    if(reply->type != REDIS_REPLY_INTEGER)
    {
        ONION_ERROR("Error getting session_id");
        freeReplyObject(reply);
        goto exit;
    }
    else
    {
        if(reply->integer == 1)
        {
            freeReplyObject(reply);
            reply = redisCommand(p->context, "HGET SESSIONS %b", session_id, strlen(session_id));

            if(reply->type != REDIS_REPLY_STRING)
            {
                ONION_ERROR("Error loading session.");
                freeReplyObject(reply);
                goto exit;
            }
            else
            {
                ret = onion_dict_from_json(reply->str);
                freeReplyObject(reply);
                goto exit;
            }
        }
        else
        {
            ONION_DEBUG0("No session found. Returning NULL");
            freeReplyObject(reply);
            goto exit;
        }
    }
exit:
#ifdef HAVE_PTHREADS
    pthread_mutex_unlock(&p->mutex);
#endif
    return ret;
}
Beispiel #6
0
/**
 * @short Handles a propfind
 * 
 * @param path the shared path.
 */
onion_connection_status onion_webdav_propfind(const char *filename, onion_webdav *wd, onion_request* req, onion_response* res){
	// Prepare the basepath, necesary for props.
	char *basepath=NULL;
	int pathlen=0;
	const char *current_path=onion_request_get_path(req);
	const char *fullpath=onion_request_get_fullpath(req);
	pathlen=(current_path-fullpath);
	basepath=alloca(pathlen+1);
	memcpy(basepath, fullpath, pathlen+1);
	ONION_DEBUG0("Pathbase initial <%s> %d", basepath, pathlen); 
	while(basepath[pathlen]=='/' && pathlen>0)
		pathlen--;
	basepath[pathlen+1]=0;
				 
	ONION_DEBUG0("PROPFIND; pathbase %s", basepath);
	int depth;
	{
		const char *depths=onion_request_get_header(req, "Depth");
		if (!depths){
			ONION_ERROR("Missing Depth header on webdav request");
			return OCS_INTERNAL_ERROR;
		}
		if (strcmp(depths,"infinity")==0){
			ONION_ERROR("Infinity depth not supported yet.");
			return OCS_INTERNAL_ERROR;
		}
		depth=atoi(depths);
	}

	int props=onion_webdav_parse_propfind(onion_request_get_data(req));
	ONION_DEBUG("Asking for props %08X, depth %d", props, depth);
	
	onion_block *block=onion_webdav_write_propfind(basepath, filename, onion_request_get_path(req), depth, props);
	
	if (!block) // No block, resource does not exist
		return onion_shortcut_response("Not found", HTTP_NOT_FOUND, req, res);
		
	ONION_DEBUG0("Printing block %s", onion_block_data(block));
	
	onion_response_set_header(res, "Content-Type", "text/xml; charset=\"utf-8\"");
	onion_response_set_length(res, onion_block_size(block));
	onion_response_set_code(res, HTTP_MULTI_STATUS);
	onion_response_write_headers(res);
	onion_response_flush(res);
	
	onion_response_write(res, onion_block_data(block), onion_block_size(block));
	
	onion_block_free(block);
	
	return OCS_PROCESSED;
}
Beispiel #7
0
/**
 * @short Prepares the PUT
 *
 * It saves the data to a temporal file, which name is stored at data.
 */
static onion_connection_status prepare_PUT(onion_request *req){
	onion_token *token=req->parser_data;
	const char *content_size=onion_dict_get(req->headers, "Content-Length");
	if (!content_size){
		ONION_ERROR("I need the Content-Length header to get data");
		return OCS_INTERNAL_ERROR;
	}
	size_t cl=atol(content_size);

	if (cl>req->connection.listen_point->server->max_file_size){
		ONION_ERROR("Trying to PUT a file bigger than allowed size");
		return OCS_INTERNAL_ERROR;
	}

	req->data=onion_block_new();

	char filename[]="/tmp/onion-XXXXXX";
	int fd=mkstemp(filename);
	if (fd<0)
		ONION_ERROR("Could not create temporary file at %s.", filename);

	onion_block_add_str(req->data, filename);
	ONION_DEBUG0("Creating PUT file %s (%d bytes long)", filename, token->extra_size);

	if (!req->FILES){
		req->FILES=onion_dict_new();
	}
	{
	const char *filename=onion_block_data(req->data);
	onion_dict_add(req->FILES,"filename", filename, 0);
	}


	if (cl==0){
		ONION_DEBUG0("Created 0 length file");
		close(fd);
		return OCS_REQUEST_READY;
	}

	int *pfd=onion_low_scalar_malloc(sizeof(fd));
	*pfd=fd;

	assert(token->extra==NULL);
	token->extra=(char*)pfd;
	token->extra_size=cl;
	token->pos=0;

	req->parser=parse_PUT;
	return OCS_NEED_MORE_DATA;
}
Beispiel #8
0
/**
 * @short Gets the dict with the cookies
 * @memberof onion_request_t
 *
 * @param req Request to get the cookies from
 *
 * @returns A dict with all the cookies. It might be empty.
 *
 * First call it generates the dict.
 */
onion_dict* onion_request_get_cookies_dict(onion_request* req) {
    if (req->cookies)
        return req->cookies;

    req->cookies=onion_dict_new();

    const char *ccookies=onion_request_get_header(req, "Cookie");
    if (!ccookies)
        return req->cookies;
    char *cookies=onion_low_strdup(ccookies); // I prepare a temporary string, will modify it.
    char *val=NULL;
    char *key=NULL;
    char *p=cookies;

    int first=1;
    while(*p) {
        if (*p!=' ' && !key && !val) {
            key=p;
        }
        else if (*p=='=' && key && !val) {
            *p=0;
            val=p+1;
        }
        else if (*p==';' && key && val) {
            *p=0;
            if (first) {
                // The first cookie is special as it is the pointer to the reserved area for all the keys and values
                // for all th eother cookies, to free at dict free.
                onion_dict_add(req->cookies, cookies, val, OD_FREE_KEY);
                first=0;
            }
            else
                onion_dict_add(req->cookies, key, val, 0); /// Can use as static data as will be freed at first cookie free
            ONION_DEBUG0("Add cookie <%s>=<%s> %d", key, val, first);
            val=NULL;
            key=NULL;
        }
        p++;
    }
    if (key && val && val<p) { // A final element, with value.
        if (first)
            onion_dict_add(req->cookies, cookies, val, OD_FREE_KEY);
        else
            onion_dict_add(req->cookies, key, val, 0);
        ONION_DEBUG0("Add cookie <%s>=<%s> %d", key, val, first);
    }

    return req->cookies;
}
Beispiel #9
0
/**
 * @short Parses the query part to a given dictionary.
 *
 * The data is overwriten as necessary. It is NOT dupped, so if you free this char *p, please free the tree too.
 */
static void onion_request_parse_query_to_dict(onion_dict *dict, char *p){
	ONION_DEBUG0("Query to dict %s",p);
	char *key=NULL, *value=NULL;
	int state=0;  // 0 key, 1 value
	key=p;
	while(*p){
		if (state==0){
			if (*p=='='){
				*p='\0';
				value=p+1;
				state=1;
			}
			else if (*p=='&'){
				*p='\0';
				onion_unquote_inplace(key);
				ONION_DEBUG0("Adding key %s",key);
				onion_dict_add(dict, key, "", 0);
				key=p+1;
				state=0;
			}
		}
		else{
			if (*p=='&'){
				*p='\0';
				onion_unquote_inplace(key);
				onion_unquote_inplace(value);
				ONION_DEBUG0("Adding key %s=%-16s",key,value);
				onion_dict_add(dict, key, value, 0);
				key=p+1;
				state=0;
			}
		}
		p++;
	}
	if (state==0){
		if (key[0]!='\0'){
			onion_unquote_inplace(key);
			ONION_DEBUG0("Adding key %s",key);
			onion_dict_add(dict, key, "", 0);
		}
	}
	else{
		onion_unquote_inplace(key);
		onion_unquote_inplace(value);
		ONION_DEBUG0("Adding key %s=%-16s",key,value);
		onion_dict_add(dict, key, value, 0);
	}
}
Beispiel #10
0
/**
 * @short Reads from the data to fulfill content-length data.
 * 
 * All data is writen a temporal file, which will be removed later.
 */
static onion_connection_status parse_PUT(onion_request *req, onion_buffer *data){
	onion_token *token=req->parser_data;
	int length=data->size-data->pos;
	int exit=0;
	
	if (length>=token->extra_size-token->pos){
		exit=1;
		length=token->extra_size-token->pos;
	}

	//ONION_DEBUG0("Writing %d. %d / %d bytes", length, token->pos+length, token->extra_size);

	int *fd=(int*)token->extra;
	ssize_t w=write(*fd, &data->data[data->pos], length);
	if (w<0){
		ONION_ERROR("Could not write all data to temporal file.");
		return OCS_INTERNAL_ERROR;
	}
	data->pos+=length;
	token->pos+=length;

#if __DEBUG__
	const char *filename=onion_block_data(req->data);
	ONION_DEBUG0("Done with PUT. Created %s (%d bytes)", filename, token->pos);
#endif
	
	if (exit){
		close (*fd);
		free(fd);
		token->extra=NULL;
		return onion_request_process(req);
	}
	
	return OCS_NEED_MORE_DATA;
}
Beispiel #11
0
/**
 * @short Adds a file descriptor to poll.
 * @memberof onion_poller_t
 * @ingroup poller
 *
 * When new data is available (read/write/event) the given function
 * is called with that data.
 *
 * Once the slot is added is not safe anymore to set data on it, unless its detached from the epoll. (see EPOLLONESHOT)
 */
int onion_poller_add(onion_poller *poller, onion_poller_slot *el){
	ONION_DEBUG0("Adding fd %d for polling (%d)", el->fd, poller->n);

	pthread_mutex_lock(&poller->mutex);
	// I like head to be always the same, so I do some tricks to keep it. This is beacuse at head normally I have the listen fd, which is very accessed
	if (!poller->head)
		poller->head=el;
	else{
		el->next=poller->head->next;
		poller->head->next=el;
		poller->n++;
	}
	pthread_mutex_unlock(&poller->mutex);

	struct epoll_event ev;
	memset(&ev, 0, sizeof(ev));
	ev.events=el->type;
	ev.data.ptr=el;
	if (epoll_ctl(poller->fd, EPOLL_CTL_ADD, el->fd, &ev) < 0){
		ONION_ERROR("Error add descriptor to listen to. %s", strerror(errno));
		return 1;
	}
	onion_poller_timer_check(poller, el->timeout_limit);

	return 1;
}
Beispiel #12
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");
}
Beispiel #13
0
static onion_connection_status parse_headers_KEY(onion_request *req, onion_buffer *data){
	onion_token *token=req->parser_data;
	
	int res=token_read_KEY(token, data);

	if (res<=1000)
		return res;

  ONION_DEBUG0("Got %d at KEY",res);
  
	if ( res == NEW_LINE ){
#if 0
		if ((req->flags&OR_METHODS)==OR_POST)
			return prepare_POST(req);
#endif
		if ((req->flags&OR_METHODS)==OR_PUT)
			return prepare_PUT(req);
		if (onion_request_get_header(req, "Content-Length")){ // Soem length, not POST, get data.
			int n=atoi(onion_request_get_header(req, "Content-Length"));
			if (n>0)
				return prepare_CONTENT_LENGTH(req);
		}
		
		return onion_request_process(req);
	}
	
	token->extra=strdup(token->str);
	
	req->parser=parse_headers_VALUE;
	return parse_headers_VALUE(req, data);
}
Beispiel #14
0
/**
 * @short Deletes a request and all its data
 * @memberof onion_request_t
 */
void onion_request_free(onion_request *req){
  ONION_DEBUG0("Free request %p", req);
	onion_dict_free(req->headers);
	
	if (req->parser_data){
		onion_request_parser_data_free(req->parser_data);
		req->parser_data=NULL;
	}
	
	if (req->fullpath)
		free(req->fullpath);
	if (req->GET)
		onion_dict_free(req->GET);
	if (req->POST)
		onion_dict_free(req->POST);
	if (req->FILES){
		onion_dict_preorder(req->FILES, unlink_files, NULL);
		onion_dict_free(req->FILES);
	}
	if (req->parser_data)
		free(req->parser_data);
	if (req->client_info)
		free(req->client_info);
	if (req->session_id)
		free(req->session_id);
	if (req->session)
		onion_dict_free(req->session); // Not really remove, just dereference
	if (req->data)
		onion_block_free(req->data);

	free(req);
}
Beispiel #15
0
/**
 * @short Gets the sessionid cookie, if any, and sets it to req->session_id.
 * @memberof onion_request_t
 */
void onion_request_guess_session_id(onion_request *req){
	if (req->session_id) // already known.
		return;
	const char *v=onion_dict_get(req->headers, "Cookie");
	ONION_DEBUG("Session ID, maybe from %s",v);
	char *r=NULL;
	onion_dict *session;
	
	do{ // Check all possible sessions
		if (r)
			free(r);
		if (!v)
			return;
		v=strstr(v,"sessionid=");
		if (!v) // exit point, no session found.
			return;
		v+=10;
		r=strdup(v); // Maybe allocated more memory, not much anyway.
		char *p=r;
		while (*p!='\0' && *p!=';') p++;
		*p='\0';
		ONION_DEBUG0("Checking if %s exists in sessions", r);
		session=onion_sessions_get(req->server->sessions, r);
	}while(!session);
	
	req->session_id=r;
	req->session=session;
	ONION_DEBUG("Session ID, from cookie, is %s",req->session_id);
}
Beispiel #16
0
/**
 * @short Cleans a request object to reuse it.
 * @memberof onion_request_t
 */
void onion_request_clean(onion_request* req){
  ONION_DEBUG0("Clean request %p", req);
  onion_dict_free(req->headers);
  req->headers=onion_dict_new();
  if (req->parser_data){
    onion_request_parser_data_free(req->parser_data);
    req->parser_data=NULL;
  }
  req->parser=NULL;
  req->flags&=0x0F00; // I keep server flags.
  if (req->fullpath){
    free(req->fullpath);
    req->path=req->fullpath=NULL;
  }
  if (req->GET){
    onion_dict_free(req->GET);
    req->GET=NULL;
  }
  if (req->POST){
    onion_dict_free(req->POST);
    req->POST=NULL;
  }
  if (req->FILES){
    onion_dict_preorder(req->FILES, unlink_files, NULL);
    onion_dict_free(req->FILES);
    req->FILES=NULL;
  }
  if (req->data){
    onion_block_free(req->data);
    req->data=NULL;
  }
}
Beispiel #17
0
/**
 * @short Writes all the header to the given response
 * @memberof onion_response_t
 * @ingroup response
 *
 * It writes the headers and depending on the method, return OR_SKIP_CONTENT. this is set when in head mode. Handlers
 * should react to this return by not trying to write more, but if they try this object will just skip those writtings.
 *
 * Explicit calling to this function is not necessary, as as soon as the user calls any write function this will
 * be performed.
 *
 * As soon as the headers are written, any modification on them will be just ignored.
 *
 * @returns 0 if should procced to normal data write, or OR_SKIP_CONTENT if should not write content.
 */
int onion_response_write_headers(onion_response * res) {
  if (!res->request) {
    ONION_ERROR
        ("Bad formed response. Need a request at creation. Will not write headers.");
    return -1;
  }

  res->flags |= OR_HEADER_SENT; // I Set at the begining so I can do normal writing.
  res->request->flags |= OR_HEADER_SENT;
  char chunked = 0;

  if (res->request->flags & OR_HTTP11) {
    onion_response_printf(res, "HTTP/1.1 %d %s\r\n", res->code,
                          onion_response_code_description(res->code));
    //ONION_DEBUG("Response header: HTTP/1.1 %d %s\n",res->code, onion_response_code_description(res->code));
    if (!(res->flags & OR_LENGTH_SET) && onion_request_keep_alive(res->request)) {
      onion_response_write(res, CONNECTION_CHUNK_ENCODING,
                           sizeof(CONNECTION_CHUNK_ENCODING) - 1);
      chunked = 1;
    }
  } else {
    onion_response_printf(res, "HTTP/1.0 %d %s\r\n", res->code,
                          onion_response_code_description(res->code));
    //ONION_DEBUG("Response header: HTTP/1.0 %d %s\n",res->code, onion_response_code_description(res->code));
    if (res->flags & OR_LENGTH_SET)     // On HTTP/1.0, i need to state it. On 1.1 it is default.
      onion_response_write(res, CONNECTION_KEEP_ALIVE,
                           sizeof(CONNECTION_KEEP_ALIVE) - 1);
  }

  if (!(res->flags & OR_LENGTH_SET) && !chunked
      && !(res->flags & OR_CONNECTION_UPGRADE))
    onion_response_write(res, CONNECTION_CLOSE, sizeof(CONNECTION_CLOSE) - 1);

  if (res->flags & OR_CONNECTION_UPGRADE)
    onion_response_write(res, CONNECTION_UPGRADE,
                         sizeof(CONNECTION_UPGRADE) - 1);

  onion_dict_preorder(res->headers, write_header, res);

  if (res->request->session_id && (onion_dict_count(res->request->session) > 0))        // I have session with something, tell user
    onion_response_printf(res, "Set-Cookie: sessionid=%s; httponly; Path=/\n",
                          res->request->session_id);

  onion_response_write(res, "\r\n", 2);

  ONION_DEBUG0("Headers written");
  res->sent_bytes = -res->buffer_pos;   // the header size is not counted here. It will add again so start negative.

  if ((res->request->flags & OR_METHODS) == OR_HEAD) {
    onion_response_flush(res);
    res->flags |= OR_SKIP_CONTENT;
    return OR_SKIP_CONTENT;
  }
  if (chunked) {
    onion_response_flush(res);
    res->flags |= OR_CHUNKED;
  }

  return 0;
}
Beispiel #18
0
static onion_connection_status parse_headers_KEY(onion_request *req, onion_buffer *data){
	onion_token *token=req->parser_data;

	int res=token_read_KEY(token, data);

	if (res<=1000)
		return res;

  ONION_DEBUG0("Got %d at KEY",res);

	if ( res == NEW_LINE ){
		if ((req->flags&OR_METHODS)==OR_POST){
			const char *content_type=onion_request_get_header(req, "Content-Type");
			if (content_type && (strstr(content_type,"application/x-www-form-urlencoded") || strstr(content_type, "boundary")))
				return prepare_POST(req);
		}
		if ((req->flags&OR_METHODS)==OR_PUT)
			return prepare_PUT(req);
		if (onion_request_get_header(req, "Content-Length")){ // Some length, not POST, get data.
			int n=atoi(onion_request_get_header(req, "Content-Length"));
			if (n>0)
				return prepare_CONTENT_LENGTH(req);
		}

		return OCS_REQUEST_READY;
	}
	assert(token->extra==NULL);
	token->extra=onion_low_strdup(token->str);

	req->parser=parse_headers_VALUE;
	return parse_headers_VALUE(req, data);
}
Beispiel #19
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));
	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;
}
Beispiel #20
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=malloc(sizeof(onion));
	o->flags=flags&0x0FF;
	o->listenfd=0;
	o->server=onion_server_new();
	o->timeout=5000; // 5 seconds of timeout, default.
	o->port=strdup("8080");
	o->hostname=strdup("::");
	o->username=NULL;
#ifdef HAVE_GNUTLS
	o->flags|=O_SSL_AVAILABLE;
#endif
#ifdef HAVE_PTHREADS
	o->flags|=O_THREADS_AVALIABLE;
	o->max_threads=15;
	if (o->flags&O_THREADED){
		o->flags|=O_THREADS_ENABLED;
		sem_init(&o->thread_count, 0, o->max_threads);
		pthread_mutex_init(&o->mutex, NULL);
	}
#endif
	o->poller=NULL;
	
	return o;
}
Beispiel #21
0
/**
 *  @short Creates a request object
 * @memberof onion_request_t
 *
 * @param op Listen point this request is listening to, to be able to read and write data
 */
onion_request *onion_request_new(onion_listen_point *op) {
    onion_request *req;
    req=onion_low_calloc(1, sizeof(onion_request));

    req->connection.listen_point=op;
    req->connection.fd=-1;

    //req->connection=con;
    req->headers=onion_dict_new();
    onion_dict_set_flags(req->headers, OD_ICASE);
    ONION_DEBUG0("Create request %p", req);

    if (op) {
        if (op->request_init) {
            if (op->request_init(req)<0) {
                ONION_DEBUG("Invalid request, closing");
                onion_request_free(req);
                return NULL;
            }
        }
        else
            onion_listen_point_request_init_from_socket(req);
    }
    return req;
}
Beispiel #22
0
/**
 * @short Internal processor of just one request.
 * @memberof onion_t
 *
 * It can be used on one processing, on threaded, on one_loop...
 * 
 * It reads all the input passes it to the request, until onion_request_writes returns that the 
 * connection must be closed. This function do no close the connection, but the caller must 
 * close it when it returns.
 * 
 * It also do all the SSL startup when appropiate.
 * 
 * @param o Onion struct.
 * @param clientfd is the POSIX file descriptor of the connection.
 */
static void onion_process_request(onion *o, int clientfd, struct sockaddr_storage *client_addr, socklen_t client_len){
	ONION_DEBUG0("Processing request");
	onion_request *req=onion_connection_start(o, clientfd, client_addr, client_len);
  if (!req){
    close(clientfd);
    return;
  }
	onion_connection_status cs=OCS_KEEP_ALIVE;
	struct pollfd pfd;
	pfd.events=POLLIN;
	pfd.fd=clientfd;
	while ((cs>=0) && (poll(&pfd,1, o->timeout)>0)){
		cs=onion_connection_read(req);
	}
	ONION_DEBUG0("Shutdown connection");
	onion_connection_shutdown(req);
}
Beispiel #23
0
/**
 * @short Frees the parser data.
 */
void onion_request_parser_data_free(void *t){
	ONION_DEBUG0("Free parser data");
	onion_token *token=t;
	if (token->extra){
		onion_low_free(token->extra);
		token->extra=NULL;
	}
	onion_low_free(token);
}
Beispiel #24
0
/**
 * @short Default implementation that just closes the connection
 * @memberof onion_listen_point_t
 * 
 * @param oc The request
 */
void onion_listen_point_request_close_socket(onion_request *oc){
	int fd=oc->connection.fd;
	ONION_DEBUG0("Closing connection socket %d",fd);
	if (fd>=0){
		shutdown(fd,SHUT_RDWR);
		close(fd);
		oc->connection.fd=-1;
	}
}
Beispiel #25
0
/**
 * @short Cleans a request object to reuse it.
 * @memberof onion_request_t
 */
void onion_request_clean(onion_request* req) {
    ONION_DEBUG0("Clean request %p", req);
    onion_dict_free(req->headers);
    req->headers=onion_dict_new();
    onion_dict_set_flags(req->headers, OD_ICASE);
    req->flags&=OR_NO_KEEP_ALIVE; // I keep keep alive.
    if (req->parser_data) {
        onion_request_parser_data_free(req->parser_data);
        req->parser_data=NULL;
    }
    if (req->fullpath) {
        onion_low_free(req->fullpath);
        req->path=req->fullpath=NULL;
    }
    if (req->GET) {
        onion_dict_free(req->GET);
        req->GET=NULL;
    }
    if (req->POST) {
        onion_dict_free(req->POST);
        req->POST=NULL;
    }
    if (req->FILES) {
        onion_dict_preorder(req->FILES, unlink_files, NULL);
        onion_dict_free(req->FILES);
        req->FILES=NULL;
    }
    if (req->session_id) {
        if (onion_dict_count(req->session)==0) {
            onion_request_session_free(req);
        }
        else {
            onion_sessions_save(req->connection.listen_point->server->sessions, req->session_id, req->session);
            onion_dict_free(req->session); // Not really remove, just dereference
            req->session=NULL;
            onion_low_free(req->session_id);
            req->session_id=NULL;
        }
    }
    if (req->data) {
        onion_block_free(req->data);
        req->data=NULL;
    }
    if (req->connection.cli_info) {
        onion_low_free(req->connection.cli_info);
        req->connection.cli_info=NULL;
    }
    if (req->cookies) {
        onion_dict_free(req->cookies);
        req->cookies=NULL;
    }
    if (req->free_list) {
        onion_ptr_list_foreach(req->free_list, onion_low_free);
        onion_ptr_list_free(req->free_list);
        req->free_list=NULL;
    }
}
Beispiel #26
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_data(onion_request *req, onion_buffer *data){
	onion_token *token=req->parser_data;
	onion_multipart_buffer *multipart=(onion_multipart_buffer*)token->extra;
	const char *p=data->data+data->pos;
	char *d=&multipart->data[token->pos];

	for (;data->pos<data->size;data->pos++){
		if (multipart->pos==0 && *p=='\n') // \r is optional.
			multipart->pos=1;
		if (*p==multipart->boundary[multipart->pos]){ // Check boundary
			multipart->pos++;
			if (multipart->pos==multipart->size){
				multipart->pos=0;
				data->pos++; // Not sure why this is needed. FIXME.
				if (token->pos>0 && *(d-1)=='\n'){
					token->pos--;
					d--;
				}
				if (token->pos>0 && *(d-1)=='\r')
					d--;

				*d='\0';
				ONION_DEBUG0("Adding POST data '%s'",multipart->name);
				onion_dict_add(req->POST, multipart->name, multipart->data, 0);
				multipart->data=multipart->data+token->pos+1;
				token->pos=0;
				req->parser=parse_POST_multipart_next;
				return OCS_NEED_MORE_DATA;
			}
		}
		else{ // No boundary
			if (multipart->pos!=0){// Maybe i was checking before... so I need to copy as much as I wrongly thought I got.
				if (multipart->post_total_size<=multipart->pos){
					ONION_ERROR("No space left for this post.");
					return OCS_INTERNAL_ERROR;
				}
				multipart->post_total_size-=multipart->pos;
				memcpy(d,p,multipart->pos);
				d+=multipart->pos;
				token->pos+=multipart->pos;
				multipart->pos=0;
			}
			if (multipart->post_total_size<=0){
				ONION_ERROR("No space left for this post.");
				return OCS_INTERNAL_ERROR;
			}
			multipart->post_total_size--;
			//ONION_DEBUG0("%d %d",multipart->post_total_size,token->extra_size - (int) (d-token->extra));
			*d=*p;
			d++;
			token->pos++;
		}
		p++;
	}
	return OCS_NEED_MORE_DATA;
}
Beispiel #27
0
/// Just appends to the handler. Must be big enought or segfault.. Just for tests.
int buffer_append(buffer *handler, const char *data, unsigned int length){
	ONION_DEBUG0("Appending %d bytes",length);
	int l=length;
	if (handler->pos+length>handler->size){
		l=handler->size-handler->pos;
	}
	memcpy(handler->data+handler->pos,data,l);
	handler->pos+=l;
	return l;
}
Beispiel #28
0
void onion_poller_slot_set_type(onion_poller_slot *el, int type){
	el->type=EPOLLONESHOT;
	if (type&O_POLL_READ)
		el->type|=EPOLLIN;
	if (type&O_POLL_WRITE)
		el->type|=EPOLLOUT;
	if (type&O_POLL_OTHER)
		el->type|=EPOLLERR|EPOLLHUP|EPOLLPRI;
	ONION_DEBUG0("Setting type to %d, %d", el->fd, el->type);
}
Beispiel #29
0
/**
 * @short Performs the real request: checks if its for me, and then calls the inside level.
 */
int onion_url_handler(onion_url_data **dd, onion_request *request, onion_response *response){
	onion_url_data *next=*dd;
	regmatch_t match[16];
	int i;
	
	const char *path=onion_request_get_path(request);
	while (next){
		ONION_DEBUG0("Check %s against %s", onion_request_get_path(request), next->orig);
		if (next->flags&OUD_STRCMP){
			if (strcmp(path, next->str)==0){
				ONION_DEBUG0("Ok, simple match.");

				onion_request_advance_path(request, strlen(next->str));
				return onion_handler_handle(next->inside, request, response);
			}
		}
		else if (regexec(&next->regexp, onion_request_get_path(request), 16, match, 0)==0){
			//ONION_DEBUG("Ok,match");
			onion_dict *reqheader=request->GET;
			for (i=1;i<16;i++){
				regmatch_t *rm=&match[i];
				if (rm->rm_so!=-1){
					char *tmp=malloc(rm->rm_eo-rm->rm_so);
					memcpy(tmp, &path[rm->rm_so], rm->rm_eo-rm->rm_so);
					char tmpn[4];
					snprintf(tmpn,sizeof(tmpn),"%d",i);
					onion_dict_add(reqheader, tmpn, tmp, OD_DUP_KEY|OD_FREE_VALUE);
					ONION_DEBUG("Add group %d: %s (%d-%d)", i, tmp, rm->rm_so, rm->rm_eo);
				}
				else
					break;
			}
			onion_request_advance_path(request, match[0].rm_eo);
			ONION_DEBUG0("Ok, regexp match.");

			
			return onion_handler_handle(next->inside, request, response);
		}
		
		next=next->next;
	}
	return 0;
}
Beispiel #30
0
/// simple answer to the password question, needed by pam
static int authPAM_passwd(int num_msg, const struct pam_message **msg,
                struct pam_response **resp, void *appdata_ptr){
	ONION_DEBUG0("Num messages %d",num_msg);
	struct pam_response *r;
	
	*resp=r=(struct pam_response*)calloc(num_msg,sizeof(struct pam_response));
	if (r==NULL)
		return PAM_BUF_ERR;
	
	int i;
	
	for (i=0;i<num_msg;i++){
		ONION_DEBUG0("Question %d: %s",i, (*msg)[i].msg);
		r->resp=strdup((const char*)appdata_ptr);
		r->resp_retcode=0;
		r++;
	}

	return PAM_SUCCESS;
}