Ejemplo n.º 1
0
/**
 * @short Sets the port to listen to.
 * @memberof onion_t
 * 
 * Default listen point is HTTP at localhost:8080.
 * 
 * @param server The onion server to act on.
 * @param port The number of port to listen to, or service name, as string always.
 */
int onion_add_listen_point(onion* server, const char* hostname, const char* port, onion_listen_point* protocol){
	if (protocol==NULL){
		ONION_ERROR("Trying to add an invalid entry point. Ignoring.");
		return -1;
	}
	
	protocol->server=server;
	if (hostname)
		protocol->hostname=onion_low_strdup(hostname);
	if (port)
		protocol->port=onion_low_strdup(port);
	
	if (server->listen_points){
		onion_listen_point **p=server->listen_points;
		int protcount=0;
		while (*p++) protcount++;
		server->listen_points=
		  (onion_listen_point**)onion_low_realloc(server->listen_points,
							 (protcount+2)*sizeof(onion_listen_point));
		server->listen_points[protcount]=protocol;
		server->listen_points[protcount+1]=NULL;
	}
	else{
		server->listen_points=onion_low_malloc(sizeof(onion_listen_point*)*2);
		server->listen_points[0]=protocol;
		server->listen_points[1]=NULL;
	}
	
	ONION_DEBUG("add %p listen_point (%p, %p, %p)", protocol, server->listen_points, *server->listen_points, *(server->listen_points+1));
	return 0;
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
/// Sets the data on the node, on the right way.
static void onion_dict_set_node_data(onion_dict_node_data *data, const char *key, const void *value, int flags){
	//ONION_DEBUG("Set data %02X",flags);
	if ((flags&OD_DUP_KEY)==OD_DUP_KEY) // not enought with flag, as its a multiple bit flag, with FREE included
		data->key=onion_low_strdup(key);
	else
		data->key=key;
	if ((flags&OD_DUP_VALUE)==OD_DUP_VALUE){
		if (flags&OD_DICT)
			data->value=onion_dict_hard_dup((onion_dict*)value);
		else
			data->value=onion_low_strdup(value);
	}
	else
		data->value=value;
	data->flags=flags;
}
Ejemplo n.º 4
0
/**
 * @short Returns the language code of the current request
 * @memberof onion_request_t
 *
 * Returns the language code for the current request, from the header.
 * If none the returns "C".
 *
 * Language code is short code. No localization by the moment.
 *
 * @returns The language code for this request or C. Data must be freed.
 */
const char *onion_request_get_language_code(onion_request *req) {
    const char *lang=onion_dict_get(req->headers, "Accept-Language");
    if (lang) {
        char *l=onion_low_strdup(lang);
        char *p=l;
        while (*p) { // search first part
            if (*p=='-' || *p==';' || *p==',') {
                *p=0;
                break;
            }
            p++;
        }
        //ONION_DEBUG("Language is %s", l);
        return l;
    }
    return onion_low_strdup("C");
}
Ejemplo n.º 5
0
/// Sets the hostname on which to listen
void onion_set_hostname(onion *server, const char *hostname){
	if (server->listen_points){
		onion_low_free(server->listen_points[0]->hostname);
		server->listen_points[0]->hostname=onion_low_strdup(hostname);
	}
	else{
		onion_add_listen_point(server, hostname, NULL, onion_http_new());
	}
}
Ejemplo n.º 6
0
/// Sets the port to listen
void onion_set_port(onion *server, const char *port){
	if (server->listen_points){
		onion_low_free(server->listen_points[0]->port);
		server->listen_points[0]->port=onion_low_strdup(port);
	}
	else{
		onion_add_listen_point(server, NULL, port, onion_http_new());
	}
}
Ejemplo n.º 7
0
/**
 * @short Creates a static handler that just writes some static data.
 *
 * Path is a regex for the url, as arrived here.
 */
onion_handler *onion_handler_static(const char *text, int code){
	onion_handler_static_data *priv_data=onion_low_malloc(sizeof(onion_handler_static_data));
	if (!priv_data)
		return NULL;

	priv_data->code=code;
	priv_data->data=onion_low_strdup(text);

	onion_handler *ret=onion_handler_new((onion_handler_handler)onion_handler_static_handler,
																			 priv_data,(onion_handler_private_data_free) onion_handler_static_delete);
	return ret;
}
Ejemplo n.º 8
0
/**
 * @short Returns a string with the client's description.
 * @memberof onion_request_t
 *
 * @return A const char * with the client description
 */
const char *onion_request_get_client_description(onion_request *req) {
    if (!req->connection.cli_info && req->connection.cli_len) {
        char tmp[256];
        if (getnameinfo((struct sockaddr *)&req->connection.cli_addr, req->connection.cli_len, tmp, sizeof(tmp)-1,
                        NULL, 0, NI_NUMERICHOST) == 0) {
            tmp[sizeof(tmp)-1]='\0';
            req->connection.cli_info=onion_low_strdup(tmp);
        }
        else
            req->connection.cli_info=NULL;
    }
    return req->connection.cli_info;
}
Ejemplo n.º 9
0
Archivo: onion.c Proyecto: Nov11/onion
/// Set a certificate for use in the connection
int onion_set_certificate_va(onion *onion, onion_ssl_certificate_type type, const char *filename, va_list va){
#ifdef HAVE_GNUTLS
	if (!onion->listen_points){
		onion_add_listen_point(onion,NULL,NULL,onion_https_new());
	}
	else{
		onion_listen_point *first_listen_point=onion->listen_points[0];
		if (first_listen_point->write!=onion_https_write){
			if (first_listen_point->write!=onion_http_write){
				ONION_ERROR("First listen point is not HTTP not HTTPS. Refusing to promote it to HTTPS. Use proper onion_https_new.");
				return -1;
			}
			ONION_DEBUG("Promoting from HTTP to HTTPS");
			char *port=first_listen_point->port
			  ? onion_low_strdup(first_listen_point->port) : NULL;
			char *hostname=first_listen_point->hostname
			  ? onion_low_strdup(first_listen_point->hostname) : NULL;
			onion_listen_point_free(first_listen_point);
			onion_listen_point *https=onion_https_new();
			https->server = onion;
			if (NULL==https){
				ONION_ERROR("Could not promote from HTTP to HTTPS. Certificate not set.");
			}
			https->port=port;
			https->hostname=hostname;
			onion->listen_points[0]=https;
			first_listen_point=https;
		}
	}
	int r=onion_https_set_certificate_argv(onion->listen_points[0], type, filename, va);

	return r;
#else
	ONION_ERROR("GNUTLS is not enabled. Recompile onion with GNUTLS support");
	return -1;
#endif
}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
0
static onion_connection_status parse_headers_URL(onion_request *req, onion_buffer *data){
	onion_token *token=req->parser_data;
	int res=token_read_STRING(token, data);

	if (res<=1000)
		return res;

	req->fullpath=onion_low_strdup(token->str);
	onion_request_parse_query(req);
	ONION_DEBUG0("URL path is %s", req->fullpath);

  if (res==STRING_NEW_LINE){ // Old HTTP/0? or lazy user, dont set the HTTP version, straigth new line.
    req->parser=parse_headers_KEY;
    return parse_headers_KEY(req, data);
  }
  else{
    req->parser=parse_headers_VERSION;
    return parse_headers_VERSION(req, data);
  }
}
Ejemplo n.º 12
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 *ov=onion_dict_get(req->headers, "Cookie");
    const char *v=ov;
    ONION_DEBUG("Session ID, maybe from %s",v);
    char *r=NULL;
    onion_dict *session=NULL;

    do { // Check all possible sessions
        if (r) {
            onion_low_free(r);
            r=NULL;
        }
        if (!v)
            return;
        v=strstr(v,"sessionid=");
        if (!v) // exit point, no session found.
            return;
        if (v>ov && isalnum(v[-1])) {
            ONION_DEBUG("At -1: %c %d (%p %p)",v[-1],isalnum(v[-1]),v,ov);
            v=strstr(v,";");
        }
        else {
            v+=10;
            r=onion_low_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->connection.listen_point->server->sessions, r);
        }
    } while(!session);

    req->session_id=r;
    req->session=session;
    ONION_DEBUG("Session ID, from cookie, is %s",req->session_id);
}
Ejemplo n.º 13
0
/**
 * @short Generates a new response object
 * @memberof onion_response_t
 *
 * This response is generated from a request, and gets from there the writer and writer data.
 *
 * Also fills some important data, as server Id, License type, and the default content type.
 *
 * Default content type is HTML, as normally this is what is needed. This is nontheless just
 * the default, and can be changed to any other with a call to:
 *
 *   onion_response_set_header(res, "Content-Type", my_type);
 *
 * The response object must be freed with onion_response_free, which also returns the keep alive
 * status.
 *
 * onion_response objects are passed by onion internally to process the request, and should not be
 * created by user normally. Nontheless the option exist.
 *
 * @returns An onion_response object for that request.
 */
