diff --git a/src/media_delivery/media_delivery.h b/src/media_delivery/media_delivery.h index b4e62f278..cc162134c 100644 --- a/src/media_delivery/media_delivery.h +++ b/src/media_delivery/media_delivery.h @@ -70,9 +70,8 @@ struct md_file_load { * Верхняя граница выдачи — sqlite_sequence, включая удалённые ID и промежутки других групп. */ struct media_served_node { - struct ll_entry ll; // индекс по node_id (8 байт) - uint64_t node_id; - uint64_t group_id; + uint64_t node_id; // начало составного ключа (узел, группа), 16 байт + uint64_t group_id; // CHAT-группа регистрации int64_t joined_at; // timestamp регистрации }; diff --git a/tests/test_etcp_router_unit.c b/tests/test_etcp_router_unit.c index f5bf672e3..2ecb426b5 100644 --- a/tests/test_etcp_router_unit.c +++ b/tests/test_etcp_router_unit.c @@ -61,6 +61,7 @@ static void rx_reset(uint8_t marker) { // ======================== Inject helper ======================== static struct ETCP_CONN fake_conn; static uint8_t g_sign_ed25519_privkey[SC_PRIVKEY_SIZE]; +static uint64_t inject_relay; static void handshake_packet(etcp_recv_fn cb, struct UTUN_INSTANCE* inst, struct SVC_ROUTE_HDR* h) { int sign = (inst->router_bindings.required_crypto[h->svc_id] & ROUTER_FLAG_SIGNED) != 0; @@ -76,10 +77,12 @@ static void handshake_packet(etcp_recv_fn cb, struct UTUN_INSTANCE* inst, struct static void inject_wire(struct ETCP_CONN* conn, struct ll_entry* e) { size_t len = e->len; uint64_t src = ((struct SVC_ROUTE_HDR*)e->dgram)->src_node_id; - uint8_t* wire = u_malloc(len + 9); if (!wire) abort(); - memcpy(wire, e->dgram, len); memcpy(wire + len, &src, 8); wire[len + 8] = 1; - queue_dgram_free(e); e->dgram = wire; e->len = len + 9; - conn->peer_node_id = src; + size_t size = len + (inject_relay ? 17 : 9); + uint8_t* wire = u_malloc(size); if (!wire) abort(); + memcpy(wire, e->dgram, len); memcpy(wire + len, &src, 8); wire[size - 1] = inject_relay ? 2 : 1; + if (inject_relay) memcpy(wire + len + 8, &inject_relay, 8); + queue_dgram_free(e); e->dgram = wire; e->len = size; + conn->peer_node_id = inject_relay ? inject_relay : src; conn->instance->api_bindings.callbacks[ETCP_ID_SVC_ROUTE](conn, e); } @@ -1479,14 +1482,19 @@ static int test_authenticated_access(void) { TESTASSERT(!c->closed && c->peer_reset_id == SIGNER_NODE && !c->tx_acked && !c->peer_receive_blocked); } TESTASSERT(g_notify_count == before); + // Допуск конечного автора сохраняется через посредника, которого политика сервиса не допускает. + uint8_t byte = 0x61; + inject_relay = TEST_REMOTE_NODE; + inject_signed(recv_cb, &inst, SIGNER_NODE, TEST_SVC_ID, 0, &byte, 1, 0, 0); + inject_relay = 0; + TESTASSERT(rx.delivered == 1 && c->rx_seq == 1); rx_reset(0x61); TESTASSERT(!etcp_router_bind(&inst, TEST_SVC_ID2, test_handler)); struct ETCP_ROUTER_CONN* other_group = etcp_router_conn_get(&inst, 222, SIGNER_NODE, TEST_SVC_ID); struct ETCP_ROUTER_CONN* other_service = etcp_router_conn_get(&inst, h.group_id, SIGNER_NODE, TEST_SVC_ID2); TESTASSERT(other_group && other_service); // Готовые пакеты остаются в reorder-очереди до допуска потребителя. receive_capacity = 0; etcp_router_set_receive_ready(&inst, TEST_SVC_ID, receive_ready); - uint8_t byte = 0x61; - for (int i = 0; i < 3; i++) inject_signed(recv_cb, &inst, SIGNER_NODE, TEST_SVC_ID, i, &byte, 1, 0, 0); + for (int i = 0; i < 3; i++) inject_signed(recv_cb, &inst, SIGNER_NODE, TEST_SVC_ID, i + 1, &byte, 1, 0, 0); for (int i = 0; i < 8; i++) uasync_poll(ua, 0); TESTASSERT(c->recv_q->count == 3 && !rx.delivered); // После первой доставки политика меняется: остальные буферизованные пакеты не попадут в сервис. diff --git a/tests/test_media_blocks.c b/tests/test_media_blocks.c index a03480dbe..d954ca2e5 100644 --- a/tests/test_media_blocks.c +++ b/tests/test_media_blocks.c @@ -573,6 +573,29 @@ static void test_membership_revocation(void) { puts("membership revoke: stop relay, reject inactive peers, fail over holder, stop self, preserve neighbour: PASS"); } +static void test_service_group_scope(void) { + reset(); struct UTUN_INSTANCE* inst = instance(SERVER); + uint64_t peer = CLIENT, key[2] = {CLIENT, 222}; + media_test_group(inst, 222, &peer, 1); + struct packet p = {.from = peer, .group = GROUP, .len = sizeof(struct media_pkt_serve_reg)}; + struct media_pkt_serve_reg* reg = (void*)p.data; + reg->subcmd = MEDIA_SUBCMD_SERVE_REG; reg->group_id = 222; + receive(inst, &p); REQUIRE(!inst->md.served_nodes); + reg->group_id = GROUP; receive(inst, &p); + reg->group_id = p.group = 222; receive(inst, &p); + REQUIRE(inst->md.served_nodes->count == 2 && queue_find_data_by_index(inst->md.served_nodes, key)); + struct media_pkt_serve_leave* leave = (void*)p.data; + p.group = GROUP; p.len = sizeof(*leave); leave->subcmd = MEDIA_SUBCMD_SERVE_LEAVE; leave->group_id = 222; + receive(inst, &p); REQUIRE(inst->md.served_nodes->count == 2); + leave->group_id = GROUP; receive(inst, &p); + REQUIRE(inst->md.served_nodes->count == 1 && queue_find_data_by_index(inst->md.served_nodes, key)); + REQUIRE(etcp_router_conn_get(inst, 222, peer, ETCP_RT_ID_MEDIA_DELIVERY)); + REQUIRE(sqlite3_exec(inst->md.db, "UPDATE peers_222 SET admitted=0 WHERE node_id=512", NULL, NULL, NULL) == SQLITE_OK); + etcp_router_revalidate_group(inst, 222); REQUIRE(!inst->md.served_nodes->count); + destroy(inst); + puts("SERVE_REG/LEAVE: signed envelope group, independent registrations and revoke cleanup: PASS"); +} + int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG); @@ -588,7 +611,7 @@ int main(void) { test_identity_and_bounds(); test_rejected_attempts(); test_io_failures(); test_reentrant_cancel(); test_growing_relay(); test_relay_cancel(); test_relay_generation(); test_destinations(); test_registration_failure_and_keys(); test_relay_limit_and_idle(); - test_indexed_source(); test_membership_revocation(); + test_indexed_source(); test_membership_revocation(); test_service_group_scope(); REQUIRE(!rmdir(directory)); uasync_destroy(ua, 0); puts("media block regressions: PASS"); return 0; }