From 8883002e3f05541e89b0590d4c393be7cf9cf123 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 13 Oct 2026 01:13:03 +0200 Subject: [PATCH] Distinguish relay-only stream subscriptions in member profiles --- src/stream/stream_member.c | 20 +++++++++++++++++--- src/stream/stream_member.h | 5 ++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/stream/stream_member.c b/src/stream/stream_member.c index 28c0380d..7520146f 100644 --- a/src/stream/stream_member.c +++ b/src/stream/stream_member.c @@ -3,6 +3,7 @@ #include "../../lib/debug_config.h" #include "../../lib/json_flat.h" +#include "../../lib/mem.h" #include "../utun_instance.h" #include "../routing_layer/topo_node_sqlite.h" #include "stream_block.h" @@ -70,9 +71,10 @@ static int member_option(const char *key, const char *value, void *arg) { const char *p = value; while (*p) { if (parse->state.count == STREAM_MEMBER_MAX_SUBSCRIPTIONS) return -1; - struct stream_subscription_key entry; + struct stream_subscription_key entry = {0}; if (member_hex(&p, &entry.source_node_id) != STREAM_OK || *p++ != ':' || member_hex(&p, &entry.source_epoch) != STREAM_OK) return -1; + if (*p == ':') { p++; if (*p++ != 'r') return -1; entry.relay_only = 1; } if (parse->state.count) { const struct stream_subscription_key *previous = &parse->state.subscriptions[parse->state.count - 1]; if (entry.source_node_id < previous->source_node_id @@ -132,8 +134,9 @@ int stream_member_format(const struct stream_member_state *state, char source[MS && entry->source_epoch <= state->subscriptions[i - 1].source_epoch)))) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "member format invalid subscription index=%zu", i); return STREAM_ERR_ARGUMENT; } - int length = snprintf(encoded_subscriptions + used, sizeof(encoded_subscriptions) - used, "%s%016llx:%016llx", - i ? "," : "", (unsigned long long)entry->source_node_id, (unsigned long long)entry->source_epoch); + int length = snprintf(encoded_subscriptions + used, sizeof(encoded_subscriptions) - used, "%s%016llx:%016llx%s", + i ? "," : "", (unsigned long long)entry->source_node_id, (unsigned long long)entry->source_epoch, + entry->relay_only ? ":r" : ""); if (length < 0 || (size_t)length >= sizeof(encoded_subscriptions) - used) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "member subscriptions exceed limit count=%zu", state->count); return STREAM_ERR_ARGUMENT; } @@ -217,3 +220,14 @@ int stream_member_online(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t struct TOPO_GROUP_NODE *node = topo_node_find_by_id(group, member_id); return node && topo_node_best_path(node) != NULL; } + +/* Роль относится к каналу, не к устройству или глобальному media_delivery. */ +int stream_member_supernode(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t member_id) { + struct TOPO_GROUP *group = inst && inst->topo_groups ? topo_groups_find(inst->topo_groups, group_id) : NULL; + if (!group || group->stopping || group->group_type != TOPO_GROUP_TYPE_CHAT) return 0; + struct topo_connect_peer *peers = NULL; size_t count = 0; + if (topo_node_sqlite_get_connect_peers(inst->topo_sqlite_db, group->channel_id, &peers, &count) < 0) return -1; + int supernode = 0; + for (size_t i = 0; i < count; i++) if (peers[i].node_id == member_id) { supernode = peers[i].supernode; break; } + u_free(peers); return supernode; +} diff --git a/src/stream/stream_member.h b/src/stream/stream_member.h index fd46b2c4..6a52a0a5 100644 --- a/src/stream/stream_member.h +++ b/src/stream/stream_member.h @@ -1,6 +1,6 @@ /* Строковые опции подписанного блока мембера, без отдельного протокола объявлений. * stream_source = epoch(16 hex):n:r:RS-size; отсутствие/пустое значение — не публикует. - * stream_subscriptions = source(16 hex):epoch(16 hex),...; сортировка по source/epoch. + * stream_subscriptions = source(16 hex):epoch(16 hex)[:r],...; :r — только помощь, без локального зрителя. * Указанная в stream_source собственная трансляция также должна быть в subscriptions. * load проверяет подпись профиля и принадлежность ключа источника, BGP-доступность — отдельно. * parse/format не меняют выход при ошибке. Все вызовы — один uasync-поток. */ @@ -23,6 +23,7 @@ struct UTUN_INSTANCE; struct stream_subscription_key { uint64_t source_node_id; uint64_t source_epoch; + int relay_only; }; struct stream_member_state { @@ -43,6 +44,8 @@ int stream_member_has_subscription(const struct stream_member_state *state, uint int stream_member_load(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t member_id, struct stream_member_state *state, uint8_t public_key[SC_PUBKEY_SIZE]); int stream_member_online(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t member_id); +/* Канальная роль из принятого блока владельца; -1 — ошибка чтения, 0/1 — отсутствие/наличие роли. */ +int stream_member_supernode(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t member_id); #ifdef __cplusplus }