Browse Source

Make privilege, port and fd handling reliable in tests

master
evgeny 2 days ago
parent
commit
3f9edc88c4
  1. 8
      doc/tasks.md
  2. 18
      src/transport_layer/stcp_server.c
  3. 2
      src/transport_layer/stcp_server.h
  4. 10
      tests/Makefile.am
  5. 6
      tests/test_reality_tls.c
  6. 34
      tests/test_uasync_regressions.c

8
doc/tasks.md

@ -4,6 +4,14 @@
## Текущие задачи
[+] **test_reality_tls: коллизии порта слушателя** — 10.10.2026:
В общем прогоне 140 passed, 1 failed: stcp_server_create не смог занять порт 34922, bind errno=98 (EADDRINUSE).
Причина: выбор порта как 34000 + time(NULL) % 1000 без гарантии его доступности.
Слушатель создаётся с port=0; stcp_server_get_port возвращает назначенный ОС порт для подключения клиента.
Логи показывают фактические порты REALITY и HTTPS. Сборка прошла; 8 параллельных TLS/HTTP-тестов прошли
при занятом всём прежнем диапазоне 34000–34999. ./check.sh: 141 passed, 0 failed, 0 skipped;
proxy/full, burst и load — PASS.
[ ] **Member files: исчерпание кеша результатов массовых изменений** — 10.10.2026:
Воспроизведено test_member_files_browser::recursiveDeletion(server-busy): из 76 объектов удаляются 64,
следующая команда получает MF_BUSY, остаются 12. storage.c: STORE_RECEIPTS=64, RECEIPT_TB=1200000 (120 секунд).

18
src/transport_layer/stcp_server.c

@ -335,11 +335,27 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port,
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "listen failed err=%d", socket_get_error());
socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL;
}
uint16_t bound_port = stcp_server_get_port(srv);
if (!bound_port) { socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL; }
srv->listen_id = uasync_add_socket_t(ua, srv->listen_sock, server_accept_cb, NULL, NULL, "stcp_listen", srv);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: listening on port %u family=%s", port, use_family == AF_INET ? "v4" : "v6");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: listening on port %u family=%s", bound_port, use_family == AF_INET ? "v4" : "v6");
return srv;
}
uint16_t stcp_server_get_port(const struct stcp_server *srv) {
if (!srv) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_server_get_port: invalid server"); return 0; }
struct sockaddr_storage address;
socklen_t size = sizeof(address);
if (getsockname(srv->listen_sock, (struct sockaddr *)&address, &size) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_server_get_port: getsockname failed err=%d", socket_get_error());
return 0;
}
if (address.ss_family == AF_INET) return ntohs(((struct sockaddr_in *)&address)->sin_port);
if (address.ss_family == AF_INET6) return ntohs(((struct sockaddr_in6 *)&address)->sin6_port);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_server_get_port: unexpected family=%d", address.ss_family);
return 0;
}
void stcp_server_destroy(struct stcp_server *srv) {
if (!srv) return;
if (srv->listen_id) uasync_remove_socket_t(srv->ua, srv->listen_sock);

2
src/transport_layer/stcp_server.h

@ -24,6 +24,8 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port,
stcp_connect_cb connect_cb, void *arg,
stcp_close_cb close_cb, void *close_arg,
int family);
// Фактический TCP-порт слушателя (port=0 при create выбирает ОС); 0 при ошибке.
uint16_t stcp_server_get_port(const struct stcp_server *srv);
void stcp_server_destroy(struct stcp_server *srv);
// Включить REALITY-камуфляж на этом TCP-сервере: перед STCP-хендшейком сервер

10
tests/Makefile.am

@ -691,8 +691,14 @@ check-local: $(check_PROGRAMS)
total=$$((total + 1)); \
logfile="$(TEST_LOG_DIR)/$$test.log"; \
if test -f "./$$test"; then \
set -- "./$$test"; \
if test "$$test" = test_auto_socket_dynamic && test "$$(id -u)" -ne 0 \
&& command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then \
set -- sudo -n -- "./$$test"; \
printf "[INFO] %s: running as root via sudo -n\n" "$$test"; \
fi; \
start=$$(date +%s%N); \
if "./$$test" > "$$logfile" 2>&1; then \
if "$$@" > "$$logfile" 2>&1; then \
end=$$(date +%s%N); \
elapsed_ms=$$(( (end - start) / 1000000 )); \
printf "$${G}[PASS]$${N} %s (%sms)\n" "$$test" "$$elapsed_ms"; \
@ -700,7 +706,7 @@ check-local: $(check_PROGRAMS)
elif test $$? -eq 77; then \
end=$$(date +%s%N); \
elapsed_ms=$$(( (end - start) / 1000000 )); \
printf "$${Y}[SKIP]$${N} %s (requires root)\n" "$$test"; \
printf "$${Y}[SKIP]$${N} %s (see %s)\n" "$$test" "$$logfile"; \
skipped=$$((skipped + 1)); \
else \
end=$$(date +%s%N); \