onion_response *onion_response_new(onion_request *req){
	onion_response *res=onion_low_malloc(sizeof(onion_response));

	res->request=req;
	res->headers=onion_dict_new();
	res->code=200; // The most normal code, so no need to overwrite it in other codes.
	res->flags=0;
	res->sent_bytes_total=res->length=res->sent_bytes=0;
	res->buffer_pos=0;

#ifndef DONT_USE_DATE_HEADER
	{
		time_t t;
		struct tm *tmp;

		t = time(NULL);

		// onion_response_last_date_header is set to t later. It should be more or less atomic.
		// If not no big deal, as we will just use slightly more CPU on those "ephemeral" moments.

		if (t!=onion_response_last_time){
			ONION_DEBUG("Recalculating date header");
			char current_datetime[200];

			tmp = localtime(&t);
			if (tmp == NULL) {
					perror("localtime");
					exit(EXIT_FAILURE);
			}

			if (strftime(current_datetime, sizeof(current_datetime), "%a, %d %b %Y %H:%M:%S %Z", tmp) == 0) {
					fprintf(stderr, "strftime returned 0");
					exit(EXIT_FAILURE);
			}
			// Risky, not using mutex...
#ifdef HAVE_PTHREAD
			pthread_rwlock_wrlock(&onion_response_date_lock);
#endif
			onion_response_last_time=t;
			if (onion_response_last_date_header)
				onion_low_free(onion_response_last_date_header);
			onion_response_last_date_header=onion_low_strdup(current_datetime);
#ifdef HAVE_PTHREAD
			pthread_rwlock_unlock(&onion_response_date_lock);
#endif
		}
	}
#ifdef HAVE_PTHREAD
	pthread_rwlock_rdlock(&onion_response_date_lock);
#endif
	assert(onion_response_last_date_header);
	onion_dict_add(res->headers, "Date", onion_response_last_date_header, OD_DUP_VALUE);
#ifdef HAVE_PTHREAD
	pthread_rwlock_unlock(&onion_response_date_lock);
#endif
#endif // USE_DATE_HEADER
	// Sorry for the advertisment.
	onion_dict_add(res->headers, "Server", "libonion v" ONION_VERSION " - coralbits.com", 0);
	onion_dict_add(res->headers, "Content-Type", "text/html", 0); // Maybe not the best guess, but really useful.
	//time_t t=time(NULL);
	//onion_dict_add(res->headers, "Date", asctime(localtime(&t)), OD_DUP_VALUE);

	return res;
}
Ejemplo n.º 14
0
/**
 * @short User to which drop priviledges when listening
 * @memberof onion_t
 * 
 * Drops the priviledges of current program as soon as it starts listening.
 * 
 * This is the easiest way to allow low ports and other sensitive info to be used,
 * but the proper way should be use capabilities and/or SELinux.
 */
void onion_set_user(onion *server, const char *username){
	server->username=onion_low_strdup(username);
}