Browse Source
Добавлен модуль 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 не пришлось.chatgui
12 changed files with 713 additions and 41 deletions
@ -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 <stdlib.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#ifndef _WIN32 |
||||
#include <unistd.h> |
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <netdb.h> |
||||
#include <arpa/inet.h> |
||||
#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); |
||||
} |
||||
@ -0,0 +1,78 @@
|
||||
// socks_proxy.h — SOCKS5 / HTTP CONNECT proxy (client side)
|
||||
#ifndef SOCKS_PROXY_H |
||||
#define SOCKS_PROXY_H |
||||
|
||||
#include <stdint.h> |
||||
#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
|
||||
Loading…
Reference in new issue