@ -1813,9 +1813,14 @@ pxy_conn_autossl_peek_and_upgrade(pxy_conn_ctx_t *ctx)
struct evbuffer * inbuf ;
struct evbuffer_iovec vec_out [ 1 ] ;
const unsigned char * chello ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Checking for a client hello \n " ) ;
}
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_conn_autossl_peek_and_upgrade: ENTER, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
/* peek the buffer */
inbuf = bufferevent_get_input ( ctx - > src . bev ) ;
if ( evbuffer_peek ( inbuf , 1024 , 0 , vec_out , 1 ) ) {
@ -1823,6 +1828,7 @@ pxy_conn_autossl_peek_and_upgrade(pxy_conn_ctx_t *ctx)
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Peek found ClientHello \n " ) ;
}
ctx - > srv_dst . ssl = pxy_dstssl_create ( ctx ) ;
if ( ! ctx - > srv_dst . ssl ) {
log_err_level_printf ( LOG_CRIT , " Error creating SSL for upgrade \n " ) ;
@ -1830,6 +1836,7 @@ pxy_conn_autossl_peek_and_upgrade(pxy_conn_ctx_t *ctx)
}
ctx - > srv_dst . bev = bufferevent_openssl_filter_new ( ctx - > evbase , ctx - > srv_dst . bev , ctx - > srv_dst . ssl ,
BUFFEREVENT_SSL_CONNECTING , BEV_OPT_DEFER_CALLBACKS ) ;
bufferevent_setcb ( ctx - > srv_dst . bev , pxy_bev_readcb , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
# ifdef DEBUG_PROXY
@ -1842,6 +1849,7 @@ pxy_conn_autossl_peek_and_upgrade(pxy_conn_ctx_t *ctx)
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_err_level_printf ( LOG_INFO , " Replaced srv_dst bufferevent, new one is %p \n " , ( void * ) ctx - > srv_dst . bev ) ;
}
ctx - > clienthello_search = 0 ;
ctx - > clienthello_found = 1 ;
return 1 ;
@ -2120,8 +2128,8 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
# ifdef DEBUG_PROXY
char * event_name = pxy_get_event_name ( bev , ctx ) ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_readcb: ENTER %s, fd=%d, child_fd=%d, size=%zu\n " ,
event_name , ctx - > fd , ctx - > child_fd , evbuffer_get_length ( bufferevent_get_input ( bev ) ) ) ;
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_readcb: ENTER %s, fd=%d, size=%zu\n " ,
event_name , ctx - > fd , evbuffer_get_length ( bufferevent_get_input ( bev ) ) ) ;
# endif /* DEBUG_PROXY */
if ( ! ctx - > connected ) {
@ -2130,6 +2138,12 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
return ;
}
if ( ctx - > clienthello_search ) {
if ( pxy_conn_autossl_peek_and_upgrade ( ctx ) ) {
return ;
}
}
struct evbuffer * inbuf = bufferevent_get_input ( bev ) ;
pxy_conn_desc_t * other ;
if ( ctx - > passthrough ) {
@ -2154,12 +2168,6 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
} else if ( bev = = ctx - > src . bev ) {
ctx - > thr - > intif_in_bytes + = inbuf_size ;
if ( ctx - > clienthello_search ) {
if ( pxy_conn_autossl_peek_and_upgrade ( ctx ) ) {
return ;
}
}
// We insert our special header line to the first packet we get, e.g. right after the first \r\n in the case of http
// @todo Should we look for GET/POST or Host header lines to detect the first packet?
// But there is no guarantee that they will exist, due to fragmentation.
@ -2203,6 +2211,17 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
packet_size , ctx - > fd , ( int ) packet_size , packet ) ;
# endif /* DEBUG_PROXY */
if ( ctx - > clienthello_search ) {
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_readcb: clienthello_search Duping packet to srv_dst (size=%zu), fd=%d: \n %.*s \n " ,
packet_size , ctx - > fd , ( int ) packet_size , packet ) ;
# endif /* DEBUG_PROXY */
// Dup packet to server while searching for clienthello in autossl mode, without adding SSLproxy specific header
if ( evbuffer_add ( bufferevent_get_output ( ctx - > srv_dst . bev ) , packet , packet_size ) = = - 1 ) {
log_err_printf ( " pxy_bev_readcb: clienthello_search srv_dst evbuffer_add failed, fd=%d \n " , ctx - > fd ) ;
}
}
// @attention Cannot use string manipulation functions; we are dealing with binary arrays here, not NULL-terminated strings
if ( ! ctx - > sent_header ) {
memmove ( packet + header_len + 2 , packet , packet_size ) ;
@ -2249,6 +2268,18 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_readcb: dst packet size=%zu, fd=%d \n " , inbuf_size , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
}
} else if ( ctx - > clienthello_search ) {
// bev == ctx->srv_dst.bev
// Discard packets to client while searching for clienthello in autossl mode, because child conn passes them along already
// Otherwise client would receive the same packet twice
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_readcb: clienthello_search srv_dst Discarding packet, size=%zu, fd=%d \n " ,
inbuf_size , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
if ( evbuffer_drain ( inbuf , inbuf_size ) = = - 1 ) {
log_err_printf ( " pxy_bev_readcb: clienthello_search srv_dst evbuffer_drain failed, fd=%d \n " , ctx - > fd ) ;
}
return ;
}
if ( pxy_log_content_inbuf ( ctx , inbuf , ( bev = = ctx - > src . bev ) ) = = - 1 ) {
@ -2290,6 +2321,38 @@ pxy_bev_readcb_child(struct bufferevent *bev, void *arg)
return ;
}
// Autossl upgrade on child connections follows the findings of parent
if ( ctx - > conn - > clienthello_found & & ! ctx - > dst . ssl ) {
if ( OPTS_DEBUG ( ctx - > conn - > opts ) ) {
log_dbg_printf ( " Completing autossl upgrade on child conn \n " ) ;
}
ctx - > dst . ssl = pxy_dstssl_create ( ctx - > conn ) ;
if ( ! ctx - > dst . ssl ) {
log_err_level_printf ( LOG_CRIT , " pxy_bev_readcb_child: Error creating SSL for upgrade \n " ) ;
ctx - > enomem = 1 ;
pxy_conn_free ( ctx - > conn , 1 ) ;
return ;
}
ctx - > dst . bev = bufferevent_openssl_filter_new ( ctx - > conn - > evbase , ctx - > dst . bev , ctx - > dst . ssl ,
BUFFEREVENT_SSL_CONNECTING , BEV_OPT_DEFER_CALLBACKS ) ;
bufferevent_setcb ( ctx - > dst . bev , pxy_bev_readcb_child , pxy_bev_writecb_child , pxy_bev_eventcb_child , ctx ) ;
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_readcb_child: Enabling dst, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
if ( ! ctx - > dst . bev ) {
ctx - > enomem = 1 ;
pxy_conn_free ( ctx - > conn , 1 ) ;
return ;
}
bufferevent_enable ( ctx - > dst . bev , EV_READ | EV_WRITE ) ;
if ( OPTS_DEBUG ( ctx - > conn - > opts ) ) {
log_err_level_printf ( LOG_INFO , " pxy_bev_readcb_child: Replaced dst bufferevent, new one is %p \n " , ( void * ) ctx - > dst . bev ) ;
}
return ;
}
struct evbuffer * inbuf = bufferevent_get_input ( bev ) ;
pxy_conn_desc_t * other = ( bev = = ctx - > src . bev ) ? & ctx - > dst : & ctx - > src ;
struct evbuffer * outbuf = bufferevent_get_output ( other - > bev ) ;
@ -2427,10 +2490,8 @@ getdtablecount(void)
* Callback for accept events on the socket listener bufferevent .
*/
static void
proxy_listener_acceptcb_child ( UNUSED struct evconnlistener * listener ,
evutil_socket_t fd ,
UNUSED struct sockaddr * peeraddr , UNUSED int peeraddrlen ,
void * arg )
proxy_listener_acceptcb_child ( UNUSED struct evconnlistener * listener , evutil_socket_t fd ,
UNUSED struct sockaddr * peeraddr , UNUSED int peeraddrlen , void * arg )
{
pxy_conn_ctx_t * conn = arg ;
@ -2511,18 +2572,9 @@ proxy_listener_acceptcb_child(UNUSED struct evconnlistener *listener,
return ;
}
}
if ( ctx - > conn - > clienthello_found ) {
if ( OPTS_DEBUG ( ctx - > conn - > opts ) ) {
log_dbg_printf ( " Completing autossl upgrade \n " ) ;
}
ctx - > dst . bev = bufferevent_openssl_filter_new ( ctx - > conn - > evbase , ctx - > dst . bev , ctx - > dst . ssl ,
BUFFEREVENT_SSL_ACCEPTING , BEV_OPT_DEFER_CALLBACKS ) ;
if ( ctx - > dst . bev ) {
bufferevent_setcb ( ctx - > dst . bev , pxy_bev_readcb_child , pxy_bev_writecb_child , pxy_bev_eventcb_child , ctx ) ;
}
} else {
ctx - > dst . bev = pxy_bufferevent_setup_child ( ctx , - 1 , ctx - > dst . ssl ) ;
}
if ( ! ctx - > dst . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating bufferevent \n " ) ;
if ( ctx - > dst . ssl ) {
@ -2728,7 +2780,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
}
}
if ( ctx - > srv_dst_connected & & ( ctx - > dst_connected | | ctx - > passthrough ) & & ! ctx - > connected ) {
if ( ctx - > srv_dst_connected & & ( ctx - > dst_connected | | ctx - > passthrough ) & & ( ! ctx - > connected | | ( ctx - > clienthello_found & & ctx - > srv_dst . bev ) ) ) {
ctx - > connected = 1 ;
if ( ! ctx - > passthrough ) {
@ -2768,6 +2820,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
// Create and set up src.bev
if ( ctx - > clienthello_found ) {
// ctx->src.bev must have already been created at this point
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Completing autossl upgrade \n " ) ;
}
@ -2846,24 +2899,28 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
}
if ( ! ctx - > passthrough ) {
// @attention Free the srv_dst of the conn asap, we don't need it, but we need its fd
// @attention Free the srv_dst of the conn asap, we don't need it anymore, but we need its fd
if ( ! ctx - > conn - > spec - > upgrade | | ( ctx - > srv_dst . bev & & ! ctx - > clienthello_search ) ) {
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINER , " pxy_connected_enable: Closing srv_dst, fd=%d, srv_dst fd=%d \n " , fd , bufferevent_getfd ( ctx - > srv_dst . bev ) ) ;
# endif /* DEBUG_PROXY */
// So save its ssl info for logging
if ( ctx - > srv_dst . ssl ) {
ctx - > srv_dst_ssl_version = strdup ( SSL_get_version ( ctx - > srv_dst . ssl ) ) ;
ctx - > srv_dst_ssl_cipher = strdup ( SSL_get_cipher ( ctx - > srv_dst . ssl ) ) ;
}
if ( ctx - > srv_dst . bev ) {
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINER , " pxy_connected_enable: Closing srv_dst, fd=%d, srv_dst fd=%d \n " , fd , bufferevent_getfd ( ctx - > srv_dst . bev ) ) ;
# endif /* DEBUG_PROXY */
// @attention Since both eventcb and writecb for srv_dst are enabled, either eventcb or writecb may get a NULL srv_dst bev, causing a crash with signal 10.
// @attention When both eventcb and writecb for srv_dst are enabled, either eventcb or writecb may get a NULL srv_dst bev, causing a crash with signal 10.
// So, from this point on, we should check if srv_dst is NULL or not.
bufferevent_free_and_close_fd ( ctx - > srv_dst . bev , ctx ) ;
ctx - > srv_dst . bev = NULL ;
ctx - > srv_dst . closed = 1 ;
}
// Skip child setup if completing autossl upgrade, after finding clienthello
//if (ctx->clienthello_search || !ctx->clienthello_found)
if ( ! ctx - > conn - > spec - > upgrade | | ( ctx - > clienthello_search & & ! ctx - > clienthello_found ) ) {
// @attention Defer child setup and evcl creation until after parent init is complete, otherwise (1) causes multithreading issues (proxy_listener_acceptcb is
// running on a different thread from the conn, and we only have thrmgr mutex), and (2) we need to clean up less upon errors.
// Child evcls use the evbase of the parent thread, otherwise we would get multithreading issues.
@ -2926,13 +2983,13 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
snprintf ( ctx - > header_str , header_len , " %s [%s]:%u,[%s]:%s,[%s]:%s,%s " ,
SSLPROXY_KEY , addr , ntohs ( child_listener_addr . sin_port ) , STRORNONE ( ctx - > srchost_str ) , STRORNONE ( ctx - > srcport_str ) ,
STRORNONE ( ctx - > dsthost_str ) , STRORNONE ( ctx - > dstport_str ) , ctx - > spec - > ssl ? " s " : " p " ) ;
}
}
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_connected_enable: Enabling src, %s, fd=%d, child_fd=%d \n " , ctx - > header_str , fd , ctx - > child_fd ) ;
# endif /* DEBUG_PROXY */
}
// Now open the gates
// Now open the gates, perhaps for a second time if in autossl mode
bufferevent_enable ( ctx - > src . bev , EV_READ | EV_WRITE ) ;
}
@ -3312,6 +3369,16 @@ pxy_bev_eventcb_child(struct bufferevent *bev, short events, void *arg)
// @attention Create and enable src.bev before, but connect here, because we check if dst.bev is NULL elsewhere
bufferevent_enable ( ctx - > src . bev , EV_READ | EV_WRITE ) ;
// Check if we have come here right after autossl upgrade, which may be triggered by readcb on src
// Autossl upgrade code leaves readcb without processing any data in input buffer of src
// So, if we don't call readcb here, the connection would stall
if ( ctx - > conn - > clienthello_found & & evbuffer_get_length ( bufferevent_get_input ( ctx - > src . bev ) ) ) {
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " pxy_bev_eventcb_child: clienthello_found src inbuf len > 0, Calling pxy_bev_readcb_child for src, fd=%d, conn fd=%d \n " , ctx - > fd , ctx - > conn - > fd ) ;
# endif /* DEBUG_PROXY */
pxy_bev_readcb_child ( ctx - > src . bev , ctx ) ;
}
}
ctx - > conn - > thr - > max_fd = MAX ( ctx - > conn - > thr - > max_fd , MAX ( bufferevent_getfd ( ctx - > src . bev ) , bufferevent_getfd ( ctx - > dst . bev ) ) ) ;
@ -3495,14 +3562,14 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
// @todo Why does event cb not fire sometimes?
// @attention BEV_OPT_DEFER_CALLBACKS seems responsible for the issue with srv_dst, libevent acts as if we call event connect() ourselves.
// @see Launching connections on socket-based bufferevents at http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html
if ( ! ctx - > passthrough ) {
if ( ctx - > passthrough | | ctx - > clienthello_search ) {
// Enable srv_dst r cb for passthrough and autossl modes
bufferevent_setcb ( ctx - > srv_dst . bev , pxy_bev_readcb , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
bufferevent_enable ( ctx - > srv_dst . bev , EV_READ | EV_WRITE ) ;
} else {
// Disable and NULL r cb, we do nothing for srv_dst in r cb
bufferevent_setcb ( ctx - > srv_dst . bev , NULL , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
bufferevent_enable ( ctx - > srv_dst . bev , EV_WRITE ) ;
} else {
// Enable srv_dst r cb for passthrough
bufferevent_setcb ( ctx - > srv_dst . bev , pxy_bev_readcb , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
bufferevent_enable ( ctx - > srv_dst . bev , EV_READ | EV_WRITE ) ;
}
/* initiate connection */