421 lines
22 KiB
C
421 lines
22 KiB
C
/**
|
||
* @file etcp_api.h
|
||
* @brief Прямой обмен кодограммами ETCP и подписки на события транспорта.
|
||
*
|
||
* bind выбирает обработчик по первому байту cmd, send передаёт кодограмму соседу.
|
||
* Для доставки через промежуточные узлы используется etcp_router.h.
|
||
* Все вызовы — в потоке uasync. Перед отправкой ждать UP и свободной send_input_q.
|
||
* Владение entry различается: send забирает его только при успехе, recv — всегда.
|
||
*/
|
||
|
||
#ifndef ETCP_API_H
|
||
#define ETCP_API_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
|
||
#include <stdint.h>
|
||
#include "../lib/ll_queue.h"
|
||
|
||
#define ETCP_MAX_BINDINGS 256
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// ЕДИНЫЙ РЕЕСТР ETCP-идентификаторов. Единственное место определения.
|
||
//
|
||
// Два независимых пространства имён (отдельные таблицы биндинга):
|
||
// • ETCP_ID_* — raw ETCP: etcp_bind() / etcp_send()
|
||
// • ETCP_RT_ID_* — router-сервисы: etcp_router_bind() / etcp_route_send()
|
||
//
|
||
// Одинаковое значение в РАЗНЫХ пространствах — НЕ коллизия (напр. 0x00 =
|
||
// ETCP_ID_DATA и ETCP_RT_ID_DATA). Коллизия — дубль значения В ПРЕДЕЛАХ одного
|
||
// пространства: последний bind затирает предыдущий обработчик и молча ломает
|
||
// сервис (реальный случай: ETCP_RT_ID_CHAT 0x11 затирал ETCP_RT_ID_CONN_MGR
|
||
// 0x11 → переставал работать conn_mgr).
|
||
//
|
||
// Часть констант ETCP_RT_ID_* исторически биндится через etcp_bind()
|
||
// (raw-пространство) — помечены «(raw)»; их значения занимают raw-реестр.
|
||
//
|
||
// raw: 0x00 DATA, 0x01 TOPO_ENTRY, 0x02 NAT_DETECTION, 0x03 SVC_ROUTE,
|
||
// 0x04 NTP_TIME, 0x05 BROADCAST, 0x06 RADIO, 0x07 STREAM, 0x12 NCD_CONTROL,
|
||
// 0x13 GROUP_INVITE, 0x20 DB_SYNC, 0x30 CHAT_SYNC, 0x31 MEMBER_SYNC
|
||
// router: 0x00 DATA, 0x02 NAT, 0x04 TCP_PROXY_SERVER, 0x05 UDP_PROXY,
|
||
// 0x06 ICMP_PROXY, 0x07 MEDIA_DELIVERY, 0x08 TCP_PROXY_CLIENT,
|
||
// 0x11 CONN_MGR, 0x32 ADMIN_KEY, 0x33 JOIN, 0x34 DM,
|
||
// 0x35 DM_MAILBOX, 0x36 CALL, 0x37 FILE_TRANSFER, 0x38 DM_MEDIA, 0x39 MEMBER_FILES,
|
||
// 0x3A GROUP_AVATAR, 0x3B NAT_DETECTION, 0x3C STREAM,
|
||
// 0x3D CALL_VIDEO_CONTROL, 0x3E CALL_VIDEO, 0x3F CALL_AUDIO, 0x40 RADIO_CALL
|
||
//
|
||
// !!! Новый ID: впиши его в нужный реестр выше и убедись, что значение
|
||
// не занято в том же пространстве.
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
// === Raw ETCP packet IDs (etcp_bind/etcp_send) ===
|
||
#define ETCP_ID_DATA 0x00 // пакет для передачи адресату
|
||
#define ETCP_ID_TOPO_ENTRY 0x01 // BGP-топология (topo_group)
|
||
#define ETCP_ID_NAT_DETECTION 0x02 // NAT-детекция (STUN-like ping)
|
||
#define ETCP_ID_SVC_ROUTE 0x03 // транспорт роутера (etcp_router)
|
||
#define ETCP_ID_NTP_TIME 0x04 // NTP time sync P2P
|
||
#define ETCP_ID_BROADCAST 0x05 // broadcast-сообщения между пирами
|
||
#define ETCP_ID_RADIO 0x06 // рация (PTT мультикаст голоса) между пирами
|
||
#define ETCP_ID_STREAM 0x07 // прямой stream INFO/выдачи/DATA
|
||
#define ETCP_RT_ID_STREAM 0x3C // stream REVERSE_REQ/CLOSED и помощь суперузлов через группу
|
||
|
||
// === Router-сервисы (etcp_router_bind/etcp_route_send) ===
|
||
#define ETCP_RT_ID_DATA 0x00 // routing.c — маршрутизация данных
|
||
#define ETCP_RT_ID_NAT 0x02 // NAT-трафик между узлами
|
||
#define ETCP_RT_ID_SVC_ROUTE 0x03 // транспорт роутера (raw, = ETCP_ID_SVC_ROUTE)
|
||
#define ETCP_RT_ID_TCP_PROXY_SERVER 0x04 // TCP proxy server (CONNECT/DATA от клиентов)
|
||
#define ETCP_RT_ID_UDP_PROXY 0x05 // UDP datagram прокси (client ↔ exit)
|
||
#define ETCP_RT_ID_ICMP_PROXY 0x06 // ICMP echo прокси (ping через exit)
|
||
#define ETCP_RT_ID_MEDIA_DELIVERY 0x07 // распространение медиа (аудио/видео)
|
||
#define ETCP_RT_ID_TCP_PROXY_CLIENT 0x08 // TCP proxy client (DATA/CLOSE/ERROR/FIN)
|
||
#define ETCP_RT_ID_CONN_MGR 0x11 // Connection Manager — management connections
|
||
#define ETCP_RT_ID_NCD_CONTROL 0x12 // node_conn_direct CLOSE/KEEP_ALIVE (raw)
|
||
#define ETCP_RT_ID_GROUP_INVITE 0x13 // topo_group_invite (raw)
|
||
#define ETCP_RT_ID_DB_SYNC 0x20 // db_sync (raw)
|
||
#define ETCP_RT_ID_CHAT_SYNC 0x30 // chat_sync (raw)
|
||
#define ETCP_RT_ID_MEMBER_SYNC 0x31 // member_sync/merkle_sync (raw)
|
||
#define ETCP_RT_ID_ADMIN_KEY 0x32 // chat_admin — канальный приватный ключ
|
||
#define ETCP_RT_ID_JOIN 0x33 // chat_join — join-протокол
|
||
#define ETCP_RT_ID_DM 0x34 // dm_core — прямой p2p чат
|
||
#define ETCP_RT_ID_DM_MAILBOX 0x35 // dm_mailbox — offline-хранение
|
||
#define ETCP_RT_ID_CALL 0x36 // call — P2P аудио-звонок
|
||
#define ETCP_RT_ID_CALL_VIDEO_CONTROL 0x3D // возможности и восстановление видеотрека
|
||
#define ETCP_RT_ID_CALL_VIDEO 0x3E // видео, отдельный ACK/debt/backpressure
|
||
#define ETCP_RT_ID_CALL_AUDIO 0x3F // аудио, независимо от сигналинга/видео
|
||
#define ETCP_RT_ID_RADIO_CALL 0x40 // адресное приглашение участника в рацию CHAT-канала
|
||
#define ETCP_RT_ID_FILE_TRANSFER 0x37 // file_transfer — передача неизменяемых файлов
|
||
#define ETCP_RT_ID_DM_MEDIA 0x38 // dm_media — custody, location и подтверждения медиа
|
||
#define ETCP_RT_ID_MEMBER_FILES 0x39 // member_files — каталоги и файлы участников группы
|
||
#define ETCP_RT_ID_GROUP_AVATAR 0x3A // подписанная аватарка группы, блоки до 4 КиБ
|
||
#define ETCP_RT_ID_NAT_DETECTION 0x3B // управление NAT-пробами через маршруты выбранной группы
|
||
#define ETCP_RT_ID_ATTACHMENT_KEYS 0x40 // запросы и ручная E2E-выдача ключей групповых вложений
|
||
|
||
// Connection status events (instance-level callback).
|
||
// NEW начинает жизнь объекта, UP/DOWN отражают доступность, DELETE завершает её.
|
||
// NEW — один раз при создании соединения.
|
||
// UP — переход links_up 0→1 (UP после UP не вызывается).
|
||
// DOWN — переход links_up 1→0 (DOWN после DOWN не вызывается).
|
||
// DELETE — один раз при закрытии, финальный; единственный статус, допустимый при state==2.
|
||
// REINIT — новая транспортная сессия; не означает потерю физического пути.
|
||
#define ETCP_CONN_STATUS_NEW 0 // соединение создано
|
||
#define ETCP_CONN_STATUS_UP 1 // соединение поднялось
|
||
#define ETCP_CONN_STATUS_DOWN 2 // соединение упало
|
||
#define ETCP_CONN_STATUS_REINIT 4 // новая сессия на существующем соединении
|
||
#define ETCP_CONN_STATUS_DELETE 3 // соединение удалено
|
||
|
||
// Forward declarations
|
||
struct ETCP_CONN;
|
||
struct ETCP_LINK;
|
||
struct ETCP_SOCKET;
|
||
struct UTUN_INSTANCE;
|
||
|
||
typedef void (*etcp_conn_status_fn)(struct ETCP_CONN* conn, int status, void* arg);
|
||
struct etcp_status_cbk_entry {
|
||
etcp_conn_status_fn fn;
|
||
void* arg;
|
||
struct etcp_status_cbk_entry* next;
|
||
};
|
||
|
||
/**
|
||
* @brief Тип коллбэка для приёма пакетов
|
||
* @param conn ETCP соединение от которого получен пакет
|
||
* @param entry Элемент очереди с данными пакета
|
||
*
|
||
* @note После обработки освободить сначала dgram через queue_dgram_free(),
|
||
* затем entry через queue_entry_free(). conn заимствован на время вызова.
|
||
* @note Raw bind получает cmd || payload. Router bind получает заголовок
|
||
* ROUTER_SVC_* и payload со смещения ROUTER_SVC_PAYLOAD_OFF (etcp_router.h).
|
||
*/
|
||
typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||
|
||
// Forward declaration
|
||
struct ETCP_CONN;
|
||
struct UTUN_INSTANCE;
|
||
struct TOPO_GROUP_NODE;
|
||
|
||
// Per-connection callback event types (bitmask).
|
||
// При state==2 допустим только терминальный ETCP_CBK_EVENT_DELETE.
|
||
// UP/DOWN зеркалят conn-status UP/DOWN; при закрытии (если соединение было UP)
|
||
// отправляется один финальный DOWN до state=2.
|
||
#define ETCP_CBK_EVENT_INIT (1 << 0)
|
||
#define ETCP_CBK_EVENT_REINIT (1 << 1)
|
||
#define ETCP_CBK_EVENT_UP (1 << 2)
|
||
#define ETCP_CBK_EVENT_DOWN (1 << 3)
|
||
#define ETCP_CBK_EVENT_NODE_CHANGED (1 << 4)
|
||
#define ETCP_CBK_EVENT_DELETE (1 << 5) // терминальное событие, state==2
|
||
|
||
typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, int event, void* arg);
|
||
struct etcp_cbk_entry {
|
||
etcp_cbk_fn fn;
|
||
void* arg;
|
||
int event_mask;
|
||
struct etcp_cbk_entry* next;
|
||
};
|
||
|
||
/**
|
||
* @brief Рассылка per-connection события
|
||
*
|
||
* @param conn ETCP соединение
|
||
* @param event Битовая маска события (ETCP_CBK_EVENT_*)
|
||
*
|
||
* @note Вызываются все коллбэки соединения, у которых event_mask
|
||
* пересекается с переданным event.
|
||
* @note При state==2 допускается только DELETE; прочие события игнорируются.
|
||
*/
|
||
void etcp_cbk_fire(struct ETCP_CONN* conn, int event);
|
||
|
||
// Instance-level callback types (separate from per-connection etcp_cbk_fn in etcp.h)
|
||
typedef void (*etcp_inst_cbk_fn)(struct ETCP_CONN* conn, void* arg);
|
||
struct etcp_inst_cbk_entry {
|
||
etcp_inst_cbk_fn fn;
|
||
void* arg;
|
||
struct etcp_inst_cbk_entry* next;
|
||
};
|
||
|
||
// ---- Per-connection unified callback API ----
|
||
/**
|
||
* @brief Зарегистрировать per-connection коллбэк с маской событий
|
||
*
|
||
* @param conn ETCP соединение
|
||
* @param fn Коллбэк (сигнатура etcp_cbk_fn)
|
||
* @param arg Пользовательский аргумент, передаваемый в коллбэк
|
||
* @param event_mask Битовая маска интересующих событий (ETCP_CBK_EVENT_*)
|
||
*/
|
||
void etcp_conn_add_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask);
|
||
|
||
/**
|
||
* @brief Удалить per-connection коллбэк
|
||
*
|
||
* @param conn ETCP соединение
|
||
* @param fn Коллбэк
|
||
* @param arg Пользовательский аргумент (для идентификации записи)
|
||
*/
|
||
void etcp_conn_remove_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||
|
||
/**
|
||
* @brief Обновить маску событий у уже зарегистрированного коллбэка
|
||
*
|
||
* @param conn ETCP соединение
|
||
* @param fn Коллбэк
|
||
* @param arg Пользовательский аргумент
|
||
* @param event_mask Новая битовая маска событий (ETCP_CBK_EVENT_*)
|
||
*/
|
||
void etcp_conn_update_cbk_mask(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask);
|
||
|
||
// ---- Instance-level callbacks ----
|
||
/**
|
||
* @brief Зарегистрировать instance-level коллбэк «создано новое соединение»
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк (сигнатура etcp_inst_cbk_fn)
|
||
* @param arg Пользовательский аргумент
|
||
*/
|
||
void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg);
|
||
|
||
/**
|
||
* @brief Удалить instance-level коллбэк «создано новое соединение»
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк
|
||
* @param arg Пользовательский аргумент
|
||
*/
|
||
void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg);
|
||
|
||
// ---- Link status change callback (instance-level) ----
|
||
typedef void (*etcp_link_status_cbk_fn)(struct ETCP_CONN* conn, struct ETCP_LINK* link,
|
||
int old_state, int old_status, void* arg);
|
||
|
||
struct etcp_link_status_cbk_entry {
|
||
etcp_link_status_cbk_fn fn;
|
||
void* arg;
|
||
struct etcp_link_status_cbk_entry* next;
|
||
};
|
||
|
||
/**
|
||
* @brief Зарегистрировать instance-level коллбэк смены состояния линка
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк (сигнатура etcp_link_status_cbk_fn)
|
||
* @param arg Пользовательский аргумент
|
||
*/
|
||
void etcp_add_link_status_cbk(struct UTUN_INSTANCE* inst, etcp_link_status_cbk_fn fn, void* arg);
|
||
|
||
/**
|
||
* @brief Удалить instance-level коллбэк смены состояния линка
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк
|
||
* @param arg Пользовательский аргумент
|
||
*/
|
||
void etcp_remove_link_status_cbk(struct UTUN_INSTANCE* inst, etcp_link_status_cbk_fn fn, void* arg);
|
||
|
||
/**
|
||
* @brief Внутренняя функция: рассылка события смены состояния линка
|
||
*
|
||
* @param link Линк, состояние которого изменилось
|
||
* @param old_state Предыдущее link_state (0-init, 1-handshake, 2-try_reconnect, 3-connected)
|
||
* @param old_status Предыдущее link_status (1-up, 0-down)
|
||
*
|
||
* @note Вызывается автоматически из etcp_connections.c при изменении
|
||
* link_state/link_status; не предназначен для вызова извне
|
||
*/
|
||
void etcp_fire_link_status_cbk(struct ETCP_LINK* link, int old_state, int old_status);
|
||
|
||
// ---- Background connection initialization ----
|
||
#define ETCP_CONNECT_EARLY 1
|
||
#define ETCP_CONNECT_LATE 2
|
||
|
||
typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type);
|
||
|
||
/**
|
||
* @brief Фоновая инициализация соединения к узлу
|
||
*
|
||
* @param instance UTUN instance
|
||
* @param node Узел из node registry (TOPO_GROUP_NODE)
|
||
* @param cb Коллбэк завершения (сигнатура etcp_connect_callback_t)
|
||
* @param arg Пользовательский аргумент
|
||
* @param flags Битовая маска этапов (ETCP_CONNECT_EARLY / LATE)
|
||
* @return 0 при успехе, -1 при ошибке
|
||
*
|
||
* @note Коллбэк вызывается на каждом запрошенном этапе с параметром
|
||
* type = ETCP_CONNECT_EARLY / ETCP_CONNECT_LATE
|
||
* (или 0 при таймауте/неудаче). При уже готовом соединении EARLY+LATE
|
||
* доставляются немедленно.
|
||
*/
|
||
int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* node,
|
||
etcp_connect_callback_t cb, void* arg, uint8_t flags);
|
||
|
||
/**
|
||
* @brief Структура bindings для ETCP API (per-instance)
|
||
*/
|
||
struct ETCP_BINDINGS {
|
||
etcp_recv_fn callbacks[ETCP_MAX_BINDINGS]; /**< Массив callbacks, NULL = не установлен */
|
||
};
|
||
|
||
/**
|
||
* @brief Отправить пакет в очередь normalizer. первый байт кодограммы - cmd (на приёмной стороне bind выберет обработчик по cmd).
|
||
*
|
||
* @param conn ETCP соединение
|
||
* @param entry Элемент очереди с данными для отправки
|
||
* @return 0 при успехе, -1 при ошибке
|
||
*
|
||
* @note Владение entry:
|
||
* - при успехе (0) entry передаётся в очередь normalizer'а; вызывающий
|
||
* НЕ должен ни освобождать, ни читать entry после вызова — если очередь
|
||
* пуста, entry обрабатывается и освобождается СИНХРОННО внутри вызова
|
||
* (queue_data_put → callback → queue_entry_free). Нужные поля entry надо
|
||
* сохранять до etcp_send().
|
||
* - при ошибке (-1) entry НЕ освобождается: вызывающий владеет entry и
|
||
* должен сам освободить его (queue_dgram_free(entry); queue_entry_free(entry)).
|
||
*/
|
||
int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||
|
||
/**
|
||
* @brief Подписаться на все пакеты с указанным ID для данного instance
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param id Идентификатор пакета (первый байт кодограммы, cmd)
|
||
* @param callback Коллбэк для обработки пакетов с этим ID
|
||
* @return 0 при успехе, -1 при ошибке
|
||
*
|
||
* @note ID=0 зарезервирован для обычных пакетов данных
|
||
* @note Можно зарегистрировать только один коллбэк на каждый ID в пределах instance
|
||
*/
|
||
int etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback);
|
||
|
||
/**
|
||
* @brief Отписаться от пакетов с указанным ID для данного instance
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param id Идентификатор пакета
|
||
* @return 0 при успехе (в том числе без прежнего binding), -1 при неверном instance
|
||
*/
|
||
int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id);
|
||
|
||
/**
|
||
* @brief Зарегистрировать instance-level коллбэк статуса соединения
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк (сигнатура etcp_conn_status_fn)
|
||
* @param arg Пользовательский аргумент
|
||
*
|
||
* @note status = ETCP_CONN_STATUS_NEW / UP / DOWN / DELETE / REINIT.
|
||
* DELETE — единственный статус при state==2. Финальный DOWN при close
|
||
* доставляется per-connection подписчикам; instance-подписчики получают DELETE.
|
||
*/
|
||
void etcp_add_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg);
|
||
|
||
/**
|
||
* @brief Удалить instance-level коллбэк статуса соединения
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк
|
||
* @param arg Пользовательский аргумент
|
||
*/
|
||
void etcp_remove_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg);
|
||
|
||
/* Socket property change events (instance-level) */
|
||
#define ETCP_SOCKET_EVENT_ADDR_CHANGED (1 << 0)
|
||
#define ETCP_SOCKET_EVENT_STATUS_CHANGED (1 << 1)
|
||
|
||
typedef void (*etcp_socket_cbk_fn)(struct ETCP_SOCKET* sock, int event, void* arg);
|
||
struct etcp_socket_cbk_entry {
|
||
etcp_socket_cbk_fn fn;
|
||
void* arg;
|
||
int event_mask;
|
||
struct etcp_socket_cbk_entry* next;
|
||
};
|
||
|
||
/**
|
||
* @brief Зарегистрировать instance-level коллбэк событий сокета
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк (сигнатура etcp_socket_cbk_fn)
|
||
* @param arg Пользовательский аргумент
|
||
* @param event_mask Битовая маска событий (ETCP_SOCKET_EVENT_ADDR_CHANGED / STATUS_CHANGED)
|
||
*/
|
||
void etcp_add_socket_cbk(struct UTUN_INSTANCE* inst, etcp_socket_cbk_fn fn, void* arg, int event_mask);
|
||
|
||
/**
|
||
* @brief Удалить instance-level коллбэк событий сокета
|
||
*
|
||
* @param inst UTUN instance
|
||
* @param fn Коллбэк
|
||
* @param arg Пользовательский аргумент
|
||
*/
|
||
void etcp_remove_socket_cbk(struct UTUN_INSTANCE* inst, etcp_socket_cbk_fn fn, void* arg);
|
||
|
||
/**
|
||
* @brief Внутренняя функция: рассылка события сокета
|
||
*
|
||
* @param sock Сокет, у которого произошло событие
|
||
* @param event Битовая маска события (ETCP_SOCKET_EVENT_*)
|
||
*
|
||
* @note Вызываются коллбэки, у которых event_mask пересекается с event
|
||
*/
|
||
void etcp_socket_cbk_fire(struct ETCP_SOCKET* sock, int event);
|
||
|
||
|
||
/**
|
||
* @brief Внутренняя функция etcp: Коллбэк для очередей output ll_queue normalizer
|
||
*
|
||
* Собирает пакеты из всех подключений и отправляет в bind->cbk
|
||
* по ID (первый байт кодограммы).
|
||
*
|
||
* @param queue Очередь из которой получен пакет
|
||
* @param arg Аргумент - указатель на ETCP_CONN
|
||
*
|
||
* @note Этот коллбэк устанавливается автоматически при создании
|
||
* ETCP подключения через pn_init()
|
||
*/
|
||
void etcp_int_recv(struct ll_queue* queue, void* arg);
|
||
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
#endif // ETCP_API_H
|