radio: PTT-радио в каналах (src/radio + chatgui + chatgui-android)
Новый модуль src/radio (radio/radio_audio/radio_headless) — push-to-talk радио поверх topo-групп, доставка аудио в канал, headless-режим и тесты. Интеграция в chatgui (ptt_hotkey_manager, radio_audio_engine, радионастройки, вызовы) и chatgui-android (RadioAudioEngine, RadioOverlayService, UI).
This commit is contained in:
1 parent
398078a28f
commit
b96ed8bd00
69 files changed
+5372
-51
No files matched your search
@@ -27,6 +27,7 @@
|
||||
#include "topo_group_connect.h"
|
||||
#include "topo_recovery.h"
|
||||
#include "broadcast.h"
|
||||
#include "../radio/radio.h"
|
||||
#include "../chat/chat_setting.h"
|
||||
|
||||
#define TOPO_GROUP_UTUN 0x8000000000000000ULL
|
||||
@@ -355,6 +356,7 @@ static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint
|
||||
group->conn_mgr = conn_mgr_init(group);
|
||||
|
||||
broadcast_init(group);
|
||||
radio_init(group);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "TOPO group created: group_id=%016llx", (unsigned long long)group_id);
|
||||
return group;
|
||||
@@ -374,6 +376,9 @@ static void topo_group_destroy(struct TOPO_GROUP* group) {
|
||||
broadcast_destroy(group);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[GRP_DESTROY] 2b broadcast_destroy done");
|
||||
|
||||
radio_destroy(group);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[GRP_DESTROY] 2c radio_destroy done");
|
||||
|
||||
struct ll_entry* e;
|
||||
while ((e = queue_data_get(group->senders_list)) != NULL) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e;
|
||||
@@ -628,6 +633,48 @@ void topo_group_set_nat_check_local(struct TOPO_GROUP* group, int allow) {
|
||||
nat_detection_set_allow_local(group->instance->nat_det, allow);
|
||||
}
|
||||
|
||||
/* ── Рация (PTT walkie-talkie, per-CHAT-группа) ── */
|
||||
|
||||
void topo_group_set_radio(struct TOPO_GROUP* group, int on) {
|
||||
if (!group || !group->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "set_radio: invalid args"); return; }
|
||||
if (group->group_type != TOPO_GROUP_TYPE_CHAT) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "set_radio: only CHAT groups support radio grp=%016llx type=%u",
|
||||
(unsigned long long)group->group_id, group->group_type);
|
||||
return;
|
||||
}
|
||||
uint8_t v = on ? 1 : 0;
|
||||
if (group->radio_active == v) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "set_radio: unchanged (%u) grp=%016llx ch=%s", v,
|
||||
(unsigned long long)group->group_id, group->channel_id);
|
||||
return;
|
||||
}
|
||||
group->radio_active = v;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "set_radio: %s grp=%016llx ch=%s",
|
||||
v ? "ON" : "OFF", (unsigned long long)group->group_id, group->channel_id);
|
||||
topo_group_update_my_nodeinfo(group->instance, group);
|
||||
if (group->senders_list) {
|
||||
struct ll_entry* se = group->senders_list->head;
|
||||
while (se) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data;
|
||||
if (item && item->conn && group->local_node)
|
||||
topo_group_send_nodeinfo(group, group->local_node, item->conn, 0);
|
||||
se = se->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int topo_group_radio_subscribers(struct TOPO_GROUP* group) {
|
||||
if (!group || !group->nodes) return 0;
|
||||
int n = 0;
|
||||
struct ll_entry* e = group->nodes->head;
|
||||
while (e) {
|
||||
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
|
||||
if (nq->radio && nq->node_id != group->instance->node_id) n++;
|
||||
e = e->next;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Устанавливает колбэк сохранения узла в БД (используется chatgui/member_sync). */
|
||||
void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updated_fn fn) {
|
||||
if (groups) groups->node_updated_cb = fn;
|
||||
@@ -981,6 +1028,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
|
||||
(unsigned long long)node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id);
|
||||
|
||||
nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver;
|
||||
nodeinfo1->radio = (ni->flags & TOPO_FLAG_RADIO) ? 1 : 0;
|
||||
|
||||
uint64_t hop_list[MAX_HOPS];
|
||||
if (new_hop_count > 0) memcpy(hop_list, new_hop_list, new_hop_count * 8);
|
||||
|
||||
@@ -51,6 +51,7 @@ struct CONN_MGR;
|
||||
struct TOPO_RECOVERY_CTX;
|
||||
struct TOPO_GROUP_CONNECT;
|
||||
struct broadcast_ctx;
|
||||
struct radio_ctx;
|
||||
|
||||
/** Callback when a node's info (pubkeys, addresses) is persisted in the DB */
|
||||
typedef void (*topo_node_updated_fn)(struct UTUN_INSTANCE* inst, uint64_t node_id,
|
||||
@@ -191,6 +192,8 @@ struct TOPO_GROUP {
|
||||
struct TOPO_RECOVERY_CTX* recovery_list; // список активных процедур восстановления
|
||||
struct TOPO_GROUP_CONNECT* connect; // авто-подключение к узлам группы (CHAT)
|
||||
struct broadcast_ctx* broadcast; // broadcast protocol per-group context
|
||||
struct radio_ctx* radio; // radio (PTT walkie-talkie) per-group context
|
||||
uint8_t radio_active; // 1 = я слушаю рацию этого канала (TOPO_FLAG_RADIO в NODEINFO)
|
||||
struct topo_node_cbk_entry* node_cbks; // цепочка подписчиков на события узлов
|
||||
};
|
||||
|
||||
@@ -346,7 +349,16 @@ void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updat
|
||||
/** Разрешить/запретить NAT check для локальных подсетей (тестовый хелпер, делегат в nat_detection) */
|
||||
void topo_group_set_nat_check_local(struct TOPO_GROUP* group, int allow);
|
||||
|
||||
/* ── BGP node event callbacks ── */
|
||||
/* ── Рация (PTT walkie-talkie, per-CHAT-группа) ── */
|
||||
|
||||
/* Включить/выключить прослушивание рации канала: выставляет local_node->radio и
|
||||
* рассылает NODEINFO (TOPO_FLAG_RADIO) через BGP. Работает только для CHAT-групп. */
|
||||
void topo_group_set_radio(struct TOPO_GROUP* group, int on);
|
||||
|
||||
/* Число узлов группы, слушающих рацию (radio==1), без учёта себя. */
|
||||
int topo_group_radio_subscribers(struct TOPO_GROUP* group);
|
||||
|
||||
/* BGP node event callbacks ── */
|
||||
void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg);
|
||||
void topo_group_remove_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* topo_group_connect.c — авто-подключение к узлам группы (бесконечный цикл до успеха)
|
||||
* topo_group_connect.c — авто-подключение к узлам группы (бесконечный цикл до цели)
|
||||
*
|
||||
* Phase 1: одновременный запуск node_conn_direct_open для пиров с connected=1.
|
||||
* Таймаут = TGC_DIRECT_TIMEOUT_MS. Если хоть один подключился → done.
|
||||
* Таймаут = TGC_DIRECT_TIMEOUT_MS. Если цель достигнута → done.
|
||||
*
|
||||
* Phase 2: последовательный перебор (supernode, затем public/EIM адреса).
|
||||
* После каждого TIMEOUT — пауза перед следующей попыткой.
|
||||
@@ -12,9 +12,15 @@
|
||||
*
|
||||
* После исчерпания Phase 3 → пауза → cycle_restart → Phase 1 (бесконечно).
|
||||
*
|
||||
* Цель (tgc_goal_reached): подключены хотя бы к одному суперузлу (node_type==4)
|
||||
* ЛИБО к 3+ не-мобильным клиентам (device_type != MOBILE, не суперузлам).
|
||||
* Пока цель не достигнута, успешное подключение НЕ останавливает цикл —
|
||||
* продолжается перебор следующих кандидатов.
|
||||
*
|
||||
* Пауза: активный режим — 1с; фоновый (Android standby) — standby_wait
|
||||
* (burst → сразу, sleep → до следующего burst).
|
||||
* При DOWN (active_conn_count → 0) — немедленный cycle_restart.
|
||||
* При DOWN, если цель перестала выполняться — немедленный cycle_restart
|
||||
* (уже установленные соединения не трогаются).
|
||||
* При повторном topo_group_connect_init — destroy + fresh init.
|
||||
*
|
||||
* Счётчик active_conn_count — число уникальных peer-узлов с реальными соединениями.
|
||||
@@ -33,6 +39,7 @@
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "../chat/chat_event.h"
|
||||
#ifdef UTUN_HAVE_STANDBY
|
||||
#include "standby.h"
|
||||
@@ -48,6 +55,8 @@
|
||||
#define TGC_PHASE_PAUSE 4
|
||||
#define TGC_MAX_HANDLES 128
|
||||
#define TGC_PAUSE_ACTIVE_TB 10000 /* 1s пауза в активном режиме */
|
||||
#define TGC_GOAL_SUPERNODES 1 /* цель: подключены хотя бы к одному суперузлу */
|
||||
#define TGC_GOAL_NONMOBILE 3 /* цель: подключены к 3+ не-мобильным клиентам (не суперузлам) */
|
||||
|
||||
struct TOPO_GROUP_CONNECT {
|
||||
struct TOPO_GROUP* group;
|
||||
@@ -72,6 +81,7 @@ static void tgc_phase3_try_next(struct TOPO_GROUP_CONNECT* gc);
|
||||
static void tgc_phase1_timeout(void* arg);
|
||||
static void tgc_cycle_restart(struct TOPO_GROUP_CONNECT* gc);
|
||||
static void tgc_start_pause(struct TOPO_GROUP_CONNECT* gc, void (*cb)(void*), const char* label);
|
||||
static int tgc_goal_reached(struct TOPO_GROUP_CONNECT* gc);
|
||||
|
||||
static void tgc_callback(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg);
|
||||
|
||||
@@ -97,6 +107,58 @@ static void tgc_notify_connecting(struct TOPO_GROUP_CONNECT* gc, const uint64_t*
|
||||
TGC_ID, gc->group->channel_id, count);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
* Условие достижения цели авто-подключения
|
||||
* ══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* Тип устройства пира из handshake (peer_device_type на первом UP-линке). */
|
||||
static uint8_t tgc_peer_device_type(struct ETCP_CONN* conn) {
|
||||
if (!conn || !conn->links) return CLIENT_TYPE_SERVER;
|
||||
return conn->links->peer_device_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Достигнута ли цель подключения к группе:
|
||||
* - подключены хотя бы к одному суперузлу (peers node_type==4), ЛИБО
|
||||
* - подключены к 3+ не-мобильным клиентам (device_type != MOBILE и не суперузел).
|
||||
*
|
||||
* Сканирует senders_list (активные BGP-пиры) с дедупом по node_id.
|
||||
* Состояние не хранится — пересчитывается на лету в редких точках принятия
|
||||
* решения (таймаут фазы, каждый успех, каждый down).
|
||||
*/
|
||||
static int tgc_goal_reached(struct TOPO_GROUP_CONNECT* gc) {
|
||||
if (!gc || !gc->group || !gc->group->senders_list) return 0;
|
||||
struct TOPO_GROUP* group = gc->group;
|
||||
sqlite3* db = group->instance->topo_sqlite_db;
|
||||
|
||||
uint64_t seen[TGC_MAX_HANDLES]; int seen_count = 0;
|
||||
int supernode_count = 0, nonmobile_count = 0;
|
||||
|
||||
struct ll_entry* e = group->senders_list->head;
|
||||
while (e) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data;
|
||||
struct ETCP_CONN* conn = item ? item->conn : NULL;
|
||||
if (conn) {
|
||||
int dup = 0;
|
||||
for (int i = 0; i < seen_count; i++) if (seen[i] == conn->peer_node_id) { dup = 1; break; }
|
||||
if (!dup && seen_count < TGC_MAX_HANDLES) {
|
||||
seen[seen_count++] = conn->peer_node_id;
|
||||
int is_super = db && topo_node_sqlite_get_node_type(db, group->channel_id, conn->peer_node_id) == 4;
|
||||
int is_mobile = tgc_peer_device_type(conn) == CLIENT_TYPE_MOBILE;
|
||||
if (is_super) supernode_count++;
|
||||
else if (!is_mobile) nonmobile_count++;
|
||||
}
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
int reached = (supernode_count >= TGC_GOAL_SUPERNODES) || (nonmobile_count >= TGC_GOAL_NONMOBILE);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "%s: goal ch=%s super=%d/%d nonmobile=%d/%d → %s",
|
||||
TGC_ID, group->channel_id, supernode_count, TGC_GOAL_SUPERNODES,
|
||||
nonmobile_count, TGC_GOAL_NONMOBILE, reached ? "reached" : "not reached");
|
||||
return reached;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
* Жизненный цикл
|
||||
* ══════════════════════════════════════════════════════════════════════ */
|
||||
@@ -259,8 +321,8 @@ void topo_group_connect_on_down(struct TOPO_GROUP* group, struct ETCP_CONN* conn
|
||||
if (db) topo_node_sqlite_set_connected(db, group->channel_id, peer, 0);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: DOWN peer=0x%016llx ch=%s active=%d",
|
||||
TGC_ID, (unsigned long long)peer, group->channel_id, gc->active_conn_count);
|
||||
if (gc->active_conn_count == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: DOWN no connections — cycle restart ch=%s", TGC_ID, group->channel_id);
|
||||
if (!tgc_goal_reached(gc)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: DOWN goal not reached — cycle restart ch=%s", TGC_ID, group->channel_id);
|
||||
tgc_cycle_restart(gc);
|
||||
}
|
||||
}
|
||||
@@ -272,8 +334,8 @@ void topo_group_connect_on_down(struct TOPO_GROUP* group, struct ETCP_CONN* conn
|
||||
/**
|
||||
* Единый коллбэк на результат каждой попытки подключения (успех/таймаут/обрыв).
|
||||
* В Phase 1 — просто считает результаты (итог подводит tgc_phase1_timeout).
|
||||
* В Phase 2/3 — успех завершает цикл; провал закрывает handle (чтобы не оставить
|
||||
* залипшее состояние) и ставит паузу перед следующей попыткой.
|
||||
* В Phase 2/3 — успех проверяет цель: достигнута → done, иначе следующий кандидат;
|
||||
* провал закрывает handle (чтобы не оставить залипшее состояние) и ставит паузу.
|
||||
*/
|
||||
/* Передача владения: после topo_group_new_conn (который взял свой NCD-handle)
|
||||
* закрываем connect-фазу handle, чтобы владельцем conn остался один BGP-хэндл
|
||||
@@ -312,9 +374,14 @@ static void tgc_callback(struct NODE_CONN_DIRECT* h, enum ncd_event event, void*
|
||||
break;
|
||||
case TGC_PHASE_TWO:
|
||||
if (ok) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase2 connected to 0x%016llx — done", TGC_ID, (unsigned long long)node_id);
|
||||
gc->phase = TGC_PHASE_DONE;
|
||||
tgc_notify_connecting(gc, NULL, 0);
|
||||
if (tgc_goal_reached(gc)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase2 connected to 0x%016llx — goal reached, done", TGC_ID, (unsigned long long)node_id);
|
||||
gc->phase = TGC_PHASE_DONE;
|
||||
tgc_notify_connecting(gc, NULL, 0);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase2 connected to 0x%016llx — goal not reached, try next", TGC_ID, (unsigned long long)node_id);
|
||||
tgc_phase2_try_next(gc);
|
||||
}
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase2 0x%016llx FAIL — pause then try next", TGC_ID, (unsigned long long)node_id);
|
||||
if (gc->cur_handle) { node_conn_direct_close(gc->cur_handle); gc->cur_handle = NULL; }
|
||||
@@ -323,9 +390,14 @@ static void tgc_callback(struct NODE_CONN_DIRECT* h, enum ncd_event event, void*
|
||||
break;
|
||||
case TGC_PHASE_THREE:
|
||||
if (ok) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase3 connected to 0x%016llx — done", TGC_ID, (unsigned long long)node_id);
|
||||
gc->phase = TGC_PHASE_DONE;
|
||||
tgc_notify_connecting(gc, NULL, 0);
|
||||
if (tgc_goal_reached(gc)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase3 connected to 0x%016llx — goal reached, done", TGC_ID, (unsigned long long)node_id);
|
||||
gc->phase = TGC_PHASE_DONE;
|
||||
tgc_notify_connecting(gc, NULL, 0);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase3 connected to 0x%016llx — goal not reached, try next", TGC_ID, (unsigned long long)node_id);
|
||||
tgc_phase3_try_next(gc);
|
||||
}
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase3 0x%016llx FAIL — pause then try next", TGC_ID, (unsigned long long)node_id);
|
||||
if (gc->cur_handle) { node_conn_direct_close(gc->cur_handle); gc->cur_handle = NULL; }
|
||||
@@ -368,8 +440,8 @@ static void tgc_start_pause(struct TOPO_GROUP_CONNECT* gc, void (*cb)(void*), co
|
||||
|
||||
/**
|
||||
* Таймаут параллельных попыток Phase 1 (2 секунды). Закрывает неудачные
|
||||
* handles и сбрасывает connected в БД. Если хоть один узел подключился —
|
||||
* цикл завершён; иначе переходим к Phase 2.
|
||||
* handles и сбрасывает connected в БД. Если цель достигнута — цикл завершён;
|
||||
* иначе переходим к Phase 2 (дополнительные попытки).
|
||||
*/
|
||||
static void tgc_phase1_timeout(void* arg) {
|
||||
struct TOPO_GROUP_CONNECT* gc = (struct TOPO_GROUP_CONNECT*)arg;
|
||||
@@ -400,12 +472,13 @@ static void tgc_phase1_timeout(void* arg) {
|
||||
}
|
||||
gc->handle_count = remaining;
|
||||
|
||||
if (gc->connected_count > 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase1 done — %d connected, skipping Phase 2", TGC_ID, gc->connected_count);
|
||||
if (tgc_goal_reached(gc)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase1 done — goal reached (%d connected), skipping Phase 2", TGC_ID, gc->connected_count);
|
||||
gc->phase = TGC_PHASE_DONE;
|
||||
tgc_notify_connecting(gc, NULL, 0);
|
||||
return;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s: Phase1 done — goal not reached (%d connected) → Phase 2", TGC_ID, gc->connected_count);
|
||||
gc->phase = TGC_PHASE_TWO;
|
||||
tgc_phase2_try_next(gc);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
/**
|
||||
* @file topo_group_connect.h
|
||||
* @brief Авто-подключение к узлам группы — бесконечный цикл до успеха.
|
||||
* @brief Авто-подключение к узлам группы — бесконечный цикл до цели.
|
||||
*
|
||||
* При старте CHAT-группы (topo_group_connect_init):
|
||||
* Phase 1 — параллельный запуск ко всем пирам с connected=1 (таймаут 2с).
|
||||
* Если хоть один подключился → done.
|
||||
* Если цель достигнута → done.
|
||||
* Phase 2 — последовательный перебор (supernode, затем public/EIM).
|
||||
* После каждого TIMEOUT — пауза перед следующей попыткой.
|
||||
* Phase 3 — последовательный перебор локальных/strict NAT.
|
||||
* После каждого TIMEOUT — пауза перед следующей попыткой.
|
||||
* После исчерпания Phase 3 → пауза → cycle_restart → Phase 1 (бесконечно).
|
||||
*
|
||||
* Цель (tgc_goal_reached): подключены хотя бы к одному суперузлу (node_type==4)
|
||||
* ЛИБО к 3+ не-мобильным клиентам (device_type != MOBILE, не суперузлам).
|
||||
* Пока цель не достигнута, успех НЕ останавливает цикл.
|
||||
*
|
||||
* Пауза: активный режим — 1с; фоновый (Android standby) — standby_wait
|
||||
* (burst → сразу, sleep → до следующего burst).
|
||||
* При DOWN (active_conn_count → 0) — немедленный cycle_restart из любой фазы.
|
||||
* При DOWN, если цель перестала выполняться — немедленный cycle_restart.
|
||||
* При повторном вызове topo_group_connect_init — destroy + fresh init.
|
||||
*
|
||||
* active_conn_count — количество уникальных peer-узлов с живыми соединениями.
|
||||
|
||||
@@ -376,6 +376,7 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq,
|
||||
msg.client_type = ni->client_type;
|
||||
msg.client_activity = ni->client_activity;
|
||||
msg.cumulative_rtt = cumulative_rtt;
|
||||
if (nq->radio) msg.flags |= TOPO_FLAG_RADIO;
|
||||
|
||||
size_t total = TOPOMSG_NODE_HDR_SIZE + (size_t)topo_node_dyn_size(&msg);
|
||||
if (total > out_max) return -1;
|
||||
@@ -853,6 +854,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
|
||||
|| (vc6 != (group->local_node->subnets ? topo_list_count((struct _topo_head*)group->local_node->subnets->v6_subnets) : 0))
|
||||
|| (oni->client_type != instance->client_type)
|
||||
|| (oni->client_activity != instance->client_activity)
|
||||
|| (group->local_node->radio != group->radio_active)
|
||||
|| (memcmp(oni->ed25519_public_key, group->ed25519_public_key, SC_PUBKEY_SIZE) != 0);
|
||||
}
|
||||
}
|
||||
@@ -879,6 +881,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
|
||||
ni->ver = (old_ver % 255) + 1;
|
||||
ni->client_type = instance->client_type;
|
||||
ni->client_activity = instance->client_activity;
|
||||
lq->radio = group->radio_active;
|
||||
memcpy(ni->public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(ni->ed25519_public_key, group->ed25519_public_key, SC_PUBKEY_SIZE);
|
||||
if (name_len) { ni->node_name = u_malloc(name_len + 1); if (ni->node_name) { memcpy(ni->node_name, instance->name, name_len); ni->node_name[name_len] = 0; } }
|
||||
|
||||
@@ -82,6 +82,7 @@ struct UTUN_INSTANCE;
|
||||
#define NCONN_BGP (1 << 2) // узел виден в топологии (BGP)
|
||||
|
||||
#define TOPO_FLAG_SEND_SUBNETS 0x01
|
||||
#define TOPO_FLAG_RADIO 0x02
|
||||
#define TOPO_GROUP_UTUN 0x8000000000000000ULL
|
||||
#define TOPO_GROUP_TYPE_UTUN 1
|
||||
#define TOPO_GROUP_TYPE_CHAT 2
|
||||
@@ -211,6 +212,7 @@ struct TOPO_GROUP_NODE {
|
||||
struct TOPO_CONNECTIVITY connectivity; // результаты ping-проб (interface/nat/real)
|
||||
uint8_t conn_presence; // NODE_CONN_* — какие подключения есть в принципе
|
||||
uint8_t conn_up; // NODE_CONN_* — какие из них подняты
|
||||
uint8_t radio; // 1 = узел слушает рацию канала (TOPO_FLAG_RADIO)
|
||||
struct NODE_CONN_DIRECT* handle; // активный ncd-handle прямого соединения к узлу
|
||||
};
|
||||
|
||||
|
||||
@@ -1237,3 +1237,17 @@ int topo_node_sqlite_get_local_peers(sqlite3* db, const char* channel_id,
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_node_type(sqlite3* db, const char* channel_id, uint64_t node_id) {
|
||||
if (!db || !channel_id || !node_id) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql), "SELECT node_type FROM \"%s\" WHERE node_id=?", peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id);
|
||||
int node_type = -1;
|
||||
if (sqlite3_step(st) == SQLITE_ROW) node_type = sqlite3_column_int(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
return node_type;
|
||||
}
|
||||
@@ -112,4 +112,7 @@ int topo_node_sqlite_get_public_peers(sqlite3* db, const char* channel_id, uint6
|
||||
int topo_node_sqlite_get_local_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count, uint64_t self_node_id);
|
||||
int topo_node_sqlite_get_supernode_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count);
|
||||
|
||||
/* node_type узла в канале (peers_<ch>.node_type); -1 если нет записи/ошибка. */
|
||||
int topo_node_sqlite_get_node_type(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user