// tcp_proxy_client.c — TCP прокси-клиент: стек lwIP TCP → ETCP → удалённый exit узел #include "tcp_proxy_client.h" #include "socks_proxy.h" #include "lwip_tcp/lwip_tcp.h" #include "lwip_tcp/lwip_tcp_priv.h" #include "lwip_tcp/lwip_tcp_opts.h" #include "config_parser.h" #include "tun_if.h" #include "utun_instance.h" #include "etcp.h" #include "etcp_api.h" #include "../routing_layer/topo_node.h" #include "etcp_router.h" #include "tcp_proxy_server.h" #include "udp_proxy.h" #include "icmp_proxy.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #include "../lib/ll_queue.h" #include "../lib/memory_pool.h" #include "../lib/mem.h" #include #include #include #ifndef _WIN32 #include #include #endif #ifndef INADDR_ANY #define INADDR_ANY 0 #endif // ==================================================================== // Предварительные объявления // ==================================================================== static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); static err_t tcp_proxy_client_sent_cb(void *arg, struct tcp_pcb *pcb, uint16_t len); static void tcp_proxy_client_err_cb(void *arg, err_t err); static err_t tcp_proxy_client_poll_cb(void *arg, struct tcp_pcb *pcb); static err_t tcp_proxy_client_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err); static err_t tcp_proxy_client_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip); static void tcp_proxy_client_feed_from_transport(struct tcp_proxy_client_conn *pc); static struct ll_entry* tcp_proxy_client_entry_from_data(struct memory_pool* pool, const uint8_t* data, uint16_t len); static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc); static void tcp_proxy_client_conn_finish(struct tcp_proxy_client_conn *pc); static int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len, int force); static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len, int force); static void tcp_proxy_client_tx_queue_drain_cb(struct ll_queue* q, void* arg); static void tcp_proxy_client_pause_resume_cb(struct ll_queue* q, void* arg); static void tcp_proxy_client_retry_timer_cb(void* arg); // ==================================================================== // Помощник ll_entry // ==================================================================== static struct ll_entry* tcp_proxy_client_entry_from_data(struct memory_pool* pool, const uint8_t* data, uint16_t len) { struct ll_entry* e = queue_entry_new_from_pool(pool); if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client_entry_from_data: pool exhausted"); return NULL; } e->len = 0; e->dgram = NULL; if (len > 0) { uint8_t* buf = u_malloc(len); if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client_entry_from_data: malloc(%u) failed", len); queue_entry_free(e); return NULL; } memcpy(buf, data, len); e->dgram = buf; e->len = len; } return e; } // ==================================================================== // Протокол: сборка и отправка сообщений прокси через ETCP // ==================================================================== static int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len, int force) { struct ll_entry* e = queue_entry_new(0); if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; } e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + len); if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; } e->dgram[0] = ETCP_RT_ID_TCP_PROXY_SERVER; e->dgram[1] = subcmd; memcpy(e->dgram + 2, &sid, 4); if (len > 0) memcpy(e->dgram + TCP_PROXY_HDR_SIZE, data, len); e->len = TCP_PROXY_HDR_SIZE + len; return etcp_route_send(inst, group_id, dst, e, force); } static int tcp_proxy_client_send_connect(struct tcp_proxy_client_conn* pc) { uint8_t buf[6]; memcpy(buf, pc->dest_ip, 4); memcpy(buf + 4, &pc->dest_port, 2); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client: CONNECT sid=%08x to %d.%d.%d.%d:%d via node %016llx", pc->stream_id, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port), (unsigned long long)pc->proxy->via_node_id); return tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_CONNECT, pc->stream_id, buf, 6, 1); } static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len, int force) { int ret = tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_DATA, pc->stream_id, data, len, force); if (ret == 0) pc->bytes_to_exit += len; else { pc->bp_count++; DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP send-fail sid=%08x len=%u ret=%d tot=%u bp#=%u rcv_wnd=%u", pc->stream_id, len, ret, pc->bytes_to_exit, pc->bp_count, pc->pcb ? pc->pcb->rcv_wnd : 0); } return ret; } static int tcp_proxy_client_send_close(struct tcp_proxy_client_conn* pc) { DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY SEND sid=%08x", pc->stream_id); int ret = tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_CLOSE, pc->stream_id, NULL, 0, 1); if (ret < 0) { pc->close_pending = 1; return -1; } pc->close_pending = 0; pc->close_sent = 1; return 0; } static int tcp_proxy_client_send_error(struct tcp_proxy_client_conn* pc) { int ret = tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_ERROR, pc->stream_id, NULL, 0, 1); if (ret < 0) { pc->close_pending = 1; return -1; } pc->close_pending = 0; pc->close_sent = 1; return 0; } static int tcp_proxy_client_send_fin(struct tcp_proxy_client_conn* pc) { DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY FIN RELAY sid=%08x", pc->stream_id); return tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_FIN, pc->stream_id, NULL, 0, 1); } // ==================================================================== // Вывод: lwIP TCP отправляет IP пакеты через этот callback // ==================================================================== static err_t tcp_proxy_client_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip) { struct tcp_proxy_client *proxy = (struct tcp_proxy_client *)arg; (void)src_ip; (void)dst_ip; uint16_t len = p->tot_len; if (len > 2000) return LERR_BUF; uint8_t buf[2002]; if (proxy->tun) { pbuf_copy_partial(p, buf, len, 0); ssize_t wr = tun_platform_write(proxy->tun, buf, len); if (wr != (ssize_t)len) { uint16_t ip_total = ((uint16_t)buf[2] << 8) | buf[3]; uint32_t sip, dip; memcpy(&sip, buf + 12, 4); memcpy(&dip, buf + 16, 4); DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TUN_WRITE_ERR ret=%zd len=%u ip_total=%u", wr, len, ip_total); } } return LERR_OK; } // ==================================================================== // Обработчик не-TCP (UDP/ICMP прокси) // ==================================================================== static int tcp_proxy_client_handle_non_tcp(struct tcp_proxy_client* p, uint8_t* buf, size_t len) { if (len < 20) return 0; uint8_t ip_ver = (buf[0] >> 4) & 0xF; if (ip_ver != 4) return 0; uint8_t proto = buf[9]; if (proto == IPPROTO_UDP) { if (len < 28) return 0; uint32_t src_ip, dst_ip; uint16_t src_port, dst_port; memcpy(&src_ip, buf + 12, 4); memcpy(&dst_ip, buf + 16, 4); memcpy(&src_port, buf + 20, 2); memcpy(&dst_port, buf + 22, 2); udp_proxy_send_to_exit(p->inst, p->via_node_id, src_ip, src_port, dst_ip, dst_port, buf + 28, len - 28); return 1; } if (proto == IPPROTO_ICMP) { if (len < 28) return 0; uint8_t icmp_type = buf[20]; if (icmp_type != 8) return 0; uint32_t src_ip, dst_ip; memcpy(&src_ip, buf + 12, 4); memcpy(&dst_ip, buf + 16, 4); uint16_t icmp_id, icmp_seq; memcpy(&icmp_id, buf + 24, 2); memcpy(&icmp_seq, buf + 26, 2); icmp_proxy_send_to_exit(p->inst, p->via_node_id, dst_ip, src_ip, icmp_id, icmp_seq, buf + 28, len - 28); return 1; } return 0; } // ==================================================================== // Помощник: передача данных из очереди to_lwip в lwIP TCP // ==================================================================== static void tcp_proxy_client_feed_from_transport(struct tcp_proxy_client_conn *pc) { if (!pc->to_lwip || !pc->pcb) return; if (pc->rem_closed) return; int sent_any = 0; uint32_t q_pre = queue_entry_count(pc->to_lwip); while (1) { uint16_t space = tcp_sndbuf(pc->pcb); if (space < TCP_MSS / 2) break; struct ll_entry *e = queue_data_get(pc->to_lwip); if (!e) break; if (e->len <= space) { err_t ret = tcp_write(pc->pcb, e->dgram, e->len, TCP_WRITE_FLAG_COPY); if (ret == LERR_OK) { sent_any = 1; queue_dgram_free(e); queue_entry_free(e); } else { queue_data_put_first(pc->to_lwip, e); break; } } else { queue_data_put_first(pc->to_lwip, e); break; } queue_resume_callback(pc->to_lwip); } queue_resume_callback(pc->to_lwip); if (sent_any) { uint32_t unsent = 0; struct tcp_seg* s; for (s = pc->pcb->unsent; s; s = s->next) unsent++; DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY FEED exit->client sid=%08x fed=%u q=%u->%u snd_wnd=%u cwnd=%u unsent=%u", pc->stream_id, sent_any, q_pre, queue_entry_count(pc->to_lwip), pc->pcb->snd_wnd, pc->pcb->cwnd, unsent); tcp_output(pc->pcb); } } // ==================================================================== // lwIP TCP коллбэки // ==================================================================== static err_t tcp_proxy_client_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) { struct tcp_proxy_client *p = (struct tcp_proxy_client *)arg; if (err != LERR_OK || !newpcb) return LERR_ABRT; struct tcp_proxy_client_conn *pc = u_calloc(1, sizeof(struct tcp_proxy_client_conn)); if (!pc) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: accept alloc failed"); return LERR_MEM; } pc->proxy = p; pc->pcb = newpcb; pc->stream_id = ++p->next_stream_id; int i; for (i = 0; i < p->mapping_count; i++) { if (p->mappings[i].local_port == newpcb->local_port) { struct in_addr ra; ra.s_addr = inet_addr(p->mappings[i].remote_ip); memcpy(pc->dest_ip, &ra.s_addr, 4); pc->dest_port = htons(p->mappings[i].remote_port); break; } } if (i == p->mapping_count) { memcpy(pc->dest_ip, &newpcb->local_ip, 4); pc->dest_port = htons(newpcb->local_port); } tcp_arg(newpcb, pc); tcp_recv(newpcb, tcp_proxy_client_recv_cb); tcp_sent(newpcb, tcp_proxy_client_sent_cb); tcp_err(newpcb, tcp_proxy_client_err_cb); tcp_poll(newpcb, tcp_proxy_client_poll_cb, 2); tcp_nagle_disable(newpcb); pc->to_lwip = queue_new(p->ua, 0, 0, 0, "to_lwip"); pc->tx_queue = queue_new(p->ua, 0, 0, 0, "tx_queue"); if (!pc->tx_queue) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: tx_queue alloc failed sid=%08x", pc->stream_id); queue_free(pc->to_lwip); pc->to_lwip = NULL; tcp_arg(newpcb, NULL); tcp_abort(newpcb); u_free(pc); return LERR_MEM; } queue_set_callback(pc->tx_queue, tcp_proxy_client_tx_queue_drain_cb, pc); if (tcp_proxy_client_send_connect(pc) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: send_connect failed sid=%08x to %d.%d.%d.%d:%d", pc->stream_id, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port)); tcp_arg(newpcb, NULL); tcp_abort(newpcb); queue_free(pc->to_lwip); u_free(pc); return LERR_MEM; } pc->next = p->conns; p->conns = pc; p->conn_count++; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client: new conn sid=%08x local_port=%u -> %d.%d.%d.%d:%d total_conns=%d snd_wnd=%u mss=%u", pc->stream_id, newpcb->local_port, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port), p->conn_count, newpcb->snd_wnd, newpcb->mss); return LERR_OK; } // ==================================================================== // Backpressure: буфер lwIP→ETCP (tx_queue) с пачковым восстановлением окна. // Зеркало tcp_proxy_server.c read_queue_drain_cb + pause_resume_cb + retry_timer_cb. // ==================================================================== // После отправки последних данных (очередь пуста): релеить FIN/CLOSE в exit. // Возвращает 1, если соединение завершено и pc освобождён (дальше pc/q использовать нельзя). static int tcp_proxy_client_fin_flush(struct tcp_proxy_client_conn* pc) { if (pc->fin_local && !pc->close_sent && !pc->close_pending) { if (pc->fin_remote || pc->rem_closed) tcp_proxy_client_send_close(pc); else tcp_proxy_client_send_fin(pc); } if (pc->fin_local && pc->fin_remote) { tcp_proxy_client_conn_finish(pc); return 1; } return 0; } // Дрейн tx_queue → ETCP. tcp_recved вызывается только после успешной отправки: // окно закрывается плавно (штатная backpressure), а при возобновлении открывается // пачкой (за 2 пакета wnd_inflation ≥ TCP_WND_UPDATE_THRESHOLD → немедленный ACK). static void tcp_proxy_client_tx_queue_drain_cb(struct ll_queue* q, void* arg) { struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg; if (!pc || !pc->proxy || pc->rem_closed) return; struct ll_entry* e = queue_data_get(q); if (!e) { queue_resume_callback(q); return; } int ret = tcp_proxy_client_send_data(pc, e->dgram, e->len, 0); if (ret == 0) { if (pc->pcb) tcp_recved(pc->pcb, e->len); queue_dgram_free(e); queue_entry_free(e); if (queue_entry_count(q) == 0 && tcp_proxy_client_fin_flush(pc)) return; queue_resume_callback(q); } else { queue_data_put_first(q, e); DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP stall sid=%08x q=%d rcv_wnd=%u", pc->stream_id, queue_entry_count(q), pc->pcb ? pc->pcb->rcv_wnd : 0); etcp_router_on_send_ready(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, ETCP_RT_ID_TCP_PROXY_SERVER, &pc->tx_waiter, tcp_proxy_client_pause_resume_cb, pc); if (!pc->tx_retry_timer) pc->tx_retry_timer = uasync_set_timeout(pc->proxy->ua, 5000, pc, tcp_proxy_client_retry_timer_cb, "tpc_retry"); } } // send_q освободился — возобновить дрейн tx_queue static void tcp_proxy_client_pause_resume_cb(struct ll_queue* q, void* arg) { (void)q; struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg; if (!pc || !pc->proxy || pc->rem_closed || !pc->tx_queue) return; DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP resume sid=%08x q=%d", pc->stream_id, queue_entry_count(pc->tx_queue)); queue_resume_callback(pc->tx_queue); } // Safety-net: форсированный ретрай, если waiter не сработал static void tcp_proxy_client_retry_timer_cb(void* arg) { struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg; if (!pc || !pc->proxy) return; pc->tx_retry_timer = NULL; if (pc->rem_closed || !pc->tx_queue) return; struct ll_entry* e = queue_data_get(pc->tx_queue); if (!e) { queue_resume_callback(pc->tx_queue); return; } int ret = tcp_proxy_client_send_data(pc, e->dgram, e->len, 1); DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP retry(force) sid=%08x len=%u ret=%d", pc->stream_id, e->len, ret); if (ret == 0) { if (pc->pcb) tcp_recved(pc->pcb, e->len); queue_dgram_free(e); queue_entry_free(e); if (queue_entry_count(pc->tx_queue) == 0 && tcp_proxy_client_fin_flush(pc)) return; queue_resume_callback(pc->tx_queue); } else { queue_data_put_first(pc->tx_queue, e); pc->tx_retry_timer = uasync_set_timeout(pc->proxy->ua, 5000, pc, tcp_proxy_client_retry_timer_cb, "tpc_retry"); } } static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc || !pc->proxy || pc->rem_closed) { if (p) pbuf_free(p); return LERR_OK; } if (p == NULL || err != LERR_OK) { pc->fin_local = 1; if (p == NULL && err == ERR_OK) { DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY FIN sid=%08x to_exit=%u from_exit=%u bp#=%u pcb_state=%u sndbuf=%u cwnd=%u", pc->stream_id, pc->bytes_to_exit, pc->bytes_from_exit, pc->bp_count, pcb->state, pcb->snd_buf, pcb->cwnd); { uint16_t wnd_gap = TCP_WND_MAX(pcb) - pcb->rcv_wnd; if (wnd_gap > 0) tcp_recved(pcb, wnd_gap); } if (pc->tx_queue && queue_entry_count(pc->tx_queue) == 0) { if (tcp_proxy_client_fin_flush(pc)) return LERR_OK; } } else { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY ERROR recv sid=%08x pcb_state=%u err=%d", pc->stream_id, pcb->state, err); pc->error = 1; tcp_proxy_client_send_error(pc); } return LERR_OK; } uint16_t len = p->tot_len; if (pc->error || pc->rem_closed) { tcp_recved(pcb, len); pbuf_free(p); return LERR_OK; } uint8_t *data = u_malloc(len); if (!data) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY recv malloc(%u) failed sid=%08x", len, pc->stream_id); pbuf_free(p); return LERR_OK; } pbuf_copy_partial(p, data, len, 0); struct ll_entry* e = queue_entry_new_from_pool(pc->proxy->entry_pool); if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY recv entry pool exhausted sid=%08x", pc->stream_id); u_free(data); pbuf_free(p); return LERR_OK; } e->dgram = data; e->len = len; queue_data_put(pc->tx_queue, e); pbuf_free(p); return LERR_OK; } static err_t tcp_proxy_client_sent_cb(void *arg, struct tcp_pcb *pcb, uint16_t len) { (void)len; struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc || !pc->proxy || pc->rem_closed) return LERR_OK; tcp_proxy_client_feed_from_transport(pc); return LERR_OK; } static void tcp_proxy_client_err_cb(void *arg, err_t err) { struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc || !pc->proxy || pc->rem_closed) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY tcp_proxy_client_err_cb: pc=%p proxy=%p rem_closed=%d err=%d", (void*)pc, pc ? (void*)pc->proxy : NULL, pc ? pc->rem_closed : 0, err); return; } DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: error %d sid=%08x fin_local=%d rem_closed=%d", err, pc->stream_id, pc->fin_local, pc->rem_closed); pc->pcb = NULL; // pcb уже освобождён (или вот-вот) стеком lwip — не разыменовывать if (err == LERR_CLSD) { // graceful close активной стороной — ERROR слать не нужно tcp_proxy_client_conn_free(pc); return; } pc->error = 1; if (tcp_proxy_client_send_error(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY send_error failed sid=%08x", pc->stream_id); } static err_t tcp_proxy_client_poll_cb(void *arg, struct tcp_pcb *pcb) { struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc || !pc->proxy || pc->rem_closed) return LERR_OK; if (pc->error) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY CLEANUP error sid=%08x", pc->stream_id); if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_recv(pc->pcb, NULL); tcp_sent(pc->pcb, NULL); tcp_err(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); tcp_abort(pc->pcb); pc->pcb = NULL; } tcp_proxy_client_conn_free(pc); return LERR_OK; } if (pc->close_pending) { if (pc->error) tcp_proxy_client_send_error(pc); else tcp_proxy_client_send_close(pc); } return LERR_OK; } // ==================================================================== // Обеспечить динамический listen pcb для прозрачного исходящего TCP проксирования // ==================================================================== static void tcp_proxy_client_ensure_outbound_listen(struct tcp_proxy_client* p, uint16_t dport_net) { uint16_t dport_host = ntohs(dport_net); struct tcp_pcb* lp = p->lwip->listen_pcbs; while (lp) { if (lp->local_port == dport_host) return; lp = lp->next; } struct tcp_pcb* lpcb = tcp_new(p->lwip); if (!lpcb) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: tcp_new failed for dynamic listen port %u", dport_host); return; } tcp_bind(lpcb, INADDR_ANY, dport_net); struct tcp_pcb* listen_pcb = tcp_listen(lpcb); if (listen_pcb) { tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client: dynamic listen on port %u", dport_host); } } // ==================================================================== // Ввод: IP пакет → lwip_tcp (из TUN output_queue) // ==================================================================== static void tcp_proxy_client_tun_input(struct ll_queue* q, void* arg) { struct tcp_proxy_client* p = (struct tcp_proxy_client*)arg; struct ll_entry* entry = queue_data_get(q); if (!entry) return; if (entry->dgram && entry->len > 1) { uint8_t* ip = entry->dgram + 1; size_t len = entry->len - 1; if (len > 20 && len <= 2000) { if (!tcp_proxy_client_handle_non_tcp(p, ip, len)) { uint8_t proto = ip[9]; if (proto == IPPROTO_TCP) { uint16_t ip_hdr_len = (ip[0] & 0x0F) * 4; uint16_t ip_total = ((uint16_t)ip[2] << 8) | ip[3]; if (ip_hdr_len >= 20 && ip_total >= ip_hdr_len && len >= ip_total) { uint16_t dport_net; memcpy(&dport_net, ip + ip_hdr_len + 2, 2); tcp_proxy_client_ensure_outbound_listen(p, dport_net); uint32_t src_ip, dst_ip; memcpy(&src_ip, ip + 12, 4); memcpy(&dst_ip, ip + 16, 4); uint16_t tcp_len = ip_total - ip_hdr_len; struct pbuf *pb = pbuf_alloc(PBUF_RAW, tcp_len); if (pb) { pbuf_take(pb, ip + ip_hdr_len, tcp_len); lwip_tcp_input(p->lwip, pb, src_ip, dst_ip); } } } } } } queue_dgram_free(entry); queue_entry_free(entry); queue_resume_callback(q); } // ==================================================================== // Очистка tcp_proxy_client_conn // ==================================================================== static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc) { if (!pc) return; struct tcp_proxy_client *p = pc->proxy; uint32_t pending = pc->to_lwip ? queue_entry_count(pc->to_lwip) : 0; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "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, TOPO_GROUP_UTUN, 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) { struct ll_entry *e; while ((e = queue_data_get(pc->to_lwip))) { queue_dgram_free(e); queue_entry_free(e); } queue_free(pc->to_lwip); pc->to_lwip = NULL; } if (pc->tx_queue) { struct ll_entry *e; while ((e = queue_data_get(pc->tx_queue))) { queue_dgram_free(e); queue_entry_free(e); } queue_free(pc->tx_queue); pc->tx_queue = NULL; } if (pc->tx_retry_timer) { uasync_cancel_timeout(p->ua, pc->tx_retry_timer); pc->tx_retry_timer = NULL; } if (p && p->inst) etcp_router_cancel_send_ready(p->inst, TOPO_GROUP_UTUN, p->via_node_id, ETCP_RT_ID_TCP_PROXY_SERVER, &pc->tx_waiter); u_free(pc); } // Завершить локальное TCP-соединение: отвязать коллбэки, сбросить pc->pcb // (lwIP дальше сам освободит pcb — в т.ч. молча по истечении TIME_WAIT) и освободить conn. // Вызывается, когда обе стороны обменялись FIN (fin_local && fin_remote) — релей завершён. static void tcp_proxy_client_conn_finish(struct tcp_proxy_client_conn *pc) { if (!pc) return; if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_recv(pc->pcb, NULL); tcp_sent(pc->pcb, NULL); tcp_err(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); pc->pcb = NULL; } tcp_proxy_client_conn_free(pc); } // ==================================================================== // Обработчики входящих сообщений (сторона клиента) // ==================================================================== static struct tcp_proxy_client_conn* tcp_proxy_client_find_conn(struct tcp_proxy_client* p, uint32_t stream_id) { struct tcp_proxy_client_conn* pc; for (pc = p->conns; pc; pc = pc->next) if (pc->stream_id == stream_id) return pc; return NULL; } 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) { DEBUG_INFO(DEBUG_CATEGORY_PROXY, "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_RECV_HDR_SIZE; DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY DATA <- sid=%08x len=%zu", stream_id, data_len); if (data_len > 0) { pc->bytes_from_exit += (uint32_t)data_len; struct ll_entry* e = tcp_proxy_client_entry_from_data(pc->proxy->entry_pool, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, (uint16_t)data_len); if (e) queue_data_put(pc->to_lwip, e); tcp_proxy_client_feed_from_transport(pc); } queue_dgram_free(entry); queue_entry_free(entry); } 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_WARN(DEBUG_CATEGORY_PROXY, "PROXY CLOSE sid=%08x — no conn, dropping", stream_id); return; } DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY REM_CLOSED sid=%08x fin_local=%d pcb_state=%u", stream_id, pc->fin_local, pc->pcb ? pc->pcb->state : 0); pc->rem_closed = 1; if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_recv(pc->pcb, NULL); tcp_sent(pc->pcb, NULL); tcp_err(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); 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_INFO(DEBUG_CATEGORY_PROXY, "PROXY ERROR sid=%08x — no conn, dropping", stream_id); return; } DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY ERROR from exit sid=%08x fin_local=%d", stream_id, pc->fin_local); if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_recv(pc->pcb, NULL); tcp_sent(pc->pcb, NULL); tcp_err(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); tcp_close(pc->pcb); pc->pcb = NULL; } pc->rem_closed = 1; tcp_proxy_client_conn_free(pc); } 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; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY FIN FROM exit sid=%08x pcb_state=%u — shutdown write (send FIN to local)", stream_id, pc->pcb->state); pc->fin_remote = 1; if (pc->pcb->state != TIME_WAIT && pc->pcb->state != CLOSED) { tcp_shutdown(pc->pcb, 0, 1); tcp_sent(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); } if (pc->fin_local && !pc->close_sent && !pc->close_pending) tcp_proxy_client_send_close(pc); if (pc->fin_local) tcp_proxy_client_conn_finish(pc); } // ==================================================================== // ETCP коллбэк клиентской стороны (принимает DATA/CLOSE/ERROR/FIN от сервера) // ==================================================================== void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; struct tcp_proxy_client* proxy = inst ? inst->tcp_proxy_client : NULL; if (!entry || !entry->dgram) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } // CLOSE_ALL / restart-уведомление: [svc_id][src][dst] без payload if (entry->len == ROUTER_SVC_HDR_SIZE && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY_CLIENT) { uint64_t peer_id; memcpy(&peer_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY CLOSE_ALL from %016llx — clearing client conns for peer", (unsigned long long)peer_id); if (proxy) { struct tcp_proxy_client_conn *pc, *next; for (pc = proxy->conns; pc; pc = next) { next = pc->next; if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_recv(pc->pcb, NULL); tcp_sent(pc->pcb, NULL); tcp_err(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); tcp_abort(pc->pcb); pc->pcb = NULL; } tcp_proxy_client_conn_free(pc); } proxy->conns = NULL; proxy->conn_count = 0; socks_proxy_conn_free_all(&proxy->socks_conns, &proxy->socks_conn_count); socks_proxy_conn_free_all(&proxy->http_conns, &proxy->http_conn_count); } queue_dgram_free(entry); queue_entry_free(entry); return; } if (entry->len < TCP_PROXY_RECV_HDR_SIZE) { queue_dgram_free(entry); queue_entry_free(entry); return; } uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF]; uint32_t stream_id; memcpy(&stream_id, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4); DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY CLIENT RECV subcmd=%02x sid=%08x pkt_len=%u", subcmd, stream_id, entry->len); if (proxy) { size_t data_len = entry->len > TCP_PROXY_RECV_HDR_SIZE ? entry->len - TCP_PROXY_RECV_HDR_SIZE : 0; // Пробуем SOCKS/HTTP first (у них приоритет — могут быть без TUN) if (proxy->socks_enabled && socks_proxy_handle_etcp(&proxy->socks_conns, &proxy->socks_conn_count, stream_id, subcmd, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, data_len)) { queue_dgram_free(entry); queue_entry_free(entry); return; } if (proxy->http_proxy_enabled && socks_proxy_handle_etcp(&proxy->http_conns, &proxy->http_conn_count, stream_id, subcmd, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, data_len)) { queue_dgram_free(entry); queue_entry_free(entry); return; } // Существующие lwIP conns 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_FIN) { tcp_proxy_client_handle_fin(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } } if (subcmd == TCP_PROXY_SUBCMD_DATA || subcmd == TCP_PROXY_SUBCMD_CLOSE || subcmd == TCP_PROXY_SUBCMD_FIN || subcmd == TCP_PROXY_SUBCMD_ERROR) { DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY subcmd=%02x sid=%08x — client conn not found, silent drop", subcmd, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } DEBUG_WARN(DEBUG_CATEGORY_PROXY, "TCP proxy client: unhandled subcmd=%02x sid=%08x", subcmd, stream_id); queue_dgram_free(entry); queue_entry_free(entry); } // ==================================================================== // Публичное API // ==================================================================== struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* tun_name, const char* tun_ip, int mtu, int test_mode, struct tcp_proxy_client_mapping_config* mappings, int mapping_count, uint64_t via_node_id, int socks_enabled, const char* socks_addr, int http_proxy_enabled, const char* http_proxy_addr) { if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "ua is NULL"); return NULL; } int need_tun = tun_name && tun_name[0] && tun_ip && tun_ip[0]; if (!need_tun && !socks_enabled && !http_proxy_enabled) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "no proxy mode enabled"); return NULL; } struct tcp_proxy_client* p = u_calloc(1, sizeof(struct tcp_proxy_client)); if (!p) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "u_calloc failed"); return NULL; } p->inst = inst; p->ua = ua; p->next_stream_id = 1; p->via_node_id = via_node_id; p->mappings = mappings; p->mapping_count = mapping_count; p->socks_enabled = socks_enabled; p->http_proxy_enabled = http_proxy_enabled; p->entry_pool = memory_pool_init(sizeof(struct ll_entry), "entry_pool"); if (!p->entry_pool) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "memory_pool_init failed"); u_free(p); return NULL; } if (need_tun) { p->tun = tun_init_nat(ua, tun_name, tun_ip, mtu > 0 ? mtu : 1500, test_mode); if (!p->tun) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: failed to create TUN %s", tun_name); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; } queue_set_callback(p->tun->output_queue, tcp_proxy_client_tun_input, p); p->lwip = lwip_tcp_init(ua, tcp_proxy_client_output_cb, p); if (!p->lwip) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "lwip_tcp_init failed"); tun_close(p->tun); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; } if (mapping_count > 0) { int j; for (j = 0; j < mapping_count; j++) { uint16_t port_net = htons(mappings[j].local_port); struct tcp_pcb *lpcb = tcp_new(p->lwip); if (!lpcb) continue; tcp_bind(lpcb, INADDR_ANY, port_net); struct tcp_pcb *listen_pcb = tcp_listen(lpcb); if (listen_pcb) { tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); } } } } if (socks_enabled && socks_addr && socks_addr[0]) { p->socks_listen = socks_proxy_init_listen(ua, socks_addr, &p->socks_conns, &p->socks_conn_count, &p->next_stream_id, inst, via_node_id, 0); if (!p->socks_listen) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: SOCKS init failed on %s", socks_addr); } } if (http_proxy_enabled && http_proxy_addr && http_proxy_addr[0]) { p->http_listen = socks_proxy_init_listen(ua, http_proxy_addr, &p->http_conns, &p->http_conn_count, &p->next_stream_id, inst, via_node_id, 1); if (!p->http_listen) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "tcp_proxy_client: HTTP proxy init failed on %s", http_proxy_addr); } } if (inst) { if (etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY_CLIENT, tcp_proxy_client_router_recv_cb) != 0) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "TCP proxy client: etcp_router_bind failed"); } else { DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client: etcp_router bind registered for ID=0x%02x", ETCP_RT_ID_TCP_PROXY_CLIENT); if (need_tun) { udp_proxy_init(inst, ua); icmp_proxy_init(inst, ua); } } } DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client created: tun=%s socks=%s(http=%s) mappings=%d via_node=%016llx", need_tun ? "yes" : "no", socks_enabled ? "yes" : "no", http_proxy_enabled ? "yes" : "no", mapping_count, (unsigned long long)via_node_id); return p; } void tcp_proxy_client_destroy(struct tcp_proxy_client* p) { if (!p) return; int total = p->conn_count + p->socks_conn_count + p->http_conn_count; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client destroying: lwip=%d socks=%d http=%d", p->conn_count, p->socks_conn_count, p->http_conn_count); if (p->inst) etcp_router_unbind(p->inst, ETCP_RT_ID_TCP_PROXY_CLIENT); udp_proxy_destroy(p->inst); icmp_proxy_destroy(p->inst); if (p->socks_listen) socks_proxy_close_listen(p->ua, p->socks_listen, NULL); socks_proxy_conn_free_all(&p->socks_conns, &p->socks_conn_count); if (p->http_listen) socks_proxy_close_listen(p->ua, p->http_listen, NULL); socks_proxy_conn_free_all(&p->http_conns, &p->http_conn_count); struct tcp_proxy_client_conn* pc = p->conns; while (pc) { struct tcp_proxy_client_conn* next = pc->next; if (pc->pcb && !memory_pool_is_freed(p->lwip->pcb_pool, pc->pcb) && pc->pcb->state != CLOSED) { tcp_arg(pc->pcb, NULL); tcp_recv(pc->pcb, NULL); tcp_sent(pc->pcb, NULL); tcp_err(pc->pcb, NULL); tcp_poll(pc->pcb, NULL, 0); tcp_abort(pc->pcb); } pc->pcb = NULL; if (pc->to_lwip) { struct ll_entry *e; while ((e = queue_data_get(pc->to_lwip))) { queue_dgram_free(e); queue_entry_free(e); } queue_free(pc->to_lwip); pc->to_lwip = NULL; } if (pc->tx_queue) { struct ll_entry *e; while ((e = queue_data_get(pc->tx_queue))) { queue_dgram_free(e); queue_entry_free(e); } queue_free(pc->tx_queue); pc->tx_queue = NULL; } if (pc->tx_retry_timer) { uasync_cancel_timeout(p->ua, pc->tx_retry_timer); pc->tx_retry_timer = NULL; } if (p->inst) etcp_router_cancel_send_ready(p->inst, TOPO_GROUP_UTUN, p->via_node_id, ETCP_RT_ID_TCP_PROXY_SERVER, &pc->tx_waiter); u_free(pc); pc = next; } p->conns = NULL; if (p->lwip) { lwip_tcp_destroy(p->lwip); p->lwip = NULL; } if (p->tun) tun_close(p->tun); if (p->entry_pool) memory_pool_destroy(p->entry_pool); u_free(p); }