diff --git a/lib/tcp_io.c b/lib/tcp_io.c index 2c7550fd..beacc753 100644 --- a/lib/tcp_io.c +++ b/lib/tcp_io.c @@ -329,3 +329,10 @@ void tcp_conn_set_flushed(struct tcp_conn* tc, void (*on_flushed)(struct tcp_con if (!tc) return; tc->on_flushed = on_flushed; } + +void tcp_conn_pause_read(struct tcp_conn* tc) { + if (!tc || tc->sock == SOCKET_INVALID) return; + uasync_set_socket_read(tc->ua, tc->socket_id, 0); + queue_waiter_cancel(tc->read_queue, &tc->read_waiter); + tc->read_paused = 1; +} diff --git a/lib/tcp_io.h b/lib/tcp_io.h index 6df99757..6d829730 100644 --- a/lib/tcp_io.h +++ b/lib/tcp_io.h @@ -94,4 +94,7 @@ void tcp_conn_destroy(struct tcp_conn* tc); // После вызова сбрасывается. Установить повторно можно в любой момент. void tcp_conn_set_flushed(struct tcp_conn* tc, void (*on_flushed)(struct tcp_conn* tc, void* arg)); +// Принудительная остановка чтения: убирает EPOLLIN и отменяет read_waiter. +void tcp_conn_pause_read(struct tcp_conn* tc); + #endif diff --git a/src/lwip_tcp/lwip_tcp.h b/src/lwip_tcp/lwip_tcp.h index 0b363d91..9300d549 100644 --- a/src/lwip_tcp/lwip_tcp.h +++ b/src/lwip_tcp/lwip_tcp.h @@ -247,6 +247,7 @@ err_t tcp_connect(struct tcp_pcb *pcb, uint32_t ip, uint16_t port, tcp_connected err_t tcp_write(struct tcp_pcb *pcb, const void *data, uint16_t len, uint8_t apiflags); err_t tcp_output(struct tcp_pcb *pcb); err_t tcp_close(struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); void tcp_abort(struct tcp_pcb *pcb); void tcp_recved(struct tcp_pcb *pcb, uint16_t len); diff --git a/src/proxy/tcp_proxy_client.c b/src/proxy/tcp_proxy_client.c index 2b5a7dd7..ced637aa 100644 --- a/src/proxy/tcp_proxy_client.c +++ b/src/proxy/tcp_proxy_client.c @@ -94,6 +94,7 @@ static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const ui } static int tcp_proxy_client_send_close(struct tcp_proxy_client_conn* pc) { + DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "PROXY SEND sid=%08x", pc->stream_id); int ret = tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_CLOSE, pc->stream_id, NULL, 0, 1); if (ret < 0) { pc->close_pending = 1; return -1; } @@ -111,6 +112,12 @@ static int tcp_proxy_client_send_error(struct tcp_proxy_client_conn* pc) { return 0; } +static int tcp_proxy_client_send_fin(struct tcp_proxy_client_conn* pc) { + DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "PROXY FIN RELAY sid=%08x", pc->stream_id); + return tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, + TCP_PROXY_SUBCMD_FIN, pc->stream_id, NULL, 0, 1); +} + // ==================================================================== // Вывод: lwIP TCP отправляет IP пакеты через этот callback // ==================================================================== @@ -248,7 +255,7 @@ static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, void* arg) { tcp_recved(pc->pcb, pc->tx_len); u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; if (pc->tun_closed && !pc->close_sent && !pc->close_pending) - tcp_proxy_client_send_close(pc); + tcp_proxy_client_send_fin(pc); } } @@ -257,15 +264,13 @@ static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbu if (!pc) { if (p) pbuf_free(p); return LERR_OK; } if (p == NULL || err != LERR_OK) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY FIN sid=%08x pcb_state=%u sndbuf=%u cwnd=%u unsent=%p unacked=%p close_sent=%d", + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY FIN sid=%08x pcb_state=%u sndbuf=%u cwnd=%u unsent=%p unacked=%p", pc->stream_id, pcb->state, pcb->snd_buf, pcb->cwnd, - (void*)pcb->unsent, (void*)pcb->unacked, pc->close_sent); + (void*)pcb->unsent, (void*)pcb->unacked); { uint16_t wnd_gap = TCP_WND_MAX(pcb) - pcb->rcv_wnd; if (wnd_gap > 0) tcp_recved(pcb, wnd_gap); } pc->tun_closed = 1; - if (!pc->tx_buf && !pc->close_sent && !pc->close_pending) { - if (tcp_proxy_client_send_close(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY FIN send_close failed sid=%08x", pc->stream_id); - } + if (!pc->tx_buf) tcp_proxy_client_send_fin(pc); return LERR_OK; } @@ -314,35 +319,16 @@ static err_t tcp_proxy_client_poll_cb(void *arg, struct tcp_pcb *pcb) { if (!pc) return LERR_OK; if (pc->error) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY CLEANUP error sid=%08x tun_closed=%d rem_closed=%d", pc->stream_id, pc->tun_closed, pc->rem_closed); + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY CLEANUP error sid=%08x", pc->stream_id); if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_abort(pc->pcb); pc->pcb = NULL; } tcp_proxy_client_conn_free(pc); return LERR_OK; } - /* Retry pending CLOSE/ERROR if previous send failed */ if (pc->close_pending) { if (pc->error) tcp_proxy_client_send_error(pc); else tcp_proxy_client_send_close(pc); } - - if (pc->tun_closed && pc->rem_closed && pc->pcb) { - uint32_t pending = pc->to_lwip ? queue_entry_count(pc->to_lwip) : 0; - int done = (pending == 0 && pcb->unsent == NULL && pcb->unacked == NULL); - uint32_t sndbuf = tcp_sndbuf(pcb); - if (done || sndbuf == 0) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY CLEANUP sid=%08x state=%u sndbuf=%u cwnd=%u pending=%u unsent=%p unacked=%p", - pc->stream_id, pcb->state, pcb->snd_buf, pcb->cwnd, - pending, (void*)pcb->unsent, (void*)pcb->unacked); - struct tcp_pcb *save = pc->pcb; pc->pcb = NULL; - tcp_arg(save, NULL); - { uint16_t wnd_gap = TCP_WND_MAX(save) - save->rcv_wnd; - if (wnd_gap > 0) tcp_recved(save, wnd_gap); } - while (save->unsent) { struct tcp_seg *seg = save->unsent; save->unsent = seg->next; u_free(seg); } - tcp_close(save); - tcp_proxy_client_conn_free(pc); - } - } return LERR_OK; } @@ -403,6 +389,11 @@ static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc) { uint32_t pending = pc->to_lwip ? queue_entry_count(pc->to_lwip) : 0; DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY FREE sid=%08x total_conns=%d to_lwip_q=%u", pc->stream_id, p->conn_count, pending); + if (pc->close_pending && p && p->inst) { + pc->close_pending = 0; + uint8_t subcmd = pc->error ? TCP_PROXY_SUBCMD_ERROR : TCP_PROXY_SUBCMD_CLOSE; + tcp_proxy_client_send_msg(p->inst, p->via_node_id, subcmd, pc->stream_id, NULL, 0, 1); + } struct tcp_proxy_client_conn **prev = &p->conns; while (*prev) { if (*prev == pc) { *prev = pc->next; p->conn_count--; break; } prev = &(*prev)->next; } if (pc->to_lwip) { @@ -427,7 +418,7 @@ static struct tcp_proxy_client_conn* tcp_proxy_client_find_conn(struct tcp_proxy static void tcp_proxy_client_handle_data(struct tcp_proxy_client* p, struct ETCP_CONN* conn, uint32_t stream_id, struct ll_entry* entry) { struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); if (!pc || pc->rem_closed || pc->error || pc->tun_closed) { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY DATA sid=%08x — no conn/closed, dropping", stream_id); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY DATA sid=%08x — no conn/closed, dropping", stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } size_t data_len = entry->len - TCP_PROXY_HDR_SIZE; @@ -442,24 +433,26 @@ static void tcp_proxy_client_handle_data(struct tcp_proxy_client* p, struct ETCP static void tcp_proxy_client_handle_close(struct tcp_proxy_client* p, uint32_t stream_id) { struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); - if (pc) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY REM_CLOSED sid=%08x tun_closed=%d", - stream_id, pc->tun_closed); - pc->rem_closed = 1; - } else { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY CLOSE sid=%08x — no conn, dropping", stream_id); - } + if (!pc) { DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY CLOSE sid=%08x — no conn, dropping", stream_id); return; } + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY REM_CLOSED sid=%08x tun_closed=%d", stream_id, pc->tun_closed); + pc->rem_closed = 1; + if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; } + etcp_router_waiter_cancel(pc->proxy->inst, pc->proxy->via_node_id, &pc->tx_waiter); + if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_close(pc->pcb); pc->pcb = NULL; } + tcp_proxy_client_conn_free(pc); } static void tcp_proxy_client_handle_error(struct tcp_proxy_client* p, uint32_t stream_id) { struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); - if (pc) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY ERROR from exit sid=%08x tun_closed=%d rem_closed=%d", - stream_id, pc->tun_closed, pc->rem_closed); - if (!pc->error) { pc->error = 1; tcp_proxy_client_send_close(pc); } - } else { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — no conn, silent drop", stream_id); - } + if (!pc) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — no conn, dropping", stream_id); return; } + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY ERROR from exit sid=%08x tun_closed=%d", stream_id, pc->tun_closed); + tcp_proxy_client_handle_close(p, stream_id); +} + +static void tcp_proxy_client_handle_fin(struct tcp_proxy_client* p, uint32_t stream_id) { + struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); + if (!pc || !pc->pcb) return; + tcp_shutdown(pc->pcb, 0, 1); } // ==================================================================== @@ -506,13 +499,15 @@ void tcp_proxy_client_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entr if (rc) { if (subcmd == TCP_PROXY_SUBCMD_DATA) { tcp_proxy_server_handle_data(inst, conn, entry, stream_id); return; } if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { tcp_proxy_server_handle_close(inst, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_ERROR) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "RP ERROR recv sid=%08x", stream_id); tcp_proxy_server_conn_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_ERROR) { tcp_proxy_server_handle_error(inst, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_FIN) { tcp_proxy_server_handle_fin(inst, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } } } if (proxy) { if (subcmd == TCP_PROXY_SUBCMD_DATA) { tcp_proxy_client_handle_data(proxy, conn, stream_id, entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { tcp_proxy_client_handle_close(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_ERROR) { tcp_proxy_client_handle_error(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { tcp_proxy_client_handle_close(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_ERROR) { tcp_proxy_client_handle_error(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_FIN) { tcp_proxy_client_handle_fin(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } } if (subcmd == TCP_PROXY_SUBCMD_ERROR) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — conn not found, silent drop", stream_id); diff --git a/src/proxy/tcp_proxy_server.c b/src/proxy/tcp_proxy_server.c index 5e4ed755..8be367b6 100644 --- a/src/proxy/tcp_proxy_server.c +++ b/src/proxy/tcp_proxy_server.c @@ -99,6 +99,12 @@ static void send_error(struct tcp_proxy_server_conn* rc) { } } +static void send_fin(struct tcp_proxy_server_conn* rc) { + struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; + if (!inst) return; + send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_FIN, rc->stream_id, NULL, 0, 1); +} + // ==================================================================== // Коллбэки tcp_io // ==================================================================== @@ -108,14 +114,13 @@ static void on_fin_cb(struct tcp_conn* tc, void* arg) { if (write_pending(tc)) tcp_conn_set_flushed(tc, on_flushed_cb); else - send_close(rc); + send_fin(rc); } static void on_flushed_cb(struct tcp_conn* tc, void* arg) { - (void)tc; struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; - if (rc->cli_closed) { tcp_proxy_server_conn_free(rc); return; } - send_close(rc); + if (rc->cli_closed) { shutdown(tc->sock, SHUT_WR); tcp_proxy_server_conn_free(rc); return; } + send_fin(rc); } static void on_error_cb(struct tcp_conn* tc, int err, void* arg) { @@ -161,6 +166,15 @@ static void read_queue_drain_cb(struct ll_queue* q, void* arg) { struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; struct ll_entry* e = queue_data_get(q); if (!e) { queue_resume_callback(q); return; } + if (rc->cli_closed) { + do { + memory_pool_free(rc->tc->data_pool, e->dgram); + queue_entry_free(e); + e = queue_data_get(q); + } while (e); + queue_resume_callback(q); + return; + } int ret = send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, e->dgram, e->len, 0); DEBUG_TRACE(DEBUG_CATEGORY_SOCKET, "SOCK:RECV fd=%d sid=%08x len=%u total=%d", (int)rc->tc->sock, rc->stream_id, e->len, rc->ctx ? rc->ctx->conn_count : 0); @@ -205,6 +219,11 @@ void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc) { int total = conn_total(rc); DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:FREE fd=%d sid=%08x total=%d cli_closed=%d fin=%d error=%d", rc->tc ? (int)rc->tc->sock : -1, rc->stream_id, total, rc->cli_closed, rc->tc ? rc->tc->fin : 0, rc->tc ? rc->tc->error : 0); + if (rc->close_pending && rc->ctx && rc->ctx->inst) { + rc->close_pending = 0; + uint8_t subcmd = (rc->tc && rc->tc->error) ? TCP_PROXY_SUBCMD_ERROR : TCP_PROXY_SUBCMD_CLOSE; + send_msg(rc->ctx->inst, rc->peer_node_id, subcmd, rc->stream_id, NULL, 0, 1); + } if (rc->ctx) { struct tcp_proxy_server_conn** prev = &rc->ctx->conns; while (*prev) { if (*prev == rc) { *prev = rc->next; rc->ctx->conn_count--; break; } prev = &(*prev)->next; } @@ -275,8 +294,8 @@ int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* c if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return -1; } struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(ctx, stream_id); - if (!rc || !rc->tc || rc->tc->sock == SOCKET_INVALID || rc->tc->error) { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TPS handle_data: no/error conn for sid=%08x, dropping", stream_id); + if (!rc || !rc->tc || rc->tc->sock == SOCKET_INVALID || rc->tc->error || rc->cli_closed) { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TPS handle_data: no/closed conn sid=%08x, dropping", stream_id); queue_dgram_free(entry); queue_entry_free(entry); return -1; } size_t data_len = entry->len - TCP_PROXY_HDR_SIZE; @@ -300,22 +319,47 @@ int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* c void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_id) { if (!inst) return; struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; - struct tcp_proxy_server_conn** prev = &ctx->conns; - while (*prev) { - struct tcp_proxy_server_conn* rc = *prev; - if (rc->stream_id == stream_id) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:CLOSE_RECV fd=%d sid=%08x total=%d fin=%d write_pend=%d", - rc->tc ? (int)rc->tc->sock : -1, stream_id, conn_total(rc), rc->tc ? rc->tc->fin : 0, rc->tc ? write_pending(rc->tc) : 0); - rc->cli_closed = 1; - if (rc->tc && rc->tc->connected && !write_pending(rc->tc)) - shutdown(rc->tc->sock, SHUT_WR); - if (rc->tc && rc->tc->fin && !write_pending(rc->tc)) - tcp_proxy_server_conn_free(rc); - return; + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(ctx, stream_id); + if (!rc) { DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy server: CLOSE sid=%08x — no conn", stream_id); return; } + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:CLOSE_RECV fd=%d sid=%08x total=%d fin=%d write_pend=%d", + rc->tc ? (int)rc->tc->sock : -1, stream_id, conn_total(rc), + rc->tc ? rc->tc->fin : 0, rc->tc ? write_pending(rc->tc) : 0); + rc->cli_closed = 1; + if (!rc->tc) { tcp_proxy_server_conn_free(rc); return; } + tcp_conn_pause_read(rc->tc); + { + struct ll_entry* e; + while ((e = queue_data_get(rc->tc->read_queue))) { + memory_pool_free(rc->tc->data_pool, e->dgram); + queue_entry_free(e); } - prev = &rc->next; + queue_resume_callback(rc->tc->read_queue); + } + if (write_pending(rc->tc)) { + tcp_conn_set_flushed(rc->tc, on_flushed_cb); + } else { + if (rc->tc->connected) shutdown(rc->tc->sock, SHUT_WR); + tcp_proxy_server_conn_free(rc); } - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy server: CLOSE sid=%08x — no conn, dropping", stream_id); +} + +void tcp_proxy_server_handle_error(struct UTUN_INSTANCE* inst, uint32_t stream_id) { + if (!inst) return; + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(ctx, stream_id); + if (!rc) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy server: ERROR sid=%08x — no conn", stream_id); return; } + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:ERROR_RECV fd=%d sid=%08x total=%d fin=%d write_pend=%d", + rc->tc ? (int)rc->tc->sock : -1, stream_id, conn_total(rc), + rc->tc ? rc->tc->fin : 0, rc->tc ? write_pending(rc->tc) : 0); + tcp_proxy_server_handle_close(inst, stream_id); +} + +void tcp_proxy_server_handle_fin(struct UTUN_INSTANCE* inst, uint32_t stream_id) { + if (!inst) return; + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(ctx, stream_id); + if (!rc || !rc->tc || !rc->tc->connected) return; + shutdown(rc->tc->sock, SHUT_WR); } // ==================================================================== diff --git a/src/proxy/tcp_proxy_server.h b/src/proxy/tcp_proxy_server.h index 14db2290..9c57d311 100644 --- a/src/proxy/tcp_proxy_server.h +++ b/src/proxy/tcp_proxy_server.h @@ -16,6 +16,7 @@ struct ETCP_CONN; #define TCP_PROXY_SUBCMD_DATA 0x03 #define TCP_PROXY_SUBCMD_CLOSE 0x04 #define TCP_PROXY_SUBCMD_ERROR 0x05 +#define TCP_PROXY_SUBCMD_FIN 0x06 #define TCP_PROXY_HDR_SIZE 6 // svc_id(1)+subcmd(1)+stream_id(4) #define TCP_PROXY_CONNECT_HDR_SIZE 12 // HDR_SIZE + dest_ip(4)+dest_port(2) @@ -58,5 +59,7 @@ int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry uint32_t stream_id, uint64_t src_node_id); int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id); void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_id); +void tcp_proxy_server_handle_error(struct UTUN_INSTANCE* inst, uint32_t stream_id); +void tcp_proxy_server_handle_fin(struct UTUN_INSTANCE* inst, uint32_t stream_id); #endif