/** * @short Marks the poller to stop ASAP * @memberof onion_poller_t * @ingroup poller */ void onion_poller_stop(onion_poller *p){ ONION_DEBUG("Stopping poller"); #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->stop=1; pthread_mutex_unlock(&p->mutex); #else p->stop=1; #endif char data[8]={0,0,0,0, 0,0,0,1}; int __attribute__((unused)) r=read(p->eventfd, data, 8); // Flush eventfd data, discard data #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); int n=p->npollers; pthread_mutex_unlock(&p->mutex); if (n>0){ int w=write(p->eventfd,data,8); // Tell another thread to exit if (w<0){ ONION_ERROR("Error signaling poller to stop!"); } } else ONION_DEBUG("Poller stopped"); #endif }
/** * @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); }
/// Gets the output data int oterm_out(process *o, onion_request *req, onion_response *res){ pthread_mutex_lock(&o->mutex); if (onion_request_get_query(req, "initial")){ if (o->buffer[BUFFER_SIZE-1]!=0){ // If 0 then never wrote on it. So if not, write from pos to end too, first. onion_response_write(res, &o->buffer[o->buffer_pos], BUFFER_SIZE-o->buffer_pos); } onion_response_write(res, o->buffer, o->buffer_pos); onion_response_printf(res, "\033]oterm;%d;", o->buffer_pos); onion_response_printf(res, "\033]url;https://localhost:8080/uuid/%s/;", o->uuid); pthread_mutex_unlock(&o->mutex); return OCS_PROCESSED; } int16_t p=atoi(onion_request_get_queryd(req, "pos", "0")); //o->buffer_pos; ONION_DEBUG("Wait for data at %d", p); while(p==o->buffer_pos) // We need it to be diferent, if not does not make sense to wake up pthread_cond_wait(&o->dataReady, &o->mutex); ONION_DEBUG("Data ready at %d (waiting from %d)", o->buffer_pos, p); if (o->buffer_pos<p){ onion_response_write(res, &o->buffer[p], BUFFER_SIZE-p); p=0; } onion_response_write(res, &o->buffer[p], o->buffer_pos-p); onion_response_printf(res, "\033]oterm;%d;", o->buffer_pos); pthread_mutex_unlock(&o->mutex); return OCS_PROCESSED; }
/** * @short Removes the allocated data * @memberof onion_t */ void onion_free(onion *onion){ ONION_DEBUG("Onion free"); onion_listen_stop(onion); if (onion->poller) onion_poller_free(onion->poller); if (onion->username) free(onion->username); if (onion->listen_points){ onion_listen_point **p=onion->listen_points; while(*p!=NULL){ ONION_DEBUG("Free %p listen_point", *p); onion_listen_point_free(*p++); } free(onion->listen_points); } if (onion->root_handler) onion_handler_free(onion->root_handler); if (onion->internal_error_handler) onion_handler_free(onion->internal_error_handler); onion_mime_set(NULL); if (onion->sessions) onion_sessions_free(onion->sessions); #ifdef HAVE_PTHREADS if (onion->threads) free(onion->threads); #endif free(onion); }
onion_connection_status oterm_uuid(void *data, onion_request *req, onion_response *res){ const char *path=onion_request_get_path(req); ONION_DEBUG("Ask path %s (%p)", path, data); // split id / function int l=strlen(path)+1; char *id=alloca(l); char *function=NULL; int i; memcpy(id,path,l); int func_pos=0; for (i=0;i<l;i++){ if (id[i]=='/'){ if (!function && id[i+1]!='\0') function=id+i+1; id[i]=0; func_pos=i; break; } } ONION_DEBUG("Id %s, function %s", id, function); process *term=oterm_get_process_by_uuid(data, id); if (!term) return OCS_INTERNAL_ERROR; if (!function) return onion_shortcut_internal_redirect("static/oterm.html", req, res); // do it onion_request_advance_path(req, func_pos); return oterm_process(data, term, function, req, res); }
onion_connection_status index_html_template(onion_dict *context){ char *lang = (char*)malloc (3*sizeof(char)); strcpy(lang,"en\0"); ONION_DEBUG("Add to dict"); if (context) onion_dict_add(context, "LANG", lang, OD_FREE_VALUE); ONION_DEBUG("Free dict"); if (context) onion_dict_free(context); // if you remove this line, it works correctly return OCS_PROCESSED; }
/** * @short Checks a specific set of major.minor.patch and returns if the current using onion is ABI compatible. * * Onion uses SEMVER (http://semver.org/), and with this simple function its * possible to check if your compiled code is compatible with the onion * version. * * It also allows to in the rare case that there is some really bad version of * onion to warn the users. * * Normally users need just to add a onion_version_is_compatible() check, and * if not compatible abort: * * if (!onion_version_is_compatible()) abort(); * */ bool onion_version_is_compatible3(int major, int minor, int patch){ if (major != ONION_VERSION_MAJOR){ ONION_DEBUG("Onion major version (%d) is not compatible with program's (%d). Should match.", ONION_VERSION_MAJOR, major); return false; } if (minor > ONION_VERSION_MINOR){ ONION_DEBUG("Onion minor version (%d) is not compatible with program's (%d). Program's has to be equal or greater.", ONION_VERSION_MINOR, minor); return false; } return true; }
/** * @short Closes the https connection * @memberof onion_https_t * * It frees local data and closes the socket. * * @param req to close. */ static void onion_https_close(onion_request *req){ ONION_DEBUG("Close HTTPS connection"); gnutls_session_t session=(gnutls_session_t)req->connection.user_data; if (session){ ONION_DEBUG("Free session %p", session); gnutls_bye (session, GNUTLS_SHUT_WR); gnutls_deinit(session); } onion_listen_point_request_close_socket(req); }
/** * @short Removes the allocated data * @memberof onion_t */ void onion_free(onion *onion){ ONION_DEBUG("Onion free"); if (onion->flags&O_LISTENING) onion_listen_stop(onion); if (onion->poller) onion_poller_free(onion->poller); if (onion->username) onion_low_free(onion->username); if (onion->listen_points){ onion_listen_point **p=onion->listen_points; while(*p!=NULL){ ONION_DEBUG("Free %p listen_point", *p); onion_listen_point_free(*p++); } onion_low_free(onion->listen_points); } if (onion->root_handler) onion_handler_free(onion->root_handler); if (onion->internal_error_handler) onion_handler_free(onion->internal_error_handler); onion_mime_set(NULL); if (onion->sessions) onion_sessions_free(onion->sessions); { #ifdef HAVE_PTHREADS pthread_mutex_lock (&onion->mutex); #endif void* data = onion->client_data; onion->client_data = NULL; if (data && onion->client_data_free) onion->client_data_free (data); onion->client_data_free = NULL; #ifdef HAVE_PTHREADS pthread_mutex_unlock (&onion->mutex); pthread_mutex_destroy (&onion->mutex); #endif }; #ifdef HAVE_PTHREADS if (onion->threads) onion_low_free(onion->threads); #endif if (!(onion->flags&O_NO_SIGTERM)){ signal(SIGINT,SIG_DFL); signal(SIGTERM,SIG_DFL); } last_onion=NULL; onion_low_free(onion); }
/** * @short Default implementation that initializes the request from a socket * @memberof onion_listen_point_t * * Accepts the connection and initializes it. * * @param req Request to initialize * @returns <0 if error opening the connection */ int onion_listen_point_request_init_from_socket(onion_request *req){ onion_listen_point *op=req->connection.listen_point; int listenfd=op->listenfd; if (listenfd<0){ ONION_DEBUG("Listen point closed, no request allowed"); return -1; } /// Follows default socket implementation. If your protocol is socket based, just use it. req->connection.cli_len = sizeof(req->connection.cli_addr); int set_cloexec=SOCK_CLOEXEC == 0; int clientfd=accept4(listenfd, (struct sockaddr *) &req->connection.cli_addr, &req->connection.cli_len, SOCK_CLOEXEC); if (clientfd<0){ ONION_DEBUG("Second try? errno %d, clientfd %d", errno, clientfd); if (errno==ENOSYS){ clientfd=accept(listenfd, (struct sockaddr *) &req->connection.cli_addr, &req->connection.cli_len); } ONION_DEBUG("How was it? errno %d, clientfd %d", errno, clientfd); if (clientfd<0){ ONION_ERROR("Error accepting connection: %s",strerror(errno),errno); onion_listen_point_request_close_socket(req); return -1; } } req->connection.fd=clientfd; /// Thanks to Andrew Victor for pointing that without this client may block HTTPS connection. It could lead to DoS if occupies all connections. { struct timeval t; t.tv_sec = op->server->timeout / 1000; t.tv_usec = ( op->server->timeout % 1000 ) * 1000; setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval)); } if(set_cloexec) { // Good compiler know how to cut this out int flags=fcntl(clientfd, F_GETFD); if (flags==-1){ ONION_ERROR("Retrieving flags from connection"); } flags|=FD_CLOEXEC; if (fcntl(clientfd, F_SETFD, flags)==-1){ ONION_ERROR("Setting FD_CLOEXEC to connection"); } } ONION_DEBUG0("New connection, socket %d",clientfd); return 0; }
void *t08_thread_write(onion_dict *d){ int n=0; while (n!=N_READERS){ int i; n=0; //ONION_DEBUG("Lock read"); onion_dict_lock_read(d); //ONION_DEBUG("Got read lock"); for (i=0;i<N_READERS;i++){ char tmp[16]; snprintf(tmp,16,"%d",i+1); const char *r=onion_dict_get(d,tmp); if (r) n++; } //ONION_DEBUG("Unlock"); onion_dict_unlock(d); //ONION_DEBUG("Lock write"); onion_dict_lock_write(d); //ONION_DEBUG("Got write lock"); onion_dict_add(d, "test", "test", OD_DUP_ALL|OD_REPLACE); //ONION_DEBUG("Unlock"); onion_dict_unlock(d); ONION_DEBUG("Found %d answers, should be %d.", n, N_READERS); usleep(200); } onion_dict_free(d); return (char*)1; }
void POST_a_lot(void) { sleep(1); onion_block *tosend = onion_block_new(); onion_block_add_str(tosend, "POST /configuration HTTP/1.1\nHost: example.com\nContent-Type: x-application/garbage\nContent-Length: 1000000\n\n"); { int i; onion_block *bl = onion_block_new(); for (i = 0; i < 1000; i++) { onion_block_add_char(bl, rand() & 255); } for (i = 0; i < 1000; i++) { onion_block_add_block(tosend, bl); } onion_block_free(bl); } onion_block *bl = connect_and_send("127.0.0.1", "8080", tosend, 1024 * 64); onion_block_free(tosend); ONION_DEBUG("%p", strstr(onion_block_data(bl), "\n1000000\n")); FAIL_IF_NOT(strstr(onion_block_data(bl), "\n1000000\n") != NULL); onion_block_free(bl); }
char *t08_thread_read(onion_dict *d){ char done=0; char *ret=NULL; while (!done){ //ONION_DEBUG("Lock read"); onion_dict_lock_write(d); //ONION_DEBUG("Got read lock"); const char *test=onion_dict_get(d,"test"); if (test){ //ONION_DEBUG("Unlock"); //onion_dict_lock_write(d); //ONION_DEBUG("Got write lock"); char tmp[16]; snprintf(tmp,16,"%d",onion_dict_count(d)); onion_dict_remove(d,"test"); onion_dict_add(d,tmp,"test",OD_DUP_ALL); ONION_DEBUG("Write answer %d", onion_dict_count(d)); done=1; //ONION_DEBUG("Unlock"); onion_dict_unlock(d); ret=(char*)1; break; } //ONION_DEBUG("Unlock"); onion_dict_unlock(d); usleep(200); } //ONION_DEBUG("dict free"); onion_dict_free(d); return ret; }
void t05_post_content_json(){ INIT_LOCAL(); onion *server=onion_new(0); onion_listen_point *lp=onion_buffer_listen_point_new(); json_response post_json = { 0 }; onion_add_listen_point(server,NULL,NULL,lp); onion_set_root_handler(server, onion_handler_new((void*)&post_json_check,&post_json,NULL)); onion_request *req=onion_request_new(lp); #define POST_HEADER "POST / HTTP/1.1\nContent-Type: application/json\nContent-Length: %d\n\n" char tmp[1024]; int json_length=sizeof(JSON_EXAMPLE); ONION_DEBUG("Post size is about %d",json_length); snprintf(tmp, sizeof(tmp), POST_HEADER, json_length); // ONION_DEBUG("%s",tmp); onion_request_write(req,tmp,strlen(tmp)); onion_request_write(req,JSON_EXAMPLE,json_length); // ONION_DEBUG("%s",JSON_EXAMPLE); FAIL_IF_NOT_EQUAL_INT(post_json.processed, 2); onion_request_free(req); onion_free(server); END_LOCAL(); }
/** * @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=strdup(hostname); if (port) protocol->port=strdup(port); if (server->listen_points){ onion_listen_point **p=server->listen_points; int protcount=0; while (*p++) protcount++; server->listen_points=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=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; }
/** * @short Returns a poller object that helps polling on sockets and files * @memberof onion_poller_t * * This poller is implemented through epoll, but other implementations are possible * */ onion_poller *onion_poller_new(int n){ onion_poller *p=onion_low_malloc(sizeof(onion_poller)); p->fd=epoll_create1(EPOLL_CLOEXEC); if (p->fd < 0){ ONION_ERROR("Error creating the poller. %s", strerror(errno)); onion_low_free(p); return NULL; } p->eventfd=eventfd(0,EFD_CLOEXEC | EFD_NONBLOCK); #if EFD_CLOEXEC == 0 fcntl(p->eventfd,F_SETFD,FD_CLOEXEC); #endif p->head=NULL; p->n=0; p->stop=0; #ifdef HAVE_PTHREADS ONION_DEBUG("Init thread stuff for poll. Eventfd at %d", p->eventfd); p->npollers=0; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&p->mutex, &attr); pthread_mutexattr_destroy(&attr); #endif onion_poller_slot *ev=onion_poller_slot_new(p->eventfd,onion_poller_stop_helper,p); onion_poller_add(p,ev); return p; }
/** * @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_AVALIABLE; o->nthreads=8; if (o->flags&O_THREADED) o->flags|=O_THREADS_ENABLED; #endif return o; }
/// @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"); }
/** * @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; }
/** * @short Simple put on webdav is just move a file from tmp to the final destination (or copy if could not move). * */ onion_connection_status onion_webdav_put(const char *filename, onion_webdav *wd, onion_request *req, onion_response *res){ ONION_DEBUG("Webdav puts %s", filename); const char *tmpfile=onion_block_data(onion_request_get_data(req)); int ok=onion_shortcut_rename(tmpfile, filename); if (ok==0){ ONION_DEBUG("Created %s succesfully", filename); return onion_shortcut_response("201 Created", 201, req, res); } else{ ONION_ERROR("Could not rename %s to %s (%s)", tmpfile, filename, strerror(errno)); return onion_shortcut_response("Could not create resource", HTTP_FORBIDDEN, req, res); } }
/// 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; }
/// 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; }
void do_petition_set_threaded(float wait_s, float wait_c, int nrequests, char close, int nthreads){ ONION_DEBUG("Using %d threads, %d petitions per thread",nthreads,nrequests); processed=0; params_t params; params.wait_s=wait_s; params.wait_t=wait_c; params.n_requests=nrequests; params.close_at_n=close; pthread_t *thread=malloc(sizeof(pthread_t*)*nthreads); pthread_t listen_thread; int i; pthread_create(&listen_thread, NULL, (void*)do_listen, NULL); for (i=0;i<nthreads;i++){ pthread_create(&thread[i], NULL, (void*)do_requests, ¶ms); } for (i=0;i<nthreads;i++){ pthread_join(thread[i], NULL); } free(thread); if (close==2){ usleep(wait_s*1000000); onion_listen_stop(o); } pthread_join(listen_thread, NULL); FAIL_IF_NOT_EQUAL_INT(params.n_requests * nthreads, processed); }
/** * @short Initializes a connection on a request * @memberof onion_https_t * * Do the accept of the request, and the SSL handshake. * * @param req The request * @returns <0 in case of error. */ static int onion_https_request_init(onion_request *req){ onion_listen_point_request_init_from_socket(req); onion_https *https=(onion_https*)req->connection.listen_point->user_data; ONION_DEBUG("Accept new request, fd %d",req->connection.fd); gnutls_session_t session; gnutls_init (&session, GNUTLS_SERVER); gnutls_priority_set (session, https->priority_cache); gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, https->x509_cred); /* Set maximum compatibility mode. This is only suggested on public webservers * that need to trade security for compatibility */ gnutls_session_enable_compatibility_mode (session); gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t)(long) req->connection.fd); int ret; do{ ret = gnutls_handshake (session); }while (ret < 0 && gnutls_error_is_fatal (ret) == 0); if (ret<0){ // could not handshake. assume an error. ONION_ERROR("Handshake has failed (%s)", gnutls_strerror (ret)); gnutls_bye (session, GNUTLS_SHUT_WR); gnutls_deinit(session); onion_listen_point_request_close_socket(req); return -1; } req->connection.user_data=(void*)session; return 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; }
/** * @short Internal function to accept one connection. * * It performs timeout setting, CLOEXEC as needed by accept4/accept, and get client info. * * @param o onion object. * @param cli_info char pointer to where to store the client info. * @param info_len size available at cli_info. * * @returns new connection socket file descriptor */ static int onion_accept(onion *o, struct sockaddr_storage *cli_addr, socklen_t *clilen){ *clilen = sizeof(*cli_addr); int clientfd=accept4(o->listenfd, (struct sockaddr *) cli_addr, clilen, SOCK_CLOEXEC); if (clientfd<0){ ONION_ERROR("Error accepting connection: %s",strerror(errno)); return -1; } /// Thanks to Andrew Victor for pointing that without this client may block HTTPS connection. It could lead to DoS if occupies all connections. { struct timeval t; t.tv_sec = o->timeout / 1000; t.tv_usec = ( o->timeout % 1000 ) * 1000; setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval)); } if(SOCK_CLOEXEC == 0) { // Good compiler know how to cut this out int flags=fcntl(clientfd, F_GETFD); if (flags==-1){ ONION_ERROR("Retrieving flags from connection"); } flags|=FD_CLOEXEC; if (fcntl(clientfd, F_SETFD, flags)==-1){ ONION_ERROR("Setting FD_CLOEXEC to connection"); } } ONION_DEBUG("Accepted connection"); return clientfd; }
/** * @short Write some response data. * @memberof onion_response_t * * This is the main write data function. If the headers have not been sent yet, they are now. * * It's internally used also by the write0 and printf versions. * * Also it does some buffering, so data is not sent as written by code, but only in chunks. * These chunks are when the response is finished, or when the internal buffer is full. This * helps performance, and eases the programming on the user side. * * If length is 0, forces the write of pending data. * * @returns The bytes written, normally just length. On error returns OCS_CLOSE_CONNECTION. */ ssize_t onion_response_write(onion_response *res, const char *data, size_t length){ if (res->flags&OR_SKIP_CONTENT){ if (!(res->flags&OR_HEADER_SENT)){ // Automatic header write onion_response_write_headers(res); } ONION_DEBUG("Skipping content as we are in HEAD mode"); return OCS_CLOSE_CONNECTION; } if (length==0){ onion_response_flush(res); return 0; } //ONION_DEBUG0("Write %d bytes [%d total] (%p)", length, res->sent_bytes, res); int l=length; int w=0; while (res->buffer_pos+l>sizeof(res->buffer)){ int wb=sizeof(res->buffer)-res->buffer_pos; memcpy(&res->buffer[res->buffer_pos], data, wb); res->buffer_pos=sizeof(res->buffer); if (onion_response_flush(res)<0) return w; l-=wb; data+=wb; w+=wb; } memcpy(&res->buffer[res->buffer_pos], data, l); res->buffer_pos+=l; w+=l; return w; }
void t05_server_timeout_threaded_ssl(){ INIT_LOCAL(); CURL *curl=prepare_curl("https://localhost:8081"); ONION_DEBUG("%s",__FUNCTION__); o=onion_new(O_THREADED | O_DETACH_LISTEN); onion_set_root_handler(o,onion_handler_new((void*)process_request,NULL,NULL)); FAIL_IF_NOT_EQUAL_INT(onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, "mycert.pem", "mycert.pem"),0); onion_set_port(o,"8081"); onion_set_timeout(o,3000); onion_listen(o); sleep(1); int fd=connect_to("localhost","8081"); sleep(4); // Should have closed the connection int w=write(fd,"GET /\n\n",7); FAIL_IF_NOT_EQUAL_INT(w,7); char data[256]; FAIL_IF(read(fd, data,sizeof(data))>0); close(fd); FAIL_IF_NOT(curl_get(curl, "https://localhost:8081")); onion_free(o); curl_easy_cleanup(curl); END_LOCAL(); }
int onion_assets_file_free(onion_assets_file *f){ fseek(f->file, 0, SEEK_SET); 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, f->file); if (wlength!=length){ ONION_ERROR("Could not write all data. Aborting"); abort(); } wlength=fwrite("\n",1, 1, f->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(f->file, "#endif\n"); assert(fclose(f->file)==0); free(f); return 0; }
onion_connection_status upload_file(upload_file_data *data, onion_request *req, onion_response *res){ if (onion_request_get_flags(req)&OR_POST){ const char *name=onion_request_get_post(req,"file"); const char *filename=onion_request_get_file(req,"file"); if (name && filename){ char finalname[1024]; snprintf(finalname,sizeof(finalname),"%s/%s",data->abspath,name); ONION_DEBUG("Copying from %s to %s",filename,finalname); unlink(finalname); // Just try to unlink it, if fail, sure its because it does not exist. int src=open(filename,O_RDONLY); int dst=open(finalname, O_WRONLY|O_CREAT, 0666); if (!src || !dst){ ONION_ERROR("Could not open src or dst file (%d %d)",src,dst); return OCS_INTERNAL_ERROR; } ssize_t r,w; char buffer[1024*4]; while ( (r=read(src,buffer,sizeof(buffer))) > 0){ w=write(dst,buffer,r); if (w!=r){ ONION_ERROR("Error writing file"); break; } } close(src); close(dst); } } return 0; // I just ignore the request, but process over the FILE data }