6
tests/test_reality_tls.c

@ -8,7 +8,6 @@
#include <openssl/x509.h>
#include <openssl/err.h>
#include <string.h>
#include <time.h>
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "FAIL line=%d: %s", __LINE__, #x); return 1; } } while (0)
struct https_peer {
@ -111,9 +110,10 @@ int main(void) {
cfg.enabled = 1; cfg.short_id_count = 1;
uint8_t pub[32]; CHECK(!reality_generate_keypair(cfg.private_key, pub));
snprintf(cfg.dest, sizeof(cfg.dest), "127.0.0.1:%u", ntohs(address.sin_port));
uint16_t port = 34000 + (uint16_t)(time(NULL) % 1000);
struct stcp_server *reality = stcp_server_create(ua, port, &keys, NULL, &inst, unexpected_stcp, NULL, NULL, NULL, AF_INET);
struct stcp_server *reality = stcp_server_create(ua, 0, &keys, NULL, &inst, unexpected_stcp, NULL, NULL, NULL, AF_INET);
CHECK(reality); stcp_server_set_reality(reality, &cfg);
uint16_t port = stcp_server_get_port(reality); CHECK(port);
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "test listeners: REALITY=127.0.0.1:%u HTTPS=%s", port, cfg.dest);
socket_t sock = socket(AF_INET, SOCK_STREAM, 0); CHECK(sock != SOCKET_INVALID);
address.sin_port = htons(port); CHECK(!connect(sock, (struct sockaddr *)&address, sizeof(address)));
socket_set_nonblocking(sock);

34
tests/test_uasync_regressions.c

@ -5,6 +5,7 @@
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <unistd.h>
@ -102,10 +103,41 @@ static int test_fd_mapping(int param) {
socket_cleanup(&t); return 0;
}
static int test_lookup(int param) {
const int min_fd = FD_SETSIZE + 16;
if (param & 4) {
struct rlimit limit;
if (getrlimit(RLIMIT_NOFILE, &limit) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "getrlimit(RLIMIT_NOFILE): %s", strerror(errno));
return 1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "High-fd test: min_fd=%d, soft=%llu, hard=%llu", min_fd,
(unsigned long long)limit.rlim_cur, (unsigned long long)limit.rlim_max);
if (limit.rlim_max <= (rlim_t)min_fd) {
DEBUG_WARN(DEBUG_CATEGORY_SYS, "Skipping high-fd test: fd >= %d requires RLIMIT_NOFILE > %d, hard=%llu",
min_fd, min_fd, (unsigned long long)limit.rlim_max);
return 77;
}
// Каждый случай работает в дочернем процессе; лимит родителя не меняется.
if (limit.rlim_cur <= (rlim_t)min_fd) {
limit.rlim_cur = (rlim_t)min_fd + 1;
if (setrlimit(RLIMIT_NOFILE, &limit) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "setrlimit(RLIMIT_NOFILE, %llu): %s",
(unsigned long long)limit.rlim_cur, strerror(errno));
return 1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "High-fd test: raised soft limit to %llu", (unsigned long long)limit.rlim_cur);
}
}
struct socket_test t; CHECK_EQ(socket_setup(&t, param & 3, 1, 0, 0), 0);
if (param & 4) {
CHECK_EQ(uasync_remove_socket(t.ua, t.id), ERR_OK); t.id = NULL;
int high = fcntl(t.fd[0], F_DUPFD, FD_SETSIZE + 16); CHECK(high >= FD_SETSIZE);
int high = fcntl(t.fd[0], F_DUPFD, min_fd);
if (high < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "fcntl(fd=%d, F_DUPFD, min_fd=%d): %s", t.fd[0], min_fd, strerror(errno));
socket_cleanup(&t); return 1;
}
CHECK(high >= min_fd);
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "High-fd test: allocated fd=%d", high);
close(t.fd[0]); t.fd[0] = high;
t.id = uasync_add_socket(t.ua, high, read_cb, NULL, NULL, "high fd", &t); CHECK(t.id);
}

Loading…
Cancel
Save