Fix autossl feature, needs refactoring because the code has become complex now

pull/13/head
Soner Tari 6 years ago
parent 59bf997d57
commit c8e3736dea

@ -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 */

Loading…
Cancel
Save