/*
* SSLsplit - transparent SSL / TLS interception
* Copyright ( c ) 2009 - 2016 , Daniel Roethlisberger < daniel @ roe . ch >
* All rights reserved .
* http : //www.roe.ch/SSLsplit
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "proxy.h"
# include "privsep.h"
# include "pxythrmgr.h"
# include "pxyconn.h"
# include "cachemgr.h"
# include "opts.h"
# include "log.h"
# include "attrib.h"
# include "sys.h"
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <signal.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <errno.h>
# include <event2/event.h>
# include <event2/listener.h>
# include <event2/bufferevent.h>
# include <event2/bufferevent_ssl.h>
# include <event2/buffer.h>
# include <event2/thread.h>
/*
* Proxy engine , built around libevent 2. x .
*/
static int signals [ ] = { SIGQUIT , SIGHUP , SIGINT , SIGPIPE , SIGUSR1 } ;
struct proxy_ctx {
pxy_thrmgr_ctx_t * thrmgr ;
struct event_base * evbase ;
struct event * sev [ sizeof ( signals ) / sizeof ( int ) ] ;
struct event * gcev ;
struct proxy_listener_ctx * lctx ;
opts_t * opts ;
} ;
static proxy_listener_ctx_t *
proxy_listener_ctx_new ( pxy_thrmgr_ctx_t * thrmgr , proxyspec_t * spec ,
opts_t * opts ) MALLOC ;
static proxy_listener_ctx_t *
proxy_listener_ctx_new ( pxy_thrmgr_ctx_t * thrmgr , proxyspec_t * spec ,
opts_t * opts )
{
proxy_listener_ctx_t * ctx = malloc ( sizeof ( proxy_listener_ctx_t ) ) ;
if ( ! ctx )
return NULL ;
memset ( ctx , 0 , sizeof ( proxy_listener_ctx_t ) ) ;
ctx - > thrmgr = thrmgr ;
ctx - > spec = spec ;
ctx - > opts = opts ;
return ctx ;
}
static void
proxy_listener_ctx_free ( proxy_listener_ctx_t * ctx ) NONNULL ( 1 ) ;
static void
proxy_listener_ctx_free ( proxy_listener_ctx_t * ctx )
{
if ( ctx - > evcl ) {
evconnlistener_free ( ctx - > evcl ) ;
}
if ( ctx - > next ) {
proxy_listener_ctx_free ( ctx - > next ) ;
}
free ( ctx ) ;
}
/*
* Callback for error events on the socket listener bufferevent .
*/
static void
proxy_listener_errorcb ( struct evconnlistener * listener , UNUSED void * ctx )
{
proxy_conn_meta_ctx_t * mctx = ctx ;
int fd2 = - 1 ;
if ( mctx ) {
fd2 = mctx - > fd2 ;
}
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " >############################# proxy_listener_errorcb: ERROR, fd2=%d \n " , fd2 ) ;
struct event_base * evbase = evconnlistener_get_base ( listener ) ;
int err = EVUTIL_SOCKET_ERROR ( ) ;
log_err_printf ( " Error %d on listener: %s \n " , err ,
evutil_socket_error_to_string ( err ) ) ;
event_base_loopbreak ( evbase ) ;
}
/*
* Callback for accept events on the socket listener bufferevent .
*/
static void
proxy_listener_acceptcb_e2 ( UNUSED struct evconnlistener * listener ,
evutil_socket_t fd ,
struct sockaddr * peeraddr , int peeraddrlen ,
void * arg )
{
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2() ENTER \n " ) ;
proxy_conn_meta_ctx_t * mctx = arg ;
if ( ! mctx ) {
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2: NULL mctx <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< GONE \n " ) ;
return ;
} else {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2: ENTER 1 fd=%d, fd2=%d \n " , mctx - > fd , mctx - > fd2 ) ;
}
pthread_mutex_t * cmutex = & mctx - > mutex ;
// @todo Enabling this lock sometimes causes ^C to fail?: Cannot quit the program on the command line using ^C
// int err = my_pthread_mutex_lock(cmutex);
int err = my_pthread_mutex_lock ( mctx ) ;
if ( ! mctx ) {
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2: NULL mctx <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< GONE after lock \n " ) ;
goto leave ;
} else {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2: ENTER 2 fd=%d, fd2=%d \n " , mctx - > fd , mctx - > fd2 ) ;
}
mctx - > access_time = time ( NULL ) ;
pxy_conn_ctx_t * parent_ctx = mctx - > parent_ctx ;
evutil_socket_t pfd = - 1 ;
if ( parent_ctx ) {
pfd = parent_ctx - > fd ;
}
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2() lock err=%d \n " , err ) ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): child fd=%d, pfd=%d \n " , fd , pfd ) ;
char * host , * port ;
if ( sys_sockaddr_str ( peeraddr , peeraddrlen , & host , & port ) ! = 0 ) {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): PEER failed \n " ) ;
} else {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): PEER [%s]:%s <<<<< child fd=%d, pfd=%d \n " , host , port , fd , pfd ) ;
free ( host ) ;
free ( port ) ;
}
// @todo Check the return value of pxy_conn_setup_e2()
pxy_conn_setup_e2 ( fd , mctx ) ;
leave :
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): EXIT \n " ) ;
// my_pthread_mutex_unlock(cmutex);
my_pthread_mutex_unlock ( mctx ) ;
}
static proxy_conn_meta_ctx_t *
pxy_conn_meta_ctx_new ( )
{
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>................... pxy_conn_meta_ctx_new(): ENTER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \n " ) ;
proxy_conn_meta_ctx_t * ctx = malloc ( sizeof ( proxy_conn_meta_ctx_t ) ) ;
if ( ! ctx )
return NULL ;
memset ( ctx , 0 , sizeof ( proxy_conn_meta_ctx_t ) ) ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>................... pxy_conn_meta_ctx_new: sizeof(proxy_conn_meta_ctx_t)=%lu <<<<<< \n " , sizeof ( proxy_conn_meta_ctx_t ) ) ;
ctx - > uuid = malloc ( sizeof ( uuid_t ) ) ;
// @todo Set this switch at compile time
# ifdef OPENBSD
uuid_create ( ctx - > uuid , NULL ) ;
char * uuid_str ;
uuid_to_string ( ctx - > uuid , & uuid_str , NULL ) ;
if ( uuid_str ) {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>................... pxy_conn_meta_ctx_new(): uuid = %s <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \n " , uuid_str ) ;
free ( uuid_str ) ;
}
# else
uuid_generate ( ctx - > uuid ) ;
# endif /* OPENBSD */
ctx - > access_time = time ( NULL ) ;
ctx - > next = NULL ;
pthread_mutex_init ( & ctx - > mutex , NULL ) ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>>................... pxy_conn_meta_ctx_new(): EXIT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \n " ) ;
return ctx ;
}
static void
proxy_listener_acceptcb ( UNUSED struct evconnlistener * listener ,
evutil_socket_t fd ,
struct sockaddr * peeraddr , int peeraddrlen ,
void * arg )
{
proxy_listener_ctx_t * lctx = arg ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< LOCK \n " ) ;
// proxy_conn_meta_ctx_t *expired = NULL;
// pxy_thrmgr_get_expired_conns(lctx->thrmgr, &expired);
//
// time_t now = time(NULL);
//
// while (expired) {
// proxy_conn_meta_ctx_t *next = expired->delete;
//
//// int err = my_pthread_mutex_lock(&expired->mutex);
// int err = my_pthread_mutex_lock(expired);
// if (err != 0) {
// // @attention The expired mctx may be gone by now, hence this check
// // @todo What to do to prevent this case?
// log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: CANNOT LOCK expired conn, err=%d\n", err);
// } else {
// log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: DELETE thr=%d, fd=%d, fd2=%d, time=%lld <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TIMED OUT\n",
// expired->thridx, expired->fd, expired->fd2, (long int) now - expired->access_time);
// pxy_all_conn_free(expired);
// // XXX: Releasing the lock causes callback functions to continue with a deleted mctx?
// //pthread_mutex_unlock(&expired->mutex);
// pxy_conn_meta_ctx_free(expired);
// }
//
// expired = next;
// }
proxy_conn_meta_ctx_t * mctx = pxy_conn_meta_ctx_new ( ) ;
pthread_mutex_t * cmutex = & mctx - > mutex ;
// my_pthread_mutex_lock(cmutex);
// my_pthread_mutex_lock(mctx);
char * host , * port ;
if ( sys_sockaddr_str ( peeraddr , peeraddrlen , & host , & port ) ! = 0 ) {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: PEER failed \n " ) ;
} else {
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: PEER [%s]:%s <<<<< fd=%d \n " , host , port , fd ) ;
free ( host ) ;
free ( port ) ;
}
mctx - > lctx = lctx ;
mctx - > fd = fd ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: SETTING UP E2, fd=%d, lctx->clisock=%d \n " , fd , lctx - > clisock ) ;
evutil_socket_t fd2 ;
struct evconnlistener * evcl2 ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: FIRST E2 setup <<<<<< \n " ) ;
if ( ( fd2 = privsep_client_opensock_e2 ( lctx - > clisock , lctx - > spec ) ) = = - 1 ) {
log_err_printf ( " Error opening socket: %s (%i) \n " , strerror ( errno ) , errno ) ;
return ;
}
mctx - > fd2 = fd2 ;
pxy_conn_ctx_t * parent_ctx = pxy_conn_setup ( fd , peeraddr , peeraddrlen , mctx ) ;
my_pthread_mutex_lock ( mctx ) ;
mctx - > parent_ctx = parent_ctx ;
// evutil_socket_t fd2;
// struct evconnlistener *evcl2;
//
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: FIRST E2 setup <<<<<<\n");
// if ((fd2 = privsep_client_opensock_e2(lctx->clisock, lctx->spec)) == -1) {
// log_err_printf("Error opening socket: %s (%i)\n", strerror(errno), errno);
// return;
// }
// mctx->fd2 = fd2;
// evcl2 = evconnlistener_new(evconnlistener_get_base(lctx->evcl), proxy_listener_acceptcb_e2, mctx, LEV_OPT_CLOSE_ON_FREE, 1024, fd2);
evcl2 = evconnlistener_new ( mctx - > thr - > evbase , proxy_listener_acceptcb_e2 , mctx , LEV_OPT_CLOSE_ON_FREE , 1024 , fd2 ) ;
if ( ! evcl2 ) {
log_err_printf ( " Error creating evconnlistener e2: %s, fd=%d, fd2=%d <<<<<< \n " , strerror ( errno ) , fd , fd2 ) ;
// @attention Do not call proxy_listener_ctx_free() on evcl2, evcl2 does not have any next listener
// @todo Create a new struct for evcl2 and related functions
//proxy_listener_ctx_free(evcl2);
evconnlistener_free ( evcl2 ) ;
evutil_closesocket ( fd2 ) ;
// my_pthread_mutex_unlock(cmutex);
my_pthread_mutex_unlock ( mctx ) ;
return ;
}
mctx - > evcl2 = evcl2 ;
evconnlistener_set_error_cb ( evcl2 , proxy_listener_errorcb ) ;
log_dbg_level_printf ( LOG_DBG_MODE_FINER , " >>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: FINISHED SETTING UP E2 SUCCESS, parent fd=%d, NEW fd2=%d \n " , fd , fd2 ) ;
// my_pthread_mutex_unlock(cmutex);
my_pthread_mutex_unlock ( mctx ) ;
}
/*
* Dump a description of an evbase to debugging code .
*/
static void
proxy_debug_base ( const struct event_base * ev_base )
{
log_dbg_printf ( " Using libevent backend '%s' \n " ,
event_base_get_method ( ev_base ) ) ;
enum event_method_feature f ;
f = event_base_get_features ( ev_base ) ;
log_dbg_printf ( " Event base supports: edge %s, O(1) %s, anyfd %s \n " ,
( ( f & EV_FEATURE_ET ) ? " yes " : " no " ) ,
( ( f & EV_FEATURE_O1 ) ? " yes " : " no " ) ,
( ( f & EV_FEATURE_FDS ) ? " yes " : " no " ) ) ;
}
/*
* Set up the listener for a single proxyspec and add it to evbase .
* Returns the proxy_listener_ctx_t pointer if successful , NULL otherwise .
*/
static proxy_listener_ctx_t *
proxy_listener_setup ( struct event_base * evbase , pxy_thrmgr_ctx_t * thrmgr ,
proxyspec_t * spec , opts_t * opts , int clisock )
{
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " >>>>> proxy_listener_setup \n " ) ;
proxy_listener_ctx_t * lctx ;
int fd ;
if ( ( fd = privsep_client_opensock ( clisock , spec ) ) = = - 1 ) {
log_err_printf ( " Error opening socket: %s (%i) \n " ,
strerror ( errno ) , errno ) ;
return NULL ;
}
lctx = proxy_listener_ctx_new ( thrmgr , spec , opts ) ;
if ( ! lctx ) {
log_err_printf ( " Error creating listener context \n " ) ;
evutil_closesocket ( fd ) ;
return NULL ;
}
lctx - > clisock = clisock ;
// @todo Should we enable threadsafe event structs?
lctx - > evcl = evconnlistener_new ( evbase , proxy_listener_acceptcb ,
lctx , LEV_OPT_CLOSE_ON_FREE , 1024 , fd ) ;
// lctx, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_THREADSAFE, 1024, fd);
if ( ! lctx - > evcl ) {
log_err_printf ( " Error creating evconnlistener: %s \n " ,
strerror ( errno ) ) ;
proxy_listener_ctx_free ( lctx ) ;
evutil_closesocket ( fd ) ;
return NULL ;
}
evconnlistener_set_error_cb ( lctx - > evcl , proxy_listener_errorcb ) ;
return lctx ;
}
/*
* Signal handler for SIGQUIT , SIGINT , SIGHUP , SIGPIPE and SIGUSR1 .
*/
static void
proxy_signal_cb ( evutil_socket_t fd , UNUSED short what , void * arg )
{
proxy_ctx_t * ctx = arg ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Received signal %i \n " , fd ) ;
}
switch ( fd ) {
case SIGQUIT :
case SIGINT :
case SIGHUP :
proxy_loopbreak ( ctx ) ;
break ;
case SIGUSR1 :
if ( log_reopen ( ) = = - 1 ) {
log_err_printf ( " Warning: Failed to reopen logs \n " ) ;
} else {
log_dbg_printf ( " Reopened log files \n " ) ;
}
break ;
case SIGPIPE :
log_err_printf ( " Warning: Received SIGPIPE; ignoring. \n " ) ;
break ;
default :
log_err_printf ( " Warning: Received unexpected signal %i \n " , fd ) ;
break ;
}
}
/*
* Garbage collection handler .
*/
static void
proxy_gc_cb ( UNUSED evutil_socket_t fd , UNUSED short what , void * arg )
{
proxy_ctx_t * ctx = arg ;
if ( OPTS_DEBUG ( ctx - > opts ) )
log_dbg_printf ( " Garbage collecting caches started. \n " ) ;
cachemgr_gc ( ) ;
if ( OPTS_DEBUG ( ctx - > opts ) )
log_dbg_printf ( " Garbage collecting caches done. \n " ) ;
}
/*
* Set up the core event loop .
* Socket clisock is the privsep client socket used for binding to ports .
* Returns ctx on success , or NULL on error .
*/
proxy_ctx_t *
proxy_new ( opts_t * opts , int clisock )
{
proxy_listener_ctx_t * head ;
proxy_ctx_t * ctx ;
struct evdns_base * dnsbase ;
int rc ;
/* adds locking, only required if accessed from separate threads */
evthread_use_pthreads ( ) ;
# ifndef PURIFY
if ( OPTS_DEBUG ( opts ) ) {
event_enable_debug_mode ( ) ;
}
# endif /* PURIFY */
ctx = malloc ( sizeof ( proxy_ctx_t ) ) ;
if ( ! ctx ) {
log_err_printf ( " Error allocating memory \n " ) ;
goto leave0 ;
}
memset ( ctx , 0 , sizeof ( proxy_ctx_t ) ) ;
ctx - > opts = opts ;
ctx - > evbase = event_base_new ( ) ;
if ( ! ctx - > evbase ) {
log_err_printf ( " Error getting event base \n " ) ;
goto leave1 ;
}
if ( opts_has_dns_spec ( opts ) ) {
/* create a dnsbase here purely for being able to test parsing
* resolv . conf while we can still alert the user about it . */
dnsbase = evdns_base_new ( ctx - > evbase , 0 ) ;
if ( ! dnsbase ) {
log_err_printf ( " Error creating dns event base \n " ) ;
goto leave1b ;
}
rc = evdns_base_resolv_conf_parse ( dnsbase , DNS_OPTIONS_ALL ,
" /etc/resolv.conf " ) ;
evdns_base_free ( dnsbase , 0 ) ;
if ( rc ! = 0 ) {
log_err_printf ( " evdns cannot parse resolv.conf: "
" %s (%d) \n " ,
rc = = 1 ? " failed to open file " :
rc = = 2 ? " failed to stat file " :
rc = = 3 ? " file too large " :
rc = = 4 ? " out of memory " :
rc = = 5 ? " short read from file " :
rc = = 6 ? " no nameservers in file " :
" unknown error " , rc ) ;
goto leave1b ;
}
}
if ( OPTS_DEBUG ( opts ) ) {
proxy_debug_base ( ctx - > evbase ) ;
}
ctx - > thrmgr = pxy_thrmgr_new ( opts ) ;
if ( ! ctx - > thrmgr ) {
log_err_printf ( " Error creating thread manager \n " ) ;
goto leave1b ;
}
head = ctx - > lctx = NULL ;
for ( proxyspec_t * spec = opts - > spec ; spec ; spec = spec - > next ) {
head = proxy_listener_setup ( ctx - > evbase , ctx - > thrmgr ,
spec , opts , clisock ) ;
if ( ! head )
goto leave2 ;
head - > next = ctx - > lctx ;
ctx - > lctx = head ;
char * specstr = proxyspec_str ( spec ) ;
if ( ! specstr ) {
fprintf ( stderr , " out of memory \n " ) ;
exit ( EXIT_FAILURE ) ;
}
log_dbg_printf ( " >>>>> proxy_listener_setup - %s \n " , specstr ) ;
free ( specstr ) ;
}
for ( size_t i = 0 ; i < ( sizeof ( signals ) / sizeof ( int ) ) ; i + + ) {
ctx - > sev [ i ] = evsignal_new ( ctx - > evbase , signals [ i ] ,
proxy_signal_cb , ctx ) ;
if ( ! ctx - > sev [ i ] )
goto leave3 ;
evsignal_add ( ctx - > sev [ i ] , NULL ) ;
}
struct timeval gc_delay = { 60 , 0 } ;
ctx - > gcev = event_new ( ctx - > evbase , - 1 , EV_PERSIST , proxy_gc_cb , ctx ) ;
if ( ! ctx - > gcev )
goto leave4 ;
evtimer_add ( ctx - > gcev , & gc_delay ) ;
// @attention Do not close privsep sock, we bind to new sockets on the egress path
//privsep_client_close(clisock);
return ctx ;
leave4 :
if ( ctx - > gcev ) {
event_free ( ctx - > gcev ) ;
}
leave3 :
for ( size_t i = 0 ; i < ( sizeof ( ctx - > sev ) / sizeof ( ctx - > sev [ 0 ] ) ) ; i + + ) {
if ( ctx - > sev [ i ] ) {
event_free ( ctx - > sev [ i ] ) ;
}
}
leave2 :
if ( ctx - > lctx ) {
proxy_listener_ctx_free ( ctx - > lctx ) ;
}
pxy_thrmgr_free ( ctx - > thrmgr ) ;
leave1b :
event_base_free ( ctx - > evbase ) ;
leave1 :
free ( ctx ) ;
leave0 :
return NULL ;
}
/*
* Run the event loop . Returns when the event loop is cancelled by a signal
* or on failure .
*/
void
proxy_run ( proxy_ctx_t * ctx )
{
if ( ctx - > opts - > detach ) {
event_reinit ( ctx - > evbase ) ;
}
# ifndef PURIFY
if ( OPTS_DEBUG ( ctx - > opts ) ) {
event_base_dump_events ( ctx - > evbase , stderr ) ;
}
# endif /* PURIFY */
if ( pxy_thrmgr_run ( ctx - > thrmgr ) = = - 1 ) {
log_err_printf ( " Failed to start thread manager \n " ) ;
return ;
}
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Starting main event loop. \n " ) ;
}
event_base_dispatch ( ctx - > evbase ) ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Main event loop stopped. \n " ) ;
}
}
/*
* Break the loop of the proxy , causing the proxy_run to return .
*/
void
proxy_loopbreak ( proxy_ctx_t * ctx )
{
event_base_loopbreak ( ctx - > evbase ) ;
}
/*
* Free the proxy data structures .
*/
void
proxy_free ( proxy_ctx_t * ctx )
{
if ( ctx - > gcev ) {
event_free ( ctx - > gcev ) ;
}
if ( ctx - > lctx ) {
proxy_listener_ctx_free ( ctx - > lctx ) ;
}
for ( size_t i = 0 ; i < ( sizeof ( ctx - > sev ) / sizeof ( ctx - > sev [ 0 ] ) ) ; i + + ) {
if ( ctx - > sev [ i ] ) {
event_free ( ctx - > sev [ i ] ) ;
}
}
if ( ctx - > thrmgr ) {
pxy_thrmgr_free ( ctx - > thrmgr ) ;
}
if ( ctx - > evbase ) {
event_base_free ( ctx - > evbase ) ;
}
free ( ctx ) ;
}
/* vim: set noet ft=c: */