/* Exit TCP-прокси: принимает CONNECT/DATA через UTUN-router и открывает обычный TCP к назначению. * Поток определяется парой peer_node_id/stream_id; proxy_flow согласует окна и half-close. * init учитывает tcp_proxy_server_enabled в конфиге; destroy закрывает потоки и общий UDP/ICMP-прокси. * Сначала init, затем приём через router callback, в конце destroy; повторный init живого сервера недопустим. * Состояние принадлежит UTUN_INSTANCE, операции и callbacks выполняются в его uasync-потоке. * CLOSE дренирует запись и прекращает чтение; ERROR немедленно уничтожает поток, FIN — half-close. */ #ifndef TCP_PROXY_SERVER_H #define TCP_PROXY_SERVER_H #ifdef __cplusplus extern "C" { #endif #include #include "../lib/socket_compat.h" #include "../lib/ll_queue.h" #include "../lib/tcp_io.h" #include "../routing_layer/etcp_router.h" struct UTUN_INSTANCE; struct UASYNC; struct ll_entry; struct ETCP_CONN; #include "proxy_protocol.h" struct tcp_proxy_server_conn { struct tcp_proxy_server_conn* next; // следующий поток exit struct tcp_proxy_server* ctx; // заимствованный владелец в inst struct tcp_conn* tc; // собственный OS TCP к назначению struct proxy_flow flow; // окно, FIN и control retry uint32_t stream_id; // ID клиента; уникален в паре с peer_node_id uint64_t peer_node_id; // конечный ingress-узел, не транспортный сосед uint8_t dest_ip[4]; // IPv4 назначения в сетевом порядке uint16_t dest_port; // порт назначения в сетевом порядке uint8_t close_queued; // close-маркер уже поставлен в write_queue TCP uint8_t cli_closed; // client sent CLOSE void* diag_timer; // 1-секундный таймер диагностики uint8_t freed; // логически удалён; сама структура освобождается через call_soon struct queue_waiter_handle pause_waiter; // ожидание свободного места в router send_q void* retry_timer; // fallback 500 мс: возобновить read_queue, DATA отправляется с force=0 uint32_t bytes_sent; // сумма входящих DATA, учитывается до проверки/записи в TCP uint32_t bytes_relayed; // байт принято router для клиента uint32_t drain_count; // число успешно отправленных DATA из read_queue uint32_t data_count; // счётчик входящих DATA от клиента struct UASYNC* ua; // заимствованный loop для таймеров и отложенного free }; struct tcp_proxy_server { int enabled; // exit обслуживает CONNECT/UDP/ICMP REQUEST int conn_count; // число потоков в conns struct tcp_proxy_server_conn* conns; // собственные потоки всех пиров struct UTUN_INSTANCE* inst; // заимствованный instance, содержащий сервер }; // Обнулить состояние и учесть config->global.tcp_proxy_server_enabled; NULL inst → -1, иначе 0. // Без конфига/при отключении не bind-ит сервис. При включении bind SERVER и вызывает UDP/ICMP init. // 0 не гарантирует успешный bind/UDP/ICMP: их ошибки не пробрасываются вызывающему коду. int tcp_proxy_server_init(struct UTUN_INSTANCE* inst); // Удалить потоки, выключить сервер, снять binding и уничтожить общие UDP/ICMP; NULL inst допустим. // Перед этим остановить tcp_proxy_client того же inst; дать ua обработать отложенные освобождения. void tcp_proxy_server_destroy(struct UTUN_INSTANCE* inst); // Линейный поиск в ненулевом ctx по паре peer/sid; заимствованный указатель или NULL. struct tcp_proxy_server_conn* tcp_proxy_server_find_conn(struct tcp_proxy_server* ctx, uint64_t peer, uint32_t stream_id); // Немедленно удалить rc из списка, закрыть TCP и отменить таймеры/waiter; NULL допустим. // Память rc освобождается отложенно в ua; сама функция не отправляет CLOSE/ERROR пиру. void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc); // Router callback: conn->instance — владелец, peer берётся из ROUTER_SVC_SRC_OFF entry. // Принимает владение entry/dgram при любом исходе. Пустой payload router удаляет потоки этого peer. void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); // entry — полный приёмный пакет CONNECT с IPv4/портом; принимает владение entry/dgram. // inst должен быть включён, sid/src — уже разобранные поля. 0 = connect запущен/готов, -1 = ошибка. // CONNECTED отправляется только после реального connect; таймаут — из connect_timeout_ms (0 = отключён). int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry, uint32_t stream_id, uint64_t src_node_id); // Полный entry DATA не короче RECV_HDR_SIZE; принимает владение entry/dgram. conn сейчас не используется. // Peer берётся из entry; проверяет окно и ставит данные в OS write_queue. 0 = обработано, -1 = отклонено. // Ошибка выделения в write_queue может закрыть поток и при возврате 0: это не подтверждение OS send. int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id); // CLOSE: прекратить чтение, отбросить read_queue и закрыть TCP после записи уже принятой write_queue. void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint64_t peer, uint32_t stream_id); // ERROR: немедленно удалить поток вместе с непрочитанными/незаписанными данными. void tcp_proxy_server_handle_error(struct UTUN_INSTANCE* inst, uint64_t peer, uint32_t stream_id); // FIN: поставить shutdown(SHUT_WR) после уже принятых DATA; обратное направление продолжает работать. // handle_close/error/fin допускают NULL inst и отсутствующую пару peer/sid. void tcp_proxy_server_handle_fin(struct UTUN_INSTANCE* inst, uint64_t peer, uint32_t stream_id); #ifdef __cplusplus } #endif #endif