Browse Source

transport: reality-релей idle-timeout (конфиг, дефолт 5с) + warn при >64 релеях; убрать лимит FD_SETSIZE; проверка socket_id в stcp_server

proxy
evgeny 1 week ago
parent
commit
fb4343fca6
  1. 10
      lib/u_async.c
  2. 3
      src/config_parser.c
  3. 1
      src/transport_layer/reality.c
  4. 2
      src/transport_layer/reality.h
  5. 34
      src/transport_layer/reality_relay.c
  6. 5
      src/transport_layer/reality_relay.h
  7. 9
      src/transport_layer/stcp_server.c

10
lib/u_async.c

@ -138,10 +138,6 @@ static int socket_array_add_internal(struct socket_array* sa, int fd, socket_t s
socket_t_callback_t read_cbk_sock, socket_t_callback_t write_cbk_sock,
socket_callback_t except_cbk, const char* name, void* user_data) {
if (!sa || fd < 0) return -1;
// FD_SETSIZE check only for POSIX systems - Windows sockets can have any value
#ifndef _WIN32
if (fd >= FD_SETSIZE) return -1;
#endif
if (fd >= sa->capacity) {
// Need to resize - double the capacity
int new_capacity = sa->capacity * 2;
@ -234,7 +230,7 @@ static int socket_array_add_socket_t(struct socket_array* sa, socket_t sock, soc
if (fd < 0) return -1; // Windows sockets can have any value, only check negative
#else
int fd = sock; // On POSIX, socket_t is int
if (fd < 0 || fd >= FD_SETSIZE) return -1;
if (fd < 0) return -1;
#endif
return socket_array_add_internal(sa, fd, sock, SOCKET_NODE_TYPE_SOCK,
NULL, NULL, read_cbk, write_cbk, except_cbk, name, user_data);
@ -690,10 +686,6 @@ err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) {
// Instance version
void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, const char* name, void* user_data) {
if (!ua || fd < 0) return NULL;
// FD_SETSIZE check only for POSIX systems - Windows sockets can have any value
#ifndef _WIN32
if (fd >= FD_SETSIZE) return NULL;
#endif
int index = socket_array_add(ua->sockets, fd, read_cbk, write_cbk, except_cbk, name, user_data);
if (index < 0) return NULL;

3
src/config_parser.c

@ -918,12 +918,13 @@ static int parse_reality(const char *key, const char *value, struct reality_conf
return 0;
}
if (strcmp(key, "time_window") == 0) { rc->time_window_sec = atoi(value); return 0; }
if (strcmp(key, "relay_idle_timeout") == 0) { rc->relay_idle_timeout_sec = atoi(value); return 0; }
if (strcmp(key, "fingerprint") == 0) {
if (strcmp(value, "chrome") == 0) { rc->fingerprint = REALITY_FP_CHROME; return 0; }
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: unknown reality fingerprint '%s' (valid: chrome)", filename, line_num, value);
return -1;
}
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown reality option '%s'. Valid: enabled, server_name, dest, short_id, short_ids, public_key, private_key, version, time_window, fingerprint", filename, line_num, key);
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown reality option '%s'. Valid: enabled, server_name, dest, short_id, short_ids, public_key, private_key, version, time_window, relay_idle_timeout, fingerprint", filename, line_num, key);
return -1;
}

1
src/transport_layer/reality.c

@ -116,6 +116,7 @@ void reality_config_set_defaults(struct reality_config *cfg) {
cfg->has_private_key = 0;
cfg->version[0] = 1; cfg->version[1] = 0; cfg->version[2] = 0;
cfg->time_window_sec = 30;
cfg->relay_idle_timeout_sec = 5;
cfg->fingerprint = REALITY_FP_CHROME;
}

2
src/transport_layer/reality.h

@ -57,6 +57,7 @@ struct reality_server_config {
int short_id_count;
uint8_t version[REALITY_VERSION_SIZE]; // принимаемая версия
int64_t time_window_sec; // допуск timestamp (антиреплей)
int relay_idle_timeout_sec; // idle-timeout релея неавторизованных (0=выкл)
int fingerprint;
};
@ -75,6 +76,7 @@ struct reality_config {
uint8_t has_private_key;
uint8_t version[REALITY_VERSION_SIZE]; // версия протокола
int time_window_sec;
int relay_idle_timeout_sec; // idle-timeout релея неавторизованных (0=выкл)
int fingerprint;
};

34
src/transport_layer/reality_relay.c

@ -21,6 +21,7 @@
#endif
#define RELAY_IO_BUF 65536
#define RELAY_WARN_CONNS 64
#ifdef _WIN32
#define RELAY_SHUT_WR SD_SEND
@ -36,6 +37,9 @@ struct reality_relay {
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 если нет)
@ -53,8 +57,11 @@ struct reality_relay {
};
static struct reality_relay *g_relays = NULL; // активные релеи
static int g_relay_count = 0; // число активных релеев (для warn)
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);
@ -69,12 +76,14 @@ static void relay_free_cb(void *arg) {
static void relay_free(struct reality_relay *r) {
if (!r || r->closed) return;
r->closed = 1;
g_relay_count--;
{ // вынуть из реестра активных релеев
struct reality_relay **pp = &g_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; }
@ -86,6 +95,21 @@ static void relay_free(struct reality_relay *r) {
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) {
@ -157,6 +181,7 @@ static void relay_client_read_cb(socket_t sock, void *arg) {
#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);
}
@ -183,6 +208,7 @@ static void relay_dest_read_cb(socket_t sock, void *arg) {
}
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);
}
@ -263,7 +289,8 @@ static void relay_dns_done_cb(const struct adns_result *res, void *arg) {
int reality_relay_start(struct UASYNC *ua, socket_t client_sock,
const char *dest,
const uint8_t *initial_data, size_t initial_len) {
const uint8_t *initial_data, size_t initial_len,
int idle_timeout_sec) {
if (!ua || client_sock == SOCKET_INVALID || !dest || !dest[0]) {
DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "reality_relay_start: invalid args");
return -1;
@ -275,7 +302,12 @@ int reality_relay_start(struct UASYNC *ua, socket_t client_sock,
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 = g_relays; g_relays = r;
g_relay_count++;
if (g_relay_count > RELAY_WARN_CONNS)
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "reality_relay: %d concurrent relays (over %d) — возможен скан-флуд", g_relay_count, RELAY_WARN_CONNS);
relay_touch(r); // таймер покрывает DNS + connect (застрявший connect тоже чистится)
// берём владение client_sock сразу (снимаем старую регистрацию) — до парсинга,
// чтобы любая ошибка ниже не оставила сокет зарегистрированным в uasync.

5
src/transport_layer/reality_relay.h

@ -21,10 +21,13 @@ struct UASYNC;
// в uasync) и проксирует его на dest ("host:port").
// initial_data/initial_len — уже прочитанные байты от клиента (ClientHello),
// отправляются на dest первыми (буфер КОПИРУЕТСЯ — владение остаётся у вызывающего).
// idle_timeout_sec — время простоя (без трафика в обе стороны), после которого
// релей принудительно закрывается; 0 = таймер выключен.
// Возвращает 0 = ok, -1 = ошибка (client_sock уже закрыт внутри).
int reality_relay_start(struct UASYNC *ua, socket_t client_sock,
const char *dest,
const uint8_t *initial_data, size_t initial_len);
const uint8_t *initial_data, size_t initial_len,
int idle_timeout_sec);
// Принудительно завершить все активные релеи (вызывается при shutdown).
// Освобождение отложено через uasync_call_soon — после вызова нужно

9
src/transport_layer/stcp_server.c

@ -196,6 +196,7 @@ void stcp_server_set_reality(struct stcp_server *srv, const struct reality_confi
srv->reality_srv.short_id_count = rc->short_id_count;
memcpy(srv->reality_srv.version, rc->version, REALITY_VERSION_SIZE);
srv->reality_srv.time_window_sec = rc->time_window_sec;
srv->reality_srv.relay_idle_timeout_sec = rc->relay_idle_timeout_sec;
srv->reality_srv.fingerprint = rc->fingerprint;
snprintf(srv->reality_dest, sizeof(srv->reality_dest), "%s", rc->dest);
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "stcp_server_set_reality: enabled, short_ids=%d dest=%s", rc->short_id_count, rc->dest);
@ -216,7 +217,7 @@ static void reality_server_start_relay(struct stcp_conn *c,
c->socket_id = NULL;
c->sock = SOCKET_INVALID;
reality_relay_start(c->ua, cli_sock, c->reality_dest, init, init_len);
reality_relay_start(c->ua, cli_sock, c->reality_dest, init, init_len, c->reality_srv.relay_idle_timeout_sec);
u_free(init); /* reality_relay_start копирует данные в свой буфер — владение остаётся у нас */
stcp_conn_do_close(c, 0);
}
@ -307,6 +308,12 @@ static void server_accept_cb(socket_t listen_sock, void *arg) {
c->on_close = srv->close_cb;
c->close_arg = srv->close_arg;
c->socket_id = uasync_add_socket_t(srv->ua, cli_sock, server_conn_read_cb, stcp_write_cb, NULL, "stcp_conn", c);
if (!c->socket_id) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_server: uasync_add_socket_t failed for accepted fd=%d", (int)cli_sock);
socket_close_wrapper(cli_sock);
u_free(c);
return;
}
c->inst = srv->inst;
c->device_type = srv->inst ? srv->inst->client_type : 0;
c->keepalive_interval = srv->inst ? srv->inst->keepalive_interval : 200;

Loading…
Cancel
Save