From c8e3736deaf631e8b426d667a9eb129b32f908f1 Mon Sep 17 00:00:00 2001 From: Soner Tari Date: Wed, 26 Sep 2018 22:13:07 +0300 Subject: [PATCH] Fix autossl feature, needs refactoring because the code has become complex now --- pxyconn.c | 255 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 161 insertions(+), 94 deletions(-) diff --git a/pxyconn.c b/pxyconn.c index 624a2a3..9a72993 100644 --- a/pxyconn.c +++ b/pxyconn.c @@ -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); - } + + 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,93 +2899,97 @@ 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 - // 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) { + // @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 */ - // @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. + // 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)); + } + + // @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; } - // @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. - if ((ctx->child_fd = privsep_client_opensock_child(ctx->clisock, ctx->spec)) == -1) { - log_err_level_printf(LOG_CRIT, "Error opening child socket: %s (%i)\n", strerror(errno), errno); - pxy_conn_free(ctx, 1); - return -1; - } - ctx->thr->max_fd = MAX(ctx->thr->max_fd, ctx->child_fd); + // 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. + if ((ctx->child_fd = privsep_client_opensock_child(ctx->clisock, ctx->spec)) == -1) { + log_err_level_printf(LOG_CRIT, "Error opening child socket: %s (%i)\n", strerror(errno), errno); + pxy_conn_free(ctx, 1); + return -1; + } + ctx->thr->max_fd = MAX(ctx->thr->max_fd, ctx->child_fd); - // @attention Do not pass NULL as user-supplied pointer - struct evconnlistener *child_evcl = evconnlistener_new(ctx->thr->evbase, proxy_listener_acceptcb_child, ctx, LEV_OPT_CLOSE_ON_FREE, 1024, ctx->child_fd); - if (!child_evcl) { - log_err_level_printf(LOG_CRIT, "Error creating child evconnlistener: %s\n", strerror(errno)); + // @attention Do not pass NULL as user-supplied pointer + struct evconnlistener *child_evcl = evconnlistener_new(ctx->thr->evbase, proxy_listener_acceptcb_child, ctx, LEV_OPT_CLOSE_ON_FREE, 1024, ctx->child_fd); + if (!child_evcl) { + log_err_level_printf(LOG_CRIT, "Error creating child evconnlistener: %s\n", strerror(errno)); #ifdef DEBUG_PROXY - log_dbg_level_printf(LOG_DBG_MODE_FINER, "pxy_connected_enable: Error creating child evconnlistener: %s, fd=%d, child_fd=%d\n", strerror(errno), fd, ctx->child_fd); + log_dbg_level_printf(LOG_DBG_MODE_FINER, "pxy_connected_enable: Error creating child evconnlistener: %s, fd=%d, child_fd=%d\n", strerror(errno), fd, ctx->child_fd); #endif /* DEBUG_PROXY */ - // @attention Cannot call proxy_listener_ctx_free() on child_evcl, child_evcl does not have any ctx with next listener - // @attention Close child fd separately, because child evcl does not exist yet, hence fd would not be closed by calling pxy_conn_free() - evutil_closesocket(ctx->child_fd); - pxy_conn_free(ctx, 1); - return -1; - } - ctx->child_evcl = child_evcl; + // @attention Cannot call proxy_listener_ctx_free() on child_evcl, child_evcl does not have any ctx with next listener + // @attention Close child fd separately, because child evcl does not exist yet, hence fd would not be closed by calling pxy_conn_free() + evutil_closesocket(ctx->child_fd); + pxy_conn_free(ctx, 1); + return -1; + } + ctx->child_evcl = child_evcl; - evconnlistener_set_error_cb(child_evcl, proxy_listener_errorcb); + evconnlistener_set_error_cb(child_evcl, proxy_listener_errorcb); #ifdef DEBUG_PROXY - log_dbg_level_printf(LOG_DBG_MODE_FINER, "pxy_connected_enable: Finished setting up child, fd=%d, NEW child_fd=%d\n", fd, ctx->child_fd); + log_dbg_level_printf(LOG_DBG_MODE_FINER, "pxy_connected_enable: Finished setting up child, fd=%d, NEW child_fd=%d\n", fd, ctx->child_fd); #endif /* DEBUG_PROXY */ - struct sockaddr_in child_listener_addr; - socklen_t child_listener_len = sizeof(child_listener_addr); + struct sockaddr_in child_listener_addr; + socklen_t child_listener_len = sizeof(child_listener_addr); - if (getsockname(ctx->child_fd, (struct sockaddr *)&child_listener_addr, &child_listener_len) < 0) { - log_err_level_printf(LOG_CRIT, "Error in getsockname: %s\n", strerror(errno)); - // @todo If getsockname() fails, should we really terminate the connection? - // @attention Do not close the child fd here, because child evcl exists now, hence pxy_conn_free() will close it while freeing child_evcl - pxy_conn_free(ctx, 1); - return -1; - } + if (getsockname(ctx->child_fd, (struct sockaddr *)&child_listener_addr, &child_listener_len) < 0) { + log_err_level_printf(LOG_CRIT, "Error in getsockname: %s\n", strerror(errno)); + // @todo If getsockname() fails, should we really terminate the connection? + // @attention Do not close the child fd here, because child evcl exists now, hence pxy_conn_free() will close it while freeing child_evcl + pxy_conn_free(ctx, 1); + return -1; + } - // @attention Children are always listening on an IPv4 loopback address - char addr[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, &child_listener_addr.sin_addr, addr, INET_ADDRSTRLEN)) { - pxy_conn_free(ctx, 1); - return -1; - } + // @attention Children are always listening on an IPv4 loopback address + char addr[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, &child_listener_addr.sin_addr, addr, INET_ADDRSTRLEN)) { + pxy_conn_free(ctx, 1); + return -1; + } - // SSLproxy: [127.0.0.1]:34649,[192.168.3.24]:47286,[74.125.206.108]:465,s - // @todo Port may be less than 5 chars - // SSLproxy: + + [ + addr + ] + : + p + , + [ + srchost_str + ] + : + srcport_str + , + [ + dsthost_str + ] + : + dstport_str + , + s + NULL - // SSLPROXY_KEY_LEN + 1 + 1 + strlen(addr) + 1 + 1 + 5 + 1 + 1 + strlen(ctx->srchost_str) + 1 + 1 + strlen(ctx->srcport_str) + 1 + 1 + strlen(ctx->dsthost_str) + 1 + 1 + strlen(ctx->dstport_str) + 1 + 1 + 1 - int header_len = SSLPROXY_KEY_LEN + strlen(addr) + strlen(ctx->srchost_str) + strlen(ctx->srcport_str) + strlen(ctx->dsthost_str) + strlen(ctx->dstport_str) + 20; - // @todo Always check malloc retvals. Should we close the conn if malloc fails? - ctx->header_str = malloc(header_len); - if (!ctx->header_str) { - pxy_conn_free(ctx, 1); - return -1; + // SSLproxy: [127.0.0.1]:34649,[192.168.3.24]:47286,[74.125.206.108]:465,s + // @todo Port may be less than 5 chars + // SSLproxy: + + [ + addr + ] + : + p + , + [ + srchost_str + ] + : + srcport_str + , + [ + dsthost_str + ] + : + dstport_str + , + s + NULL + // SSLPROXY_KEY_LEN + 1 + 1 + strlen(addr) + 1 + 1 + 5 + 1 + 1 + strlen(ctx->srchost_str) + 1 + 1 + strlen(ctx->srcport_str) + 1 + 1 + strlen(ctx->dsthost_str) + 1 + 1 + strlen(ctx->dstport_str) + 1 + 1 + 1 + int header_len = SSLPROXY_KEY_LEN + strlen(addr) + strlen(ctx->srchost_str) + strlen(ctx->srcport_str) + strlen(ctx->dsthost_str) + strlen(ctx->dstport_str) + 20; + // @todo Always check malloc retvals. Should we close the conn if malloc fails? + ctx->header_str = malloc(header_len); + if (!ctx->header_str) { + pxy_conn_free(ctx, 1); + return -1; + } + 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"); } - 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); + 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 */