From d6eecd6f1c1fc23f30fb4e5ec5228109992539a0 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 00:27:56 +0200 Subject: [PATCH] Document STCP listener arguments, accepted ownership and current handshake phases --- src/transport_layer/stcp_server.h | 6 +++++ src/transport_layer/stcp_server_doc.md | 35 ++++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/transport_layer/stcp_server.h b/src/transport_layer/stcp_server.h index 47fbdb40..a6d5abe6 100644 --- a/src/transport_layer/stcp_server.h +++ b/src/transport_layer/stcp_server.h @@ -17,6 +17,11 @@ struct UTUN_INSTANCE; typedef void (*stcp_connect_cb)(struct stcp_conn *conn, void *arg); typedef void (*stcp_close_cb)(struct stcp_conn *conn, int err, void *arg); +/* Создать listener на всех адресах family: AF_INET6 — IPv6-only, иначе IPv4; port=0 выбирает ОС. + * ua/keys/connect_cb обязательны; X25519-ключи и необязательный my_ed25519_pubkey[32] копируются. + * inst заимствован до destroy для padding/эпох ETCP; NULL допустим для самостоятельного STCP. + * connect_cb(conn,arg) получает готовый заимствованный conn, close_cb(conn,err,close_arg) — его закрытие. + * Результат — владеющий server handle или NULL при ошибке; принятые conn освобождает серверный STCP. */ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, struct SC_MYKEYS *keys, const uint8_t *my_ed25519_pubkey, @@ -26,6 +31,7 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, int family); // Фактический TCP-порт слушателя (port=0 при create выбирает ОС); 0 при ошибке. uint16_t stcp_server_get_port(const struct stcp_server *srv); +/* Закрыть listener и все принятые conn с их close callbacks; освободить srv. NULL допустим. */ void stcp_server_destroy(struct stcp_server *srv); // Включить REALITY-камуфляж на этом TCP-сервере: перед STCP-хендшейком сервер diff --git a/src/transport_layer/stcp_server_doc.md b/src/transport_layer/stcp_server_doc.md index 34967593..ab3e7148 100644 --- a/src/transport_layer/stcp_server_doc.md +++ b/src/transport_layer/stcp_server_doc.md @@ -10,21 +10,31 @@ STCP Server — серверная сторона протокола STCP. Со ```c struct stcp_server *srv = stcp_server_create(ua, port, &my_keys, + my_ed25519_pubkey, instance, on_connect_cb, conn_arg, // вызывается после успешного рукопожатия - on_close_cb, close_arg); // вызывается при закрытии/ошибке + on_close_cb, close_arg, // вызывается при закрытии/ошибке + AF_INET); // AF_INET6 — отдельный IPv6-only listener +if (!srv) return -1; + +uint16_t actual_port = stcp_server_get_port(srv); // port=0 выбирает ОС // ... use ... stcp_server_destroy(srv); ``` -После завершения рукопожатия соединение переходит в `STCP_STATE_DATA`, вызывается `connect_cb`. Вышестоящий код может слать сообщения через `tx_queue` — они будут зашифрованы с CRC32 и отправлены. Входящие расшифрованные сообщения попадают в `rx_queue`. +После завершения обычного рукопожатия соединение переходит в `STCP_STATE_DATA`, вызывается +`connect_cb(conn, arg)`. Он получает заимствованный conn; память отдельно не освобождается вызывающим. +`tx_queue` принимает сообщения для шифрования с CRC32, `rx_queue` — отдельные копии расшифрованных сообщений. +Все операции выполняются в uasync-потоке. Полный контракт очередей и close находится в `stcp.h`. Ключевые нюансы: - Сервер принимает клиентский pubkey+salt (обфусцированный), извлекает pubkey через `sc_obfuscate_pubkey`, выполняет ECDH. - Отправляет ответ: открытый блок salt/pubkey, зашифрованное тело handshake с CRC32 и зашифрованный padding. -- После рукопожатая трафик шифруется потоковым AES-CTR (XOR), для каждого направления — свой stream. -- Рукопожатие обрабатывается в два этапа: `process_client_handshake` (извлечение ключа, дешифровка) → `finish_client_handshake` (отправка ответа, переход в DATA). +- Handshake, padding и последующие кадры DATA используют непрерывный AES-128-CTR stream каждого направления. +- `server_hs_phase1_cb` разбирает обязательные 91 байт, `server_hs_phase2_cb` принимает padding и формирует ответ. +- Флаг `STCP_HANDSHAKE_FLAG_PING` вызывает ответ и мягкое закрытие пробы без `connect_cb` и DATA-сессии. +- `stcp_server_set_reality` включает внешний REALITY-handshake и relay неавторизованных клиентов; параметры — `reality_config`. ## 3. API @@ -32,13 +42,22 @@ stcp_server_destroy(srv); Слушающий сокет, ключи сервера, коллбэки `connect_cb` / `close_cb`. Не экспортируется, создаётся только через `stcp_server_create`. ### Функции -- `stcp_server_create(ua, port, keys, connect_cb, arg, close_cb, close_arg)` — создать сервер: открыть TCP-сокет, bind к порту (IPV4, INADDR_ANY), listen(16), зарегистрировать accept-коллбэк. При приёме соединения создаёт `struct stcp_conn` в состоянии `STCP_STATE_HS_SERVER_WAIT`. -- `stcp_server_destroy(srv)` — закрыть слушающий сокет, освободить память. +- `stcp_server_create(ua, port, keys, ed25519_pubkey, inst, connect_cb, arg, close_cb, close_arg, family)` — + создать TCP listener на всех адресах IPv4 или IPv6, `listen(16)`, зарегистрировать accept callback; + результат — владеющий handle либо NULL. Ключи копируются, `ua/inst` заимствуются до destroy. +- `stcp_server_get_port(srv)` — фактический порт listener, 0 при ошибке. +- `stcp_server_destroy(srv)` — закрыть listener и все принятые conn, включая вызовы их close callbacks, затем освободить сервер. ### Протокол рукопожатия (серверная сторона) 1. Сервер читает `SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT` байт (открытый salt/pubkey и 51 байт зашифрованного тела). -2. Деобфусцирует клиентский pubkey через XOR с `SHA256(salt || server_pubkey)`. +2. Деобфусцирует 32-байтный клиентский pubkey через XOR с `SHA256(salt || server_pubkey)`. 3. Выполняет ECDH (`sc_init_ctx` + `sc_set_peer_public_key`) → получает `session_key`, запускает `stream_recv` с клиентской солью. Для ответа генерирует собственную соль и запускает `stream_send` с ней; соль входит в вывод CTR IV вместе с ключом и направлением. 4. Расшифровывает тело, проверяет CRC32 и границы padding_size, затем принимает и расшифровывает padding. 5. Формирует ответ: открытые salt2(8) + обфусцированный pubkey(32), затем зашифрованные 51 байт handshake и нулевой padding случайной длины. Диапазон берётся у существующего ETCP_CONN, иначе у instance. Приём padding продвигает CTR; при длине 0 handshake завершается сразу. -6. Переходит в `STCP_STATE_DATA`, вызывает `on_ready` (== `connect_cb`). +6. Для обычного соединения переходит в `STCP_STATE_DATA` и вызывает `connect_cb`; для PING завершает пробу. + +47 байт тела handshake: Ed25519 pubkey(32), got_initial_pkt(1), reset_id(8 BE), +padding_size(2 LE), device_type(1), keepalive_interval(2 BE), flags(1). +CRC32(4 LE) считается по этому телу и шифруется вместе с ним. Размер обязательной +части каждого направления — 40 + 47 + 4 = 91 байт; padding не входит в CRC тела handshake. +`reset_id` — собственная эпоха отправителя, подтверждение новой ETCP-сессии выполняет `etcp_session`.