diff --git a/src/call/call.c b/src/call/call.c index 052ba7a5e..43149d22c 100644 --- a/src/call/call.c +++ b/src/call/call.c @@ -161,7 +161,7 @@ static int call_send_invite(struct call_ctx* ctx, struct call_session* s) { int call_resolve_targets(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, int devices, uint64_t* out, unsigned capacity) { struct TOPO_GROUP* g = inst && inst->topo_groups ? topo_groups_find(inst->topo_groups, group) : NULL; - if (!g || g->group_type != TOPO_GROUP_TYPE_CHAT || !peer || !out || !capacity) { + if (!g || !topo_group_chat_allowed(inst, group, peer) || !peer || !out || !capacity) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "call target invalid group=%llx peer=%llx", (unsigned long long)group, (unsigned long long)peer); return -1; } @@ -424,6 +424,11 @@ static void call_target_tick(void* arg) { for (unsigned i = 0; i < s->target_count; ++i) { struct call_target* t = &s->targets[i]; if (!t->end_subcmd || t->ack) continue; + if (!topo_group_chat_allowed(s->ctx->inst, s->group_id, t->peer)) { + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call target retry cancelled after revoke id=%llx peer=%llx", + (unsigned long long)s->call_id, (unsigned long long)t->peer); + t->ack = 1; continue; + } if (now - t->ended_tb >= CALL_END_TIMEOUT_TB) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "call target end timeout id=%llx peer=%llx", (unsigned long long)s->call_id, (unsigned long long)t->peer); @@ -516,6 +521,30 @@ static void call_end(struct call_ctx* ctx, struct call_session* s, uint8_t reaso call_session_remove_and_free(ctx, s); } +void call_revalidate_members(struct UTUN_INSTANCE* inst, uint64_t group) { + struct call_ctx* ctx = call_of(inst); + struct ll_entry* e = ctx && ctx->sessions ? ctx->sessions->head : NULL; + while (e) { + struct call_session* s = (struct call_session*)e->data; + e = e->next; + if (s->group_id != group) continue; + int allowed = topo_group_chat_allowed(inst, group, s->peer_node_id); + for (unsigned i = 0; i < s->target_count; ++i) { + struct call_target* t = &s->targets[i]; + if (topo_group_chat_allowed(inst, group, t->peer)) continue; + t->ack = 1; + if (!s->selected && t->state != CALL_ST_ENDED) allowed = 0; + } + if (allowed) continue; + DEBUG_INFO(DEBUG_CATEGORY_CALL, "call access revoked id=%llx group=%llx peer=%llx state=%s; stopping media and retries", + (unsigned long long)s->call_id, (unsigned long long)group, + (unsigned long long)s->peer_node_id, call_state_name(s->state)); + if (s->cm_handle) { conn_mgr_close(s->cm_handle); s->cm_handle = NULL; } + s->end_subcmd = 0; + call_end(ctx, s, CALL_REASON_CONNECT_FAIL, 0); + } +} + /* ─────────────────── conn_mgr ─────────────────── */ static void call_traffic_timeout_cb(void* arg); @@ -1114,8 +1143,10 @@ int call_init(struct UTUN_INSTANCE* inst) { ctx->initialized = 1; if (etcp_router_require_crypto(inst, ETCP_RT_ID_CALL, ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT) + || etcp_router_set_authorize(inst, ETCP_RT_ID_CALL, topo_group_chat_allowed) || etcp_router_bind(inst, ETCP_RT_ID_CALL_AUDIO, call_etcp_recv) || etcp_router_require_crypto(inst, ETCP_RT_ID_CALL_AUDIO, ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT) + || etcp_router_set_authorize(inst, ETCP_RT_ID_CALL_AUDIO, topo_group_chat_allowed) || call_video_transport_bind(inst)) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call media channel bind failed"); call_destroy(inst); return -1; @@ -1165,8 +1196,9 @@ int call_busy(struct UTUN_INSTANCE* inst) { /* Только uasync: проверка доступности перед созданием сессии; очереди групп не межпоточные. */ static int call_is_online(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t peer_node_id) { if (!inst || !ch_id || !ch_id[0] || !inst->topo_groups) return 0; - if (peer_node_id == inst->node_id) return 1; uint64_t group_id = strtoull(ch_id, NULL, 10); + if (!topo_group_chat_allowed(inst, group_id, peer_node_id)) return 0; + if (peer_node_id == inst->node_id) return 1; struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, group_id); if (!g) return 0; return topo_node_find_by_id(g, peer_node_id) != NULL; diff --git a/src/call/call.h b/src/call/call.h index 5b8657c74..472e49d00 100644 --- a/src/call/call.h +++ b/src/call/call.h @@ -7,6 +7,9 @@ // CHAT-группы или из DM (group_id — группа маршрутизации, как в dm_core). // Сигналинг, аудио и видео идут через etcp_router с обязательными ROUTE_CRYPTO_SIGN|ENCRYPT; соединение // оптимизируется через параллельные пробы conn_mgr (DIRECT/REVERSE/INDIRECT). +// Оба узла должны оставаться допущенными участниками группы доставки. При отзыве доступа +// или остановке группы звонок завершается, очереди медиа очищаются. Группа активного звонка +// не меняется; новый звонок из DM заново выбирает доступную общую группу через dm_call_route. // // Все функции, кроме явно отмеченных, — в uasync-потоке. GUI/headless вызывают через trampoline // (gui_bridge_post_uasync_fn / uasync_post). @@ -34,6 +37,10 @@ int call_init(struct UTUN_INSTANCE* inst); * Регистрация call_audio_ops у ядра сохраняется, DSP и устройства frontend здесь не освобождаются. */ void call_destroy(struct UTUN_INSTANCE* inst); +/* uasync: после изменения участников/остановки CHAT-группы завершить затронутые звонки + * с CONNECT_FAIL, отменить их медиа/CM/повторы отозванным пирам. Остальные группы не затрагивает. */ +void call_revalidate_members(struct UTUN_INSTANCE* inst, uint64_t group); + // ── сигналинг (uasync-поток) ── /* Caller: ch_id — группа либо "dm:" (BGP-группа выбирается при запуске).