From 393a641e18a57582ec4caf0abe239348c50db66c Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 1 Jul 2026 20:33:11 +0300 Subject: [PATCH] =?UTF-8?q?socks/http=20proxy=20client:=20SOCKS5=20+=20HTT?= =?UTF-8?q?P=20CONNECT=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20ETCP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлен модуль socks_proxy.c/h — SOCKS5 и HTTP CONNECT proxy на клиенте. Использует tcp_io для локальных TCP соединений, туннелит трафик через существующий ETCP-протокол (ETCP_ID_TCP_PROXY/ETCP_ID_TCP_PROXY_CLIENT). Конфиг [tcp_proxy_client]: - socks_enabled/socks_addr — включить SOCKS5 сервер на указанном IP:PORT - http_proxy_enabled/http_proxy_addr — включить HTTP CONNECT сервер - Можно включать TUN, SOCKS и HTTP прокси одновременно или раздельно Весь трефик идёт через exit node (tcp_proxy_server), менять exit не пришлось. --- src/Makefile.am | 1 + src/config_parser.c | 18 +- src/config_parser.h | 4 + src/proxy/socks_proxy.c | 502 ++++++++++++++++++++++++++++++++++ src/proxy/socks_proxy.h | 78 ++++++ src/proxy/tcp_proxy_client.c | 92 +++++-- src/proxy/tcp_proxy_client.h | 18 +- src/utun_instance.c | 13 +- tests/Makefile.am | 1 + tests/test_tcp_proxy_client.c | 4 +- tests/test_tcp_proxy_remote.c | 6 +- utun.conf.sample | 17 +- 12 files changed, 713 insertions(+), 41 deletions(-) create mode 100644 src/proxy/socks_proxy.c create mode 100644 src/proxy/socks_proxy.h diff --git a/src/Makefile.am b/src/Makefile.am index 3d286e75..706463dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,6 +45,7 @@ utun_CORE_SOURCES = \ etcp_router.c \ proxy/tcp_proxy_server.c \ proxy/udp_proxy.c \ + proxy/socks_proxy.c \ proxy/icmp_proxy.c \ lwip_tcp/lwip_pbuf.c \ lwip_tcp/lwip_tcp.c \ diff --git a/src/config_parser.c b/src/config_parser.c index 4f781f2b..94a62efe 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -476,6 +476,22 @@ static int parse_tcp_proxy_client(const char *key, const char *value, struct glo global->tcp_proxy_client_via_node_id = strtoull(value, NULL, 16); return 0; } + if (strcmp(key, "socks_enabled") == 0) { + global->tcp_proxy_client_socks_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0; + return 0; + } + if (strcmp(key, "socks_addr") == 0) { + strncpy(global->tcp_proxy_client_socks_addr, value, sizeof(global->tcp_proxy_client_socks_addr) - 1); + return 0; + } + if (strcmp(key, "http_proxy_enabled") == 0) { + global->tcp_proxy_client_http_proxy_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0; + return 0; + } + if (strcmp(key, "http_proxy_addr") == 0) { + strncpy(global->tcp_proxy_client_http_proxy_addr, value, sizeof(global->tcp_proxy_client_http_proxy_addr) - 1); + return 0; + } if (strcmp(key, "forward") == 0) { if (global->tcp_proxy_client_mapping_count >= MAX_TCP_PROXY_CLIENT_MAPPINGS) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Too many tcp_proxy_client forward rules (max %d)", MAX_TCP_PROXY_CLIENT_MAPPINGS); @@ -512,7 +528,7 @@ static int parse_tcp_proxy_client(const char *key, const char *value, struct glo global->tcp_proxy_client_mapping_count++; return 0; } - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown tcp_proxy_client option '%s'. Valid: enabled, tun_name, tun_ip, mtu, via_node, forward", filename, line_num, key); + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown tcp_proxy_client option '%s'. Valid: enabled, tun_name, tun_ip, mtu, via_node, forward, socks_enabled, socks_addr, http_proxy_enabled, http_proxy_addr", filename, line_num, key); return -1; } diff --git a/src/config_parser.h b/src/config_parser.h index ec520b84..057a9042 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -162,6 +162,10 @@ struct global_config { uint64_t tcp_proxy_client_via_node_id; // через этот узел проксируются все forward-правила struct tcp_proxy_client_mapping_config tcp_proxy_client_mappings[MAX_TCP_PROXY_CLIENT_MAPPINGS]; int tcp_proxy_client_mapping_count; + int tcp_proxy_client_socks_enabled; + char tcp_proxy_client_socks_addr[64]; // e.g. "127.0.0.1:1080" + int tcp_proxy_client_http_proxy_enabled; + char tcp_proxy_client_http_proxy_addr[64]; // e.g. "127.0.0.1:8080" // TCP proxy server (exit node) configuration ([tcp_proxy_server] section) int tcp_proxy_server_enabled; diff --git a/src/proxy/socks_proxy.c b/src/proxy/socks_proxy.c new file mode 100644 index 00000000..00152f0a --- /dev/null +++ b/src/proxy/socks_proxy.c @@ -0,0 +1,502 @@ +// socks_proxy.c — SOCKS5 / HTTP CONNECT proxy (client side) +#include "socks_proxy.h" +#include "tcp_proxy_server.h" +#include "etcp.h" +#include "etcp_api.h" +#include "etcp_router.h" +#include "utun_instance.h" +#include "../lib/u_async.h" +#include "../lib/debug_config.h" +#include "../lib/ll_queue.h" +#include "../lib/memory_pool.h" +#include "../lib/tcp_io.h" +#include "../lib/mem.h" +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif + +static void on_accept_cb(socket_t sock, void* arg); +static void on_read_cb(struct ll_queue* q, void* arg); +static void on_fin_cb(struct tcp_conn* tc, void* arg); +static void on_error_cb(struct tcp_conn* tc, int err, void* arg); +static void on_closed_cb(struct tcp_conn* tc, void* arg); +static void tx_waiter_cb(struct ll_queue* q, void* arg); + +static int send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len, int force); + +struct listen_ctx { + socket_t listen_sock; + void* socket_id; + struct UASYNC* ua; + struct UTUN_INSTANCE* inst; + uint64_t via_node_id; + int is_http; + struct socks_proxy_conn** conns; + int* conn_count; + uint32_t* next_stream_id; +}; + +// ==================================================================== +// Отправка сообщений через ETCP +// ==================================================================== +static int send_msg(struct UTUN_INSTANCE* inst, 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_SOCKET, "socks_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_SOCKET, "socks_proxy: malloc(%zu) failed", TCP_PROXY_HDR_SIZE + len); 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 = (uint16_t)(TCP_PROXY_HDR_SIZE + len); + return etcp_route_send(inst, dst, e, force); +} + +static int send_connect(struct socks_proxy_conn* c) { + uint8_t buf[6]; + memcpy(buf, c->dest_ip, 4); memcpy(buf + 4, &c->dest_port, 2); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCKS proxy: CONNECT sid=%08x to %d.%d.%d.%d:%d via_node=%016llx %s", + c->stream_id, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], + ntohs(c->dest_port), (unsigned long long)c->via_node_id, c->is_http ? "http" : "socks"); + return send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_CONNECT, c->stream_id, buf, 6, 1); +} + +static int send_data(struct socks_proxy_conn* c, const uint8_t* data, uint16_t len) { + return send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_DATA, c->stream_id, data, len, 0); +} + +static void send_close(struct socks_proxy_conn* c) { + if (send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_CLOSE, c->stream_id, NULL, 0, 1) < 0) c->close_pending = 1; + else { c->close_pending = 0; c->close_sent = 1; } +} + +static void send_error(struct socks_proxy_conn* c) { + if (send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_ERROR, c->stream_id, NULL, 0, 1) < 0) c->close_pending = 1; + else { c->close_pending = 0; c->close_sent = 1; } +} + +static void send_fin(struct socks_proxy_conn* c) { + send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_FIN, c->stream_id, NULL, 0, 1); +} + +// ==================================================================== +// Запись ответа в TCP клиенту через write_queue +// ==================================================================== +static int write_to_client(struct socks_proxy_conn* c, const uint8_t* data, uint16_t len) { + struct ll_entry* e = queue_entry_new_from_pool(c->tc->entry_pool); + uint8_t* buf = memory_pool_alloc(c->tc->data_pool); + if (!e || !buf) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: write_to_client alloc failed sid=%08x", c->stream_id); + if (e) queue_entry_free(e); if (buf) memory_pool_free(c->tc->data_pool, buf); + return -1; + } + memcpy(buf, data, len); + e->dgram = buf; e->len = len; + queue_data_put(c->tc->write_queue, e); + return 0; +} + +// ==================================================================== +// SOCKS5 handshake +// ==================================================================== +static void process_socks_greeting(struct socks_proxy_conn* c) { + if (c->buf_len < 3) return; + uint8_t ver = c->buf[0], nmethods = c->buf[1]; + if (ver != 5 || c->buf_len < (uint16_t)(2 + nmethods)) return; + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: greeting ver=%d nmethods=%d", ver, nmethods); + uint8_t reply[] = { 0x05, 0x00 }; + write_to_client(c, reply, 2); + c->buf_len = 0; + c->state = SOCKS_STATE_REQUEST; +} + +static void process_socks_request(struct socks_proxy_conn* c) { + if (c->buf_len < 10) return; + uint8_t ver = c->buf[0], cmd = c->buf[1], atyp = c->buf[3]; + if (ver != 5) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad ver=%d", ver); goto error; } + if (cmd != 1) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: unsupported cmd=%d (only CONNECT supported)", cmd); goto error; } + + uint16_t need; + if (atyp == 1) need = 10; // IPv4: 4+2=6 more bytes + else if (atyp == 3) { // domain: 1+len+2 + if (c->buf_len < 5) return; + need = (uint16_t)(5 + c->buf[4] + 2); + } + else if (atyp == 4) need = 22; // IPv6: 16+2=18 more + else { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: unsupported atyp=%d", atyp); goto error; } + + if (c->buf_len < need) return; + + if (atyp == 1) { + memcpy(c->dest_ip, c->buf + 4, 4); + memcpy(&c->dest_port, c->buf + 8, 2); + } else if (atyp == 4) { + // Извлекаем первые 4 байта IPv6 в dest_ip (для упрощения: IPv6 mapped IPv4 или реальный IPv6) + memcpy(c->dest_ip, c->buf + 12, 4); + memcpy(&c->dest_port, c->buf + 20, 2); + // TODO: proper IPv6 support + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: IPv6 unsupported, using last 4 bytes of addr"); + } else { // atyp == 3 (domain) + uint8_t dlen = c->buf[4]; + char domain[256]; memcpy(domain, c->buf + 5, dlen); domain[dlen] = '\0'; + memcpy(&c->dest_port, c->buf + 5 + dlen, 2); + // resolve domain + struct hostent* he = gethostbyname(domain); + if (!he || he->h_addrtype != AF_INET) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", domain); + goto error; + } + memcpy(c->dest_ip, he->h_addr, 4); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: resolved %s → %d.%d.%d.%d:%d", + domain, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], ntohs(c->dest_port)); + } + + c->buf_len = 0; + c->state = SOCKS_STATE_CONNECTING; + if (send_connect(c) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: send_connect failed sid=%08x", c->stream_id); + uint8_t err_reply[] = { 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + write_to_client(c, err_reply, 10); + tcp_conn_push_close(c->tc); + } + return; + +error: { + uint8_t err_reply[] = { 0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + write_to_client(c, err_reply, 10); + tcp_conn_push_close(c->tc); +} +} + +// ==================================================================== +// HTTP CONNECT handshake +// ==================================================================== +static void process_http_request(struct socks_proxy_conn* c) { + // Ищем "\r\n\r\n" или первую "\r\n" для строки CONNECT + char* end = memmem(c->buf, c->buf_len, "\r\n\r\n", 4); + if (!end) { end = memmem(c->buf, c->buf_len, "\r\n", 2); if (!end) return; } + + uint16_t line_len = (uint16_t)((uint8_t*)end - c->buf); + if (line_len < 8) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: http request too short"); goto error; } + + // Парсим "CONNECT host:port HTTP/1.1" + char line[512]; if (line_len > sizeof(line) - 1) line_len = sizeof(line) - 1; + memcpy(line, c->buf, line_len); line[line_len] = '\0'; + + char host_port[256]; + if (sscanf(line, "CONNECT %255s HTTP/", host_port) != 1) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad http connect: %s", line); + goto error; + } + + char* colon = strrchr(host_port, ':'); + if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: no port in %s", host_port); goto error; } + *colon = '\0'; int port = atoi(colon + 1); + if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d", port); goto error; } + + struct hostent* he = gethostbyname(host_port); + if (!he || he->h_addrtype != AF_INET) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", host_port); + uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n"; + write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return; + } + memcpy(c->dest_ip, he->h_addr, 4); c->dest_port = htons((uint16_t)port); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP CONNECT %s → %d.%d.%d.%d:%d", + host_port, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], port); + + c->buf_len = 0; + c->state = HTTP_STATE_CONNECTING; + if (send_connect(c) < 0) { + uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n"; + write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); + } + return; + +error: { + uint8_t resp[] = "HTTP/1.1 400 Bad Request\r\n\r\n"; + write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); +} +} + +// ==================================================================== +// tcp_io read callback — парсинг рукопожатия + релей данных +// ==================================================================== +static void on_read_cb(struct ll_queue* q, void* arg) { + struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg; + struct ll_entry* e = queue_data_get(q); + if (!e) { queue_resume_callback(q); return; } + + if (c->rem_closed) { + queue_dgram_free(e); queue_entry_free(e); queue_resume_callback(q); return; + } + + if (c->state == SOCKS_STATE_GREETING || c->state == SOCKS_STATE_REQUEST || + c->state == HTTP_STATE_REQUEST) { + // накапливаем данные для рукопожатия + uint16_t space = sizeof(c->buf) - c->buf_len; + if (space < e->len) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: handshake buffer overflow sid=%08x", c->stream_id); + memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e); + tcp_conn_push_close(c->tc); queue_resume_callback(q); return; + } + memcpy(c->buf + c->buf_len, e->dgram, e->len); + c->buf_len += e->len; + memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e); + queue_resume_callback(q); + + if (c->is_http) { + process_http_request(c); + } else { + if (c->state == SOCKS_STATE_GREETING) process_socks_greeting(c); + if (c->state == SOCKS_STATE_REQUEST) process_socks_request(c); + } + return; + } + + // RELAY = релей данных в ETCP + if (c->state == SOCKS_STATE_CONNECTING || c->state == HTTP_STATE_CONNECTING) { + // буферизуем данные пока ждём ответа от exit + memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e); + queue_resume_callback(q); return; + } + + int ret = send_data(c, e->dgram, e->len); + DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS PROXY SEND sid=%08x len=%u", c->stream_id, e->len); + if (ret == 0) { + memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e); + queue_resume_callback(q); + } else { + memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e); + etcp_router_waiter_register(c->inst, c->via_node_id, &c->tx_waiter, tx_waiter_cb, c); + } +} + +static void tx_waiter_cb(struct ll_queue* q, void* arg) { + (void)q; + struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg; + if (c->rem_closed || c->close_sent) return; + queue_resume_callback(c->tc->read_queue); +} + +// ==================================================================== +// tcp_io: FIN / Error / Closed +// ==================================================================== +static void on_fin_cb(struct tcp_conn* tc, void* arg) { + struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg; + if (c->rem_closed || c->close_sent) return; + if (!tc->fin_local && !tc->write_buf && !tc->write_queue->head) { + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: local FIN → relay FIN sid=%08x", c->stream_id); + send_fin(c); + if (c->fin_remote && !c->close_sent && !c->close_pending) send_close(c); + } else { + // данные ещё в write_queue, отложим FIN + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: local FIN deferred (wq=%d wbuf=%s) sid=%08x", + tc->write_queue->count, tc->write_buf ? "y" : "n", c->stream_id); + tcp_conn_set_flushed(tc, NULL); + } +} + +static void on_error_cb(struct tcp_conn* tc, int err, void* arg) { + (void)err; + struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg; + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: tcp error sid=%08x err=%d", c->stream_id, err); + if (!c->close_sent && !c->close_pending) send_error(c); + socks_proxy_conn_free(c); +} + +static void on_closed_cb(struct tcp_conn* tc, void* arg) { + struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg; + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: tcp closed sid=%08x", c->stream_id); + if (!c->close_sent && !c->close_pending) send_close(c); + socks_proxy_conn_free(c); +} + +// ==================================================================== +// Accept callback для слушающего сокета +// ==================================================================== +static void on_accept_cb(socket_t sock, void* arg) { + struct listen_ctx* ctx = (struct listen_ctx*)arg; + struct sockaddr_in addr; socklen_t alen = sizeof(addr); + socket_t csock = accept(sock, (struct sockaddr*)&addr, &alen); + if (csock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: accept failed errno=%d", errno); return; } + socket_set_nonblocking(csock); + + struct socks_proxy_conn* c = u_calloc(1, sizeof(struct socks_proxy_conn)); + if (!c) { socket_close_wrapper(csock); DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: u_calloc failed"); return; } + c->stream_id = ++(*ctx->next_stream_id); + c->is_http = (uint8_t)ctx->is_http; + c->ua = ctx->ua; c->inst = ctx->inst; c->via_node_id = ctx->via_node_id; + c->state = ctx->is_http ? HTTP_STATE_REQUEST : SOCKS_STATE_GREETING; + c->head = ctx->conns; c->count = ctx->conn_count; + + c->tc = tcp_conn_create(ctx->ua, csock, 4096, 4096, 8, 0, 0, on_fin_cb, on_error_cb, c); + if (!c->tc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: tcp_conn_create failed"); u_free(c); socket_close_wrapper(csock); return; } + c->tc->on_closed = on_closed_cb; + queue_set_callback(c->tc->read_queue, on_read_cb, c); + queue_set_waiter_defer(c->tc->read_queue, 1); + + c->next = *ctx->conns; *ctx->conns = c; (*ctx->conn_count)++; + + char ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &addr.sin_addr, ip, sizeof(ip)); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: accepted %s conn from %s:%d sid=%08x total=%d", + ctx->is_http ? "http" : "socks", ip, ntohs(addr.sin_port), c->stream_id, *ctx->conn_count); +} + +// ==================================================================== +// Публичное API для tcp_proxy_client (etcp dispatch) +// ==================================================================== +struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id) { + struct socks_proxy_conn* c; + for (c = head; c; c = c->next) if (c->stream_id == stream_id) return c; + return NULL; +} + +int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count, + uint32_t stream_id, uint8_t subcmd, + const uint8_t* data, size_t data_len) { + struct socks_proxy_conn* c = socks_proxy_find_conn(*head, stream_id); + if (!c) return 0; + + if (subcmd == TCP_PROXY_SUBCMD_DATA) { + if (c->state == SOCKS_STATE_CONNECTING) { + uint8_t reply[] = { 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + write_to_client(c, reply, 10); + c->state = SOCKS_STATE_RELAY; + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: SOCKS connect ok sid=%08x → RELAY", stream_id); + } else if (c->state == HTTP_STATE_CONNECTING) { + uint8_t resp[] = "HTTP/1.1 200 Connection Established\r\n\r\n"; + write_to_client(c, resp, (uint16_t)strlen((char*)resp)); + c->state = HTTP_STATE_RELAY; + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP connect ok sid=%08x → RELAY", stream_id); + } + if (data_len > 0) { + struct ll_entry* e = queue_entry_new_from_pool(c->tc->entry_pool); + uint8_t* buf = memory_pool_alloc(c->tc->data_pool); + if (e && buf) { + memcpy(buf, data, data_len); e->dgram = buf; e->len = (uint16_t)data_len; + queue_data_put(c->tc->write_queue, e); + } else { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: handle_data alloc failed sid=%08x", stream_id); + if (e) queue_entry_free(e); if (buf) memory_pool_free(c->tc->data_pool, buf); + } + } + return 1; + } + + if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: REM_CLOSED sid=%08x", stream_id); + if (c->state == SOCKS_STATE_CONNECTING || c->state == HTTP_STATE_CONNECTING) { + if (c->is_http) { + uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n"; + write_to_client(c, resp, (uint16_t)strlen((char*)resp)); + } else { + uint8_t err_reply[] = { 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + write_to_client(c, err_reply, 10); + } + } + c->rem_closed = 1; + etcp_router_waiter_cancel(c->inst, c->via_node_id, &c->tx_waiter); + tcp_conn_push_close(c->tc); + return 1; + } + + if (subcmd == TCP_PROXY_SUBCMD_ERROR) { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: ERROR from exit sid=%08x", stream_id); + if (c->state == SOCKS_STATE_CONNECTING) { + uint8_t err_reply[] = { 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + write_to_client(c, err_reply, 10); + } else if (c->state == HTTP_STATE_CONNECTING) { + uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n"; + write_to_client(c, resp, (uint16_t)strlen((char*)resp)); + } + c->rem_closed = 1; + etcp_router_waiter_cancel(c->inst, c->via_node_id, &c->tx_waiter); + tcp_conn_push_close(c->tc); + return 1; + } + + if (subcmd == TCP_PROXY_SUBCMD_FIN) { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: FIN from exit sid=%08x", stream_id); + c->fin_remote = 1; + tcp_conn_push_fin(c->tc); + if (c->tc->fin_local && !c->close_sent && !c->close_pending) send_close(c); + return 1; + } + + return 1; +} + +void socks_proxy_conn_free(struct socks_proxy_conn* c) { + if (!c) return; + struct socks_proxy_conn** head = c->head; int* count = c->count; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: FREE sid=%08x state=%d total=%d", c->stream_id, c->state, count ? *count : 0); + if (c->close_pending && c->inst) { + c->close_pending = 0; + uint8_t subcmd = c->tc && c->tc->error ? TCP_PROXY_SUBCMD_ERROR : TCP_PROXY_SUBCMD_CLOSE; + send_msg(c->inst, c->via_node_id, subcmd, c->stream_id, NULL, 0, 1); + } + if (head) { struct socks_proxy_conn** prev = head; while (*prev) { if (*prev == c) { *prev = c->next; if (count) (*count)--; break; } prev = &(*prev)->next; } } + if (c->tc) { tcp_conn_destroy(c->tc); c->tc = NULL; } + if (c->inst) etcp_router_waiter_cancel(c->inst, c->via_node_id, &c->tx_waiter); + u_free(c); +} + +void socks_proxy_conn_free_all(struct socks_proxy_conn** head, int* count) { + while (*head) { struct socks_proxy_conn* next = (*head)->next; socks_proxy_conn_free(*head); } +} + +// ==================================================================== +// Слушающий сокет +// ==================================================================== +struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_str, + struct socks_proxy_conn** conns, int* count, uint32_t* next_stream_id, + struct UTUN_INSTANCE* inst, uint64_t via_node_id, int is_http) { + char ip[64], *colon = strrchr(addr_str, ':'); + if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad addr '%s' (need IP:PORT)", addr_str); return NULL; } + size_t ip_len = (size_t)(colon - addr_str); + if (ip_len > sizeof(ip) - 1) ip_len = sizeof(ip) - 1; + memcpy(ip, addr_str, ip_len); ip[ip_len] = '\0'; + int port = atoi(colon + 1); + if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d", port); return NULL; } + + struct listen_ctx* ctx = u_calloc(1, sizeof(struct listen_ctx)); + if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: u_calloc failed"); return NULL; } + ctx->ua = ua; ctx->inst = inst; ctx->via_node_id = via_node_id; + ctx->is_http = is_http; ctx->conns = conns; ctx->conn_count = count; + ctx->next_stream_id = next_stream_id; + + socket_t sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: socket() failed errno=%d", errno); u_free(ctx); return NULL; } + socket_set_nonblocking(sock); + socket_set_reuseaddr(sock, 1); + + struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; addr.sin_port = htons((uint16_t)port); + if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: inet_pton(%s) failed", ip); socket_close_wrapper(sock); u_free(ctx); return NULL; } + if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bind(%s:%d) failed errno=%d", ip, port, errno); socket_close_wrapper(sock); u_free(ctx); return NULL; } + if (listen(sock, 32) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: listen() failed errno=%d", errno); socket_close_wrapper(sock); u_free(ctx); return NULL; } + + ctx->listen_sock = sock; + ctx->socket_id = uasync_add_socket_t(ua, sock, on_accept_cb, NULL, NULL, ctx); + if (!ctx->socket_id) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: uasync_add_socket_t failed"); socket_close_wrapper(sock); u_free(ctx); return NULL; } + + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: %s listening on %s:%d sock=%d", + is_http ? "HTTP" : "SOCKS", ip, port, (int)sock); + return ctx; +} + +void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out) { + if (!ctx) return; + if (ctx->socket_id) uasync_remove_socket_t(ua, ctx->listen_sock); + socket_close_wrapper(ctx->listen_sock); + if (sock_out) *sock_out = SOCKET_INVALID; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: listener closed sock=%d", (int)ctx->listen_sock); + u_free(ctx); +} diff --git a/src/proxy/socks_proxy.h b/src/proxy/socks_proxy.h new file mode 100644 index 00000000..c0acfd74 --- /dev/null +++ b/src/proxy/socks_proxy.h @@ -0,0 +1,78 @@ +// socks_proxy.h — SOCKS5 / HTTP CONNECT proxy (client side) +#ifndef SOCKS_PROXY_H +#define SOCKS_PROXY_H + +#include +#include "../lib/socket_compat.h" +#include "../lib/ll_queue.h" + +struct UASYNC; +struct UTUN_INSTANCE; +struct ll_entry; +struct ll_queue; +struct tcp_conn; + +enum { + SOCKS_STATE_GREETING = 0, // ждём SOCKS5 greeting (ver + methods) + SOCKS_STATE_REQUEST, // ждём SOCKS5 CONNECT request + SOCKS_STATE_CONNECTING, // отправили ETCP CONNECT, ждём ответ от exit + SOCKS_STATE_RELAY // релей данных +}; + +enum { + HTTP_STATE_REQUEST = 0, // ждём "CONNECT host:port HTTP/1.1\r\n" + HTTP_STATE_CONNECTING, // отправили ETCP CONNECT, ждём ответ от exit + HTTP_STATE_RELAY // релей данных +}; + +struct socks_proxy_conn { + struct socks_proxy_conn* next; + struct socks_proxy_conn** head; // указатель на голову списка (для удаления) + int* count; // указатель на счётчик списка + struct tcp_conn* tc; + struct UASYNC* ua; + struct UTUN_INSTANCE* inst; + uint64_t via_node_id; + uint32_t stream_id; + uint8_t dest_ip[4]; + uint16_t dest_port; + uint8_t fin_remote; + uint8_t rem_closed; + uint8_t close_sent; + uint8_t close_pending; + uint8_t is_http; + uint8_t state; + uint8_t buf[1024]; + uint16_t buf_len; + struct queue_waiter_handle tx_waiter; +}; + +struct listen_ctx; + +// Создать слушающий сокет для SOCKS/HTTP proxy. addr_str = "IP:PORT". +// listen_ctx должен быть освобождён через socks_proxy_close_listen. +// Возвращает listen_ctx при успехе, NULL при ошибке. +struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_str, + struct socks_proxy_conn** conns, int* count, uint32_t* next_stream_id, + struct UTUN_INSTANCE* inst, uint64_t via_node_id, int is_http); + +// Закрыть слушающий сокет и освободить listen_ctx. +// sock_out получает значение сокета (для последующего close). +void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out); + +// Найти соединение по stream_id +struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id); + +// Обработать входящее ETCP сообщение (DATA/CLOSE/ERROR/FIN) +// Возвращает 1 если обработано, 0 если stream_id не найден +int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count, + uint32_t stream_id, uint8_t subcmd, + const uint8_t* data, size_t data_len); + +// Освободить одно соединение (удаляет из списка) +void socks_proxy_conn_free(struct socks_proxy_conn* c); + +// Освободить все соединения в списке +void socks_proxy_conn_free_all(struct socks_proxy_conn** head, int* count); + +#endif // SOCKS_PROXY_H diff --git a/src/proxy/tcp_proxy_client.c b/src/proxy/tcp_proxy_client.c index e26d92d7..fa62e3c1 100644 --- a/src/proxy/tcp_proxy_client.c +++ b/src/proxy/tcp_proxy_client.c @@ -1,5 +1,6 @@ // 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" @@ -525,6 +526,8 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en struct tcp_proxy_client_conn *pc, *next; for (pc = proxy->conns; pc; pc = next) { next = pc->next; 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); } } if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } @@ -534,6 +537,17 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4); if (proxy) { + size_t data_len = entry->len > TCP_PROXY_HDR_SIZE ? entry->len - TCP_PROXY_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_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_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; } @@ -553,66 +567,90 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en 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) + 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_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; } + + 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_SOCKET, "tcp_proxy_client_create: 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_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->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_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 (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_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 (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_SOCKET, "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_SOCKET, "tcp_proxy_client: HTTP proxy init failed on %s", http_proxy_addr); } + } + if (inst) { if (etcp_router_bind(inst, ETCP_ID_TCP_PROXY_CLIENT, tcp_proxy_client_router_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_CLIENT); - udp_proxy_init(inst, ua); - icmp_proxy_init(inst, ua); + if (need_tun) { 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); + DEBUG_INFO(DEBUG_CATEGORY_TUN, "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; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client destroying: conns=%d", p->conn_count); + int total = p->conn_count + p->socks_conn_count + p->http_conn_count; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "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_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; diff --git a/src/proxy/tcp_proxy_client.h b/src/proxy/tcp_proxy_client.h index 8c034774..6e9c34f4 100644 --- a/src/proxy/tcp_proxy_client.h +++ b/src/proxy/tcp_proxy_client.h @@ -17,6 +17,8 @@ struct memory_pool; struct tcp_proxy_client_mapping_config; struct tcp_proxy_server; struct lwip_tcp_ctx; +struct socks_proxy_conn; +struct listen_ctx; struct tcp_pcb; @@ -57,12 +59,26 @@ struct tcp_proxy_client { struct tcp_proxy_client_mapping_config* mappings; // указатель на конфиг int mapping_count; + + // SOCKS proxy + int socks_enabled; + struct socks_proxy_conn* socks_conns; + int socks_conn_count; + struct listen_ctx* socks_listen; + + // HTTP CONNECT proxy + int http_proxy_enabled; + struct socks_proxy_conn* http_conns; + int http_conn_count; + struct listen_ctx* http_listen; }; 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); + uint64_t via_node_id, + int socks_enabled, const char* socks_addr, + int http_proxy_enabled, const char* http_proxy_addr); void tcp_proxy_client_destroy(struct tcp_proxy_client* p); void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); diff --git a/src/utun_instance.c b/src/utun_instance.c index 46fc89a6..650e5cc5 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -161,16 +161,21 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u } // TCP proxy client (from [tcp_proxy] config section) - if (config->global.tcp_proxy_client_enabled) { + if (config->global.tcp_proxy_client_enabled || config->global.tcp_proxy_client_socks_enabled || config->global.tcp_proxy_client_http_proxy_enabled) { const char* tun_name = config->global.tcp_proxy_client_tun_name[0] ? config->global.tcp_proxy_client_tun_name : "tun_tcp"; const char* tun_ip = config->global.tcp_proxy_client_tun_ip[0] ? config->global.tcp_proxy_client_tun_ip : "10.99.0.1"; int mtu = config->global.tcp_proxy_client_mtu > 0 ? config->global.tcp_proxy_client_mtu : 1500; instance->tcp_proxy_client = tcp_proxy_client_create(instance, ua, tun_name, tun_ip, mtu, g_tun_init_enabled ? 0 : 1, config->global.tcp_proxy_client_mappings, config->global.tcp_proxy_client_mapping_count, - config->global.tcp_proxy_client_via_node_id); + config->global.tcp_proxy_client_via_node_id, + config->global.tcp_proxy_client_socks_enabled, config->global.tcp_proxy_client_socks_addr, + config->global.tcp_proxy_client_http_proxy_enabled, config->global.tcp_proxy_client_http_proxy_addr); if (instance->tcp_proxy_client) { - DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client enabled: TUN=%s IP=%s MTU=%d mappings=%d", - tun_name, tun_ip, mtu, config->global.tcp_proxy_client_mapping_count); + DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client enabled: TUN=%s IP=%s MTU=%d socks=%s http=%s mappings=%d", + tun_name, tun_ip, mtu, + config->global.tcp_proxy_client_socks_enabled ? config->global.tcp_proxy_client_socks_addr : "off", + config->global.tcp_proxy_client_http_proxy_enabled ? config->global.tcp_proxy_client_http_proxy_addr : "off", + config->global.tcp_proxy_client_mapping_count); } else { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TCP proxy client"); return -1; diff --git a/tests/Makefile.am b/tests/Makefile.am index 37df79eb..0332b4d2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -112,6 +112,7 @@ ETCP_FULL_OBJS = \ $(top_builddir)/src/utun-etcp_router.o \ $(top_builddir)/src/proxy/utun-tcp_proxy_server.o \ $(top_builddir)/src/proxy/utun-udp_proxy.o \ + $(top_builddir)/src/proxy/utun-socks_proxy.o \ $(top_builddir)/src/proxy/utun-icmp_proxy.o \ $(top_builddir)/src/lwip_tcp/utun-lwip_pbuf.o \ $(top_builddir)/src/lwip_tcp/utun-lwip_tcp.o \ diff --git a/tests/test_tcp_proxy_client.c b/tests/test_tcp_proxy_client.c index 407e90d4..78adafdd 100644 --- a/tests/test_tcp_proxy_client.c +++ b/tests/test_tcp_proxy_client.c @@ -26,7 +26,7 @@ int main(void) { if (!ua) { printf("[FAIL] uasync_create\n"); return 1; } struct tcp_proxy_client_mapping_config m = {.local_port = 9090, .remote_ip = "127.0.0.1", .remote_port = 9999}; - struct tcp_proxy_client* p = tcp_proxy_client_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, &m, 1, 0); + struct tcp_proxy_client* p = tcp_proxy_client_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, &m, 1, 0, 0, NULL, 0, NULL); if (!p) { printf("[FAIL] tcp_proxy_client_create with mapping\n"); uasync_destroy(ua, 0); return 1; } tcp_proxy_client_destroy(p); @@ -39,7 +39,7 @@ int main(void) { struct UASYNC* ua = uasync_create(); if (!ua) { printf("[FAIL] uasync_create\n"); return 1; } - struct tcp_proxy_client* p = tcp_proxy_client_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, NULL, 0, 0); + struct tcp_proxy_client* p = tcp_proxy_client_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, NULL, 0, 0, 0, NULL, 0, NULL); if (!p) { printf("[FAIL] tcp_proxy_client_create without mapping\n"); uasync_destroy(ua, 0); return 1; } tcp_proxy_client_destroy(p); diff --git a/tests/test_tcp_proxy_remote.c b/tests/test_tcp_proxy_remote.c index cbe7258a..915af76b 100644 --- a/tests/test_tcp_proxy_remote.c +++ b/tests/test_tcp_proxy_remote.c @@ -76,11 +76,11 @@ done: close(cli); close(srv); _exit(0); static void start_test(void) { struct tcp_proxy_client_mapping_config m = {.local_port=9090,.remote_ip="127.0.0.1",.remote_port=g_echo_port}; - g_proxy_b = tcp_proxy_client_create(g_b, g_ua, NULL, NULL, 0, 0, &m, 1, 0, g_pair[1], 0xCCCC000000000001ULL); + g_proxy_b = tcp_proxy_client_create(g_b, g_ua, "tun_tcp", "10.99.0.1", 0, 0, &m, 1, 0xCCCC000000000001ULL, 0, NULL, 0, NULL); if (!g_proxy_b) { printf("[FAIL] proxy_b create\n"); g_done=-1; return; } - g_b->tcp_proxy = g_proxy_b; + g_b->tcp_proxy_client = g_proxy_b; - g_cli = tcp_proxy_client_create(NULL, g_ua, NULL, NULL, 0, 0, NULL, 0, 0, g_pair[0], 0); + g_cli = tcp_proxy_client_create(NULL, g_ua, "tun_tcp", "10.99.0.1", 0, 0, NULL, 0, 0, 0, NULL, 0, NULL); if (!g_cli) { printf("[FAIL] cli create\n"); g_done=-1; return; } g_conn_idx = tcp_proxy_active_open(g_cli, "10.99.0.100", 9090); diff --git a/utun.conf.sample b/utun.conf.sample index 992143d6..fb7b72d0 100644 --- a/utun.conf.sample +++ b/utun.conf.sample @@ -79,17 +79,28 @@ allow=all # --- TCP Proxy Client (локальный TCP/UDP/ICMP прокси) --- # Проксирует входящие TCP (через lwIP), UDP и ICMP ping через указанный via_node. -# Все три протокола идут через один и тот же удалённый узел. +# Также поддерживает SOCKS5 и HTTP CONNECT proxy без TUN (напрямую через ETCP). +# Режимы можно включать раздельно или одновременно. #[tcp_proxy_client] #enabled=yes + +## --- TUN режим (прозрачный) --- #tun_name=tun_tcp # имя TUN интерфейса (по умолчанию tun_tcp) #tun_ip=10.99.0.1 # IP адрес TUN интерфейса #mtu=1500 # MTU -#eim_timeout=300 # таймаут EIM маппингов (сек) -#via_node=0xABCD000000000001 # узел для проксирования (или свой node_id для локального) #forward=8000 -> 10.0.0.50:80 #forward=2222 -> 10.0.0.50:22 +## --- SOCKS5 режим (прямой) --- +#socks_enabled=yes +#socks_addr=127.0.0.1:1080 # адрес для SOCKS5 сервера (bind) + +## --- HTTP CONNECT режим (прямой) --- +#http_proxy_enabled=yes +#http_proxy_addr=127.0.0.1:8080 # адрес для HTTP CONNECT сервера (bind) + +#via_node=0xABCD000000000001 # узел для проксирования (общий для всех режимов) + # --- TCP Proxy Server (удаленный exit node) --- # Принимает CONNECT-запросы от других нод и открывает OS сокеты к адресатам. #[tcp_proxy_server]