Files
utun2/src/transport_layer/stcp.h
T
evgeny f6e6e2c73f stcp: write-error закрывает conn отложенно (фикс UAF keepalive/etcp_tcp_send)
Синхронный send-путь (stcp_link_send → queue_data_put → tx_queue_cb →
flush_pending → try_send) при ошибке сокета вызывал stcp_conn_do_close →
on_close → tcp_link_close_cb → etcp_link_close внутри стека вызывающего,
освобождая ETCP_LINK под keepalive_timer_cb/etcp_tcp_send (use-after-free,
SIGSEGV в keepalive_timer_cb:210).

Теперь write-error планирует отложенный close (uasync_call_soon) с отменой
handle в stcp_conn_do_close; первый err сохраняется; flush_pending не работает
вне STCP_STATE_DATA. Мёртвый hook on_write_error удалён.
2026-09-26 16:42:51 +03:00

202 lines
8.5 KiB
C

// stcp.h — Streaming TCP: shared structures, constants, connection lifecycle
#ifndef STCP_H
#define STCP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "secure_channel.h"
#include "reality.h"
#include "crc32.h"
#include "../lib/u_async.h"
#include "../lib/socket_compat.h"
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include <stdint.h>
struct UTUN_INSTANCE;
struct ETCP_CONN;
struct stcp_server;
typedef void (*stcp_ping_cb)(int success, uint16_t rtt, void *arg);
#define STCP_MAX_MSG_SIZE 65535
#define STCP_RECV_BUF_INIT 8192
#define STCP_RECV_BUF_MAX 131072
#define STCP_HS_TIMEOUT 50000 // 5s in 0.1ms timebase units
#define STCP_CONNECT_TIMEOUT 100000 // 10s in 0.1ms timebase units
#define STCP_HS_CLIENT_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT) // 40+51=91
#define STCP_HS_SERVER_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER) // 40+51=91
#define STCP_HS_ENC_CLIENT 51 // ed25519_pubkey(32) + got_initial_pkt(1) + reset_id(8) + padding_size(2) + device_type(1) + keepalive(2) + flags(1) + CRC32(4)
#define STCP_HS_ENC_SERVER 51 // ed25519_pubkey(32) + got_initial_pkt(1) + reset_id(8) + padding_size(2) + device_type(1) + keepalive(2) + flags(1) + CRC32(4)
// handshake plain layout (before CRC):
// [0..31] ed25519_pubkey
// [32] got_initial_pkt
// [33..40] reset_id (epoch)
// [41..42] padding_size
// [43] device_type
// [44..45] keepalive_interval
// [46] flags
#define STCP_HS_PLAIN_SIZE 47 // без CRC32
#define STCP_HS_PADDING_MAX 512 // верхняя граница padding_size в рукопожатии (легитимно 8)
#define STCP_HANDSHAKE_FLAG_PING 0x01
#define STCP_STREAM_CLIENT_SEND 0
#define STCP_STREAM_SERVER_SEND 1
#define STCP_RX_QUEUE_MAX_PACKETS 64
#define STCP_RX_QUEUE_MAX_BYTES (256 * 1024)
enum stcp_state {
STCP_STATE_INIT,
STCP_STATE_HS_CLIENT_SENT,
STCP_STATE_HS_SERVER_WAIT,
STCP_STATE_DATA,
STCP_STATE_CLOSED,
STCP_STATE_ERROR
};
struct pending_entry {
struct pending_entry *next;
uint8_t *data;
size_t len;
};
struct stcp_conn {
socket_t sock;
struct UASYNC *ua;
void *socket_id;
struct UTUN_INSTANCE *inst; // для lookup ETCP_CONN при хендшейке (сервер)
struct ETCP_CONN *etcp_conn; // клиент: родительский conn для live-чтения gop/reset_id при handshake
enum stcp_state state;
uint8_t is_server;
uint8_t allocated; // 1 = allocated by u_calloc, free in stcp_conn_free
uint8_t session_key[SC_SESSION_KEY_SIZE];
struct sc_stream_state stream_send;
struct sc_stream_state stream_recv;
struct SC_MYKEYS my_keys;
uint8_t peer_pubkey[SC_PUBKEY_SIZE];
uint8_t peer_pubkey_set;
uint8_t peer_ed25519_pubkey[SC_PUBKEY_SIZE];
uint8_t peer_ed25519_set;
uint8_t my_ed25519_pubkey[SC_PUBKEY_SIZE];
uint8_t got_initial_pkt; // local, sent to peer during handshake
uint8_t peer_got_initial_pkt; // remote, received from peer during handshake
uint64_t reset_id; // local, sent to peer during handshake (reset epoch)
uint64_t peer_reset_id; // remote, received from peer during handshake
uint8_t device_type; // CLIENT_TYPE_* sent to peer during handshake
uint16_t keepalive_interval; // keepalive interval sent to peer during handshake
uint8_t peer_device_type; // CLIENT_TYPE_* from peer handshake
uint16_t peer_keepalive_interval; // keepalive interval from peer handshake
uint8_t hs_flags; // client: handshake flags to send (STCP_HANDSHAKE_FLAG_*)
uint8_t peer_flags; // flags received from peer handshake
uint64_t hs_send_time; // client: handshake send time (0.1ms tb), for ping RTT
uint8_t close_after_send; // server: graceful close once pending send is flushed
// reality-камуфляж (только для серверной стороны accept-пути)
uint8_t reality_enabled; // 1 = перед STCP-хендшейком ждём/отвечаем reality ClientHello
uint8_t reality_hdr[5]; // сохранённый TLS record header при двухфазном чтении ClientHello
struct reality_server_config reality_srv; // конфиг сервера (копия)
char reality_dest[REALITY_DEST_MAX]; // "host:port" для релея неавторизованных
struct ll_queue *rx_queue;
struct ll_queue *tx_queue;
void (*tx_cb)(struct ll_queue *q, void *arg);
uint8_t *recv_buf;
size_t recv_buf_len;
size_t recv_buf_cap;
uint8_t *send_buf;
size_t send_len;
size_t send_offset;
struct pending_entry *pending_head;
struct pending_entry *pending_tail;
void (*on_ready)(struct stcp_conn *conn, void *arg);
void *ready_arg;
void (*on_close)(struct stcp_conn *conn, int err, void *arg);
void *close_arg;
void *hs_timer; // handshake timeout handle, NULL when not active
void *free_on_close; // если не NULL — uasync_call_soon для u_free(arg) при закрытии
void *close_call_soon; // handle отложенного do_close при write-error; NULL = не запланирован
int close_err; // сохранённый err для отложенного close
// recv state machine (stcp_recv_try)
size_t recv_need; // expected next chunk size (0 = disabled)
uint8_t recv_streaming; // 0=raw (handshake), 1=stream (DATA, auto-XOR+CRC)
uint8_t recv_in_meta; // stream: waiting for 2-byte header
uint16_t recv_msg_size; // stream: size from decoded header
void (*recv_on_chunk)(struct stcp_conn *c, uint8_t *data, size_t len);
uint8_t rx_paused; // reading paused due to rx_queue overflow
struct stcp_server *srv; // владеющий сервер (accept-сторона), NULL для клиентских коннектов
struct stcp_conn *srv_next; // звено в списке принятых коннектов сервера
};
void stcp_conn_free(struct stcp_conn *c);
/* Адрес пира соединения для логов (статический буфер). "?" если недоступен. */
const char* stcp_conn_peer_str(struct stcp_conn *c);
void stcp_conn_set_tx_queue(struct stcp_conn *c, struct ll_queue *q);
void stcp_conn_set_rx_queue(struct stcp_conn *c, struct ll_queue *q);
void stcp_conn_set_on_close(struct stcp_conn *c, void (*cb)(struct stcp_conn *conn, int err, void *arg), void *arg);
// tx: encrypts frame, XOR covers len+data+CRC (continuous stream)
int stcp_frame_encrypt(struct stcp_conn *c, const uint8_t *data, size_t data_len, uint8_t *output, size_t *output_len);
// decrypt: XOR-in-place data+CRC blob (handshake only; DATA uses stcp_recv_try stream mode)
int stcp_frame_decrypt(uint8_t *data, size_t len, struct sc_stream_state *stream, size_t *out_len);
void stcp_pending_queue(struct stcp_conn *c, const uint8_t *data, size_t len);
void stcp_pending_clear(struct stcp_conn *c);
// takes ownership of data (must be u_malloc'd, will be u_free'd)
int stcp_try_send(struct stcp_conn *c, uint8_t *data, size_t len);
void stcp_write_cb(socket_t sock, void *arg);
void stcp_flush_pending(struct stcp_conn *c);
// unified recv: read from TCP, grow recv_buf, calls stcp_recv_try. returns 1=data, 0=wait, -1=error/closed
int stcp_conn_read(struct stcp_conn *c);
// set next expected chunk: need=0+streaming=1 enters DATA stream mode
void stcp_recv_set(struct stcp_conn *c, size_t need, int streaming,
void (*on_chunk)(struct stcp_conn *c, uint8_t *data, size_t len));
// try to process buffered data according to recv_need/streaming/on_chunk
void stcp_recv_try(struct stcp_conn *c);
// unified close: closes socket, frees buffers, calls on_close. does NOT clean streams (stcp_conn_free does)
void stcp_conn_do_close(struct stcp_conn *c, int err);
// снимает принятый коннект из списка владеющего сервера (реализация в stcp_server.c)
void stcp_server_remove_conn(struct stcp_conn *c);
// handshake timeout callback (for use with uasync_set_timeout)
void hs_timeout_cb(void *arg);
// unified tx queue callback (for use as tx_cb)
void stcp_tx_queue_cb(struct ll_queue *q, void *arg);
// push decoded data to rx_queue; pauses reading if queue over threshold
void stcp_rx_push(struct stcp_conn *c, uint8_t *data, size_t len);
// call after consumer dequeues from rx_queue; resumes reading when below threshold
void stcp_rx_resume_if_needed(struct stcp_conn *c);
#ifdef __cplusplus
}
#endif
#endif