diff --git a/src/tcp_proxy.c b/src/tcp_proxy.c index 84d96257..ff81c3a2 100644 --- a/src/tcp_proxy.c +++ b/src/tcp_proxy.c @@ -93,13 +93,21 @@ static int send_data(struct proxy_conn* pc, const uint8_t* data, uint16_t len) { } static int send_close(struct proxy_conn* pc) { - return proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, - TCP_PROXY_SUBCMD_CLOSE, pc->stream_id, NULL, 0); + int ret = proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, + TCP_PROXY_SUBCMD_CLOSE, pc->stream_id, NULL, 0); + if (ret < 0) { pc->close_pending = 1; return -1; } + pc->close_pending = 0; + pc->close_sent = 1; + return 0; } static int send_error(struct proxy_conn* pc) { - return proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, - TCP_PROXY_SUBCMD_ERROR, pc->stream_id, NULL, 0); + int ret = proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, + TCP_PROXY_SUBCMD_ERROR, pc->stream_id, NULL, 0); + if (ret < 0) { pc->close_pending = 1; return -1; } + pc->close_pending = 0; + pc->close_sent = 1; + return 0; } // ==================================================================== @@ -238,9 +246,8 @@ static err_t proxy_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t { 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->close_sent) { + if (!pc->close_sent && !pc->close_pending) { if (send_close(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY FIN send_close failed sid=%08x", pc->stream_id); - pc->close_sent = 1; } return LERR_OK; } @@ -276,7 +283,6 @@ static void proxy_err_cb(void *arg, err_t err) { err, pc->stream_id, pc->pcb ? pc->pcb->state : 0, pc->tun_closed, pc->rem_closed, pc->connected); pc->error = 1; if (send_error(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY send_error failed sid=%08x", pc->stream_id); - pc->close_sent = 1; } static err_t proxy_poll_cb(void *arg, struct tcp_pcb *pcb) { @@ -290,6 +296,12 @@ static err_t proxy_poll_cb(void *arg, struct tcp_pcb *pcb) { return LERR_OK; } + /* Retry pending CLOSE/ERROR if previous send failed */ + if (pc->close_pending) { + if (pc->error) send_error(pc); + else 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); diff --git a/src/tcp_proxy.h b/src/tcp_proxy.h index 00b6bd8b..f6369fa4 100644 --- a/src/tcp_proxy.h +++ b/src/tcp_proxy.h @@ -32,6 +32,7 @@ struct proxy_conn { uint8_t rem_closed; // exit отправил CLOSE uint8_t error; // ошибка, немедленная очистка uint8_t close_sent; // отправили CLOSE/ERROR в exit + uint8_t close_pending; // CLOSE/ERROR не доставлен, ждём повтора uint8_t connected; // CONNECTED(OK) получен от exit uint8_t dest_ip[4];