/*-
* SSLsplit - transparent SSL / TLS interception
* https : //www.roe.ch/SSLsplit
*
* Copyright ( c ) 2009 - 2019 , Daniel Roethlisberger < daniel @ roe . ch > .
* Copyright ( c ) 2017 - 2020 , Soner Tari < sonertari @ gmail . com > .
* All rights reserved .
*
* 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 COPYRIGHT HOLDER AND CONTRIBUTORS ` ` 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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "pxythrmgr.h"
# include "sys.h"
# include "log.h"
# include "pxyconn.h"
# include <string.h>
# include <event2/bufferevent.h>
/*
* Proxy thread manager : manages the connection handling worker threads
* and the per - thread resources ( i . e . event bases ) . The load is shared
* across num_cpu * 2 connection handling threads , using the number of
* currently assigned connections as the sole metric .
*/
/*
* Create new thread manager but do not start any threads yet .
* This gets called before forking to background .
*/
pxy_thrmgr_ctx_t *
pxy_thrmgr_new ( global_t * global )
{
pxy_thrmgr_ctx_t * ctx ;
if ( ! ( ctx = malloc ( sizeof ( pxy_thrmgr_ctx_t ) ) ) )
return NULL ;
memset ( ctx , 0 , sizeof ( pxy_thrmgr_ctx_t ) ) ;
ctx - > global = global ;
ctx - > num_thr = 2 * sys_get_cpu_cores ( ) ;
return ctx ;
}
/*
* Start the thread manager and associated threads .
* This must be called after forking .
*
* Returns - 1 on failure , 0 on success .
*/
int
pxy_thrmgr_run ( pxy_thrmgr_ctx_t * ctx )
{
int idx = - 1 , dns = 0 ;
dns = global_has_dns_spec ( ctx - > global ) ;
if ( ! ( ctx - > thr = malloc ( ctx - > num_thr * sizeof ( pxy_thr_ctx_t * ) ) ) ) {
log_dbg_printf ( " Failed to allocate memory \n " ) ;
goto leave ;
}
memset ( ctx - > thr , 0 , ctx - > num_thr * sizeof ( pxy_thr_ctx_t * ) ) ;
for ( idx = 0 ; idx < ctx - > num_thr ; idx + + ) {
if ( ! ( ctx - > thr [ idx ] = malloc ( sizeof ( pxy_thr_ctx_t ) ) ) ) {
log_dbg_printf ( " Failed to allocate memory \n " ) ;
goto leave ;
}
memset ( ctx - > thr [ idx ] , 0 , sizeof ( pxy_thr_ctx_t ) ) ;
ctx - > thr [ idx ] - > evbase = event_base_new ( ) ;
if ( ! ctx - > thr [ idx ] - > evbase ) {
log_dbg_printf ( " Failed to create evbase %d \n " , idx ) ;
goto leave ;
}
if ( dns ) {
/* only create dns base if we actually need it later */
ctx - > thr [ idx ] - > dnsbase = evdns_base_new (
ctx - > thr [ idx ] - > evbase , 1 ) ;
if ( ! ctx - > thr [ idx ] - > dnsbase ) {
log_dbg_printf ( " Failed to create dnsbase %d \n " ,
idx ) ;
goto leave ;
}
}
ctx - > thr [ idx ] - > load = 0 ;
ctx - > thr [ idx ] - > running = 0 ;
ctx - > thr [ idx ] - > conns = NULL ;
ctx - > thr [ idx ] - > thridx = idx ;
ctx - > thr [ idx ] - > timeout_count = 0 ;
ctx - > thr [ idx ] - > thrmgr = ctx ;
if ( ( ctx - > global - > opts - > user_auth | | global_has_userauth_spec ( ctx - > global ) ) & & sqlite3_prepare_v2 ( ctx - > global - > userdb , " SELECT user,ether,atime,desc FROM users WHERE ip = ?1 " , 100 , & ctx - > thr [ idx ] - > get_user , NULL ) ) {
log_err_level_printf ( LOG_CRIT , " Error preparing get_user sql stmt: %s \n " , sqlite3_errmsg ( ctx - > global - > userdb ) ) ;
goto leave ;
}
//if (pthread_mutex_init(&ctx->thr[idx]->mutex, NULL)) {
// log_dbg_printf("Failed to initialize thr mutex\n");
// goto leave;
//}
}
log_dbg_printf ( " Initialized %d connection handling threads \n " ,
ctx - > num_thr ) ;
for ( idx = 0 ; idx < ctx - > num_thr ; idx + + ) {
if ( pthread_create ( & ctx - > thr [ idx ] - > thr , NULL , pxy_thr , ctx - > thr [ idx ] ) )
goto leave_thr ;
while ( ! ctx - > thr [ idx ] - > running ) {
sched_yield ( ) ;
}
}
log_dbg_printf ( " Started %d connection handling threads \n " ,
ctx - > num_thr ) ;
return 0 ;
leave_thr :
idx - - ;
while ( idx > = 0 ) {
pthread_cancel ( ctx - > thr [ idx ] - > thr ) ;
pthread_join ( ctx - > thr [ idx ] - > thr , NULL ) ;
idx - - ;
}
idx = ctx - > num_thr - 1 ;
leave :
while ( idx > = 0 ) {
if ( ctx - > thr [ idx ] ) {
if ( ctx - > thr [ idx ] - > dnsbase ) {
evdns_base_free ( ctx - > thr [ idx ] - > dnsbase , 0 ) ;
}
if ( ctx - > thr [ idx ] - > evbase ) {
event_base_free ( ctx - > thr [ idx ] - > evbase ) ;
}
if ( ctx - > global - > opts - > user_auth | | global_has_userauth_spec ( ctx - > global ) ) {
sqlite3_finalize ( ctx - > thr [ idx ] - > get_user ) ;
}
//pthread_mutex_destroy(&ctx->thr[idx]->mutex);
free ( ctx - > thr [ idx ] ) ;
}
idx - - ;
}
if ( ctx - > thr ) {
free ( ctx - > thr ) ;
ctx - > thr = NULL ;
}
return - 1 ;
}
/*
* Destroy the event manager and stop all threads .
*/
void
pxy_thrmgr_free ( pxy_thrmgr_ctx_t * ctx )
{
if ( ctx - > thr ) {
for ( int idx = 0 ; idx < ctx - > num_thr ; idx + + ) {
event_base_loopbreak ( ctx - > thr [ idx ] - > evbase ) ;
sched_yield ( ) ;
}
for ( int idx = 0 ; idx < ctx - > num_thr ; idx + + ) {
pthread_join ( ctx - > thr [ idx ] - > thr , NULL ) ;
}
for ( int idx = 0 ; idx < ctx - > num_thr ; idx + + ) {
if ( ctx - > thr [ idx ] - > dnsbase ) {
evdns_base_free ( ctx - > thr [ idx ] - > dnsbase , 0 ) ;
}
if ( ctx - > thr [ idx ] - > evbase ) {
event_base_free ( ctx - > thr [ idx ] - > evbase ) ;
}
if ( ctx - > global - > opts - > user_auth | | global_has_userauth_spec ( ctx - > global ) ) {
sqlite3_finalize ( ctx - > thr [ idx ] - > get_user ) ;
}
//pthread_mutex_destroy(&ctx->thr[idx]->mutex);
free ( ctx - > thr [ idx ] ) ;
}
free ( ctx - > thr ) ;
}
free ( ctx ) ;
}
/*
* Attach a new connection to a thread . Chooses the thread with the fewest
* currently active connections , returns the appropriate event bases .
* No need to be so accurate about balancing thread loads , so uses
* thread - level mutexes , instead of a thrmgr level mutex .
* Returns the index of the chosen thread .
* This function cannot fail .
*/
void
pxy_thrmgr_attach ( pxy_conn_ctx_t * ctx )
{
log_finest ( " ENTER " ) ;
pxy_thrmgr_ctx_t * tmctx = ctx - > thrmgr ;
size_t minload = pxy_thr_get_load ( tmctx - > thr [ 0 ] ) ;
# ifdef DEBUG_THREAD
log_dbg_printf ( " ===> Proxy connection handler thread status: \n thr[0]: %zu \n " , minload ) ;
# endif /* DEBUG_THREAD */
int thridx = 0 ;
for ( int idx = 1 ; idx < tmctx - > num_thr ; idx + + ) {
size_t thrload = pxy_thr_get_load ( tmctx - > thr [ idx ] ) ;
if ( minload > thrload ) {
minload = thrload ;
thridx = idx ;
}
# ifdef DEBUG_THREAD
log_dbg_printf ( " thr[%d]: %zu \n " , idx , thrload ) ;
# endif /* DEBUG_THREAD */
}
ctx - > thr = tmctx - > thr [ thridx ] ;
# ifdef DEBUG_THREAD
log_dbg_printf ( " thridx: %d \n " , thridx ) ;
# endif /* DEBUG_THREAD */
}
/* vim: set noet ft=c: */