penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

commit 6d7ed556d28d928e6fb16772c6637232d9827bf8

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-06-09T16:52:16Z
subjectfix stream connection setup mishandling
commit 6d7ed556d28d928e6fb16772c6637232d9827bf8
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-06-09T16:52:16Z

    fix stream connection setup mishandling
    
    Keep the client bufferevent disabled while the upstream TCP
    connect is still in progress. Only enable the upstream bufferevent
    during setup so the async connect can complete.
    
    This prevents client data from being read, sniffed, and queued
    before the upstream connection is established. In the failure case,
    tinyproxy could log client SNI while the upstream server never saw
    the connection.
---
 src/stream_conn.c  | 11 ++++++++---
 src/stream_route.h |  1 +
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/stream_conn.c b/src/stream_conn.c
index 85f1dd0..8f4f422 100644
--- a/src/stream_conn.c
+++ b/src/stream_conn.c
@@ -225,10 +225,10 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
 	char peer_buf[128];
 
 	if (events & BEV_EVENT_CONNECTED) {
+		conn->upstream_connected = true;
 		set_idle_timeouts(conn, conn->route);
 
 		bufferevent_enable(conn->client, EV_READ | EV_WRITE);
-		bufferevent_enable(conn->upstream, EV_READ | EV_WRITE);
 		return;
 	}
 
@@ -239,7 +239,9 @@ void stream_upstream_event_cb(struct bufferevent *bev, short events, void *arg)
 			client_sni = stream_sniff_log_sni(&conn->sniff);
 		}
 
-		LOG_WARN("upstream connection timed out",
+		LOG_WARN(conn->upstream_connected
+				? "upstream I/O timed out"
+				: "upstream connect timed out",
 			"listen", _LOGV_ENDPOINT(&r->listen),
 			"upstream", _LOGV_ENDPOINT(&r->upstream),
 			"client_addr", _LOGV_SOCKADDR(&conn->peer_addr, conn->peer_addr_len,
@@ -501,7 +503,10 @@ void worker_adopt_client_fd(struct worker *w, struct worker_stream_client_msg *a
 		}
 	}
 
-	bufferevent_enable(conn->client, EV_READ | EV_WRITE);
+	/*
+	 * Enable upstream writes so the async connect can complete.
+	 * Client reads are enabled only after BEV_EVENT_CONNECTED.
+	 */
 	bufferevent_enable(conn->upstream, EV_READ | EV_WRITE);
 }
 
diff --git a/src/stream_route.h b/src/stream_route.h
index 30fec74..adc08a8 100644
--- a/src/stream_route.h
+++ b/src/stream_route.h
@@ -32,6 +32,7 @@ typedef struct conn_s {
 
 	struct stream_sniff sniff;
 
+	bool upstream_connected;
 	bool close_after_client_eof;
 	bool close_client_after_drain;
 } conn_t;