Verify idle radio calls have no timers and reject invalid invitation expiry
This commit is contained in:
1 parent
e61dc8cd06
commit
c0626e0387
2 files changed
+49
-7
No files matched your search
@@ -1,6 +1,6 @@
|
||||
/* Короткоживущие приглашения в рацию; один pending, ограниченный кэш результатов.
|
||||
* Повторы нужны также после смены router epoch. Один таймер владеет ближайшим
|
||||
* всеми deadline; callback не хранит указателей на освобождаемые сессии. */
|
||||
* Повторы нужны также после смены router epoch. Один таймер обслуживает ближайший
|
||||
* deadline; callback не хранит указателей на освобождаемые сессии. */
|
||||
#include <string.h>
|
||||
#include "radio_call.h"
|
||||
#include "radio.h"
|
||||
@@ -205,10 +205,10 @@ static void rc_receive(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
struct radio_call_session* s = rc_slot(ctx, id, group, peer, 0);
|
||||
if (command == RC_INVITE && !s) {
|
||||
int64_t expires; memcpy(&expires, b + 9, 8);
|
||||
int64_t remaining = expires - ntp_time_get_us(ctx->inst);
|
||||
if (remaining <= 0 || remaining > 4000000 || !rc_group(ctx->inst, group, peer)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_RADIO, "radio call stale/invalid invite id=%016llx remaining_us=%lld",
|
||||
(unsigned long long)id, (long long)remaining); goto done;
|
||||
int64_t now_us = ntp_time_get_us(ctx->inst);
|
||||
if (expires <= now_us || expires > now_us + 4000000 || !rc_group(ctx->inst, group, peer)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_RADIO, "radio call stale/invalid invite id=%016llx expires_us=%lld now_us=%lld",
|
||||
(unsigned long long)id, (long long)expires, (long long)now_us); goto done;
|
||||
}
|
||||
int busy = radio_call_busy(ctx->inst) || call_busy(ctx->inst);
|
||||
s = rc_slot(ctx, id, group, peer, 1);
|
||||
|
||||
+43
-1
@@ -10,6 +10,8 @@
|
||||
// кадры в порядке seq 0..4 и FIN.
|
||||
// 3. C radio_off (обрезание) → A снова говорит → B получает, C не получает.
|
||||
// 4. Без FIN: C снимает индикацию при radio_off, B — по таймауту источника.
|
||||
// До передачи: вызовы через B, deadline 2с/60с, потери RESULT/ACK, отказ/отмена/занятость,
|
||||
// отсутствие живого BGP-пути, некорректный срок INVITE и отсутствие таймеров рации в покое.
|
||||
|
||||
#include "chat_core.h"
|
||||
#include "chat_sync.h"
|
||||
@@ -132,6 +134,16 @@ static void radio_action(struct UTUN_INSTANCE* inst, uint64_t id, int action, ui
|
||||
radio_call_execute(&req);
|
||||
}
|
||||
|
||||
static int radio_invitation_timers(struct UASYNC* ua) {
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < ua->timeout_heap->size; ++i) {
|
||||
TimeoutEntry* entry = &ua->timeout_heap->heap[i];
|
||||
struct timeout_node* node = entry->data;
|
||||
if (!entry->deleted && node && node->name && !strcmp(node->name, "radio_call")) ++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Проверяем именно UI-события: self, прямой пир и пир за посредником. */
|
||||
static void member_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len) {
|
||||
if (type == CHAT_EVT_RADIO_CALL) {
|
||||
@@ -456,6 +468,27 @@ static void rad_tick(void* arg) {
|
||||
case 0: {
|
||||
uint8_t key[32];
|
||||
if (member_sync_verified_ed25519(A, CH_ID, nc, key) || member_sync_verified_ed25519(C, CH_ID, na, key)) break;
|
||||
if (radio_invitation_timers(t->ua)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "test idle radio invitation service has timers");
|
||||
t->result = 2; uasync_stop(t->ua); return;
|
||||
}
|
||||
/* Уже расшифрованный пакет от проверенного пира: даже INT64_MIN обязан безопасно отбрасываться. */
|
||||
struct ll_entry* expired = ll_alloc_lldgram(ROUTER_SVC_HDR_SIZE + 18);
|
||||
if (!expired) abort();
|
||||
memset(expired->dgram, 0, expired->len);
|
||||
expired->dgram[0] = ETCP_RT_ID_RADIO_CALL;
|
||||
memcpy(expired->dgram + ROUTER_SVC_SRC_OFF, &nc, 8); memcpy(expired->dgram + ROUTER_SVC_DST_OFF, &na, 8);
|
||||
memcpy(expired->dgram + ROUTER_SVC_GROUP_OFF, &gid, 8);
|
||||
expired->dgram[ROUTER_SVC_FLAGS_OFF] = ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED;
|
||||
expired->dgram[ROUTER_SVC_PAYLOAD_OFF] = 1;
|
||||
uint64_t expired_id = 108; int64_t expired_at = INT64_MIN;
|
||||
memcpy(expired->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, &expired_id, 8);
|
||||
memcpy(expired->dgram + ROUTER_SVC_PAYLOAD_OFF + 9, &expired_at, 8);
|
||||
struct ETCP_CONN source = {.instance = A};
|
||||
A->router_bindings.callbacks[ETCP_RT_ID_RADIO_CALL](&source, expired);
|
||||
if (radio_call_busy(A) || g_member_event_bad) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "test malformed invite created a call"); t->result = 2; uasync_stop(t->ua); return;
|
||||
}
|
||||
struct TOPO_GROUP* cg = topo_groups_find(C->topo_groups, gid);
|
||||
struct ETCP_CONN* path = topo_group_find_conn_for_node(cg, na);
|
||||
if (!path) break;
|
||||
@@ -544,7 +577,16 @@ static void rad_tick(void* arg) {
|
||||
t->result = 2; uasync_stop(t->ua); return;
|
||||
}
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "test radio calls passed: 2s/60s deadlines, accept, decline, cancel, busy, lost result/ACK");
|
||||
t->call_started = now; ++t->call_step;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (now - t->call_started >= 5000) {
|
||||
if (radio_invitation_timers(t->ua) || radio_call_busy(A) || radio_call_busy(B) || radio_call_busy(C)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "test finished invitations retain timers or busy state");
|
||||
t->result = 2; uasync_stop(t->ua); return;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "test radio calls passed: 2s/60s, accept/decline/cancel/busy, lost result/ACK, idle has no timer");
|
||||
t->phase = P_RADIO_ON;
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user