diff --git a/lib/u_async.c b/lib/u_async.c index fdbdf209..def04034 100644 --- a/lib/u_async.c +++ b/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; diff --git a/src/config_parser.c b/src/config_parser.c index 2bc51f45..172db13d 100644 --- a/src/config_parser.c +++ b/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; } diff --git a/src/transport_layer/reality.c b/src/transport_layer/reality.c index 3764eff8..03897cb9 100644 --- a/src/transport_layer/reality.c +++ b/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; } diff --git a/src/transport_layer/reality.h b/src/transport_layer/reality.h index 458fd4c9..8cef9ea5 100644 --- a/src/transport_layer/reality.h +++ b/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; }; diff --git a/src/transport_layer/reality_relay.c b/src/transport_layer/reality_relay.c index 0968bd42..3e92af5f 100644 --- a/src/transport_layer/reality_relay.c +++ b/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. diff --git a/src/transport_layer/reality_relay.h b/src/transport_layer/reality_relay.h index 5b1ee0e8..724a6c03 100644 --- a/src/transport_layer/reality_relay.h +++ b/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 — после вызова нужно diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index be62c90d..1dc6d7e2 100644 --- a/src/transport_layer/stcp_server.c +++ b/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;