You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
591 lines
29 KiB
591 lines
29 KiB
// tcp_proxy_client.c — TCP прокси-клиент: стек lwIP TCP → ETCP → удалённый exit узел |
|
#include "tcp_proxy_client.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 "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 <stdlib.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#ifndef _WIN32 |
|
#include <unistd.h> |
|
#include <arpa/inet.h> |
|
#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 int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len); |
|
static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len); |
|
static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, 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_SOCKET, "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_SOCKET, "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 dst, uint8_t subcmd, |
|
uint32_t sid, const uint8_t* data, size_t len) { |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_send_msg: 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_SOCKET, "tcp_proxy_client_send_msg: malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; } |
|
e->dgram[0] = ETCP_ID_TCP_PROXY; |
|
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, dst, e); |
|
} |
|
|
|
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_SOCKET, "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, pc->proxy->via_node_id, |
|
TCP_PROXY_SUBCMD_CONNECT, pc->stream_id, buf, 6); |
|
} |
|
|
|
static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len) { |
|
DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY SEND sid=%08x len=%u", pc->stream_id, len); |
|
return tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, |
|
TCP_PROXY_SUBCMD_DATA, pc->stream_id, data, len); |
|
} |
|
|
|
static int tcp_proxy_client_send_close(struct tcp_proxy_client_conn* pc) { |
|
int ret = tcp_proxy_client_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 tcp_proxy_client_send_error(struct tcp_proxy_client_conn* pc) { |
|
int ret = tcp_proxy_client_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; |
|
} |
|
|
|
// ==================================================================== |
|
// Вывод: 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_TRAFFIC, "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 || pc->tun_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) { |
|
etcp_router_consumer_ack(pc->proxy->inst, pc->proxy->via_node_id, ETCP_ID_TCP_PROXY); |
|
uint32_t unsent = 0; struct tcp_seg* s; |
|
for (s = pc->pcb->unsent; s; s = s->next) unsent++; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_TRAFFIC, "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_SOCKET, "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"); |
|
|
|
if (tcp_proxy_client_send_connect(pc) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "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_SOCKET, "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: waiter callback при освобождении normalizer очереди |
|
// ==================================================================== |
|
static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, void* arg) { |
|
(void)q; |
|
struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg; |
|
if (!pc || !pc->tx_buf) return; |
|
int ret = tcp_proxy_client_send_data(pc, pc->tx_buf, pc->tx_len); |
|
if (ret == 0) { |
|
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); |
|
} |
|
} |
|
|
|
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) { 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", |
|
pc->stream_id, pcb->state, pcb->snd_buf, pcb->cwnd, |
|
(void*)pcb->unsent, (void*)pcb->unacked, pc->close_sent); |
|
{ 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); |
|
} |
|
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; |
|
} |
|
|
|
if (pc->tx_buf) { pbuf_free(p); return LERR_OK; } |
|
|
|
uint8_t *data = u_malloc(len); |
|
if (data) { |
|
pbuf_copy_partial(p, data, len, 0); |
|
DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY RECV client->exit sid=%08x len=%u", pc->stream_id, len); |
|
int ret = tcp_proxy_client_send_data(pc, data, len); |
|
if (ret == 0) { tcp_recved(pcb, len); u_free(data); } |
|
else { |
|
pc->tx_buf = data; pc->tx_len = len; |
|
etcp_router_waiter_register(pc->proxy->inst, pc->proxy->via_node_id, &pc->tx_waiter, |
|
tcp_proxy_client_tx_waiter_cb, pc); |
|
} |
|
} else DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY recv malloc(%u) failed sid=%08x", len, pc->stream_id); |
|
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) 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) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY tcp_proxy_client_err_cb: pc=NULL err=%d", err); return; } |
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy client: error %d sid=%08x pcb_state=%u tun_closed=%d rem_closed=%d", |
|
err, pc->stream_id, pc->pcb ? pc->pcb->state : 0, pc->tun_closed, pc->rem_closed); |
|
pc->error = 1; |
|
if (tcp_proxy_client_send_error(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "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) 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); |
|
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; |
|
} |
|
|
|
// ==================================================================== |
|
// Обеспечить динамический 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_SOCKET, "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_SOCKET, "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_SOCKET, "PROXY FREE sid=%08x total_conns=%d to_lwip_q=%u", |
|
pc->stream_id, p->conn_count, pending); |
|
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_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; } |
|
if (pc->proxy && pc->proxy->inst) etcp_router_waiter_cancel(pc->proxy->inst, pc->proxy->via_node_id, &pc->tx_waiter); |
|
u_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 || pc->tun_closed) { |
|
DEBUG_WARN(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; |
|
DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY DATA <- sid=%08x len=%zu", stream_id, data_len); |
|
if (data_len > 0) { |
|
struct ll_entry* e = tcp_proxy_client_entry_from_data(pc->proxy->entry_pool, entry->dgram + TCP_PROXY_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_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); |
|
} |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
// ==================================================================== |
|
// Единый обработчик etcp_router (диспетчеризация в tcp_proxy_client или tcp_proxy_server) |
|
// ==================================================================== |
|
void tcp_proxy_client_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
if (!entry || !entry->dgram || entry->len < TCP_PROXY_HDR_SIZE) { |
|
if (entry) { DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy client: bad entry len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); } |
|
return; |
|
} |
|
uint8_t subcmd = entry->dgram[1]; |
|
uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4); |
|
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; |
|
struct tcp_proxy_client* proxy = inst ? inst->tcp_proxy_client : NULL; |
|
|
|
if (subcmd == TCP_PROXY_SUBCMD_CONNECT) { |
|
uint64_t src_node_id = conn ? conn->peer_node_id : (inst ? inst->node_id : 0); |
|
tcp_proxy_server_handle_connect(inst, entry, stream_id, src_node_id); |
|
return; |
|
} |
|
if (inst && inst->tcp_proxy_server.enabled) { |
|
struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(&inst->tcp_proxy_server, stream_id); |
|
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); rc->error = 1; tcp_proxy_server_conn_free(rc); 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_ERROR) { |
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — conn not found, silent drop", stream_id); |
|
queue_dgram_free(entry); queue_entry_free(entry); return; |
|
} |
|
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "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) |
|
{ |
|
if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: ua is NULL"); return NULL; } |
|
if (!tun_name || !tun_ip) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: tun name/ip required"); return NULL; } |
|
|
|
struct tcp_proxy_client* p = u_calloc(1, sizeof(struct tcp_proxy_client)); |
|
if (!p) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: 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->entry_pool = memory_pool_init(sizeof(struct ll_entry)); |
|
if (!p->entry_pool) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: memory_pool_init failed"); u_free(p); return NULL; } |
|
|
|
p->tun = tun_init_nat(ua, tun_name, tun_ip, mtu > 0 ? mtu : 1500, test_mode); |
|
if (!p->tun) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "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_SOCKET, "tcp_proxy_client_create: 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 (inst) { |
|
if (etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_client_etcp_recv_cb) != 0) { |
|
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router_bind failed"); |
|
} else { |
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router bind registered for ID=0x%02x", ETCP_ID_TCP_PROXY); |
|
udp_proxy_init(inst, ua); |
|
icmp_proxy_init(inst, ua); |
|
} |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client created: mappings=%d via_node=%016llx", mapping_count, (unsigned long long)via_node_id); |
|
return p; |
|
} |
|
|
|
void tcp_proxy_client_destroy(struct tcp_proxy_client* p) { |
|
if (!p) return; |
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client destroying: conns=%d", p->conn_count); |
|
if (p->inst) etcp_router_unbind(p->inst, ETCP_ID_TCP_PROXY); |
|
udp_proxy_destroy(p->inst); |
|
icmp_proxy_destroy(p->inst); |
|
|
|
if (p->lwip) { lwip_tcp_destroy(p->lwip); p->lwip = NULL; } |
|
|
|
struct tcp_proxy_client_conn* pc = p->conns; |
|
while (pc) { |
|
struct tcp_proxy_client_conn* next = pc->next; |
|
if (pc->pcb) { tcp_arg(pc->pcb, NULL); 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_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; } |
|
if (p->inst) etcp_router_waiter_cancel(p->inst, p->via_node_id, &pc->tx_waiter); |
|
u_free(pc); pc = next; |
|
} |
|
p->conns = NULL; |
|
|
|
if (p->tun) tun_close(p->tun); |
|
if (p->entry_pool) memory_pool_destroy(p->entry_pool); |
|
u_free(p); |
|
}
|
|
|