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.
 
 
 
 
 
 

375 lines
16 KiB

// reality_relay.c — живой релей неавторизованных клиентов на реальный HTTPS-сайт
//
// Двухсторонний TCP-пайп: клиент (приславший невалидный ClientHello) ↔ реальный
// сайт (dest). initial_data (уже прочитанный ClientHello) пересылается на dest
// первым. Поддержан half-close: FIN с одной стороны закрывает только свою
// сторону записи на другой, чтобы ответ сайта дошёл до клиента полностью.
#include "reality_relay.h"
#include "reality_io.h"
#include "../lib/u_async.h"
#include "../lib/mem.h"
#include "../lib/debug_config.h"
#include "../lib/platform_compat.h"
#ifdef UTUN_HAVE_STANDBY
#include "standby.h"
#endif
#include "../lib/async_dns.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifndef _WIN32
#include <netinet/tcp.h>
#endif
#define RELAY_IO_BUF 65536
#define RELAY_WARN_CONNS 64
#ifdef _WIN32
#define RELAY_SHUT_WR SD_SEND
#else
#define RELAY_SHUT_WR SHUT_WR
#endif
struct reality_relay {
struct reality_owner *owner;
struct UASYNC *ua;
socket_t client_sock;
void *client_sid;
socket_t dest_sock;
void *dest_sid;
int connecting; // 1 = ждём завершения connect() к dest
void *idle_timer; // handle таймера простоя (NULL если выключен)
int idle_timeout_tb; // время простоя в 0.1ms (0 = таймер выключен)
char host[256]; // dest hostname (для async DNS и логирования)
uint16_t port; // dest port
struct adns_query *dns_q; // pending async DNS query (NULL если нет)
uint8_t *c2d; size_t c2d_len, c2d_off; // pending client→dest
uint8_t *d2c; size_t d2c_len, d2c_off; // pending dest→client
int client_read_eof; // клиент закрыл свою сторону записи
int dest_read_eof; // dest закрыл свою сторону записи
int closed;
size_t bytes_c2d, bytes_d2c;
struct reality_relay *next; // цепочка активных релеев (для shutdown)
};
static void relay_free(struct reality_relay *r);
static void relay_touch(struct reality_relay *r);
static void relay_idle_timeout_cb(void *arg);
static void relay_flush_c2d(struct reality_relay *r);
static void relay_flush_d2c(struct reality_relay *r);
static void relay_client_read_cb(socket_t sock, void *arg);
static void relay_dest_read_cb(socket_t sock, void *arg);
static void relay_client_write_cb(socket_t sock, void *arg);
static void relay_dest_write_cb(socket_t sock, void *arg);
static void relay_free_cb(void *arg) {
u_free(arg);
}
static void relay_free(struct reality_relay *r) {
if (!r || r->closed) return;
r->closed = 1;
r->owner->relay_count--;
{ // вынуть из реестра активных релеев
struct reality_relay **pp = &r->owner->relays;
while (*pp && *pp != r) pp = &(*pp)->next;
if (*pp) *pp = r->next;
r->next = NULL;
}
if (r->idle_timer) { uasync_cancel_timeout(r->ua, r->idle_timer); r->idle_timer = NULL; }
if (r->dns_q) { adns_cancel(r->dns_q); r->dns_q = NULL; }
if (r->client_sid) { uasync_remove_socket_t(r->ua, r->client_sock); r->client_sid = NULL; }
if (r->dest_sid) { uasync_remove_socket_t(r->ua, r->dest_sock); r->dest_sid = NULL; }
if (r->client_sock != SOCKET_INVALID) { socket_close_wrapper(r->client_sock); r->client_sock = SOCKET_INVALID; }
if (r->dest_sock != SOCKET_INVALID) { socket_close_wrapper(r->dest_sock); r->dest_sock = SOCKET_INVALID; }
if (r->c2d) { u_free(r->c2d); r->c2d = NULL; }
if (r->d2c) { u_free(r->d2c); r->d2c = NULL; }
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "reality_relay closed: c2d=%zu d2c=%zu", r->bytes_c2d, r->bytes_d2c);
uasync_call_soon(r->ua, r, relay_free_cb);
}
// Перезапустить таймер простоя: сброс на любую активность (чтение данных).
static void relay_touch(struct reality_relay *r) {
if (r->idle_timeout_tb <= 0) return;
if (r->idle_timer) { uasync_cancel_timeout(r->ua, r->idle_timer); r->idle_timer = NULL; }
r->idle_timer = uasync_set_timeout(r->ua, r->idle_timeout_tb, r, relay_idle_timeout_cb, "relay_idle");
}
static void relay_idle_timeout_cb(void *arg) {
struct reality_relay *r = (struct reality_relay *)arg;
r->idle_timer = NULL; // handle освобождён process_timeouts сразу после колбэка
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay: idle timeout %ds c2d=%zu d2c=%zu — closing",
r->idle_timeout_tb / 10000, r->bytes_c2d, r->bytes_d2c);
relay_free(r);
}
static void relay_flush_c2d(struct reality_relay *r) {
if (!r->c2d) return;
while (r->c2d_off < r->c2d_len) {
ssize_t sent = send(r->dest_sock, r->c2d + r->c2d_off, r->c2d_len - r->c2d_off, 0);
if (sent < 0) {
int err = socket_get_error();
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) { uasync_set_socket_write(r->ua, r->dest_sid, 1); return; }
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay send to dest failed err=%d", err);
relay_free(r); return;
}
if (sent == 0) { relay_free(r); return; }
r->c2d_off += (size_t)sent;
}
r->bytes_c2d += r->c2d_len;
u_free(r->c2d); r->c2d = NULL;
uasync_set_socket_write(r->ua, r->dest_sid, 0);
if (!r->client_read_eof) uasync_set_socket_read(r->ua, r->client_sid, 1);
}
static void relay_flush_d2c(struct reality_relay *r) {
if (!r->d2c) return;
while (r->d2c_off < r->d2c_len) {
ssize_t sent = send(r->client_sock, r->d2c + r->d2c_off, r->d2c_len - r->d2c_off, 0);
if (sent < 0) {
int err = socket_get_error();
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) { uasync_set_socket_write(r->ua, r->client_sid, 1); return; }
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay send to client failed err=%d", err);
relay_free(r); return;
}
if (sent == 0) { relay_free(r); return; }
r->d2c_off += (size_t)sent;
}
r->bytes_d2c += r->d2c_len;
u_free(r->d2c); r->d2c = NULL;
uasync_set_socket_write(r->ua, r->client_sid, 0);
if (!r->dest_read_eof) uasync_set_socket_read(r->ua, r->dest_sid, 1);
}
static void relay_client_read_cb(socket_t sock, void *arg) {
struct reality_relay *r = (struct reality_relay *)arg;
(void)sock;
if (r->c2d) return;
uint8_t *buf = u_malloc(RELAY_IO_BUF);
if (!buf) { relay_free(r); return; }
ssize_t n = recv(r->client_sock, buf, RELAY_IO_BUF, 0);
if (n < 0) {
int err = socket_get_error();
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) { u_free(buf); return; }
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay recv from client failed err=%d", err);
u_free(buf); relay_free(r); return;
}
if (n == 0) {
u_free(buf);
r->client_read_eof = 1;
uasync_set_socket_read(r->ua, r->client_sid, 0);
shutdown(r->dest_sock, RELAY_SHUT_WR);
if (r->dest_read_eof) relay_free(r);
return;
}
#ifdef UTUN_HAVE_STANDBY
{
struct sockaddr_storage sa; socklen_t slen = sizeof(sa);
char who[64];
if (getpeername(r->client_sock, (struct sockaddr*)&sa, &slen) == 0)
snprintf(who, sizeof(who), "addr=%s", sockaddr_storage_to_str(&sa).str);
else snprintf(who, sizeof(who), "?");
standby_log_rx("reality relay", who);
}
#endif
r->c2d = buf; r->c2d_len = (size_t)n; r->c2d_off = 0;
uasync_set_socket_read(r->ua, r->client_sid, 0); // backpressure
relay_touch(r);
relay_flush_c2d(r);
}
static void relay_dest_read_cb(socket_t sock, void *arg) {
struct reality_relay *r = (struct reality_relay *)arg;
(void)sock;
if (r->d2c) return;
uint8_t *buf = u_malloc(RELAY_IO_BUF);
if (!buf) { relay_free(r); return; }
ssize_t n = recv(r->dest_sock, buf, RELAY_IO_BUF, 0);
if (n < 0) {
int err = socket_get_error();
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) { u_free(buf); return; }
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay recv from dest failed err=%d", err);
u_free(buf); relay_free(r); return;
}
if (n == 0) {
u_free(buf);
r->dest_read_eof = 1;
uasync_set_socket_read(r->ua, r->dest_sid, 0);
shutdown(r->client_sock, RELAY_SHUT_WR);
if (r->client_read_eof) relay_free(r);
return;
}
r->d2c = buf; r->d2c_len = (size_t)n; r->d2c_off = 0;
uasync_set_socket_read(r->ua, r->dest_sid, 0); // backpressure
relay_touch(r);
relay_flush_d2c(r);
}
static void relay_client_write_cb(socket_t sock, void *arg) {
struct reality_relay *r = (struct reality_relay *)arg;
(void)sock;
if (!r->d2c) { uasync_set_socket_write(r->ua, r->client_sid, 0); return; }
relay_flush_d2c(r);
}
static void relay_dest_write_cb(socket_t sock, void *arg) {
struct reality_relay *r = (struct reality_relay *)arg;
if (r->connecting) {
int err = 0; socklen_t elen = sizeof(err);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &elen) < 0 || err != 0) {
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay connect to dest failed err=%d", err);
relay_free(r); return;
}
r->connecting = 0;
int opt = 1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&opt, sizeof(opt));
if (r->c2d) relay_flush_c2d(r);
else { uasync_set_socket_write(r->ua, r->dest_sid, 0); uasync_set_socket_read(r->ua, r->client_sid, 1); }
return;
}
if (!r->c2d) { uasync_set_socket_write(r->ua, r->dest_sid, 0); return; }
relay_flush_c2d(r);
}
// Создать неблокирующий dest-сокет и начать connect() к уже разрезолвенному адресу.
// Вызывается сразу (literal IP) или из коллбэка async DNS.
static void relay_connect_dest(struct reality_relay *r, const struct sockaddr_in *addr) {
socket_t dest_sock = socket(AF_INET, SOCK_STREAM, 0);
if (dest_sock == SOCKET_INVALID) {
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay: socket failed for %s:%u", r->host, r->port);
relay_free(r);
return;
}
socket_set_nonblocking(dest_sock);
r->dest_sock = dest_sock;
int cr = connect(dest_sock, (const struct sockaddr *)addr, sizeof(*addr));
if (cr < 0) {
int err = socket_get_error();
if (err != EINPROGRESS && err != ERR_WOULDBLOCK) {
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay connect %s:%u failed err=%d", r->host, r->port, err);
relay_free(r);
return;
}
} else {
r->connecting = 0;
int opt = 1; setsockopt(dest_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&opt, sizeof(opt));
}
r->dest_sid = uasync_add_socket_t(r->ua, dest_sock, relay_dest_read_cb, relay_dest_write_cb, NULL, "reality_dest", r);
if (!r->dest_sid) { relay_free(r); return; }
if (!r->connecting) {
if (r->c2d) relay_flush_c2d(r);
else uasync_set_socket_read(r->ua, r->client_sid, 1);
}
}
static void relay_dns_done_cb(const struct adns_result *res, void *arg) {
struct reality_relay *r = (struct reality_relay *)arg;
r->dns_q = NULL;
if (res->status == ADNS_OK && res->count > 0) {
struct sockaddr_in sin = res->addrs[0];
sin.sin_port = htons(r->port);
const uint8_t *b = (const uint8_t *)&sin.sin_addr;
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "reality_relay: resolved %s -> %u.%u.%u.%u:%u",
r->host, b[0], b[1], b[2], b[3], r->port);
relay_connect_dest(r, &sin);
} else {
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay: DNS failed for %s status=%d", r->host, res->status);
relay_free(r);
}
}
int reality_relay_start(struct reality_owner *owner, struct UASYNC *ua, socket_t client_sock,
const char *dest,
const uint8_t *initial_data, size_t initial_len,
int idle_timeout_sec) {
if (ua && client_sock != SOCKET_INVALID) uasync_remove_socket_t(ua, client_sock);
if (!owner || !ua || client_sock == SOCKET_INVALID || !dest || !dest[0] || (!initial_data && initial_len)) {
if (client_sock != SOCKET_INVALID) socket_close_wrapper(client_sock);
DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "reality_relay_start: invalid args");
return -1;
}
struct reality_relay *r = u_calloc(1, sizeof(struct reality_relay));
if (!r) { socket_close_wrapper(client_sock); DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "reality_relay_start: calloc failed"); return -1; }
r->owner = owner;
r->ua = ua;
r->client_sock = client_sock;
r->dest_sock = SOCKET_INVALID;
r->connecting = 1;
r->idle_timeout_tb = idle_timeout_sec > 0 ? idle_timeout_sec * 10000 : 0;
r->next = owner->relays; owner->relays = r;
owner->relay_count++;
if (owner->relay_count > RELAY_WARN_CONNS)
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay: %d concurrent relays (over %d) — возможен скан-флуд", owner->relay_count, RELAY_WARN_CONNS);
relay_touch(r); // таймер покрывает DNS + connect (застрявший connect тоже чистится)
// берём владение client_sock сразу (снимаем старую регистрацию) — до парсинга,
// чтобы любая ошибка ниже не оставила сокет зарегистрированным в uasync.
uasync_remove_socket_t(ua, client_sock);
r->client_sid = uasync_add_socket_t(ua, client_sock, relay_client_read_cb, relay_client_write_cb, NULL, "reality_client", r);
if (!r->client_sid) { relay_free(r); return -1; }
// не читаем от клиента, пока dest не подключён (иначе буферили бы без границ во время DNS)
uasync_set_socket_read(ua, r->client_sid, 0);
// парсим "host:port"
{
size_t dl = strlen(dest);
if (dl >= sizeof(r->host)) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "reality_relay_start: dest too long"); relay_free(r); return -1; }
memcpy(r->host, dest, dl + 1);
char *colon = strrchr(r->host, ':');
if (colon) { *colon = '\0'; r->port = (uint16_t)atoi(colon + 1); }
else r->port = 443;
if (!r->host[0] || r->port == 0) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "reality_relay_start: bad dest '%s'", dest); relay_free(r); return -1; }
}
if (initial_len && initial_data) {
r->c2d = u_malloc(initial_len);
if (!r->c2d) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "relay initial buffer allocation failed: %zu", initial_len); relay_free(r); return -1; }
memcpy(r->c2d, initial_data, initial_len);
r->c2d_len = initial_len;
r->c2d_off = 0;
}
// literal IPv4 — без DNS
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(r->port);
if (inet_pton(AF_INET, r->host, &sin.sin_addr) == 1) {
relay_connect_dest(r, &sin);
return 0;
}
// асинхронный A-запрос — не блокирует event loop
r->dns_q = adns_resolve(ua, r->host, NULL, relay_dns_done_cb, r);
if (!r->dns_q) {
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay: async DNS start failed for %s", r->host);
relay_free(r);
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "reality_relay started: client→%s:%u initial=%zu (resolving)",
r->host, r->port, initial_len);
return 0;
}
void reality_relay_shutdown(struct reality_owner *owner) {
if (!owner) return;
int count = 0;
while (owner->relays) {
struct reality_relay *r = owner->relays;
relay_free(r); // сам вынимает r из g_relays и откладывает u_free через call_soon
count++;
}
if (count > 0)
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "reality_relay_shutdown: closed %d active relays", count);
}