diff --git a/tests/test_dm_e2e.c b/tests/test_dm_e2e.c index cd1ef026..2fb5f518 100644 --- a/tests/test_dm_e2e.c +++ b/tests/test_dm_e2e.c @@ -20,6 +20,8 @@ #include "../routing_layer/topo_node.h" #include "../utun_instance.h" #include "../transport_layer/etcp.h" +#include "../transport_layer/etcp_api.h" +#include "../routing_layer/etcp_router.h" #include "../transport_layer/secure_channel.h" #include "../ntp_time.h" #include "../lib/u_async.h" @@ -250,8 +252,29 @@ static size_t test_body(struct UTUN_INSTANCE* author, uint64_t seq, const char* return 39 + enc_len + 64; } +/* Вход сервиса после аутентификации router; отдельно от реального UDP-сценария ниже. */ +static int mailbox_packet(struct UTUN_INSTANCE* dst, uint64_t source, uint8_t cmd, const uint8_t* body, size_t len) { + struct ETCP_CONN* conn = instance_find_conn(dst, dst->node_id == g_sh.nid[IDX_C] ? g_sh.nid[IDX_A] : g_sh.nid[IDX_C]); + if (!conn || !dst->router_bindings.callbacks[ETCP_RT_ID_DM_MAILBOX]) return -1; + struct ll_entry* e = queue_entry_new(0); + if (!e) return -1; + e->len = (uint16_t)(ROUTER_SVC_PAYLOAD_OFF + 1 + len); + e->dgram = u_calloc(1, e->len); + if (!e->dgram) { queue_entry_free(e); return -1; } + uint64_t gid = strtoull(CH_ID, NULL, 10); + e->dgram[0] = ETCP_RT_ID_DM_MAILBOX; + memcpy(e->dgram + ROUTER_SVC_SRC_OFF, &source, 8); + memcpy(e->dgram + ROUTER_SVC_DST_OFF, &dst->node_id, 8); + memcpy(e->dgram + ROUTER_SVC_GROUP_OFF, &gid, 8); + e->dgram[ROUTER_SVC_FLAGS_OFF] = ROUTER_FLAG_SIGNED; + e->dgram[ROUTER_SVC_PAYLOAD_OFF] = cmd; + memcpy(e->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, body, len); + dst->router_bindings.callbacks[ETCP_RT_ID_DM_MAILBOX](conn, e); + return 0; +} + /* Ошибочный commit, tamper, точные повторы и ACK чужого тела не теряют pending. */ -static int protocol_checks(struct UTUN_INSTANCE* A, struct UTUN_INSTANCE* B) { +static int protocol_checks(struct UTUN_INSTANCE* A, struct UTUN_INSTANCE* B, struct UTUN_INSTANCE* C) { uint8_t body[512], receipt[DM_RECEIPT_SIZE], again[DM_RECEIPT_SIZE]; size_t len = test_body(A, 100, "higher", body); if (!len || dm_accept_message(B, body, len, receipt) != 0 || @@ -291,6 +314,25 @@ static int protocol_checks(struct UTUN_INSTANCE* A, struct UTUN_INSTANCE* B) { int seq_unchanged = sqlite3_step(st) == SQLITE_ROW && sqlite3_column_int64(st, 0) == 1; sqlite3_finalize(st); if (!seq_unchanged) return -1; + /* Суперузел хранит только проверенные тела и удаляет только точный подтверждённый экземпляр. */ + len = test_body(A, 97, "mailbox-exact-receipt", body); + if (!len) return -1; + uint8_t put[8 + 512]; + memcpy(put, &B->node_id, 8); + memcpy(put + 8, body, len); + if (mailbox_packet(A, A->node_id, 1, put, len + 8) != 0 || dm_mail_count(A) != 0) return -1; /* A без роли. */ + put[8 + len - 1] ^= 1; + if (mailbox_packet(C, A->node_id, 1, put, len + 8) != 0 || dm_mail_count(C) != 0) return -1; + put[8 + len - 1] ^= 1; + if (mailbox_packet(C, A->node_id, 1, put, len + 8) != 0 || dm_mail_count(C) != 1) return -1; + if (dm_accept_message(B, body, len, receipt) != 0) return -1; + memcpy(again, receipt, sizeof(again)); + again[40] ^= 1; + if (sc_ed25519_sign(B->my_ed25519_privkey, again, 72, again + 72) != SC_OK) return -1; + if (mailbox_packet(C, B->node_id, 3, again, sizeof(again)) != 0 || dm_mail_count(C) != 1) return -1; + if (mailbox_packet(C, C->node_id, 3, receipt, sizeof(receipt)) != 0 || dm_mail_count(C) != 1) return -1; + if (mailbox_packet(C, B->node_id, 3, receipt, sizeof(receipt)) != 0 || dm_mail_count(C) != 0) return -1; + if (mailbox_packet(C, A->node_id, 1, put, len + 8) != 0 || dm_mail_count(C) != 0) return -1; /* Поздний PUT. */ DEBUG_INFO(DEBUG_CATEGORY_DM, "protocol checks passed: reorder, dedup, tamper, DB rollback, exact receipt"); return 0; } @@ -394,7 +436,7 @@ static void dm_tick(void* arg) { break; case P_SEND1: if (dm_start(A, nb, g_sh.x_pub[IDX_B], g_sh.ed_pub[IDX_B], "B", CH_ID) != 0 - || dm_send(A, g_sh.conv_id, "text", (const uint8_t*)"hello1", 6) != 0 || protocol_checks(A, B) != 0) { + || dm_send(A, g_sh.conv_id, "text", (const uint8_t*)"hello1", 6) != 0 || protocol_checks(A, B, C) != 0) { fprintf(stderr, "A: dm_start/send hello1 failed\n"); t->result = 2; } else {