SSLproxy/prototcp.c
Soner Tari 844e68116a Move userauth from thrmgr thread to conn handling threads, and do not enable r/w callbacks until userauth succeeds
Lock conn thread instead of thrmgr thread while adding conns (giant thrmgr lock versus conn thread level locks), so add conn thread mutex and remove thrmgr mutex
Offload thrmgr thread by moving many conn related setup to conn handling threads
Fix signal 6 crash caused by calling pxy_thrmgr_timer_cb() while failed conn is being freed, so use conn thread mutexes and defer adding conn to thr conn list until conn setup succeeds
Other fixes, improvements, and clean-up
2019-03-14 03:47:03 +03:00

1133 lines
36 KiB
C

/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* Copyright (c) 2017-2019, 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 "prototcp.h"
#include "protopassthrough.h"
#include <sys/param.h>
#include <string.h>
/*
* Set up a bufferevent structure for either a dst or src connection,
* optionally with or without SSL. Sets all callbacks, enables read
* and write events, but does not call bufferevent_socket_connect().
*
* For dst connections, pass -1 as fd. Pass a pointer to an initialized
* SSL struct as ssl if the connection should use SSL.
*
* Returns pointer to initialized bufferevent structure, as returned
* by bufferevent_socket_new() or bufferevent_openssl_socket_new().
*/
static struct bufferevent * NONNULL(1)
prototcp_bufferevent_setup(pxy_conn_ctx_t *ctx, evutil_socket_t fd)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bufferevent_setup: ENTER, fd=%d\n", fd);
#endif /* DEBUG_PROXY */
// @todo Do we really need to defer callbacks? BEV_OPT_DEFER_CALLBACKS seems responsible for the issue with srvdst: We get writecb sometimes, no eventcb for CONNECTED event
struct bufferevent *bev = bufferevent_socket_new(ctx->evbase, fd, BEV_OPT_DEFER_CALLBACKS|BEV_OPT_THREADSAFE);
if (!bev) {
log_err_level_printf(LOG_CRIT, "Error creating bufferevent socket\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bufferevent_setup: bufferevent_socket_connect failed, fd=%d\n", fd);
#endif /* DEBUG_PROXY */
return NULL;
}
// @attention Do not set callbacks here, srvdst does not set r cb
//bufferevent_setcb(bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
// @todo Should we enable events here?
//bufferevent_enable(bev, EV_READ|EV_WRITE);
return bev;
}
static struct bufferevent * NONNULL(1)
prototcp_bufferevent_setup_child(pxy_conn_child_ctx_t *ctx, evutil_socket_t fd)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bufferevent_setup_child: ENTER, fd=%d\n", fd);
#endif /* DEBUG_PROXY */
struct bufferevent *bev = bufferevent_socket_new(ctx->conn->evbase, fd, BEV_OPT_DEFER_CALLBACKS|BEV_OPT_THREADSAFE);
if (!bev) {
log_err_level_printf(LOG_CRIT, "Error creating bufferevent socket\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bufferevent_setup_child: bufferevent_socket_connect failed, fd=%d\n", fd);
#endif /* DEBUG_PROXY */
return NULL;
}
bufferevent_setcb(bev, pxy_bev_readcb_child, pxy_bev_writecb_child, pxy_bev_eventcb_child, ctx);
// @attention We cannot enable events here, because src events will be deferred until after dst is connected
//bufferevent_enable(bev, EV_READ|EV_WRITE);
return bev;
}
/*
* Free bufferenvent and close underlying socket properly.
*/
static void
prototcp_bufferevent_free_and_close_fd(struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
{
evutil_socket_t fd = bufferevent_getfd(bev);
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINER, "prototcp_bufferevent_free_and_close_fd: in=%zu, out=%zu, fd=%d\n",
evbuffer_get_length(bufferevent_get_input(bev)), evbuffer_get_length(bufferevent_get_output(bev)), fd);
#endif /* DEBUG_PROXY */
bufferevent_free(bev);
evutil_closesocket(fd);
}
int
prototcp_setup_src(pxy_conn_ctx_t *ctx)
{
ctx->src.ssl = NULL;
ctx->src.bev = prototcp_bufferevent_setup(ctx, ctx->fd);
if (!ctx->src.bev) {
log_err_level_printf(LOG_CRIT, "Error creating src bufferevent\n");
pxy_conn_term(ctx, 1);
return -1;
}
ctx->src.free = prototcp_bufferevent_free_and_close_fd;
return 0;
}
int
prototcp_setup_dst(pxy_conn_ctx_t *ctx)
{
ctx->dst.ssl = NULL;
ctx->dst.bev = prototcp_bufferevent_setup(ctx, -1);
if (!ctx->dst.bev) {
log_err_level_printf(LOG_CRIT, "Error creating parent dst\n");
pxy_conn_term(ctx, 1);
return -1;
}
ctx->dst.free = prototcp_bufferevent_free_and_close_fd;
return 0;
}
int
prototcp_setup_srvdst(pxy_conn_ctx_t *ctx)
{
ctx->srvdst.ssl = NULL;
ctx->srvdst.bev = prototcp_bufferevent_setup(ctx, -1);
if (!ctx->srvdst.bev) {
log_err_level_printf(LOG_CRIT, "Error creating srvdst\n");
pxy_conn_term(ctx, 1);
return -1;
}
ctx->srvdst.free = prototcp_bufferevent_free_and_close_fd;
return 0;
}
static int NONNULL(1)
prototcp_conn_connect(pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
// Make a copy of fd, to prevent multithreading issues in case the conn is terminated
int fd = ctx->fd;
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_conn_connect: ENTER, fd=%d\n", fd);
#endif /* DEBUG_PROXY */
/* create server-side socket and eventbuffer */
if (prototcp_setup_srvdst(ctx) == -1) {
return -1;
}
// @attention Sometimes dst write cb fires but not event cb, especially if this listener cb is not finished yet, so the conn stalls.
// @todo Why does event cb not fire sometimes?
// @attention BEV_OPT_DEFER_CALLBACKS seems responsible for the issue with srvdst, 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
// Disable and NULL r cb, we do nothing for srvdst in r cb
bufferevent_setcb(ctx->srvdst.bev, NULL, pxy_bev_writecb, pxy_bev_eventcb, ctx);
/* initiate connection */
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
log_err_level_printf(LOG_CRIT, "prototcp_conn_connect: bufferevent_socket_connect for srvdst failed\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_conn_connect: bufferevent_socket_connect for srvdst failed, fd=%d\n", fd);
#endif /* DEBUG_PROXY */
// @attention Do not try to close the conn here on the thrmgr thread, otherwise both pxy_conn_connect() and eventcb try to free the conn using pxy_conn_free(),
// they are running on different threads, causing multithreading issues, e.g. signal 10. Just return 0.
// @todo Should we use thrmgr->mutex? Can we? Seems impossible or too difficult.
}
return 0;
}
int
prototcp_setup_src_child(pxy_conn_child_ctx_t *ctx)
{
ctx->src.ssl = NULL;
ctx->src.bev = prototcp_bufferevent_setup_child(ctx, ctx->fd);
if (!ctx->src.bev) {
log_err_level_printf(LOG_CRIT, "Error creating child src\n");
evutil_closesocket(ctx->fd);
pxy_conn_term(ctx->conn, 1);
return -1;
}
ctx->src.free = prototcp_bufferevent_free_and_close_fd;
return 0;
}
int
prototcp_setup_dst_child(pxy_conn_child_ctx_t *ctx)
{
ctx->dst.ssl = NULL;
ctx->dst.bev = prototcp_bufferevent_setup_child(ctx, -1);
if (!ctx->dst.bev) {
log_err_level_printf(LOG_CRIT, "Error creating bufferevent\n");
pxy_conn_term(ctx->conn, 1);
return -1;
}
ctx->dst.free = prototcp_bufferevent_free_and_close_fd;
return 0;
}
static void NONNULL(1)
prototcp_connect_child(pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_connect_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
/* create server-side socket and eventbuffer */
prototcp_setup_dst_child(ctx);
}
void
prototcp_fd_readcb(UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_fd_readcb: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_connect(ctx);
}
int
prototcp_try_send_userauth_msg(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
if (ctx->opts->user_auth && !ctx->user) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_send_userauth_msg: Sending userauth message, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_discard_inbuf(bev);
evbuffer_add_printf(bufferevent_get_output(bev), USERAUTH_MSG, ctx->opts->user_auth_url);
ctx->sent_userauth_msg = 1;
return 1;
}
return 0;
}
static int NONNULL(1,2,3,4)
prototcp_try_validate_proto(struct bufferevent *bev, pxy_conn_ctx_t *ctx, struct evbuffer *inbuf, struct evbuffer *outbuf)
{
if (ctx->opts->validate_proto && ctx->protoctx->validatecb && !ctx->protoctx->is_valid) {
size_t packet_size = evbuffer_get_length(inbuf);
char *packet = (char *)pxy_malloc_packet(packet_size, ctx);
if (!packet) {
return -1;
}
if (evbuffer_copyout(inbuf, packet, packet_size) == -1) {
free(packet);
return -1;
}
if (ctx->protoctx->validatecb(ctx, packet, packet_size) == -1) {
evbuffer_add(bufferevent_get_output(bev), PROTOERROR_MSG, PROTOERROR_MSG_LEN);
ctx->sent_protoerror_msg = 1;
pxy_discard_inbuf(bev);
evbuffer_drain(outbuf, evbuffer_get_length(outbuf));
free(packet);
return 1;
}
free(packet);
}
return 0;
}
static void NONNULL(1,2)
prototcp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src: ENTER, size=%zu, fd=%d\n",
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd);
#endif /* DEBUG_PROXY */
if (ctx->dst.closed) {
pxy_discard_inbuf(bev);
return;
}
if (prototcp_try_send_userauth_msg(bev, ctx)) {
return;
}
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
if (prototcp_try_validate_proto(bev, ctx, inbuf, outbuf) != 0) {
return;
}
if (!ctx->sent_sslproxy_header) {
size_t packet_size = evbuffer_get_length(inbuf);
// +2 is for \r\n
unsigned char *packet = pxy_malloc_packet(packet_size + ctx->sslproxy_header_len + 2, ctx);
if (!packet) {
return;
}
evbuffer_remove(inbuf, packet, packet_size);
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src: ORIG packet, size=%zu, fd=%d:\n%.*s\n",
packet_size, ctx->fd, (int)packet_size, packet);
#endif /* DEBUG_PROXY */
pxy_insert_sslproxy_header(ctx, packet, &packet_size);
evbuffer_add(outbuf, packet, packet_size);
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src: NEW packet, size=%zu, fd=%d:\n%.*s\n",
packet_size, ctx->fd, (int)packet_size, packet);
#endif /* DEBUG_PROXY */
free(packet);
}
else {
evbuffer_add_buffer(outbuf, inbuf);
}
pxy_try_set_watermark(bev, ctx, ctx->dst.bev);
}
static void NONNULL(1)
prototcp_bev_readcb_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_dst: ENTER, size=%zu, fd=%d\n",
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd);
#endif /* DEBUG_PROXY */
if (ctx->src.closed) {
pxy_discard_inbuf(bev);
return;
}
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
evbuffer_add_buffer(outbuf, inbuf);
pxy_try_set_watermark(bev, ctx, ctx->src.bev);
}
static void NONNULL(1)
prototcp_bev_readcb_srvdst(UNUSED struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_readcb_srvdst: readcb called on srvdst, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
log_err_printf("prototcp_bev_readcb_srvdst: readcb called on srvdst\n");
}
static void NONNULL(1)
prototcp_bev_readcb_src_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src_child: ENTER, size=%zu, child fd=%d, fd=%d\n",
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (ctx->dst.closed) {
pxy_discard_inbuf(bev);
return;
}
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
if (!ctx->removed_sslproxy_header) {
size_t packet_size = evbuffer_get_length(inbuf);
unsigned char *packet = pxy_malloc_packet(packet_size, ctx->conn);
if (!packet) {
return;
}
evbuffer_remove(inbuf, packet, packet_size);
pxy_try_remove_sslproxy_header(ctx, packet, &packet_size);
evbuffer_add(outbuf, packet, packet_size);
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_src_child: NEW packet, size=%zu, child fd=%d, fd=%d:\n%.*s\n",
packet_size, ctx->fd, ctx->conn->fd, (int)packet_size, packet);
#endif /* DEBUG_PROXY */
free(packet);
} else {
evbuffer_add_buffer(outbuf, inbuf);
}
pxy_try_set_watermark(bev, ctx->conn, ctx->dst.bev);
}
static void NONNULL(1)
prototcp_bev_readcb_dst_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_readcb_dst_child: ENTER, size=%zu, child fd=%d, fd=%d\n",
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (ctx->src.closed) {
pxy_discard_inbuf(bev);
return;
}
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
evbuffer_add_buffer(outbuf, inbuf);
pxy_try_set_watermark(bev, ctx->conn, ctx->src.bev);
}
int
prototcp_try_close_unauth_conn(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
if (ctx->opts->user_auth && !ctx->user) {
size_t outbuflen = evbuffer_get_length(bufferevent_get_output(bev));
if (outbuflen > 0) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_unauth_conn: Not closing unauth conn, outbuflen=%zu, fd=%d\n", outbuflen, ctx->fd);
#endif /* DEBUG_PROXY */
} else if (ctx->sent_userauth_msg) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_unauth_conn: Closing unauth conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 1);
} else {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_unauth_conn: Not sent userauth msg yet, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
}
return 1;
}
return 0;
}
static int NONNULL(1,2)
prototcp_try_close_protoerror_conn(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
if (ctx->opts->validate_proto && ctx->sent_protoerror_msg) {
size_t outbuflen = evbuffer_get_length(bufferevent_get_output(bev));
if (outbuflen > 0) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_protoerror_conn: Not closing protoerror conn, outbuflen=%zu, fd=%d\n", outbuflen, ctx->fd);
#endif /* DEBUG_PROXY */
} else {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_protoerror_conn: Closing protoerror conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 1);
}
return 1;
}
return 0;
}
static void NONNULL(1)
prototcp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (prototcp_try_close_unauth_conn(bev, ctx)) {
return;
}
if (prototcp_try_close_protoerror_conn(bev, ctx)) {
return;
}
if (ctx->dst.closed) {
if (pxy_try_close_conn_end(&ctx->src, ctx) == 1) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src: other->closed, terminate conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 1);
}
return;
}
pxy_try_unset_watermark(bev, ctx, &ctx->dst);
}
static int NONNULL(1,2)
prototcp_connect_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_connect_dst: writecb before connected, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
// @attention Sometimes dst write cb fires but not event cb, especially if the listener cb is not finished yet, so the conn stalls.
// This is a workaround for this error condition, nothing else seems to work.
// @attention Do not try to free the conn here, since the listener cb may not be finished yet, which causes multithreading issues
// XXX: Workaround, should find the real cause: BEV_OPT_DEFER_CALLBACKS?
ctx->protoctx->bev_eventcb(bev, BEV_EVENT_CONNECTED, ctx);
return pxy_bev_eventcb_postexec_logging_and_stats(bev, BEV_EVENT_CONNECTED, ctx);
}
static void NONNULL(1)
prototcp_bev_writecb_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_dst: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (!ctx->dst_connected) {
if (prototcp_connect_dst(bev, ctx) == -1) {
return;
}
}
if (ctx->src.closed) {
if (pxy_try_close_conn_end(&ctx->dst, ctx) == 1) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_dst: other->closed, terminate conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 0);
}
return;
}
pxy_try_unset_watermark(bev, ctx, &ctx->src);
}
static void NONNULL(1)
prototcp_bev_writecb_srvdst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_srvdst: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (!ctx->srvdst_connected) {
pxy_connect_srvdst(bev, ctx);
}
}
static void NONNULL(1)
prototcp_bev_writecb_src_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (ctx->dst.closed) {
if (pxy_try_close_conn_end(&ctx->src, ctx->conn) == 1) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src_child: other->closed, terminate conn, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term_child(ctx);
}
return;
}
pxy_try_unset_watermark(bev, ctx->conn, &ctx->dst);
}
static void NONNULL(1,2)
prototcp_connect_dst_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_connect_dst_child: writecb before connected, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
// @attention Sometimes dst write cb fires but not event cb, especially if the listener cb is not finished yet, so the conn stalls.
// This is a workaround for this error condition, nothing else seems to work.
// @attention Do not try to free the conn here, since the listener cb may not be finished yet, which causes multithreading issues
// XXX: Workaround, should find the real cause: BEV_OPT_DEFER_CALLBACKS?
ctx->protoctx->bev_eventcb(bev, BEV_EVENT_CONNECTED, ctx);
pxy_bev_eventcb_postexec_stats_child(BEV_EVENT_CONNECTED, ctx);
}
static void NONNULL(1)
prototcp_bev_writecb_dst_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_dst_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
prototcp_connect_dst_child(bev, ctx);
}
if (ctx->src.closed) {
if (pxy_try_close_conn_end(&ctx->dst, ctx->conn) == 1) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_dst_child: other->closed, terminate conn, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term_child(ctx);
}
return;
}
pxy_try_unset_watermark(bev, ctx->conn, &ctx->src);
}
static void NONNULL(1)
prototcp_close_srvdst(pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINER, "prototcp_close_srvdst: Closing srvdst, srvdst fd=%d, fd=%d\n", bufferevent_getfd(ctx->srvdst.bev), ctx->fd);
#endif /* DEBUG_PROXY */
// @attention Free the srvdst of the conn asap, we don't need it anymore, but we need its fd
// @attention When both eventcb and writecb for srvdst are enabled, either eventcb or writecb may get a NULL srvdst bev, causing a crash with signal 10.
// So, from this point on, we should check if srvdst is NULL or not.
ctx->srvdst.free(ctx->srvdst.bev, ctx);
ctx->srvdst.bev = NULL;
ctx->srvdst.closed = 1;
}
static int NONNULL(1)
prototcp_enable_src(pxy_conn_ctx_t *ctx)
{
if (prototcp_setup_src(ctx) == -1) {
return -1;
}
bufferevent_setcb(ctx->src.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
prototcp_close_srvdst(ctx);
if (pxy_setup_child_listener(ctx) == -1) {
return -1;
}
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINER, "prototcp_enable_src: Enabling src, %s, child_fd=%d, fd=%d\n", ctx->sslproxy_header, ctx->child_fd, ctx->fd);
#endif /* DEBUG_PROXY */
// Now open the gates
bufferevent_enable(ctx->src.bev, EV_READ|EV_WRITE);
return 0;
}
static void NONNULL(1,2)
prototcp_bev_eventcb_connected_src(UNUSED struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_src: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
}
static void NONNULL(1,2)
prototcp_bev_eventcb_connected_dst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_dst: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
ctx->dst_connected = 1;
if (ctx->srvdst_connected && ctx->dst_connected && !ctx->connected) {
ctx->connected = 1;
if (prototcp_enable_src(ctx) == -1) {
return;
}
}
}
static void NONNULL(1,2)
prototcp_bev_eventcb_connected_srvdst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_srvdst: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
ctx->srvdst_connected = 1;
bufferevent_enable(ctx->srvdst.bev, EV_WRITE);
if (prototcp_setup_dst(ctx) == -1) {
return;
}
bufferevent_setcb(ctx->dst.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
bufferevent_enable(ctx->dst.bev, EV_READ|EV_WRITE);
if (bufferevent_socket_connect(ctx->dst.bev, (struct sockaddr *)&ctx->spec->conn_dst_addr, ctx->spec->conn_dst_addrlen) == -1) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_connected_srvdst: FAILED bufferevent_socket_connect for dst, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 1);
return;
}
if (ctx->srvdst_connected && ctx->dst_connected && !ctx->connected) {
ctx->connected = 1;
if (prototcp_enable_src(ctx) == -1) {
return;
}
}
if (!ctx->term && !ctx->enomem) {
pxy_userauth(ctx);
}
}
void
prototcp_bev_eventcb_eof_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_src: ENTER, fd=%d\n", ctx->fd);
pxy_log_dbg_evbuf_info(ctx, &ctx->src, &ctx->dst);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
log_err_level_printf(LOG_WARNING, "EOF on outbound connection before connection establishment\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_src: EOF on outbound connection before connection establishment, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
ctx->dst.closed = 1;
} else if (!ctx->dst.closed) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_src: !other->closed, terminate conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (pxy_try_consume_last_input(bev, ctx) == -1) {
return;
}
pxy_try_close_conn_end(&ctx->dst, ctx);
}
pxy_try_disconnect(ctx, &ctx->src, &ctx->dst, 1);
}
void
prototcp_bev_eventcb_eof_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_dst: ENTER, fd=%d\n", ctx->fd);
pxy_log_dbg_evbuf_info(ctx, &ctx->dst, &ctx->src);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
log_err_level_printf(LOG_WARNING, "EOF on outbound connection before connection establishment\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_dst: EOF on outbound connection before connection establishment, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
ctx->src.closed = 1;
} else if (!ctx->src.closed) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_dst: !other->closed, terminate conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (pxy_try_consume_last_input(bev, ctx) == -1) {
return;
}
pxy_try_close_conn_end(&ctx->src, ctx);
}
pxy_try_disconnect(ctx, &ctx->dst, &ctx->src, 0);
}
void
prototcp_bev_eventcb_eof_srvdst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_srvdst: EOF on outbound connection before connection establishment, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
log_err_level_printf(LOG_WARNING, "EOF on outbound connection before connection establishment on srvdst\n");
pxy_conn_term(ctx, 0);
}
void
prototcp_bev_eventcb_error_src(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_src: BEV_EVENT_ERROR, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
ctx->dst.closed = 1;
} else if (!ctx->dst.closed) {
pxy_try_close_conn_end(&ctx->dst, ctx);
}
pxy_try_disconnect(ctx, &ctx->src, &ctx->dst, 1);
}
void
prototcp_bev_eventcb_error_dst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_dst: BEV_EVENT_ERROR, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
ctx->src.closed = 1;
} else if (!ctx->src.closed) {
pxy_try_close_conn_end(&ctx->src, ctx);
}
pxy_try_disconnect(ctx, &ctx->dst, &ctx->src, 0);
}
void
prototcp_bev_eventcb_error_srvdst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_srvdst: BEV_EVENT_ERROR, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_srvdst: ERROR !ctx->connected, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 0);
}
}
static void NONNULL(1,2)
prototcp_bev_eventcb_connected_src_child(UNUSED struct bufferevent *bev, UNUSED pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_src_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
}
static void NONNULL(1,2)
prototcp_bev_eventcb_connected_dst_child(UNUSED struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_connected_dst_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
ctx->connected = 1;
// @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);
}
static void NONNULL(1,2)
prototcp_bev_eventcb_eof_src_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_src_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
pxy_log_dbg_evbuf_info(ctx->conn, &ctx->src, &ctx->dst);
#endif /* DEBUG_PROXY */
// @todo How to handle the following case?
if (!ctx->connected) {
log_err_level_printf(LOG_WARNING, "EOF on outbound connection before connection establishment\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_src_child: EOF on outbound connection before connection establishment, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
ctx->dst.closed = 1;
} else if (!ctx->dst.closed) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_src_child: !other->closed, terminate conn, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (pxy_try_consume_last_input_child(bev, ctx) == -1) {
return;
}
pxy_try_close_conn_end(&ctx->dst, ctx->conn);
}
pxy_try_disconnect_child(ctx, &ctx->src, &ctx->dst);
}
void
prototcp_bev_eventcb_eof_dst_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_dst_child: ENTER, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
pxy_log_dbg_evbuf_info(ctx->conn, &ctx->dst, &ctx->src);
#endif /* DEBUG_PROXY */
// @todo How to handle the following case?
if (!ctx->connected) {
log_err_level_printf(LOG_WARNING, "EOF on outbound connection before connection establishment\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_eof_dst_child: EOF on outbound connection before connection establishment, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
ctx->src.closed = 1;
} else if (!ctx->src.closed) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_eventcb_eof_dst_child: !other->closed, terminate conn, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (pxy_try_consume_last_input_child(bev, ctx) == -1) {
return;
}
pxy_try_close_conn_end(&ctx->src, ctx->conn);
}
pxy_try_disconnect_child(ctx, &ctx->dst, &ctx->src);
}
static void NONNULL(1,2)
prototcp_bev_eventcb_error_src_child(UNUSED struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_src_child: BEV_EVENT_ERROR, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
/* the callout to the original destination failed,
* e.g. because it asked for client cert auth, so
* close the accepted socket and clean up */
ctx->dst.closed = 1;
} else if (!ctx->dst.closed) {
/* if the other end is still open and doesn't have data
* to send, close it, otherwise its writecb will close
* it after writing what's left in the output buffer */
pxy_try_close_conn_end(&ctx->dst, ctx->conn);
}
pxy_try_disconnect_child(ctx, &ctx->src, &ctx->dst);
}
void
prototcp_bev_eventcb_error_dst_child(UNUSED struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_bev_eventcb_error_dst_child: BEV_EVENT_ERROR, child fd=%d, fd=%d\n", ctx->fd, ctx->conn->fd);
#endif /* DEBUG_PROXY */
if (!ctx->connected) {
/* the callout to the original destination failed,
* e.g. because it asked for client cert auth, so
* close the accepted socket and clean up */
ctx->src.closed = 1;
} else if (!ctx->src.closed) {
/* if the other end is still open and doesn't have data
* to send, close it, otherwise its writecb will close
* it after writing what's left in the output buffer */
pxy_try_close_conn_end(&ctx->src, ctx->conn);
}
pxy_try_disconnect_child(ctx, &ctx->dst, &ctx->src);
}
void
prototcp_bev_eventcb_src(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
{
if (events & BEV_EVENT_CONNECTED) {
prototcp_bev_eventcb_connected_src(bev, ctx);
} else if (events & BEV_EVENT_EOF) {
prototcp_bev_eventcb_eof_src(bev, ctx);
} else if (events & BEV_EVENT_ERROR) {
prototcp_bev_eventcb_error_src(bev, ctx);
}
}
static void NONNULL(1)
prototcp_bev_eventcb_dst(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
{
if (events & BEV_EVENT_CONNECTED) {
prototcp_bev_eventcb_connected_dst(bev, ctx);
} else if (events & BEV_EVENT_EOF) {
prototcp_bev_eventcb_eof_dst(bev, ctx);
} else if (events & BEV_EVENT_ERROR) {
prototcp_bev_eventcb_error_dst(bev, ctx);
}
}
static void NONNULL(1)
prototcp_bev_eventcb_srvdst(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
{
if (events & BEV_EVENT_CONNECTED) {
prototcp_bev_eventcb_connected_srvdst(bev, ctx);
} else if (events & BEV_EVENT_EOF) {
prototcp_bev_eventcb_eof_srvdst(bev, ctx);
} else if (events & BEV_EVENT_ERROR) {
prototcp_bev_eventcb_error_srvdst(bev, ctx);
}
}
void
prototcp_bev_eventcb_src_child(struct bufferevent *bev, short events, pxy_conn_child_ctx_t *ctx)
{
if (events & BEV_EVENT_CONNECTED) {
prototcp_bev_eventcb_connected_src_child(bev, ctx);
} else if (events & BEV_EVENT_EOF) {
prototcp_bev_eventcb_eof_src_child(bev, ctx);
} else if (events & BEV_EVENT_ERROR) {
prototcp_bev_eventcb_error_src_child(bev, ctx);
}
}
void
prototcp_bev_eventcb_dst_child(struct bufferevent *bev, short events, pxy_conn_child_ctx_t *ctx)
{
if (events & BEV_EVENT_CONNECTED) {
prototcp_bev_eventcb_connected_dst_child(bev, ctx);
} else if (events & BEV_EVENT_EOF) {
prototcp_bev_eventcb_eof_dst_child(bev, ctx);
} else if (events & BEV_EVENT_ERROR) {
prototcp_bev_eventcb_error_dst_child(bev, ctx);
}
}
static void NONNULL(1)
prototcp_bev_readcb(struct bufferevent *bev, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
if (bev == ctx->src.bev) {
prototcp_bev_readcb_src(bev, ctx);
} else if (bev == ctx->dst.bev) {
prototcp_bev_readcb_dst(bev, ctx);
} else if (bev == ctx->srvdst.bev) {
prototcp_bev_readcb_srvdst(bev, ctx);
} else {
log_err_printf("prototcp_bev_readcb: UNKWN conn end\n");
}
}
void
prototcp_bev_writecb(struct bufferevent *bev, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
if (bev == ctx->src.bev) {
prototcp_bev_writecb_src(bev, ctx);
} else if (bev == ctx->dst.bev) {
prototcp_bev_writecb_dst(bev, ctx);
} else if (bev == ctx->srvdst.bev) {
prototcp_bev_writecb_srvdst(bev, ctx);
} else {
log_err_printf("prototcp_bev_writecb: UNKWN conn end\n");
}
}
static void NONNULL(1)
prototcp_bev_eventcb(struct bufferevent *bev, short events, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
if (bev == ctx->src.bev) {
prototcp_bev_eventcb_src(bev, events, ctx);
} else if (bev == ctx->dst.bev) {
prototcp_bev_eventcb_dst(bev, events, ctx);
} else if (bev == ctx->srvdst.bev) {
prototcp_bev_eventcb_srvdst(bev, events, ctx);
} else {
log_err_printf("prototcp_bev_eventcb: UNKWN conn end\n");
}
}
static void NONNULL(1)
prototcp_bev_readcb_child(struct bufferevent *bev, void *arg)
{
pxy_conn_child_ctx_t *ctx = arg;
if (bev == ctx->src.bev) {
prototcp_bev_readcb_src_child(bev, ctx);
} else if (bev == ctx->dst.bev) {
prototcp_bev_readcb_dst_child(bev, ctx);
} else {
log_err_printf("prototcp_bev_readcb_child: UNKWN conn end\n");
}
}
void
prototcp_bev_writecb_child(struct bufferevent *bev, void *arg)
{
pxy_conn_child_ctx_t *ctx = arg;
if (bev == ctx->src.bev) {
prototcp_bev_writecb_src_child(bev, ctx);
} else if (bev == ctx->dst.bev) {
prototcp_bev_writecb_dst_child(bev, ctx);
} else {
log_err_printf("prototcp_bev_writecb_child: UNKWN conn end\n");
}
}
static void NONNULL(1)
prototcp_bev_eventcb_child(struct bufferevent *bev, short events, void *arg)
{
pxy_conn_child_ctx_t *ctx = arg;
if (bev == ctx->src.bev) {
prototcp_bev_eventcb_src_child(bev, events, ctx);
} else if (bev == ctx->dst.bev) {
prototcp_bev_eventcb_dst_child(bev, events, ctx);
} else {
log_err_printf("prototcp_bev_eventcb_child: UNKWN conn end\n");
}
}
protocol_t
prototcp_setup(pxy_conn_ctx_t *ctx)
{
ctx->protoctx->proto = PROTO_TCP;
ctx->protoctx->connectcb = prototcp_conn_connect;
ctx->protoctx->fd_readcb = prototcp_fd_readcb;
ctx->protoctx->bev_readcb = prototcp_bev_readcb;
ctx->protoctx->bev_writecb = prototcp_bev_writecb;
ctx->protoctx->bev_eventcb = prototcp_bev_eventcb;
return PROTO_TCP;
}
protocol_t
prototcp_setup_child(pxy_conn_child_ctx_t *ctx)
{
ctx->protoctx->proto = PROTO_TCP;
ctx->protoctx->connectcb = prototcp_connect_child;
ctx->protoctx->bev_readcb = prototcp_bev_readcb_child;
ctx->protoctx->bev_writecb = prototcp_bev_writecb_child;
ctx->protoctx->bev_eventcb = prototcp_bev_eventcb_child;
return PROTO_TCP;
}
/* vim: set noet ft=c: */